Stack regs constraints change proposal

Jan Hubicka hubicka@atrey.karlin.mff.cuni.cz
Wed Nov 3 04:40:00 GMT 1999


Hi
I am working on your suggestions and I've got caught into more weird problems.
I found that reload constaints hack, that allows clobbered registers to
be allocated. This keeps stack registers working, but for example silently
accepts (and miscompile following:)
   asm __volatile__ ("%0 %1 %2 %3 %4 %5 %6 %7"::"f"(a), "f"(b), "f"(c), "f"(d), "f"(e), "f"(f), "f"(g), "f"(h):"st");
(here are too many registers, so one gets allocated at the place of "st").

First problem is with suggested conversion of clobbers. I am now getting following asm statement:
   asm __volatile__ ("":"=t" (c): "0"(a), "u"(b):"st(1)");
correctly converted to:

(insn 27 25 29 (parallel[ 
            (set (reg/v:SF 26)
                (asm_operands/v ("") ("=t") 0[ 
                        (reg/v:SF 24)
                        (reg/v:SF 25)
                    ] 
                    [ 
                        (asm_input:SF ("0"))
                        (asm_input:SF ("u"))
                    ]  ("asm.c") 25))
            (clobber (reg:QI 18 fpsr))
            (clobber (reg:QI 17 flags))
            (clobber (reg/v:SF 25))
        ] ) -1 (nil)
    (nil))

But because there are missing constraints for the clobber, local alloc just assign the hard register
to it and it get's allocated to different register as the "input" register 25.

Same problem I will probably hit when I try to convert clobbers to scratches or not?

Can you help me?

Here I am attaching my testcase (that contains some interesting stuff) and my patch.
You may take a look how weird job have that function do.

main()
{
   float a=1,b=2,c=3,d=4,e=5,f=6,g=7,h=8,i=9;
   /* Nonsential but correct according to the docs, currently refused, originally crashed.  */
#if 0
   /* Causes fatal signal in gcc.  */
   asm __volatile__ (""::"u"(a));
#endif
   /* Correct, accepted, miscompilled.  */
#if 0
   /* Is incorrectly handled by reg-stack - top of stack ought to be popped, but isn't.  */
   asm __volatile__ (""::"t"(a):"st");
#endif
   /* Correctly accepted and compiled. */
#if 0
   asm __volatile__ ("":"=t" (a), "=u"(a):"0"(a));  /*fsincos */
   asm __volatile__ ("":"+t" (a): "u"(b):"st(1)");  /*fyl2xp1 */
   asm __volatile__ ("":"=mt" (c));
   asm __volatile__ ("":"=t" (c):"0"(c));
#endif
   asm __volatile__ ("":"=t" (c): "0"(a), "u"(b):"st(1)");
#if 0
   /* Incorrect, refused. */
   asm __volatile__ ("":"=u" (c));  /* Rule  #2 */
   asm __volatile ("":"=u" (c):"t"(a));  /* Rule #2 */
   asm __volatile__ ("":"=t" (c):"0"(c),"u"(c),"f"(c));  /* Rule #3 */
   asm __volatile__ ("":"=f" (c));  /* Rule  #4 */
   asm __volatile__ ("":"=u" (c));  /* Rule  #5 */
   asm __volatile__ ("%0 %1 %2 %3 %4 %5 %6 %7 %8"::"f"(a), "f"(b), "f"(c), "f"(d), "f"(e), "f"(f), "f"(g), "f"(h),"f"(i));
#endif
}

*** stmt.c.or	Wed Nov  3 12:26:52 1999
--- stmt.c	Wed Nov  3 13:29:23 1999
*************** expand_asm (body)
*** 1268,1273 ****
--- 1268,1596 ----
  				TREE_STRING_POINTER (body)));
    last_expr_type = 0;
  }
