> The patch is probably quite hard to review, sorry. I've made the changelog > a bit more detailed than usual in order to list the individual points.
You meant scary, didn't you? :-) > * expmed.c (store_bit_field_1): Remove unit, offset, bitpos and > byte_offset from the outermost scope. Express conditions in terms > of bitnum rather than offset, bitpos and byte_offset. Split the > plain move cases into two, one for memory accesses and one for > register accesses. Allow simplify_gen_subreg to fail rather > than calling validate_subreg. Move the handling of multiword > OP0s after the code that coerces VALUE to an integer mode. > Use simplify_gen_subreg for this case and assert that it succeeds. > If the field still spans several words, pass it directly to > store_split_bit_field. Assume after that point that both sources > and register targets fit within a word. Replace x-prefixed > variables with non-prefixed forms. Compute the bitpos for insv > register operands directly in the chosen unit size, rather than > going through an intermediate BITS_PER_WORD unit size. > Update the call to store_fixed_bit_field. > (store_fixed_bit_field): Replace the bitpos and offset parameters > with a single bitnum parameter, of the same form as store_bit_field. > Assume that OP0 contains the full field. Simplify the memory offset > calculation. Assert that the processed OP0 has an integral mode. > (store_split_bit_field): Update the call to store_fixed_bit_field. I agree that splitting the memory and register cases is a good idea, as well as unifying the interface. A few remarks: > -476,34 +468,36 @@ store_bit_field_1 (rtx str_rtx, unsigne > } > > /* If the target is a register, overwriting the entire object, or storing > - a full-word or multi-word field can be done with just a SUBREG. + > a full-word or multi-word field can be done with just a SUBREG. */ + if > (!MEM_P (op0) > + && bitsize == GET_MODE_BITSIZE (fieldmode) > + && ((bitsize % BITS_PER_WORD == 0 > + && bitnum % BITS_PER_WORD == 0) > + || (bitsize == GET_MODE_BITSIZE (GET_MODE (op0)) > + && bitnum == 0))) > + { > + /* Use the subreg machinery either to narrow OP0 to the required > + words or to cope with mode punning between equal-sized modes. */ > + rtx sub = simplify_gen_subreg (fieldmode, op0, GET_MODE (op0), > + bitnum / BITS_PER_UNIT); > + if (sub) > + { > + emit_move_insn (sub, value); > + return true; > + } > + } I'd use the following variant for the sake of symmetry with the MEM_P case: if (!MEM_P (op0) && bitnum % BITS_PER_WORD == 0 && bitsize == GET_MODE_BITSIZE (fieldmode) && [...] > - /* 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 (BYTES_BIG_ENDIAN > - && !MEM_P (op0) > - && unit > GET_MODE_BITSIZE (GET_MODE (op0))) > - bitpos += unit - GET_MODE_BITSIZE (GET_MODE (op0)); > - > /* Storing an lsb-aligned field in a register > - can be done with a movestrict instruction. */ > + can be done with a movstrict instruction. */ > > if (!MEM_P (op0) > - && (BYTES_BIG_ENDIAN ? bitpos + bitsize == unit : bitpos == 0) > + && (BYTES_BIG_ENDIAN > + ? bitnum + bitsize == GET_MODE_BITSIZE (GET_MODE (op0)) > + : bitnum == 0) > && bitsize == GET_MODE_BITSIZE (fieldmode) > && optab_handler (movstrict_optab, fieldmode) != CODE_FOR_nothing) > { > @@ -558,8 +546,7 @@ store_bit_field_1 (rtx str_rtx, unsigned > arg0 = SUBREG_REG (arg0); > } > > - subreg_off = (bitnum % BITS_PER_WORD) / BITS_PER_UNIT > - + (offset * UNITS_PER_WORD); > + subreg_off = bitnum / BITS_PER_UNIT; > if (validate_subreg (fieldmode, GET_MODE (arg0), arg0, subreg_off)) > { > arg0 = gen_rtx_SUBREG (fieldmode, arg0, subreg_off); Aren't you losing movstrict opportunities in big-endian mode? If GET_MODE (op0) is 2 words and bitnum + bitsize == BITS_PER_WORD. > @@ -638,34 +625,6 @@ store_bit_field_1 (rtx str_rtx, unsigned > return true; > } > > - /* From here on we can assume that the field to be stored in is > - a full-word (whatever type that is), since it is shorter than a word. > */ - > - /* 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 (!MEM_P (op0)) > - { > - if (offset != 0 > - || GET_MODE_SIZE (GET_MODE (op0)) > UNITS_PER_WORD) > - { > - if (!REG_P (op0)) > - { > - /* Since this is a destination (lvalue), we can't copy > - it to a pseudo. We can remove a SUBREG that does not > - change the size of the operand. Such a SUBREG may > - have been added above. */ > - gcc_assert (GET_CODE (op0) == SUBREG > - && (GET_MODE_SIZE (GET_MODE (op0)) > - == GET_MODE_SIZE (GET_MODE (SUBREG_REG (op0))))); > - op0 = SUBREG_REG (op0); > - } > - op0 = gen_rtx_SUBREG (mode_for_size (BITS_PER_WORD, MODE_INT, 0), > - op0, (offset * UNITS_PER_WORD)); > - } > - offset = 0; > - } > - > /* If VALUE has a floating-point or complex mode, access it as an > integer of the corresponding size. This can occur on a machine > with 64 bit registers that uses SFmode for float. It can also > @@ -679,9 +638,30 @@ store_bit_field_1 (rtx str_rtx, unsigned > emit_move_insn (gen_lowpart (GET_MODE (orig_value), value), > orig_value); } > > - /* Now OFFSET is nonzero only if OP0 is memory > - and is therefore always measured in bytes. */ > + /* If OP0 is a multi-word register, narrow it to the affected word. > + If the region spans two words, defer to store_split_bit_field. */ > + if (!MEM_P (op0) && GET_MODE_SIZE (GET_MODE (op0)) > UNITS_PER_WORD) > + { > + op0 = simplify_gen_subreg (word_mode, op0, GET_MODE (op0), > + bitnum / BITS_PER_WORD * UNITS_PER_WORD); > + gcc_assert (op0); > + bitnum %= BITS_PER_WORD; > + if (bitnum + bitsize > BITS_PER_WORD) > + { > + if (!fallback_p) > + return false; > + > + store_split_bit_field (op0, bitsize, bitnum, bitregion_start, > + bitregion_end, value); > + return true; > + } > + } > + > + /* From here on we can assume that the field to be stored in fits > + within a word. If the destination is a register, it too fits > + in a word. */ I presume the reasoning is that the offset != 0 test is redundant with the test on the mode size because of the REG_P && ... early exit? > + enum machine_mode op_mode = mode_for_extraction (EP_insv, 3); > if (HAVE_insv > && GET_MODE (value) != BLKmode > && bitsize > 0 > @@ -690,25 +670,34 @@ store_bit_field_1 (rtx str_rtx, unsigned > -fstrict-volatile-bitfields is in effect. */ > && !(MEM_P (op0) && MEM_VOLATILE_P (op0) > && flag_strict_volatile_bitfields > 0) > - && ! ((REG_P (op0) || GET_CODE (op0) == SUBREG) > - && (bitsize + bitpos > GET_MODE_BITSIZE (op_mode))) > /* Do not use insv if the bit region is restricted and > op_mode integer at offset doesn't fit into the > restricted region. */ > && !(MEM_P (op0) && bitregion_end > - && bitnum - bitpos + GET_MODE_BITSIZE (op_mode) > + && bitnum - (bitnum % BITS_PER_UNIT) + GET_MODE_BITSIZE (op_mode) > > > bitregion_end + 1)) > > { > struct expand_operand ops[4]; > - int xbitpos = bitpos; > + unsigned HOST_WIDE_INT bitpos = bitnum; > rtx value1; > rtx xop0 = op0; > rtx last = get_last_insn (); > bool copy_back = false; > > - /* Add OFFSET into OP0's address. */ > + unsigned int unit = GET_MODE_BITSIZE (op_mode); > if (MEM_P (xop0)) > - xop0 = adjust_bitfield_address (xop0, byte_mode, offset); > + { > + /* Get a reference to the first byte of the field. */ > + xop0 = adjust_bitfield_address (xop0, byte_mode, > + bitpos / BITS_PER_UNIT); > + bitpos %= BITS_PER_UNIT; > + } > + else > + { > + /* Convert from counting within OP0 to counting in OP_MODE. */ > + if (BYTES_BIG_ENDIAN) > + bitpos += unit - GET_MODE_BITSIZE (GET_MODE (op0)); > + } > > /* If xop0 is a register, we need it in OP_MODE > to make it acceptable to the format of insv. */ > @@ -735,20 +724,13 @@ store_bit_field_1 (rtx str_rtx, unsigned > copy_back = true; > } > > - /* We have been counting XBITPOS within UNIT. > - Count instead within the size of the register. */ > - if (BYTES_BIG_ENDIAN && !MEM_P (xop0)) > - xbitpos += GET_MODE_BITSIZE (op_mode) - unit; > - > - unit = GET_MODE_BITSIZE (op_mode); > - > /* If BITS_BIG_ENDIAN is zero on a BYTES_BIG_ENDIAN machine, we count > "backwards" from the size of the unit we are inserting into. Otherwise, we > count bits from the most significant on a > BYTES/BITS_BIG_ENDIAN machine. */ > > if (BITS_BIG_ENDIAN != BYTES_BIG_ENDIAN) > - xbitpos = unit - bitsize - xbitpos; > + bitpos = unit - bitsize - bitpos; > > /* Convert VALUE to op_mode (which insv insn wants) in VALUE1. */ > value1 = value; I guess I see the reasoning, but I cannot say whether it's right or wrong... > - total_bits = GET_MODE_BITSIZE (mode); > - > - /* Make sure bitpos is valid for the chosen mode. Adjust BITPOS to > - 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 = adjust_bitfield_address (op0, mode, offset); > + HOST_WIDE_INT bit_offset = bitnum - bitnum % GET_MODE_BITSIZE (mode); > + op0 = adjust_bitfield_address (op0, mode, bit_offset / > BITS_PER_UNIT); + bitnum -= bit_offset; > } Gorgeous. > 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.) */ > + gcc_assert (SCALAR_INT_MODE_P (mode)); > + /* Note that bitsize + bitnum can be greater than GET_MODE_BITSIZE (mode) > + for invalid input, such as f5 from gcc.dg/pr48335-2.c. */ Blank line after the assertion. > 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; > + /* BITNUM is the distance between our msb > + and that of the containing datum. > + Convert it to the distance from the lsb. */ > + bitnum = GET_MODE_BITSIZE (mode) - bitsize - bitnum; So bitnum can be negative? Is that new or pre-existing? -- Eric Botcazou