PR 34995: EXTRA_MEMORY_CONSTRAINTs and constant addresses

Richard Sandiford rsandifo@nildram.co.uk
Wed Jan 30 14:46:00 GMT 2008


The documentation for EXTRA_MEMORY_CONSTRAINT says:

   It should return 1 if the operand type represented by the constraint
   at the start of @var{str}, the first letter of which is the letter @var{c},
   comprises a subset of all memory references including
   all those whose address is simply a base register.

What the documentation doesn't say is that if GO_IF_LEGITIMATE_ADDRESS
accepts constant-pool references of the form (mem (symbol_ref X)),
all EXTRA_MEMORY_CONSTRAINTS must too.  This is due to:

/* Return 1 if alternative number ALTNUM in constraint-string CONSTRAINT
   accepts a memory operand with constant address.  */

static int
alternative_allows_memconst (const char *constraint, int altnum)
{
  ...
  for (; (c = *constraint) && c != ',' && c != '#';
       constraint += CONSTRAINT_LEN (c, constraint))
    if (c == 'm' || c == 'o' || EXTRA_MEMORY_CONSTRAINT (c, constraint))
      return 1;
  return 0;
}

So if reload forces a constant into the constant pool for an
EXTRA_MEMORY_CONSTRAINT, and if the result is valid accordingly
to GO_IF_LEGITIMATE_ADDRESS, we unconditionally assume that the
result is valid for the extra constraint too, rather than reloading
the address into a register.

This causes problems with MIPS "W" constraints, which only allow
addresses based on BASE_REGS.  It could theoretically also affect
"R" constraints with -mno-explicit-relocs, because some valid
(mem (symbol_ref X)) addresses are not single-insn ones.

The relevant find_reloads code is:

    if (! goal_alternative_win[i]
	&& CONST_POOL_OK_P (recog_data.operand[i])
	&& ((PREFERRED_RELOAD_CLASS (recog_data.operand[i],
				     (enum reg_class) goal_alternative[i])
	     == NO_REGS)
	    || no_input_reloads)
	&& operand_mode[i] != VOIDmode)
      {
	substed_operand[i] = recog_data.operand[i]
	  = find_reloads_toplev (force_const_mem (operand_mode[i],
						  recog_data.operand[i]),
				 i, address_type[i], ind_levels, 0, insn,
				 NULL);
	if (alternative_allows_memconst (recog_data.constraints[i],
					 goal_alternative_number))
	  goal_alternative_win[i] = 1;
      }

Now if find_reloads_toplev generates an address reload for the
constant-pool reference, EXTRA_MEMORY_CONSTRAINTs must indeed accept
the result; the original address will have been a SYMBOL_REF and the
post-reload form will be a base register.  However, if no address
reloads are generated, I think EXTRA_CONSTRAINT_STR should have the
option of forcing a reload anyway.  If we don't set goal_alternative_win
to 1, such a reload will be generated by the normal 'o'/E_M_C handling:

    if (! goal_alternative_win[i])
      {
	/* Operands that match previous ones have already been handled.  */
	if (goal_alternative_matches[i] >= 0)
	  ;
	/* Handle an operand with a nonoffsettable address
	   appearing where an offsettable address will do
	   by reloading the address into a base register.

	   ??? We can also do this when the operand is a register and
	   reg_equiv_mem is not offsettable, but this is a bit tricky,
	   so we don't bother with it.  It may not be worth doing.  */
	else if (goal_alternative_matched[i] == -1
		 && goal_alternative_offmemok[i]
		 && MEM_P (recog_data.operand[i]))
	  {
            ...

Boostrapped & regression-tested on x86_64-linux-gnu.  Also
regression-tested on mipsisa64-elfoabi.  OK to install?

Richard


gcc/
	PR rtl-optimization/34995
	* reload.c (alternative_allows_const_pool_ref): Take an rtx
	parameter and return a bool.  If the rtx parameter is nonnull,
	check that it satisfies an EXTRA_MEMORY_CONSTRAINT.
	(find_reloads): Update call accordingly.  Pass the new operand
	if it needed no address reloads, otherwise pass null.

Index: gcc/reload.c
===================================================================
--- gcc/reload.c	2008-01-29 10:06:14.000000000 +0000
+++ gcc/reload.c	2008-01-29 10:32:48.000000000 +0000
@@ -263,7 +263,7 @@ static rtx find_dummy_reload (rtx, rtx, 
 static int hard_reg_set_here_p (unsigned int, unsigned int, rtx);
 static struct decomposition decompose (rtx);
 static int immune_p (rtx, rtx, struct decomposition);
-static int alternative_allows_memconst (const char *, int);
+static bool alternative_allows_const_pool_ref (rtx, const char *, int);
 static rtx find_reloads_toplev (rtx, int, enum reload_type, int, int, rtx,
 				int *);
 static rtx make_memloc (rtx, int);
@@ -3836,13 +3836,19 @@ find_reloads (rtx insn, int replace, int
 	    || no_input_reloads)
 	&& operand_mode[i] != VOIDmode)
       {
+	int this_address_reloaded;
+
+	this_address_reloaded = 0;
 	substed_operand[i] = recog_data.operand[i]
 	  = find_reloads_toplev (force_const_mem (operand_mode[i],
 						  recog_data.operand[i]),
 				 i, address_type[i], ind_levels, 0, insn,
-				 NULL);
-	if (alternative_allows_memconst (recog_data.constraints[i],
-					 goal_alternative_number))
+				 &this_address_reloaded);
+	if (alternative_allows_const_pool_ref (this_address_reloaded == 0
+					       ? substed_operand[i]
+					       : NULL,
+					       recog_data.constraints[i],
+					       goal_alternative_number))
 	  goal_alternative_win[i] = 1;
       }
 
@@ -4498,13 +4504,16 @@ find_reloads (rtx insn, int replace, int
   return retval;
 }
 
-/* Return 1 if alternative number ALTNUM in constraint-string CONSTRAINT
-   accepts a memory operand with constant address.  */
+/* Return true if alternative number ALTNUM in constraint-string
+   CONSTRAINT is guaranteed to accept a reloaded constant-pool reference.
+   MEM gives the reference if it didn't need any reloads, otherwise it
+   is null.  */
 
-static int
-alternative_allows_memconst (const char *constraint, int altnum)
+static bool
+alternative_allows_const_pool_ref (rtx mem, const char *constraint, int altnum)
 {
   int c;
+
   /* Skip alternatives before the one requested.  */
   while (altnum > 0)
     {
@@ -4512,12 +4521,21 @@ alternative_allows_memconst (const char 
       altnum--;
     }
   /* Scan the requested alternative for 'm' or 'o'.
-     If one of them is present, this alternative accepts memory constants.  */
+     If one of them is present, this alternative accepts the result of
+     passing a constant-pool reference through find_reloads_toplev.
+
+     The same is true of extra memory constraints if the address
+     was reloaded into a register.  However, the target may elect
+     to disallow the original constant address, forcing it to be
+     reloaded into a register instead.  */
   for (; (c = *constraint) && c != ',' && c != '#';
        constraint += CONSTRAINT_LEN (c, constraint))
-    if (c == 'm' || c == 'o' || EXTRA_MEMORY_CONSTRAINT (c, constraint))
-      return 1;
-  return 0;
+    if (c == 'm'
+	|| c == 'o'
+	|| (EXTRA_MEMORY_CONSTRAINT (c, constraint)
+	    && (mem == NULL || EXTRA_CONSTRAINT_STR (mem, c, constraint))))
+      return true;
+  return false;
 }
 
 /* Scan X for memory references and scan the addresses for reloading.



More information about the Gcc-patches mailing list