📄 expmed.c
字号:
#endif target = extract_fixed_bit_field (tmode, op0, offset, bitsize, bitpos, target, 1, align); } else {#ifdef HAVE_extv if (HAVE_extv) { int xbitpos = bitpos, xoffset = offset; rtx last = get_last_insn(); rtx xop0 = op0, xtarget = target; rtx xspec_target = spec_target; rtx xspec_target_subreg = spec_target_subreg; rtx pat; 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 = 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_extv][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 extv 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)); } /* If op0 is a register, we need it in SImode to make it acceptable to the format of extv. */ 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 extv insists on a register target, make sure we have one. */ if (! (*insn_operand_predicate[(int) CODE_FOR_extv][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_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#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) 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 SImode, 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 if (bitsize + bitpos <= BITS_PER_UNIT && (! SLOW_BYTE_ACCESS || (align == 1 && BIGGEST_ALIGNMENT > 1))) { /* It fits in one byte, and either bytes are fast or the alignment won't let us use anything bigger. */ total_bits = BITS_PER_UNIT; op0 = change_address (op0, QImode, plus_constant (XEXP (op0, 0), offset)); } else if ((bitsize + bitpos + (offset % GET_MODE_SIZE (HImode)) * BITS_PER_UNIT <= GET_MODE_BITSIZE (HImode)) /* If halfwords are fast, use them whenever valid. */ && (! SLOW_BYTE_ACCESS /* Use halfwords if larger is invalid due to alignment. */ || (align == GET_MODE_SIZE (HImode) && BIGGEST_ALIGNMENT > GET_MODE_SIZE (HImode)))) { /* It fits in an aligned halfword, and either halfwords are fast or the alignment won't let us use anything bigger. */ total_bits = GET_MODE_BITSIZE (HImode); /* Get ref to halfword containing the field. */ bitpos += (offset % (total_bits / BITS_PER_UNIT)) * BITS_PER_UNIT; offset -= (offset % (total_bits / BITS_PER_UNIT)); op0 = change_address (op0, HImode, plus_constant (XEXP (op0, 0), offset)); } else { /* Get ref to word containing the field. */ /* Adjust BITPOS to be position within a word, and OFFSET to be the offset of that word. */ bitpos += (offset % (BITS_PER_WORD / BITS_PER_UNIT)) * BITS_PER_UNIT; offset -= (offset % (BITS_PER_WORD / BITS_PER_UNIT)); op0 = change_address (op0, SImode, plus_constant (XEXP (op0, 0), offset)); /* Special treatment for a bit field split across two words. */ if (bitsize + bitpos > BITS_PER_WORD) return extract_split_bit_field (op0, bitsize, bitpos, unsignedp, align); } mode = GET_MODE (op0);#ifdef 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) { /* 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 ) && bitsize < HOST_BITS_PER_INT) return expand_bit_and (GET_MODE (op0), op0, gen_rtx (CONST_INT, VOIDmode, (1 << bitsize) - 1), target); 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; if (GET_MODE_BITSIZE (QImode) < GET_MODE_BITSIZE (mode) && GET_MODE_BITSIZE (QImode) >= bitsize + bitpos) mode = QImode, op0 = convert_to_mode (QImode, op0, 0); if (GET_MODE_BITSIZE (HImode) < GET_MODE_BITSIZE (mode) && GET_MODE_BITSIZE (HImode) >= bitsize + bitpos) mode = HImode, op0 = convert_to_mode (HImode, op0, 0); 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);}/* 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. */static rtxextract_split_bit_field (op0, bitsize, bitpos, unsignedp, align) rtx op0; int bitsize, bitpos, unsignedp, 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, result; /* Get the part of the bit field from the first word. */ part1 = extract_fixed_bit_field (SImode, op0, 0, bitsize_1, bitpos, 0, 1, align); /* Offset op0 by 1 word to get to the following one. */ 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 op0 = gen_rtx (SUBREG, SImode, SUBREG_REG (op0), SUBREG_WORD (op0) + 1); /* Get the part of the bit field from the second word. */ part2 = extract_fixed_bit_field (SImode, op0, 0, bitsize_2, 0, 0, 1, align); /* Shift the more significant part up to fit above the other part. */#ifdef BYTES_BIG_ENDIAN part1 = expand_shift (LSHIFT_EXPR, SImode, part1, build_int_2 (bitsize_2, 0), 0, 1);#else part2 = expand_shift (LSHIFT_EXPR, SImode, part2, build_int_2 (bitsize_1, 0), 0, 1);#endif /* Combine the two parts with bitwise or. This works because we extracted both parts as unsigned bit fields. */ result = expand_binop (SImode, ior_optab, part1, part2, 0, 1, OPTAB_LIB_WIDEN); /* Unsigned bit field: we are done. */ if (unsignedp) return result; /* Signed bit field: sign-extend with two arithmetic shifts. */ result = expand_shift (LSHIFT_EXPR, SImode, result, build_int_2 (BITS_PER_WORD - bitsize, 0), 0, 0); return expand_shift (RSHIFT_EXPR, SImode, result, build_int_2 (BITS_PER_WORD - bitsize, 0), 0, 0);}/* Add INC into TARGET. */voidexpand_inc (target, inc) rtx target, inc;{ rtx value = expand_binop (GET_MODE (target), add_optab, target, inc, target, 0, OPTAB_LIB_WIDEN); if (value != target) emit_move_insn (target, value);}/* Subtract INC from TARGET. */voidexpand_dec (target, dec) rtx target, dec;{ rtx value = expand_binop (GET_MODE (target), sub_optab, target, dec, target, 0, OPTAB_LIB_WIDEN); if (value != target) emit_move_insn (target, value);}/* Output a shift instruction for expression code CODE, with SHIFTED being the rtx for the value to shift, and AMOUNT the tree for the amount to shift by. Store the result in the rtx TARGET, if that is convenient. If UNSIGNEDP is nonzero, do a logical shift; otherwise, arithmetic. Return the rtx for where the value is. *//* Pastel, for shifts, converts shift count to SImode here independent of the mode being shifted. Should that be done in an earlier pass? It turns out not to matter for C. */rtxexpand_shift (code, mode, shifted, amount, target, unsignedp) enum tree_code code; register enum machine_mode mode; rtx shifted; tree amount; register rtx target; int unsignedp;{ register rtx op1, temp = 0; register int left = (code == LSHIFT_EXPR || code == LROTATE_EXPR); int try; int rotate = code == LROTATE_EXPR || code == RROTATE_EXPR; rtx last; /* Previously detected shift-counts computed by NEGATE_EXPR and shifted in the other direction; but that does not work on all machines. */ op1 = expand_expr (amount, 0, VOIDmode, 0); last = get_last_insn (); for (try = 0; temp == 0 && try < 3; try++) { enum optab_methods methods; delete_insns_since (last); if (try == 0) methods = OPTAB_DIRECT; else if (try == 1) methods = OPTAB_WIDEN; else methods = OPTAB_LIB_WIDEN; if (rotate) { /* Widening does not work for rotation. */ if (methods != OPTAB_DIRECT) methods = OPTAB_LIB; temp = expand_binop (mode, left ? rotl_optab : rotr_optab, shifted, op1, target, -1, methods); } else if (unsignedp) { temp = expand_binop (mode, left ? lshl_optab : lshr_optab, shifted, op1, target, unsignedp, methods); if (temp == 0 && left) temp = expand_binop (mode, ashl_optab, shifted, op1, target, unsignedp, methods); if (temp != 0) return temp; } /* Do arithmetic shifts. Also, if we are going to widen the operand, we can just as well use an arithmetic right-shift instead of a logical one. */ if (! rotate && (! unsignedp || (! left && methods == OPTAB_WIDEN))) { enum optab_methods methods1 = methods; /* If trying to widen a log shift to an arithmetic shift, don't accept an arithmetic shift of the same size. */ if (unsignedp) methods1 = OPTAB_MUST_WIDEN; /* Arithmetic shift */ temp = expand_binop (mode, left ? ashl_optab : ashr_optab, shifted, op1, target, unsignedp, methods1);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -