📄 expmed.c
字号:
? MAX (0, bitsize - (i + 1) * BITS_PER_WORD) : i * BITS_PER_WORD); rtx target_part = operand_subword (target, wordnum, 1, VOIDmode); rtx result_part = extract_bit_field (op0, MIN (BITS_PER_WORD, bitsize - i * BITS_PER_WORD), bitnum + bit_offset, 1, target_part, mode, word_mode, align, total_size); if (target_part == 0) abort (); if (result_part != target_part) emit_move_insn (target_part, result_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) { 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 (str_rtx, 1); } /* Now OFFSET is nonzero only for memory operands. */ if (unsignedp) {#ifdef HAVE_extzv if (HAVE_extzv && (GET_MODE_BITSIZE (insn_operand_mode[(int) CODE_FOR_extzv][0]) >= bitsize)) { int xbitpos = bitpos, xoffset = offset; rtx bitsize_rtx, bitpos_rtx; 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; enum machine_mode maxmode = insn_operand_mode[(int) CODE_FOR_extzv][0]; if (GET_CODE (xop0) == MEM) { int save_volatile_ok = volatile_ok; volatile_ok = 1; /* Is the memory operand acceptable? */ if (flag_force_mem || ! ((*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; /* 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 (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) goto extzv_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)); volatile_ok = save_volatile_ok; } /* If op0 is a register, we need it in MAXMODE (which is usually SImode). to make it acceptable to the format of extzv. */ 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;#endif /* Now convert from counting within UNIT to counting in MAXMODE. */#if BITS_BIG_ENDIAN if (GET_CODE (xop0) != MEM) xbitpos += GET_MODE_BITSIZE (maxmode) - unit;#endif 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 extzv insists on a register target, make sure we have one. */ if (! ((*insn_operand_predicate[(int) CODE_FOR_extzv][0]) (xtarget, maxmode))) xtarget = gen_reg_rtx (maxmode); bitsize_rtx = GEN_INT (bitsize); bitpos_rtx = GEN_INT (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 extzv_loses:#endif target = extract_fixed_bit_field (tmode, op0, offset, bitsize, bitpos, target, 1, align); } else {#ifdef HAVE_extv if (HAVE_extv && (GET_MODE_BITSIZE (insn_operand_mode[(int) CODE_FOR_extv][0]) >= bitsize)) { int xbitpos = bitpos, xoffset = offset; rtx bitsize_rtx, bitpos_rtx; rtx last = get_last_insn(); rtx xop0 = op0, xtarget = target; rtx xspec_target = spec_target; rtx xspec_target_subreg = spec_target_subreg; rtx pat; enum machine_mode maxmode = insn_operand_mode[(int) CODE_FOR_extv][0]; if (GET_CODE (xop0) == MEM) { /* Is the memory operand acceptable? */ if (! ((*insn_operand_predicate[(int) CODE_FOR_extv][1]) (xop0, GET_MODE (xop0)))) { /* No, load into a reg and extract from there. */ 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 (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) 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;#endif /* XBITPOS counts within a size of UNIT. Adjust to count within a size of MAXMODE. */#if BITS_BIG_ENDIAN if (GET_CODE (xop0) != MEM) xbitpos += (GET_MODE_BITSIZE (maxmode) - unit);#endif 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); /* 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;#endif /* 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)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -