diff mbox

PR50325 store_bit_field: Fix for big endian targets

Message ID 87fwhjcdh9.fsf@firetop.home
State New
Headers show

Commit Message

Richard Sandiford Nov. 20, 2011, 10:09 a.m. UTC
Iain Sandoe <developer@sandoe-acoustics.co.uk> writes:
> On 17 Nov 2011, at 09:25, Andreas Krebbel wrote:
>> On 11/17/2011 03:44 AM, David Edelsohn wrote:
>>> Andreas,
>>>
>>> This patch seems to have introduced a failure for all of the
>>> gcc.dg-struct-layout tests on AIX.
>>>
>>> gcc.dg-struct-layout-1/t001_test.h:8:1: internal compiler error: in
>>> int_mode_for_mode, at stor-layout.c:424
>>>
>>> After your change, int_mode_for_mode now is passed VOIDmode because
>>> the rtx is a CONST_INT.
>>
>> extract_bit_field is only able to deal with regs and mems. For  
>> constants it should not be
>> necessary anyway. Could you please test the following patch:
>>
>> Index: gcc/expmed.c
>> ===================================================================
>> *** gcc/expmed.c.orig   2011-11-15 20:03:46.000000000 +0100
>> --- gcc/expmed.c        2011-11-17 09:12:22.487783491 +0100
>> *************** store_bit_field_1 (rtx str_rtx, unsigned
>> *** 562,570 ****
>>            MIN (BITS_PER_WORD, bitsize - i * BITS_PER_WORD);
>>
>>          /* If the remaining chunk doesn't have full wordsize we have
>> !            to make that for big endian machines the higher order
>> !            bits are used.  */
>> !         if (new_bitsize < BITS_PER_WORD && BYTES_BIG_ENDIAN)
>>            value_word = extract_bit_field (value_word, new_bitsize, 0,
>>                                            true, false, NULL_RTX,
>>                                            BLKmode, word_mode);
>> --- 562,572 ----
>>            MIN (BITS_PER_WORD, bitsize - i * BITS_PER_WORD);
>>
>>          /* If the remaining chunk doesn't have full wordsize we have
>> !            to make sure that for big endian machines the higher
>> !            order bits are used.  */
>> !         if (BYTES_BIG_ENDIAN
>> !             && GET_MODE (value_word) != VOIDmode
>> !             && new_bitsize < BITS_PER_WORD)
>>            value_word = extract_bit_field (value_word, new_bitsize, 0,
>>                                            true, false, NULL_RTX,
>>                                            BLKmode, word_mode);
>>
>
> with this + r181436 on powerpc-darwin9, the ICEs are gone ..
> .. but all the struct-layout-1 tests are failing on execute.
>
> Running /GCC/gcc-live-trunk/gcc/testsuite/gcc.dg/compat/struct- 
> layout-1.exp ...
> FAIL: tmpdir-gcc.dg-struct-layout-1/t001 c_compat_x_tst.o- 
> c_compat_y_tst.o execute
> FAIL: tmpdir-gcc.dg-struct-layout-1/t002 c_compat_x_tst.o- 
> c_compat_y_tst.o execute
> FAIL: tmpdir-gcc.dg-struct-layout-1/t003 c_compat_x_tst.o- 
> c_compat_y_tst.o execute
> FAIL: tmpdir-gcc.dg-struct-layout-1/t004 c_compat_x_tst.o- 
> c_compat_y_tst.o execute
> FAIL: tmpdir-gcc.dg-struct-layout-1/t005 c_compat_x_tst.o- 
> c_compat_y_tst.o execute
> FAIL: tmpdir-gcc.dg-struct-layout-1/t006 c_compat_x_tst.o- 
> c_compat_y_tst.o execute
> FAIL: tmpdir-gcc.dg-struct-layout-1/t007 c_compat_x_tst.o- 
> c_compat_y_tst.o execute
> FAIL: tmpdir-gcc.dg-struct-layout-1/t008 c_compat_x_tst.o- 
> c_compat_y_tst.o execute

Yeah, I don't think constants are any different here.  One fix might be
to use simplify_expand_binop instead of extract_bit_field, as per the
patch below.  The patch also restricts the shifting to forward walks,
as discussed in the PR trail.

But I'm not sure I understand the fieldmode != BLKmode test in:

      unsigned int backwards = WORDS_BIG_ENDIAN && fieldmode != BLKmode;

Adding it was the (only) net effect of r7985 and r8007,
but the comment:

	 However, only do that if the value is not BLKmode.  */

is less than helpful when trying to discern a reason.  Even in those days,
the code was followed by:

      /* This is the mode we must force value to, so that there will be enough
	 subwords to extract.  Note that fieldmode will often (always?) be
	 VOIDmode, because that is what store_field uses to indicate that this
	 is a bit field, but passing VOIDmode to operand_subword_force will
	 result in an abort.  */
      fieldmode = mode_for_size (nwords * BITS_PER_WORD, MODE_INT, 0);

This latter comment is clearly talking about the value of fieldmode
before rather than after the call to mode_for_size, so there seems to
have been a bit of confusion about what fieldmode was actually supposed
to be (comment says VOIDmode, r8007 says that BLKmode is both possible
and special).

The pre-r7985 code honoured the comment:

      for (i = 0; i < nwords; i++)
	{
	  /* If I is 0, use the low-order word in both field and target;
	     if I is 1, use the next to lowest word; and so on.  */

And any trimming would always happen in the last iteration (i == nwords - 1);
in other words, in the high word.

The point of r8007 seems to be that WORDS_BIG_ENDIAN doesn't apply to
BLKmode fieldmodes, but there must surely be some endianness involved
somewhere, given that we're extracting words from a multiword value?

This of course predates the public mailing lists by several years.

Anyway, it seems on face value that the original patch for this PR
(the "experimental fix", which is different from the submitted version,
but which AIUI avoids the PowerPC and MIPS regressions) is equivalent
to removing that "&& fieldmode != BLKmode" check on BYTES_BIG_ENDIAN
== WORDS_BIG_ENDIAN targets.  If that turns out to be correct, I wonder
what should happen for BYTES_BIG_ENDIAN != WORDS_BIG_ENDIAN?  The only
two examples I can see are pdp11 and an obselete ARM mode that will be
removed in 4.8.

So if possible, I'd prefer to revert the original patch for this PR
and instead adjust the "backwards" test.  I'm just not sure whether
it should be "WORDS_BIG_ENDIAN" (reverting r8007, which was obviously
applied for a reason) or something like:

        (fieldmode == BLKmode ? BYTES_BIG_ENDIAN : WORDS_BIG_ENDIAN)

Richard


gcc/
	* optabs.h (simplify_expand_binop): Declare.
	* optabs.c (simplify_expand_binop): Make global.
	* expmed.c (store_bit_field_1): Use simplify_expand_binop
	rather than extract_bit_field to get the high bits of a word.
	Only adjust if !backwards.

Comments

Eric Botcazou Nov. 21, 2011, 8:02 a.m. UTC | #1
> Yeah, I don't think constants are any different here.  One fix might be
> to use simplify_expand_binop instead of extract_bit_field, as per the
> patch below.  The patch also restricts the shifting to forward walks,
> as discussed in the PR trail.

This looks fine to me, please apply (with a few words added to the comment, 
e.g. "...doesn't have full wordsize and we transfer in memory order...").

> The point of r8007 seems to be that WORDS_BIG_ENDIAN doesn't apply to
> BLKmode fieldmodes, but there must surely be some endianness involved
> somewhere, given that we're extracting words from a multiword value?

I think that BLKmode objects are treated as left-justified in memory order, 
i.e. left-justified on big-endian and right-justified on little-endian.

> So if possible, I'd prefer to revert the original patch for this PR
> and instead adjust the "backwards" test.  I'm just not sure whether
> it should be "WORDS_BIG_ENDIAN" (reverting r8007, which was obviously
> applied for a reason) or something like:
>
>         (fieldmode == BLKmode ? BYTES_BIG_ENDIAN : WORDS_BIG_ENDIAN)

This seems to be a little risky for stage 3 though.
Richard Sandiford Nov. 21, 2011, 11:30 a.m. UTC | #2
Eric Botcazou <ebotcazou@adacore.com> writes:
>> Yeah, I don't think constants are any different here.  One fix might be
>> to use simplify_expand_binop instead of extract_bit_field, as per the
>> patch below.  The patch also restricts the shifting to forward walks,
>> as discussed in the PR trail.
>
> This looks fine to me, please apply (with a few words added to the comment, 
> e.g. "...doesn't have full wordsize and we transfer in memory order...").
>
>> The point of r8007 seems to be that WORDS_BIG_ENDIAN doesn't apply to
>> BLKmode fieldmodes, but there must surely be some endianness involved
>> somewhere, given that we're extracting words from a multiword value?
>
> I think that BLKmode objects are treated as left-justified in memory order, 
> i.e. left-justified on big-endian and right-justified on little-endian.
>
>> So if possible, I'd prefer to revert the original patch for this PR
>> and instead adjust the "backwards" test.  I'm just not sure whether
>> it should be "WORDS_BIG_ENDIAN" (reverting r8007, which was obviously
>> applied for a reason) or something like:
>>
>>         (fieldmode == BLKmode ? BYTES_BIG_ENDIAN : WORDS_BIG_ENDIAN)
>
> This seems to be a little risky for stage 3 though.

What's the main potential problem you see?  The backwards condition:

    (fieldmode == BLKmode ? BYTES_BIG_ENDIAN : WORDS_BIG_ENDIAN)

should apply the same bit-for-bit mapping between source and target
as the patch applies.  The only difference should be in the order that
the reads and writes happen.  But does that matter?  If we were worried
about something like volatile MEMs here, I'd expect a check for that,
since volatile MEMs can be something like doubleword as well as BLKmode.

Richard
Eric Botcazou Nov. 21, 2011, 12:01 p.m. UTC | #3
> What's the main potential problem you see?  The backwards condition:
>
>     (fieldmode == BLKmode ? BYTES_BIG_ENDIAN : WORDS_BIG_ENDIAN)
>
> should apply the same bit-for-bit mapping between source and target
> as the patch applies.

Not if the objects don't have the same size.  In Ada we have BLKmode bitfields, 
i.e. fields whose type has BLKmode and whose DECL_SIZE is smaller than the 
TYPE_SIZE of the type; in this case, we want to drop the rightmost bytes in 
memory order, whatever the endianness.
Richard Sandiford Nov. 21, 2011, 12:58 p.m. UTC | #4
Eric Botcazou <ebotcazou@adacore.com> writes:
>> What's the main potential problem you see?  The backwards condition:
>>
>>     (fieldmode == BLKmode ? BYTES_BIG_ENDIAN : WORDS_BIG_ENDIAN)
>>
>> should apply the same bit-for-bit mapping between source and target
>> as the patch applies.
>
> Not if the objects don't have the same size.

Ugh.

> In Ada we have BLKmode bitfields, i.e. fields whose type has BLKmode
> and whose DECL_SIZE is smaller than the TYPE_SIZE of the type; in this
> case, we want to drop the rightmost bytes in memory order, whatever
> the endianness.

OK, I see how the two are different now, but I still don't get why the
current version is right.  If we say words are 16 bits for simplicity,
and we're storing VALUE == 0x0001_2345 into a 3-byte BLKmode bitfield,
surely we want to store 0x01_2345 rather than 0x00_0123?  It seems odd
for fieldmode (which I thought was a property of the target) to affect
the interpretation of VALUE (the source).

Richard
Eric Botcazou Nov. 22, 2011, 6:32 p.m. UTC | #5
> OK, I see how the two are different now, but I still don't get why the
> current version is right.  If we say words are 16 bits for simplicity,
> and we're storing VALUE == 0x0001_2345 into a 3-byte BLKmode bitfield,
> surely we want to store 0x01_2345 rather than 0x00_0123?

Why?  If you want this semantics, you need to use a bitfield with integer mode.
In Ada, the following case is frequent: you have a record type with BLKmode and 
an alignment that causes it to padded (because the size is always a multiple 
of the alignment).  You have a field in another record type whose type is the 
first record type but with a representation clause that prescribes the size 
without the padding.  The Ada compiler must accept that.  So the field is made 
a BLKmode bitfield, with DECL_SIZE (size w/o padding) smaller than TYPE_SIZE 
(size w/ padding).  When you're assigning a value to the field, you drop the 
rightmost bytes in memory order, which are junk since padding.
Richard Sandiford Nov. 23, 2011, 2:47 p.m. UTC | #6
Eric Botcazou <ebotcazou@adacore.com> writes:
>> OK, I see how the two are different now, but I still don't get why the
>> current version is right.  If we say words are 16 bits for simplicity,
>> and we're storing VALUE == 0x0001_2345 into a 3-byte BLKmode bitfield,
>> surely we want to store 0x01_2345 rather than 0x00_0123?
>
> Why?  If you want this semantics, you need to use a bitfield with integer mode

It's the interpretation and mode of VALUE that I'm worried about.
Doesn't FIELDMODE describe STR_RTX (the stored bitfield) instead?

If so, it seems odd that we're using a property of STR_RTX to determine
the interpretation/justification of VALUE.  That is, we're using
FIELDMODE before, rather than after:

      /* This is the mode we must force value to, so that there will be enough
	 subwords to extract.  Note that fieldmode will often (always?) be
	 VOIDmode, because that is what store_field uses to indicate that this
	 is a bit field, but passing VOIDmode to operand_subword_force
	 is not allowed.  */
      fieldmode = GET_MODE (value);
      if (fieldmode == VOIDmode)
	fieldmode = smallest_mode_for_size (nwords * BITS_PER_WORD, MODE_INT);

> In Ada, the following case is frequent: you have a record type with BLKmode and 
> an alignment that causes it to padded (because the size is always a multiple 
> of the alignment).  You have a field in another record type whose type is the 
> first record type but with a representation clause that prescribes the size 
> without the padding.  The Ada compiler must accept that.  So the field is made 
> a BLKmode bitfield, with DECL_SIZE (size w/o padding) smaller than TYPE_SIZE 
> (size w/ padding).  When you're assigning a value to the field, you drop the 
> rightmost bytes in memory order, which are junk since padding.

In this sort of assignment, what does VALUE look like?  Does it need to be
BLKmode?  Or can it have an integer mode?  (I assume the former, since the
latter seems contrary to what you said about integer modes above.)
Must VALUE be a memory?  Could we assert:

  gcc_assert ((fieldmode == BLKmode) == (GET_MODE (value) == BLKmode));
  gcc_assert (fieldmode != BLKmode || MEM_P (value));

at the beginning of store_bit_field_1?

Since Andreas was fixing C++ bugs, I assume the same situation occurs there.
What does it look like in a C++ context?

Richard
Andreas Krebbel Nov. 23, 2011, 3:01 p.m. UTC | #7
On 11/23/2011 03:47 PM, Richard Sandiford wrote:
> Since Andreas was fixing C++ bugs, I assume the same situation occurs there.
> What does it look like in a C++ context?

http://gcc.gnu.org/bugzilla/show_bug.cgi?id=50325#c4

value was (subreg:DI (reg:TI 70) 8)
and fieldmode == BLKmode

-Andreas-
Richard Earnshaw Nov. 23, 2011, 3:31 p.m. UTC | #8
On 21/11/11 08:02, Eric Botcazou wrote:
> This seems to be a little risky for stage 3 though.

Being in stage 3 shouldn't stop us trying to fix bugs in the compiler:
we're not in the final run-up to a release yet (that could still be five
months away if history is anything to go by).  It should just be a
clamp-down on major new functionality.

R.
Eric Botcazou Nov. 26, 2011, 6:56 p.m. UTC | #9
> Being in stage 3 shouldn't stop us trying to fix bugs in the compiler:
> we're not in the final run-up to a release yet (that could still be five
> months away if history is anything to go by).  It should just be a
> clamp-down on major new functionality.

But Richard's proposed change wasn't really a bugfix (he also proposed a bugfix 
which should be applied of course) but rather a cleanup.  As the ongoing story 
shows, this is a delicate area so, if we are to clean things up here at this 
stage, the level of testing should at least be adjusted.
Eric Botcazou Dec. 5, 2011, 10:15 a.m. UTC | #10
> It's the interpretation and mode of VALUE that I'm worried about.
> Doesn't FIELDMODE describe STR_RTX (the stored bitfield) instead?

Yes, FIELDMODE is the mode of the target field.

> If so, it seems odd that we're using a property of STR_RTX to determine
> the interpretation/justification of VALUE.  That is, we're using
> FIELDMODE before, rather than after:
>
>       /* This is the mode we must force value to, so that there will be
> enough subwords to extract.  Note that fieldmode will often (always?) be
> VOIDmode, because that is what store_field uses to indicate that this is a
> bit field, but passing VOIDmode to operand_subword_force
> 	 is not allowed.  */
>       fieldmode = GET_MODE (value);
>       if (fieldmode == VOIDmode)
> 	fieldmode = smallest_mode_for_size (nwords * BITS_PER_WORD, MODE_INT);

First of all, reusing FIELDMODE here is clearly very confusing, this should be 
VALUEMODE instead.

OK, I understand the concern.  But what if VALUE has VOIDmode and the field 
BLKmode?  In this case, VALUEMODE may be integral and using it to determine 
the justification will be wrong.

> In this sort of assignment, what does VALUE look like?  Does it need to be
> BLKmode?  Or can it have an integer mode?  (I assume the former, since the
> latter seems contrary to what you said about integer modes above.)

I think it must be BLKmode or VOIDmode.

> Must VALUE be a memory?

Or a constant probably.

> Could we assert: 
>
>   gcc_assert ((fieldmode == BLKmode) == (GET_MODE (value) == BLKmode));
>   gcc_assert (fieldmode != BLKmode || MEM_P (value));
>
> at the beginning of store_bit_field_1?

We could indeed try, modulo constants I think.
Eric Botcazou Dec. 5, 2011, 10:20 a.m. UTC | #11
> http://gcc.gnu.org/bugzilla/show_bug.cgi?id=50325#c4
>
> value was (subreg:DI (reg:TI 70) 8)

Not the original VALUE though, since it is 12-byte wide.
Richard Sandiford Dec. 5, 2011, 7:39 p.m. UTC | #12
Eric Botcazou <ebotcazou@adacore.com> writes:
>> If so, it seems odd that we're using a property of STR_RTX to determine
>> the interpretation/justification of VALUE.  That is, we're using
>> FIELDMODE before, rather than after:
>>
>>       /* This is the mode we must force value to, so that there will be
>> enough subwords to extract.  Note that fieldmode will often (always?) be
>> VOIDmode, because that is what store_field uses to indicate that this is a
>> bit field, but passing VOIDmode to operand_subword_force
>> 	 is not allowed.  */
>>       fieldmode = GET_MODE (value);
>>       if (fieldmode == VOIDmode)
>> 	fieldmode = smallest_mode_for_size (nwords * BITS_PER_WORD, MODE_INT);
>
> First of all, reusing FIELDMODE here is clearly very confusing, this
> should be VALUEMODE instead.
>
> OK, I understand the concern.  But what if VALUE has VOIDmode and the field 
> BLKmode?  In this case, VALUEMODE may be integral and using it to determine 
> the justification will be wrong.

Ah, so in the BLKmode case, VALUE can still be a nonzero const_int?
What are the justification rules in that case?  Naively, I'd have
expected a store of (const_int 1) into a BLKmode field to set one
of the bits to 1, but it sounds from the above like it depends on
endianness: for little-endian targets it always sets a bit, but for
big-endian targets it only sets a bit if the field width is a multiple
of UNITS_PER_WORD.

>> In this sort of assignment, what does VALUE look like?  Does it need to be
>> BLKmode?  Or can it have an integer mode?  (I assume the former, since the
>> latter seems contrary to what you said about integer modes above.)
>
> I think it must be BLKmode or VOIDmode.
>
>> Must VALUE be a memory?
>
> Or a constant probably.
>
>> Could we assert: 
>>
>>   gcc_assert ((fieldmode == BLKmode) == (GET_MODE (value) == BLKmode));
>>   gcc_assert (fieldmode != BLKmode || MEM_P (value));
>>
>> at the beginning of store_bit_field_1?
>
> We could indeed try, modulo constants I think.

OK, thanks, that sounds better.  If we can iron out what the nonzero
constant case means then I'd be happy with that.

Richard
Eric Botcazou Dec. 6, 2011, 11:50 a.m. UTC | #13
> Ah, so in the BLKmode case, VALUE can still be a nonzero const_int?

Yes, I'd think so.

> What are the justification rules in that case?  Naively, I'd have
> expected a store of (const_int 1) into a BLKmode field to set one
> of the bits to 1, but it sounds from the above like it depends on
> endianness: for little-endian targets it always sets a bit, but for
> big-endian targets it only sets a bit if the field width is a multiple
> of UNITS_PER_WORD.

For example, in Ada, the following program:

with Text_IO; use Text_IO;
with Unchecked_Conversion;

procedure P is

  type Arr is new String(1..3);

  function Conv is new Unchecked_Conversion (Integer, Arr);

  type R is record
    A : Arr;
  end record;

  My_R : R;

begin
  My_R.A := Conv (16#41424344#);
  Put (String(My_R.A));
end;

outputs DCB on little-endian and ABC on big-endian.
diff mbox

Patch

Index: gcc/optabs.h
===================================================================
--- gcc/optabs.h	2011-11-19 16:44:44.000000000 +0000
+++ gcc/optabs.h	2011-11-19 16:44:58.000000000 +0000
@@ -869,6 +869,10 @@  extern rtx expand_ternary_op (enum machi
 extern rtx expand_binop (enum machine_mode, optab, rtx, rtx, rtx, int,
 			 enum optab_methods);
 
+extern rtx simplify_expand_binop (enum machine_mode mode, optab binoptab,
+				  rtx op0, rtx op1, rtx target, int unsignedp,
+				  enum optab_methods methods);
+
 extern bool force_expand_binop (enum machine_mode, optab, rtx, rtx, rtx, int,
 				enum optab_methods);
 
Index: gcc/optabs.c
===================================================================
--- gcc/optabs.c	2011-11-19 16:44:31.000000000 +0000
+++ gcc/optabs.c	2011-11-19 16:44:34.000000000 +0000
@@ -671,7 +671,7 @@  expand_ternary_op (enum machine_mode mod
    calculated at compile time.  The arguments and return value are
    otherwise the same as for expand_binop.  */
 
-static rtx
+rtx
 simplify_expand_binop (enum machine_mode mode, optab binoptab,
 		       rtx op0, rtx op1, rtx target, int unsignedp,
 		       enum optab_methods methods)
Index: gcc/expmed.c
===================================================================
--- gcc/expmed.c	2011-11-19 16:43:26.000000000 +0000
+++ gcc/expmed.c	2011-11-20 09:22:24.000000000 +0000
@@ -564,10 +564,13 @@  store_bit_field_1 (rtx str_rtx, unsigned
 	  /* If the remaining chunk doesn't have full wordsize we have
 	     to make sure that for big endian machines the higher order
 	     bits are used.  */
-	  if (new_bitsize < BITS_PER_WORD && BYTES_BIG_ENDIAN)
-	    value_word = extract_bit_field (value_word, new_bitsize, 0,
-					    true, false, NULL_RTX,
-					    BLKmode, word_mode);
+	  if (new_bitsize < BITS_PER_WORD && BYTES_BIG_ENDIAN && !backwards)
+	    value_word = simplify_expand_binop (word_mode, lshr_optab,
+						value_word,
+						GEN_INT (BITS_PER_WORD
+							 - new_bitsize),
+						NULL_RTX, true,
+						OPTAB_LIB_WIDEN);
 
 	  if (!store_bit_field_1 (op0, new_bitsize,
 				  bitnum + bit_offset,