📄 expmed.c
字号:
> GET_MODE_BITSIZE (insn_operand_mode[(int) CODE_FOR_insv][3])))) { 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 || (SLOW_UNALIGNED_ACCESS && GET_MODE_SIZE (bestmode) > align)) 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) /* We can't just change the mode, because this might clobber op0, and we will need the original value of op0 if insv fails. */ xop0 = gen_rtx (SUBREG, maxmode, SUBREG_REG (xop0), SUBREG_WORD (xop0)); 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; /* We have been counting XBITPOS within UNIT. Count instead within the size of the register. */ if (BITS_BIG_ENDIAN && GET_CODE (xop0) != MEM) xbitpos += GET_MODE_BITSIZE (maxmode) - unit; 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); } else if (!CONSTANT_P (value)) /* Parse phase is supposed to make VALUE's data type match that of the component reference, which is a type at least as wide as the field; so VALUE should have a mode that corresponds to that type. */ abort (); } /* If this machine's insv insists on a register, get VALUE1 into a register. */ if (! ((*insn_operand_predicate[(int) CODE_FOR_insv][3]) (value1, maxmode))) value1 = force_reg (maxmode, value1); pat = gen_insv (xop0, GEN_INT (bitsize), GEN_INT (xbitpos), value1); if (pat) emit_insn (pat); else { delete_insns_since (last); store_fixed_bit_field (op0, offset, bitsize, bitpos, value, align); } } else insv_loses:#endif /* Insv is not available; store using shifts and boolean ops. */ store_fixed_bit_field (op0, offset, bitsize, bitpos, value, align); return value;}/* Use shifts and boolean operations to store VALUE into a bit field of width BITSIZE in a memory location specified by OP0 except offset by OFFSET bytes. (OFFSET must be 0 if OP0 is a register.) The field starts at position BITPOS within the byte. (If OP0 is a register, it may be a full word or a narrower mode, but BITPOS still counts within a full word, which is significant on bigendian machines.) STRUCT_ALIGN is the alignment the structure is known to have (in bytes). Note that protect_from_queue has already been done on OP0 and VALUE. */static voidstore_fixed_bit_field (op0, offset, bitsize, bitpos, value, struct_align) register rtx op0; register int offset, bitsize, bitpos; register rtx value; int struct_align;{ register enum machine_mode mode; int total_bits = BITS_PER_WORD; rtx subtarget, temp; int all_zero = 0; int all_one = 0; /* There is a case not handled here: a structure with a known alignment of just a halfword and a field split across two aligned halfwords within the structure. Or likewise a structure with a known alignment of just a byte and a field split across two bytes. Such cases are not supposed to be able to occur. */ if (GET_CODE (op0) == REG || GET_CODE (op0) == SUBREG) { if (offset != 0) abort (); /* Special treatment for a bit field split across two registers. */ if (bitsize + bitpos > BITS_PER_WORD) { store_split_bit_field (op0, bitsize, bitpos, value, BITS_PER_WORD); return; } } 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, struct_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. */ store_split_bit_field (op0, bitsize, bitpos + offset * BITS_PER_UNIT, value, struct_align); return; } 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); /* Now MODE is either some integral mode for a MEM as OP0, or is a full-word for a REG as OP0. TOTAL_BITS corresponds. The bit field is contained entirely within OP0. BITPOS is the starting bit number within OP0. (OP0's mode may actually be narrower than MODE.) */ if (BYTES_BIG_ENDIAN) /* 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; /* 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 HOST_WIDE_INT v = INTVAL (value); if (bitsize < HOST_BITS_PER_WIDE_INT) v &= ((HOST_WIDE_INT) 1 << bitsize) - 1; if (v == 0) all_zero = 1; else if ((bitsize < HOST_BITS_PER_WIDE_INT && v == ((HOST_WIDE_INT) 1 << bitsize) - 1) || (bitsize == HOST_BITS_PER_WIDE_INT && v == -1)) all_one = 1; value = lshift_value (mode, value, bitpos, bitsize); } else { int must_and = (GET_MODE_BITSIZE (GET_MODE (value)) != bitsize && bitpos + bitsize != GET_MODE_BITSIZE (mode)); 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) value = expand_binop (mode, and_optab, value, mask_rtx (mode, 0, bitsize, 0), NULL_RTX, 1, OPTAB_LIB_WIDEN); if (bitpos > 0) value = expand_shift (LSHIFT_EXPR, mode, value, build_int_2 (bitpos, 0), NULL_RTX, 1); } /* Now clear the chosen bits in OP0, except that if VALUE is -1 we need not bother. */ subtarget = (GET_CODE (op0) == REG || ! flag_force_mem) ? op0 : 0; if (! all_one) { temp = expand_binop (mode, and_optab, op0, mask_rtx (mode, bitpos, bitsize, 1), subtarget, 1, OPTAB_LIB_WIDEN); subtarget = temp; } else temp = op0; /* Now logical-or VALUE into OP0, unless it is zero. */ if (! all_zero) temp = expand_binop (mode, ior_optab, temp, value, subtarget, 1, OPTAB_LIB_WIDEN); if (op0 != temp) emit_move_insn (op0, temp);}/* Store a bit field that is split across multiple accessible memory objects. OP0 is the REG, SUBREG or MEM rtx for the first of the objects. BITSIZE is the field width; BITPOS the position of its first bit (within the word). VALUE is the value to store. ALIGN is the known alignment of OP0, measured in bytes. This is also the size of the memory objects to be used. This does not yet handle fields wider than BITS_PER_WORD. */static voidstore_split_bit_field (op0, bitsize, bitpos, value, align) rtx op0; int bitsize, bitpos; rtx value; int align;{ int unit; int bitsdone = 0; /* 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); /* If VALUE is a constant other than a CONST_INT, get it into a register in WORD_MODE. If we can do this using gen_lowpart_common, do so. Note that VALUE might be a floating-point constant. */ if (CONSTANT_P (value) && GET_CODE (value) != CONST_INT) { rtx word = gen_lowpart_common (word_mode, value); if (word && (value != word)) value = word; else value = gen_lowpart_common (word_mode, force_reg (GET_MODE (value) != VOIDmode ? GET_MODE (value) : word_mode, value)); } 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, store_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 (BYTES_BIG_ENDIAN) { int total_bits; /* We must do an endian conversion exactly the same way as it is done in extract_bit_field, so that the two calls to extract_fixed_bit_field will have comparable arguments. */ if (GET_CODE (value) != MEM) total_bits = BITS_PER_WORD; else total_bits = GET_MODE_BITSIZE (GET_MODE (value)); /* Fetch successively less significant portions. */ if (GET_CODE (value) == CONST_INT) part = GEN_INT (((unsigned HOST_WIDE_INT) (INTVAL (value)) >> (bitsize - bitsdone - thissize)) & (((HOST_WIDE_INT) 1 << thissize) - 1)); else /* The args are chosen so that the last part includes the lsb. Give extract_bit_field the value it needs (with endianness compensation) to fetch the piece we want. */ part = extract_fixed_bit_field (word_mode, value, 0, thissize, total_bits - bitsize + bitsdone, NULL_RTX, 1, align); } else { /* Fetch successively more significant portions. */ if (GET_CODE (value) == CONST_INT) part = GEN_INT (((unsigned HOST_WIDE_INT) (INTVAL (value)) >> bitsdone) & (((HOST_WIDE_INT) 1 << thissize) - 1)); else part = extract_fixed_bit_field (word_mode, value, 0, thissize, bitsdone, NULL_RTX, 1, align); } /* 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 + -