📄 expmed.c
字号:
{ /* 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. */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. */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 % BITS_PER_WORD; /* BITSIZE_2 is size of the rest (in the following word). */ int bitsize_2 = bitsize - bitsize_1; rtx part1, part2, result; int unit = GET_CODE (op0) == MEM ? BITS_PER_UNIT : BITS_PER_WORD; int offset = bitpos / unit; rtx word; /* The field must span exactly one word boundary. */ if (bitpos / BITS_PER_WORD != (bitpos + bitsize - 1) / BITS_PER_WORD - 1) abort (); /* Get the part of the bit field from the first word. If OP0 is a MEM, pass OP0 and the offset computed above. Otherwise, get the proper word and pass an offset of zero. */ word = (GET_CODE (op0) == MEM ? op0 : operand_subword_force (op0, offset, GET_MODE (op0))); part1 = extract_fixed_bit_field (word_mode, word, GET_CODE (op0) == MEM ? offset : 0, bitsize_1, bitpos % unit, NULL_RTX, 1, align); /* Offset op0 by 1 word to get to the following one. */ if (GET_CODE (op0) == SUBREG) word = operand_subword_force (SUBREG_REG (op0), SUBREG_WORD (op0) + offset + 1, VOIDmode); else if (GET_CODE (op0) == MEM) word = op0; else word = operand_subword_force (op0, offset + 1, GET_MODE (op0)); /* Get the part of the bit field from the second word. */ part2 = extract_fixed_bit_field (word_mode, word, (GET_CODE (op0) == MEM ? CEIL (offset + 1, UNITS_PER_WORD) * UNITS_PER_WORD : 0), bitsize_2, 0, NULL_RTX, 1, align); /* Shift the more significant part up to fit above the other part. */#if BYTES_BIG_ENDIAN part1 = expand_shift (LSHIFT_EXPR, word_mode, part1, build_int_2 (bitsize_2, 0), 0, 1);#else part2 = expand_shift (LSHIFT_EXPR, word_mode, 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 (word_mode, ior_optab, part1, part2, NULL_RTX, 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, word_mode, result, build_int_2 (BITS_PER_WORD - bitsize, 0), NULL_RTX, 0); return expand_shift (RSHIFT_EXPR, word_mode, result, build_int_2 (BITS_PER_WORD - bitsize, 0), NULL_RTX, 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 DEC 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. */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); register int rotate = (code == LROTATE_EXPR || code == RROTATE_EXPR); int try; /* 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, NULL_RTX, VOIDmode, 0); if (op1 == const0_rtx) return shifted; for (try = 0; temp == 0 && try < 3; try++) { enum optab_methods methods; 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_WIDEN) continue; else if (methods == OPTAB_LIB_WIDEN) methods = OPTAB_LIB; temp = expand_binop (mode, left ? rotl_optab : rotr_optab, shifted, op1, target, unsignedp, 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); } /* 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 (temp == 0 && ! 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); }#ifdef HAVE_extzv /* We can do a logical (unsigned) right shift with a bit-field extract insn. But first check if one of the above methods worked. */ if (temp != 0) return temp; if (unsignedp && code == RSHIFT_EXPR && ! BITS_BIG_ENDIAN && HAVE_extzv) { enum machine_mode output_mode = insn_operand_mode[(int) CODE_FOR_extzv][0]; if ((methods == OPTAB_DIRECT && mode == output_mode) || (methods == OPTAB_WIDEN && GET_MODE_SIZE (mode) < GET_MODE_SIZE (output_mode))) { rtx shifted1 = convert_to_mode (output_mode, protect_from_queue (shifted, 0), 1); enum machine_mode length_mode = insn_operand_mode[(int) CODE_FOR_extzv][2]; enum machine_mode pos_mode = insn_operand_mode[(int) CODE_FOR_extzv][3]; rtx target1 = 0; rtx last = get_last_insn (); rtx width; rtx xop1 = op1; rtx pat; if (target != 0) target1 = protect_from_queue (target, 1); /* We define extract insns as having OUTPUT_MODE in a register and the mode of operand 1 in memory. Since we want OUTPUT_MODE, we will always force the operand into a register. At some point we might want to support MEM directly. */ shifted1 = force_reg (output_mode, shifted1); /* If we don't have or cannot use a suggested target, make a place for the result, in the proper mode. */ if (methods == OPTAB_WIDEN || target1 == 0 || ! ((*insn_operand_predicate[(int) CODE_FOR_extzv][0]) (target1, output_mode))) target1 = gen_reg_rtx (output_mode); xop1 = protect_from_queue (xop1, 0); xop1 = convert_to_mode (pos_mode, xop1, TREE_UNSIGNED (TREE_TYPE (amount))); /* If this machine's extzv insists on a register for operand 3 (position), arrange for that. */ if (! ((*insn_operand_predicate[(int) CODE_FOR_extzv][3]) (xop1, pos_mode))) xop1 = force_reg (pos_mode, xop1); /* WIDTH gets the width of the bit field to extract: wordsize minus # bits to shift by. */ if (GET_CODE (xop1) == CONST_INT) width = GEN_INT (GET_MODE_BITSIZE (mode) - INTVAL (op1)); else { /* Now get the width in the proper mode. */ op1 = protect_from_queue (op1, 0); width = convert_to_mode (length_mode, op1, TREE_UNSIGNED (TREE_TYPE (amount))); width = expand_binop (length_mode, sub_optab, GEN_INT (GET_MODE_BITSIZE (mode)), width, NULL_RTX, 0, OPTAB_LIB_WIDEN); } /* If this machine's extzv insists on a register for operand 2 (length), arrange for that. */ if (! ((*insn_operand_predicate[(int) CODE_FOR_extzv][2]) (width, length_mode))) width = force_reg (length_mode, width); /* Now extract with WIDTH, omitting OP1 least sig bits. */ pat = gen_extzv (target1, shifted1, width, xop1); if (pat) { emit_insn (pat); temp = convert_to_mode (mode, target1, 1); } else delete_insns_since (last); } /* Can also do logical shift with signed bit-field extract followed by inserting the bit-field at a different position. That strategy is not yet implemented. */ }#endif /* HAVE_extzv */ }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -