diff mbox series

Fix ICE during MEM_REF expansion (PR middle-end/90840)

Message ID 20191119234245.GP4650@tucnak
State New
Headers show
Series Fix ICE during MEM_REF expansion (PR middle-end/90840) | expand

Commit Message

Jakub Jelinek Nov. 19, 2019, 11:42 p.m. UTC
Hi!

On the following testcase we ICE on i686-linux (32-bit), because we store
(first 96-bit, then 72-bit) structure into the first part of a 2x 96-bit
complex long double, and for 96-bit floats there is no corresponding
integral mode that covers it and we ICE when op0 is not in MEM (it is a
REG).
The following patch handles the simple case where the whole dest REG is
covered and value is a MEM using a load from the memory, and for the rest
just spills on the stack, similarly how we punt when for stores to complex
REGs if the bitsize/bitnum cover portions of both halves.

Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

2019-11-19  Jakub Jelinek  <jakub@redhat.com>

	PR middle-end/90840
	* expmed.c (store_bit_field_1): Handle the case where op0 is not a MEM
	and has a mode that doesn't have corresponding integral type.

	* gcc.c-torture/compile/pr90840.c: New test.


	Jakub

Comments

Jeff Law Nov. 20, 2019, 2:35 a.m. UTC | #1
On 11/19/19 4:42 PM, Jakub Jelinek wrote:
> Hi!
> 
> On the following testcase we ICE on i686-linux (32-bit), because we store
> (first 96-bit, then 72-bit) structure into the first part of a 2x 96-bit
> complex long double, and for 96-bit floats there is no corresponding
> integral mode that covers it and we ICE when op0 is not in MEM (it is a
> REG).
> The following patch handles the simple case where the whole dest REG is
> covered and value is a MEM using a load from the memory, and for the rest
> just spills on the stack, similarly how we punt when for stores to complex
> REGs if the bitsize/bitnum cover portions of both halves.
> 
> Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?
> 
> 2019-11-19  Jakub Jelinek  <jakub@redhat.com>
> 
> 	PR middle-end/90840
> 	* expmed.c (store_bit_field_1): Handle the case where op0 is not a MEM
> 	and has a mode that doesn't have corresponding integral type.
> 
> 	* gcc.c-torture/compile/pr90840.c: New test.
OK
jeff
Richard Biener Nov. 20, 2019, 7:29 a.m. UTC | #2
On Wed, 20 Nov 2019, Jakub Jelinek wrote:

> Hi!
> 
> On the following testcase we ICE on i686-linux (32-bit), because we store
> (first 96-bit, then 72-bit) structure into the first part of a 2x 96-bit
> complex long double, and for 96-bit floats there is no corresponding
> integral mode that covers it and we ICE when op0 is not in MEM (it is a
> REG).
> The following patch handles the simple case where the whole dest REG is
> covered and value is a MEM using a load from the memory, and for the rest
> just spills on the stack, similarly how we punt when for stores to complex
> REGs if the bitsize/bitnum cover portions of both halves.
> 
> Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

OK.

Richard.

> 2019-11-19  Jakub Jelinek  <jakub@redhat.com>
> 
> 	PR middle-end/90840
> 	* expmed.c (store_bit_field_1): Handle the case where op0 is not a MEM
> 	and has a mode that doesn't have corresponding integral type.
> 
> 	* gcc.c-torture/compile/pr90840.c: New test.
> 
> --- gcc/expmed.c.jj	2019-11-15 00:37:32.000000000 +0100
> +++ gcc/expmed.c	2019-11-19 17:09:22.035129617 +0100
> @@ -840,6 +840,27 @@ store_bit_field_1 (rtx str_rtx, poly_uin
>        if (MEM_P (op0))
>  	op0 = adjust_bitfield_address_size (op0, op0_mode.else_blk (),
>  					    0, MEM_SIZE (op0));
> +      else if (!op0_mode.exists ())
> +	{
> +	  if (ibitnum == 0
> +	      && known_eq (ibitsize, GET_MODE_BITSIZE (GET_MODE (op0)))
> +	      && MEM_P (value)
> +	      && !reverse)
> +	    {
> +	      value = adjust_address (value, GET_MODE (op0), 0);
> +	      emit_move_insn (op0, value);
> +	      return true;
> +	    }
> +	  if (!fallback_p)
> +	    return false;
> +	  rtx temp = assign_stack_temp (GET_MODE (op0),
> +					GET_MODE_SIZE (GET_MODE (op0)));
> +	  emit_move_insn (temp, op0);
> +	  store_bit_field_1 (temp, bitsize, bitnum, 0, 0, fieldmode, value,
> +			     reverse, fallback_p);
> +	  emit_move_insn (op0, temp);
> +	  return true;
> +	}
>        else
>  	op0 = gen_lowpart (op0_mode.require (), op0);
>      }
> --- gcc/testsuite/gcc.c-torture/compile/pr90840.c.jj	2019-11-19 17:18:31.361918896 +0100
> +++ gcc/testsuite/gcc.c-torture/compile/pr90840.c	2019-11-19 17:11:06.010575339 +0100
> @@ -0,0 +1,19 @@
> +/* PR middle-end/90840 */
> +struct S { long long a; int b; };
> +struct S foo (void);
> +struct __attribute__((packed)) T { long long a; char b; };
> +struct T baz (void);
> +
> +void
> +bar (void)
> +{
> +  _Complex long double c;
> +  *(struct S *) &c = foo ();
> +}
> +
> +void
> +qux (void)
> +{
> +  _Complex long double c;
> +  *(struct T *) &c = baz ();
> +}
> 
> 	Jakub
> 
>
Eric Botcazou Dec. 7, 2019, 12:21 p.m. UTC | #3
> On the following testcase we ICE on i686-linux (32-bit), because we store
> (first 96-bit, then 72-bit) structure into the first part of a 2x 96-bit
> complex long double, and for 96-bit floats there is no corresponding
> integral mode that covers it and we ICE when op0 is not in MEM (it is a
> REG).

The test triggers an ICE in simplify_subreg because the innermode is BLKmode:

  gcc_assert (innermode != BLKmode);

on PowerPC64/VxWorks at -O1 and above.  This comes from this call:

		  if (MEM_P (result))
 		    from_rtx = change_address (result, to_mode, NULL_RTX);
 		  else
 		    from_rtx
		      = simplify_gen_subreg (to_mode, result,
					     TYPE_MODE (TREE_TYPE (from)), 0);

in expand_assignment, where 'result' is not a MEM despite TREE_TYPE (from) 
having BLKmode.  That's as expected because 'from' is a CALL_EXPR whose result 
is returned as a TImode register to avoid using BLKmode and thus spilling it 
to memory in between.

It turns out that simplify_subreg contains another assertion:

  gcc_assert (GET_MODE (op) == innermode
	      || GET_MODE (op) == VOIDmode);

so we can equivalently pass GET_MODE (result) as the mode, except when it is 
VOIDmode in which case we can still use TYPE_MODE (TREE_TYPE (from)).

Tested on PowerPC64/VxWorks and x86-64/Linux, applied on mainline as obvious.


2019-12-07  Eric Botcazou  <ebotcazou@adacore.com>

	PR middle-end/90840
	* expr.c (expand_assignment): In the case of a CONCAT on the LHS, make
	sure to pass a valid inner mode in calls to simplify_gen_subreg.
diff mbox series

Patch

--- gcc/expmed.c.jj	2019-11-15 00:37:32.000000000 +0100
+++ gcc/expmed.c	2019-11-19 17:09:22.035129617 +0100
@@ -840,6 +840,27 @@  store_bit_field_1 (rtx str_rtx, poly_uin
       if (MEM_P (op0))
 	op0 = adjust_bitfield_address_size (op0, op0_mode.else_blk (),
 					    0, MEM_SIZE (op0));
+      else if (!op0_mode.exists ())
+	{
+	  if (ibitnum == 0
+	      && known_eq (ibitsize, GET_MODE_BITSIZE (GET_MODE (op0)))
+	      && MEM_P (value)
+	      && !reverse)
+	    {
+	      value = adjust_address (value, GET_MODE (op0), 0);
+	      emit_move_insn (op0, value);
+	      return true;
+	    }
+	  if (!fallback_p)
+	    return false;
+	  rtx temp = assign_stack_temp (GET_MODE (op0),
+					GET_MODE_SIZE (GET_MODE (op0)));
+	  emit_move_insn (temp, op0);
+	  store_bit_field_1 (temp, bitsize, bitnum, 0, 0, fieldmode, value,
+			     reverse, fallback_p);
+	  emit_move_insn (op0, temp);
+	  return true;
+	}
       else
 	op0 = gen_lowpart (op0_mode.require (), op0);
     }
--- gcc/testsuite/gcc.c-torture/compile/pr90840.c.jj	2019-11-19 17:18:31.361918896 +0100
+++ gcc/testsuite/gcc.c-torture/compile/pr90840.c	2019-11-19 17:11:06.010575339 +0100
@@ -0,0 +1,19 @@ 
+/* PR middle-end/90840 */
+struct S { long long a; int b; };
+struct S foo (void);
+struct __attribute__((packed)) T { long long a; char b; };
+struct T baz (void);
+
+void
+bar (void)
+{
+  _Complex long double c;
+  *(struct S *) &c = foo ();
+}
+
+void
+qux (void)
+{
+  _Complex long double c;
+  *(struct T *) &c = baz ();
+}