+ #ifdef STACK_REGS
+ /* This function check some extra rules required for i387 asm operands.
+    The syntax of asm statements as documented results in incorrect form of asm
+    statements and reload fails.  So we do some modification on the generated
+    function in order to make it correct.  Thats why it is called after
+    insn is generated.  */
+ static int
+ check_asm_stack_operands (body, n_inputs, n_outputs, constraints, n_alternatives, operand_rtx)
+      rtx body;
+      int n_inputs;
+      int n_outputs;
+      char **constraints;
+      int n_alternatives;
+      rtx *operand_rtx;
+ {
+   int n_clobbers = 0;
+   rtx **clobber_reg;
+   char *nonspecific;
+   char *earlyclobber;
+   char clobbered[LAST_STACK_REG + 1];
+   int n;
+   int alternative;
+   int i;
+   int reg_used_as_output[2];
+   int reg_used_as_input[2];
+   int reg_implicitly_dies[2];
+   int reg_used[2];
+   int inout[2];
+ 
+   /* Operands that are popped because of clobbers must be same in
+      all alternatives, so we can be able to convert clobbers to pseudos. */
+   int popped_operands[2];
+   /* Mask of clobbers to replace by popped operands.  */
+   int clobber_mask;
+ 
+   memset (clobbered, 0, sizeof (clobbered));
+   if (!(n_inputs + n_outputs))
+     return 1;
+ 
+   nonspecific = alloca (n_inputs + n_outputs);
+   earlyclobber = alloca (n_inputs + n_outputs);
+ 
+   /* Set up CLOBBER_REG.  */
+ 
+   if (GET_CODE (body) == PARALLEL)
+     {
+       clobber_reg = (rtx **) alloca (XVECLEN (body, 0) * sizeof (rtx));
+ 
+       for (i = 0; i < XVECLEN (body, 0); i++)
+ 	if (GET_CODE (XVECEXP (body, 0, i)) == CLOBBER)
+ 	  {
+ 	    rtx clobber = XVECEXP (body, 0, i);
+ 	    rtx *reg = &XEXP (clobber, 0);
+ 
+ 	    if (GET_CODE (*reg) == SUBREG && GET_CODE (SUBREG_REG (*reg)) == REG)
+ 	      reg = &SUBREG_REG (*reg);
+ 
+ 	    if (STACK_REG_P (*reg))
+ 	      {
+ 		clobber_reg[n_clobbers] = reg;
+ 		clobbered[REGNO (*reg)] = 1;
+ 		n_clobbers++;
+ 	      }
+ 	  }
+     }
+ 
+   popped_operands[0] = -1;
+   popped_operands[1] = -1;
+ 
+   /* Now search trought alternatives and verify the separately.
+ 
+      We also collect information about clobbered registers that
+      represent pop.  These are converted to pseudos later, so
+      conversions must match between all alternatives. So "schizofrenic"
+      asm statements are not allowed.  */
+ 
+   for (alternative = 0; alternative < n_alternatives; alternative++)
+     {
+       int fpreg = 0;
+       int popped_operands_here[2];
+       int clobber_mask_here = 0;
+ 
+       for (i = 0; i < 2; i++)
+ 	{
+ 	  reg_used_as_output[i] = -1;
+ 	  reg_used_as_input[i] = -1;
+ 	  reg_used[i] = -1;
+ 	  inout[i] = 0;
+ 	}
+       memset (nonspecific, 0, n_inputs + n_outputs);
+       memset (earlyclobber, 0, n_inputs + n_outputs);
+       for (n = 0; n < n_outputs; n++)
+ 	{
+ 	  while (*constraints[n] && *constraints[n] != ',')
+ 	    switch (*(constraints[n]++))
+ 	      {
+ 	      case 'V': case 'm': case 'o': case '<': case '>':
+ 	      case 'E': case 'F': case 'G': case 'H': case 'X':
+ 	      case 's': case 'i': case 'n': case 'I': case 'J':
+ 	      case 'K': case 'L': case 'M': case 'N': case 'O':
+ 	      case 'P': case 'Q': case 'R': case 'S': case 'T':
+ 	      case 'U': case 'p': case 'r': case 'g':
+ 		nonspecific[n] = 1;
+ 		break;
+ 	      case 'f':
+ 		/* Enforce rule #4: Output operands must specifically
+ 		   indicate which reg an output appears in after
+ 		   an asm.  "=f" is not allowed: the operand constraints
+ 		   must select a class with a single reg. */
+ 		error ("Output constraint may not specify 'f' class.");
+ 		return 0;
+ 		break;
+ 	      case '&':
+ 		earlyclobber[n] = 1;
+ 		break;
+ 	      case 't':
+ 		if (reg_used[0] != -1)
+ 		  {
+ 		    error ("Class 't' already occupied.");
+ 		    return 0;
+ 		  }
+ 		reg_used_as_output[0] = n;
+ 		reg_used[0] = n;
+ 		if (clobbered[FIRST_STACK_REG])
+ 		  {
+ 		    error ("Output register 'st' may not be clobbered.");
+ 		    return 0;
+ 		  }
+ 		break;
+ 	      case 'u':
+ 		if (reg_used[1] != -1)
+ 		  {
+ 		    error ("Class 'u' already occupied.");
+ 		    return 0;
+ 		  }
+ 		reg_used_as_output[1] = n;
+ 		reg_used[1] = n;
+ 		if (clobbered[FIRST_STACK_REG + 1])
+ 		  {
+ 		    error ("Output register 'st(1)' may not be clobbered.");
+ 		    return 0;
+ 		  }
+ 		break;
+ 	      }
+ 	  constraints[n]++;
+ 	  if (reg_used_as_output[0] == n && reg_used_as_output[1] == n)
+ 	    {
+ 	      error ("Single output constraint may specify both 't' and 'u'");
+ 	      return 0;
+ 	    }
+ 	}
+       /* Enforce rule #5: Output operands must start at the top of
+          the reg-stack: output operands may not "skip" a reg.  */
+ 
+       if (reg_used_as_output[0] == -1 && reg_used_as_output[1] != -1)
+ 	{
+ 	  error ("Output stack regs must be grouped at top of stack");
+ 	  return 0;
+ 	}
+ 
+ 
+       for (n = n_outputs; n < n_outputs + n_inputs; n++)
+ 	{
+ 	  while (*constraints[n] && *constraints[n] != ',')
+ 	    switch (*(constraints[n]++))
+ 	      {
+ 	      case 'V': case 'm': case 'o': case '<': case '>':
+ 	      case 'E': case 'F': case 'G': case 'H': case 'X':
+ 	      case 's': case 'i': case 'n': case 'I': case 'J':
+ 	      case 'K': case 'L': case 'M': case 'N': case 'O':
+ 	      case 'P': case 'Q': case 'R': case 'S': case 'T':
+ 	      case 'U': case 'p': case 'r': case 'g':
+ 		nonspecific[n] = 1;
+ 		break;
+ 	      case '0': case '1': case '2': case '3': case '4':
+ 	      case '5': case '6': case '7': case '8': case '9':
+ 		{
+ 		  int num = constraints[n][-1] - '0';
+ 		  for (i = 0; i < 2; i++)
+ 		    if (reg_used_as_output[i] == num)
+ 		      {
+ 		        inout[i] = 1;
+ 			reg_used_as_input[i] = n;
+ 			if (nonspecific [reg_used_as_output[i]])
+ 			  {
+ 			     error ("Implicitly popped registers must be explicitly specified.");
+ 			     return 0;
+ 			  }
+ 		      }
+ 		}
+ 		break;
+ 	      case 'f':
+ 		fpreg = 1;
+ 		break;
+ 	      case 't':
+ 		if (reg_used[0] != -1)
+ 		  {
+ 		    error ("Output class 't' already occupied.");
+ 		    return 0;
+ 		  }
+ 		reg_used_as_input[0] = n;
+ 		reg_used[0] = n;
+ 		break;
+ 	      case 'u':
+ 		if (reg_used[1] != -1)
+ 		  {
+ 		    error ("Output class 'u' already occupied.");
+ 		    return 0;
+ 		  }
+ 		reg_used_as_input[1] = n;
+ 		reg_used[1] = n;
+ 		break;
+ 	      }
+ 	  constraints[n]++;
+ 	  if (reg_used_as_input[0] == n && reg_used_as_input[1] == n)
+ 	    nonspecific[n] = 1;
+ 	}
+ 
+       /* Identify implicitly popped input operands - clobbered or inout.  */
+       for (i = 0;i < 2;i++)
+ 	if (inout[i]
+ 	    || (reg_used_as_input[i] != -1
+ 		&& clobbered[i + FIRST_STACK_REG]))
+ 	  reg_implicitly_dies[i]=1;
+         else
+ 	  reg_implicitly_dies[i]=0;
+ 
+       /* We must ensure that the constraints of implicitly dead registers
+ 	 doun't allow other posibility.  */
+       for (i=0;i<2;i++)
+          if (reg_implicitly_dies[i]
+ 	     && nonspecific [reg_used_as_input[i]])
+ 	{
+ 	  error ("Implicitly popped registers must be explicitly specified.");
+ 	  return 0;
+ 	}
+ 
+       /* Enforce rule #2: All implicitly popped input regs must be closer
+          to the top of the reg-stack than any input data that is not
+          implicitly popped.  */
+ 
+       if (reg_implicitly_dies[1] && !reg_implicitly_dies[0])
+ 	{
+ 	  error ("Implicitly popped regs must be grouped at top of stack");
+ 	  return 0;
+ 	}
+ 
+       /* Enforce rule #3: If any input operand uses the "f" constraint, all
+          output constraints must use the "&" earlyclobber.  */
+ 
+       for (i = 0; i < 2; i++)
+ 	if (fpreg && reg_used_as_output[i] != -1
+ 	    && !earlyclobber[reg_used_as_output[i]])
+ 	  {
+ 	    error ("Output operand %d must use `&' constraint",
+ 		   reg_used_as_output[i]);
+ 	    return 0;
+ 	  }
+ 
+       /* Asm statement explicitly mentioning 't', but not 'u' does not make sense
+ 	 and reg-stack is unable to cope with it.  */
+ 
+       if (reg_used[1] != -1 && (reg_used[0] == -1 || nonspecific[reg_used[0]]))
+ 	{
+ 	  error ("'u' used but top of stack is not specified.");
+ 	  return 0;
+ 	}
+        /* Identify operands that are popped because of the clobber
+ 	  and thus clobbers needs to be converted to matching constraints.  */
+        clobber_mask_here = 0;
+        for (i=0;i<2;i++)
+ 	 if (reg_implicitly_dies[i] && reg_used_as_output[i] == -1)
+ 	   {
+ 	     popped_operands_here[i] = reg_used_as_input[i];
+ 	   printf("%i %i\n",popped_operands_here[i], reg_used_as_input[i]);
+ 	     clobber_mask_here |= 1<<i;
+ 	   }
+ 	 else
+ 	   popped_operands_here[i] = -1;
+ 
+        /* Ensure that clobber_mask match.  */
+        if (alternative && clobber_mask_here != clobber_mask)
+ 	 error ("Implicitly popped and input/output operands must match in all alternatives.");
+ 
+        clobber_mask = clobber_mask_here;
+        /* Sort popped operands in decreasing order to ensure match
+ 	  and that -1 is second. */
+        if (popped_operands_here[0] < popped_operands_here[1])
+ 	 {
+ 	   int tmp = popped_operands_here[0];
+ 	   printf("%i %i\n",popped_operands_here[0], popped_operands_here[1]);
+ 	   popped_operands_here[0] = popped_operands_here[1];
+ 	   popped_operands_here[1] = tmp;
+ 	   printf("%i %i\n",popped_operands_here[0], popped_operands_here[1]);
+ 	 }
+        /* Ensure that clobber_mask match.  */
+        for (i=0;i<2;i++)
+          {
+            if (alternative && popped_operands[i] != popped_operands_here[i])
+ 	     {
+ 	        error ("Registers must be popped in same operands for all alternatives.");
+ 		return 0;
+ 	     }
+ 	   else
+ 	    popped_operands[i] = popped_operands_here[i];
+          }
+     }
+ 
+   i = 0;
+   for (n = 0; n < n_clobbers ; n++)
+     {
+       if ((1<<(REGNO (*clobber_reg[n]) - FIRST_STACK_REG)) & clobber_mask)
+ 	{
+ 	  fprintf(stderr,"Replacing %i\n",popped_operands[i]);
+ 	  debug_rtx (operand_rtx[popped_operands[i]]);
+ 	  *clobber_reg[n] = copy_rtx (operand_rtx[popped_operands[i]]);
+ 	  i++;
+ 	}
+     }
+ 
+   return 1;
+ }
+ #endif
  
  /* Generate RTL for an asm statement with arguments.
     STRING is the instruction template.
*************** expand_asm_operands (string, outputs, in
*** 1297,1313 ****
    int noutputs = list_length (outputs);
    int ninout = 0;
    int nclobbers;
    tree tail;
    register int i;
!   /* Vector of RTX's of evaluated output operands.  */
!   rtx *output_rtx = (rtx *) alloca (noutputs * sizeof (rtx));
    int *inout_opnum = (int *) alloca (noutputs * sizeof (int));
    rtx *real_output_rtx = (rtx *) alloca (noutputs * sizeof (rtx));
    enum machine_mode *inout_mode
      = (enum machine_mode *) alloca (noutputs * sizeof (enum machine_mode));
    /* The insn we have emitted.  */
    rtx insn;
  
    /* An ASM with no outputs needs to be treated as volatile, for now.  */
    if (noutputs == 0)
      vol = 1;
--- 1620,1643 ----
    int noutputs = list_length (outputs);
    int ninout = 0;
    int nclobbers;
+   int nalternatives = 0;
    tree tail;
    register int i;
!   /* Vector of RTX's of evaluated operands.  */
!   rtx *operand_rtx = (rtx *) alloca ((noutputs * 2 + ninputs) * sizeof (rtx));
    int *inout_opnum = (int *) alloca (noutputs * sizeof (int));
+   /* Vector of RTX's of all operands.  */
    rtx *real_output_rtx = (rtx *) alloca (noutputs * sizeof (rtx));
    enum machine_mode *inout_mode
      = (enum machine_mode *) alloca (noutputs * sizeof (enum machine_mode));
    /* The insn we have emitted.  */
    rtx insn;
  
+   /* Collect input and output constraint strings here.  This is usefull
+      to simplify check_asm_stack_regs.  */
+   char **constraint_str = 
+     (char **) alloca ((noutputs * 2 + ninputs) * sizeof (char **));
+ 
    /* An ASM with no outputs needs to be treated as volatile, for now.  */
    if (noutputs == 0)
      vol = 1;
*************** expand_asm_operands (string, outputs, in
*** 1338,1344 ****
    for (tail = clobbers; tail; tail = TREE_CHAIN (tail))
      {
        char *regname = TREE_STRING_POINTER (TREE_VALUE (tail));
- 
        i = decode_reg_name (regname);
        if (i >= 0 || i == -4)
  	++nclobbers;
--- 1668,1673 ----
*************** expand_asm_operands (string, outputs, in
*** 1353,1361 ****
    if (outputs || inputs)
      {
        tree tmp = TREE_PURPOSE (outputs ? outputs : inputs);
-       int nalternatives = n_occurrences (',', TREE_STRING_POINTER (tmp));
        tree next = inputs;
  
        if (nalternatives + 1 > MAX_RECOG_ALTERNATIVES)
  	{
  	  error ("too many alternatives in `asm'");
--- 1682,1691 ----
    if (outputs || inputs)
      {
        tree tmp = TREE_PURPOSE (outputs ? outputs : inputs);
        tree next = inputs;
  
+       nalternatives = n_occurrences (',', TREE_STRING_POINTER (tmp));
+ 
        if (nalternatives + 1 > MAX_RECOG_ALTERNATIVES)
  	{
  	  error ("too many alternatives in `asm'");
*************** expand_asm_operands (string, outputs, in
*** 1366,1378 ****
        while (tmp)
  	{
  	  char *constraint = TREE_STRING_POINTER (TREE_PURPOSE (tmp));
- 
  	  if (n_occurrences (',', constraint) != nalternatives)
  	    {
  	      error ("operand constraints for `asm' differ in number of alternatives");
  	      return;
  	    }
- 
  	  if (TREE_CHAIN (tmp))
  	    tmp = TREE_CHAIN (tmp);
  	  else
--- 1696,1706 ----
*************** expand_asm_operands (string, outputs, in
*** 1401,1408 ****
  	 the worst that happens if we get it wrong is we issue an error
  	 message.  */
  
!       c_len = strlen (TREE_STRING_POINTER (TREE_PURPOSE (tail)));
        constraint = TREE_STRING_POINTER (TREE_PURPOSE (tail));
  
        /* Allow the `=' or `+' to not be at the beginning of the string,
  	 since it wasn't explicitly documented that way, and there is a
--- 1729,1737 ----
  	 the worst that happens if we get it wrong is we issue an error
  	 message.  */
  
!       c_len = TREE_STRING_LENGTH (TREE_PURPOSE (tail)) - 1;
        constraint = TREE_STRING_POINTER (TREE_PURPOSE (tail));
+       constraint_str[i] = constraint;
  
        /* Allow the `=' or `+' to not be at the beginning of the string,
  	 since it wasn't explicitly documented that way, and there is a
*************** expand_asm_operands (string, outputs, in
*** 1510,1533 ****
  	  if (! allows_reg)
  	    mark_addressable (TREE_VALUE (tail));
  
! 	  output_rtx[i]
  	    = expand_expr (TREE_VALUE (tail), NULL_RTX, VOIDmode,
  			   EXPAND_MEMORY_USE_WO);
  
! 	  if (! allows_reg && GET_CODE (output_rtx[i]) != MEM)
  	    error ("output number %d not directly addressable", i);
! 	  if (! allows_mem && GET_CODE (output_rtx[i]) == MEM)
  	    {
!     	      real_output_rtx[i] = protect_from_queue (output_rtx[i], 1);
! 	      output_rtx[i] = gen_reg_rtx (GET_MODE (output_rtx[i]));
  	      if (is_inout)
! 		emit_move_insn (output_rtx[i], real_output_rtx[i]);
  	    }
  	}
        else
  	{
! 	  output_rtx[i] = assign_temp (type, 0, 0, 0);
! 	  TREE_VALUE (tail) = make_tree (type, output_rtx[i]);
  	}
  
        if (is_inout)
--- 1839,1862 ----
  	  if (! allows_reg)
  	    mark_addressable (TREE_VALUE (tail));
  
! 	  operand_rtx[i]
  	    = expand_expr (TREE_VALUE (tail), NULL_RTX, VOIDmode,
  			   EXPAND_MEMORY_USE_WO);
  
! 	  if (! allows_reg && GET_CODE (operand_rtx[i]) != MEM)
  	    error ("output number %d not directly addressable", i);
! 	  if (! allows_mem && GET_CODE (operand_rtx[i]) == MEM)
  	    {
!     	      real_output_rtx[i] = protect_from_queue (operand_rtx[i], 1);
! 	      operand_rtx[i] = gen_reg_rtx (GET_MODE (operand_rtx[i]));
  	      if (is_inout)
! 		emit_move_insn (operand_rtx[i], real_output_rtx[i]);
  	    }
  	}
        else
  	{
! 	  operand_rtx[i] = assign_temp (type, 0, 0, 0);
! 	  TREE_VALUE (tail) = make_tree (type, operand_rtx[i]);
  	}
  
        if (is_inout)
*************** expand_asm_operands (string, outputs, in
*** 1581,1587 ****
  	  return;
  	}
  
!       c_len = strlen (TREE_STRING_POINTER (TREE_PURPOSE (tail)));
        constraint = TREE_STRING_POINTER (TREE_PURPOSE (tail));
        orig_constraint = constraint;
  
--- 1910,1916 ----
  	  return;
  	}
  
!       c_len = TREE_STRING_LENGTH (TREE_PURPOSE (tail)) - 1;
        constraint = TREE_STRING_POINTER (TREE_PURPOSE (tail));
        orig_constraint = constraint;
  
*************** expand_asm_operands (string, outputs, in
*** 1593,1600 ****
  	  case '+':  case '=':  case '&':
  	    if (constraint == orig_constraint)
  	      {
! 	        error ("input operand constraint contains `%c'",
! 		       constraint[j]);
  	        return;
  	      }
  	    break;
--- 1922,1928 ----
  	  case '+':  case '=':  case '&':
  	    if (constraint == orig_constraint)
  	      {
! 	        error ("input operand constraint contains `%c'", constraint[j]);
  	        return;
  	      }
  	    break;
*************** expand_asm_operands (string, outputs, in
*** 1642,1652 ****
  		|| (j == 1 && c_len == 2 && constraint[0] == '%'))
  	      {
  		tree o = outputs;
- 
  		for (j = constraint[j] - '0'; j > 0; --j)
  		  o = TREE_CHAIN (o);
  	
! 		c_len = strlen (TREE_STRING_POINTER (TREE_PURPOSE (o)));
  		constraint = TREE_STRING_POINTER (TREE_PURPOSE (o));
  		j = 0;
  		break;
--- 1970,1979 ----
  		|| (j == 1 && c_len == 2 && constraint[0] == '%'))
  	      {
  		tree o = outputs;
  		for (j = constraint[j] - '0'; j > 0; --j)
  		  o = TREE_CHAIN (o);
  	
! 		c_len = TREE_STRING_LENGTH (TREE_PURPOSE (o)) - 1;
  		constraint = TREE_STRING_POINTER (TREE_PURPOSE (o));
  		j = 0;
  		break;
*************** expand_asm_operands (string, outputs, in
*** 1689,1695 ****
  	      emit_move_insn (memloc, op);
  	      op = memloc;
  	    }
- 
  	  else if (GET_CODE (op) == MEM && MEM_VOLATILE_P (op))
  	    /* We won't recognize volatile memory as available a
  	       memory_operand at this point.  Ignore it.  */
--- 2016,2021 ----
*************** expand_asm_operands (string, outputs, in
*** 1703,1723 ****
  	    warning ("asm operand %d probably doesn't match constraints", i);
  	}
        XVECEXP (body, 3, i) = op;
  
        XVECEXP (body, 4, i)      /* constraints */
  	= gen_rtx_ASM_INPUT (TYPE_MODE (TREE_TYPE (TREE_VALUE (tail))),
  			     orig_constraint);
        i++;
      }
  
!   /* Protect all the operands from the queue now that they have all been
!      evaluated.  */
  
    for (i = 0; i < ninputs - ninout; i++)
      XVECEXP (body, 3, i) = protect_from_queue (XVECEXP (body, 3, i), 0);
  
    for (i = 0; i < noutputs; i++)
!     output_rtx[i] = protect_from_queue (output_rtx[i], 1);
  
    /* For in-out operands, copy output rtx to input rtx. */
    for (i = 0; i < ninout; i++)
--- 2029,2051 ----
  	    warning ("asm operand %d probably doesn't match constraints", i);
  	}
        XVECEXP (body, 3, i) = op;
+       operand_rtx[noutputs + 1] = op;
  
        XVECEXP (body, 4, i)      /* constraints */
  	= gen_rtx_ASM_INPUT (TYPE_MODE (TREE_TYPE (TREE_VALUE (tail))),
  			     orig_constraint);
+       constraint_str[noutputs + i] = orig_constraint;
        i++;
      }
  
!   /* Protect all the operands from the queue,
!      now that they have all been evaluated.  */
  
    for (i = 0; i < ninputs - ninout; i++)
      XVECEXP (body, 3, i) = protect_from_queue (XVECEXP (body, 3, i), 0);
  
    for (i = 0; i < noutputs; i++)
!     operand_rtx[i] = protect_from_queue (operand_rtx[i], 1);
  
    /* For in-out operands, copy output rtx to input rtx. */
    for (i = 0; i < ninout; i++)
*************** expand_asm_operands (string, outputs, in
*** 1725,1735 ****
        int j = inout_opnum[i];
  
        XVECEXP (body, 3, ninputs - ninout + i)      /* argvec */
! 	= output_rtx[j];
        XVECEXP (body, 4, ninputs - ninout + i)      /* constraints */
  	= gen_rtx_ASM_INPUT (inout_mode[i], digit_strings[j]);
      }
  
    /* Now, for each output, construct an rtx
       (set OUTPUT (asm_operands INSN OUTPUTNUMBER OUTPUTCONSTRAINT
  			       ARGVEC CONSTRAINTS))
--- 2053,2066 ----
        int j = inout_opnum[i];
  
        XVECEXP (body, 3, ninputs - ninout + i)      /* argvec */
! 	= operand_rtx[j];
!       operand_rtx[noutputs + ninputs - ninout + 1] = operand_rtx[j];
        XVECEXP (body, 4, ninputs - ninout + i)      /* constraints */
  	= gen_rtx_ASM_INPUT (inout_mode[i], digit_strings[j]);
+       constraint_str[noutputs + ninputs - ninout + i] = digit_strings[j];
      }
  
+ 
    /* Now, for each output, construct an rtx
       (set OUTPUT (asm_operands INSN OUTPUTNUMBER OUTPUTCONSTRAINT
  			       ARGVEC CONSTRAINTS))
*************** expand_asm_operands (string, outputs, in
*** 1738,1768 ****
    if (noutputs == 1 && nclobbers == 0)
      {
        XSTR (body, 1) = TREE_STRING_POINTER (TREE_PURPOSE (outputs));
!       insn = emit_insn (gen_rtx_SET (VOIDmode, output_rtx[0], body));
      }
! 
!   else if (noutputs == 0 && nclobbers == 0)
!     {
!       /* No output operands: put in a raw ASM_OPERANDS rtx.  */
!       insn = emit_insn (body);
!     }
! 
!   else
      {
        rtx obody = body;
        int num = noutputs;
! 
!       if (num == 0)
! 	num = 1;
! 
        body = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (num + nclobbers));
  
        /* For each output operand, store a SET.  */
        for (i = 0, tail = outputs; tail; tail = TREE_CHAIN (tail), i++)
  	{
  	  XVECEXP (body, 0, i)
  	    = gen_rtx_SET (VOIDmode,
! 			   output_rtx[i],
  			   gen_rtx_ASM_OPERANDS
  			   (VOIDmode,
  			    TREE_STRING_POINTER (string),
--- 2069,2090 ----
    if (noutputs == 1 && nclobbers == 0)
      {
        XSTR (body, 1) = TREE_STRING_POINTER (TREE_PURPOSE (outputs));
!       body = gen_rtx_SET (VOIDmode, operand_rtx[0], body);
      }
!   else if (noutputs != 0 || nclobbers == 0)
      {
        rtx obody = body;
        int num = noutputs;
!       if (num == 0) num = 1;
        body = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (num + nclobbers));
  
        /* For each output operand, store a SET.  */
+ 
        for (i = 0, tail = outputs; tail; tail = TREE_CHAIN (tail), i++)
  	{
  	  XVECEXP (body, 0, i)
  	    = gen_rtx_SET (VOIDmode,
! 			   operand_rtx[i],
  			   gen_rtx_ASM_OPERANDS
  			   (VOIDmode,
  			    TREE_STRING_POINTER (string),
*************** expand_asm_operands (string, outputs, in
*** 1809,1823 ****
  	  XVECEXP (body, 0, i++)
  	    = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (QImode, j));
  	}
- 
-       insn = emit_insn (body);
      }
  
    /* For any outputs that needed reloading into registers, spill them
       back to where they belong.  */
    for (i = 0; i < noutputs; ++i)
      if (real_output_rtx[i])
!       emit_move_insn (real_output_rtx[i], output_rtx[i]);
  
    free_temp_slots ();
  }
--- 2131,2151 ----
  	  XVECEXP (body, 0, i++)
  	    = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (QImode, j));
  	}
      }
  
+ #ifdef STACK_REGS
+   if (!check_asm_stack_operands (body, ninputs, noutputs,
+ 				 constraint_str, nalternatives + 1, operand_rtx))
+     return;
+ #endif
+ 
+   insn = emit_insn (body);
+ 
    /* For any outputs that needed reloading into registers, spill them
       back to where they belong.  */
    for (i = 0; i < noutputs; ++i)
      if (real_output_rtx[i])
!       emit_move_insn (real_output_rtx[i], operand_rtx[i]);
  
    free_temp_slots ();
  }


More information about the Gcc-patches mailing list