stmt.c (parse_output_constraint): New function, split out from ...
authorMark Mitchell <mark@codesourcery.com>
Wed, 11 Jul 2001 17:35:56 +0000 (17:35 +0000)
committerMark Mitchell <mmitchel@gcc.gnu.org>
Wed, 11 Jul 2001 17:35:56 +0000 (17:35 +0000)
* stmt.c (parse_output_constraint): New function, split out
from ...
(expand_asm_operands): ... here.  Use parse_output_constraint.
* tree.h (parse_output_constraint): Declare it.

* semantics.c (finish_asm_stmt): Mark the output operands
to an asm addressable, if necessary.

From-SVN: r43941

gcc/ChangeLog
gcc/cp/ChangeLog
gcc/cp/semantics.c
gcc/stmt.c
gcc/testsuite/g++.old-deja/g++.other/asm3.C [new file with mode: 0644]
gcc/tree.h

index 82cce17..7093a5c 100644 (file)
@@ -1,3 +1,10 @@
+2001-07-11  Mark Mitchell  <mark@codesourcery.com>
+
+       * stmt.c (parse_output_constraint): New function, split out 
+       from ...
+       (expand_asm_operands): ... here.  Use parse_output_constraint.
+       * tree.h (parse_output_constraint): Declare it.
+
 2001-07-11  Richard Henderson  <rth@redhat.com>
 
        * bitmap.c: Comment some functions; fiddle whitespace.
index 03d03d3..486533b 100644 (file)
@@ -1,3 +1,8 @@
+2001-07-11  Mark Mitchell  <mark@codesourcery.com>
+
+       * semantics.c (finish_asm_stmt): Mark the output operands
+       to an asm addressable, if necessary.
+
 2001-07-11  Ben Elliston  <bje@redhat.com>
 
        * Revert this change -- there is a subtle bug.
index c82d291..2bb4051 100644 (file)
@@ -894,23 +894,65 @@ finish_asm_stmt (cv_qualifier, string, output_operands,
     }
 
   if (!processing_template_decl)
-    for (t = input_operands; t; t = TREE_CHAIN (t))
-      {
-       tree converted_operand 
-         = decay_conversion (TREE_VALUE (t)); 
-
-       /* If the type of the operand hasn't been determined (e.g.,
-          because it involves an overloaded function), then issue an
-          error message.  There's no context available to resolve the
-          overloading.  */
-       if (TREE_TYPE (converted_operand) == unknown_type_node)
-         {
-           cp_error ("type of asm operand `%E' could not be determined", 
-                     TREE_VALUE (t));
-           converted_operand = error_mark_node;
-         }
-       TREE_VALUE (t) = converted_operand;
-      }
+    {
+      int i;
+      int ninputs;
+      int noutputs;
+
+      for (t = input_operands; t; t = TREE_CHAIN (t))
+       {
+         tree converted_operand 
+           = decay_conversion (TREE_VALUE (t)); 
+         
+         /* If the type of the operand hasn't been determined (e.g.,
+            because it involves an overloaded function), then issue
+            an error message.  There's no context available to
+            resolve the overloading.  */
+         if (TREE_TYPE (converted_operand) == unknown_type_node)
+           {
+             cp_error ("type of asm operand `%E' could not be determined", 
+                       TREE_VALUE (t));
+             converted_operand = error_mark_node;
+           }
+         TREE_VALUE (t) = converted_operand;
+       }
+
+      ninputs = list_length (input_operands);
+      noutputs = list_length (output_operands);
+
+      for (i = 0, t = output_operands; t; t = TREE_CHAIN (t), ++i)
+       {
+         bool allows_mem;
+         bool allows_reg;
+         bool is_inout;
+         const char *constraint;
+         tree operand;
+
+         constraint = TREE_STRING_POINTER (TREE_PURPOSE (t));
+         operand = TREE_VALUE (output_operands);
+
+         if (!parse_output_constraint (&constraint,
+                                       i, ninputs, noutputs,
+                                       &allows_mem,
+                                       &allows_reg,
+                                       &is_inout))
+           {
+             /* By marking the type as erroneous, we will not try to
+                process this operand again in expand_asm_operands.  */
+             TREE_TYPE (operand) = error_mark_node;
+             continue;
+           }
+
+         /* If the operand is a DECL that is going to end up in
+            memory, assume it is addressable.  This is a bit more
+            conservative than it would ideally be; the exact test is
+            buried deep in expand_asm_operands and depends on the
+            DECL_RTL for the OPERAND -- which we don't have at this
+            point.  */
+         if (!allows_reg && DECL_P (operand))
+           mark_addressable (operand);
+       }
+    }
 
   r = build_stmt (ASM_STMT, cv_qualifier, string,
                  output_operands, input_operands,
index ea73647..425c185 100644 (file)
@@ -1292,6 +1292,164 @@ expand_asm (body)
   last_expr_type = 0;
 }
 
+/* Parse the output constraint pointed to by *CONSTRAINT_P.  It is the
+   OPERAND_NUMth output operand, indexed from zero.  There are NINPUTS
+   inputs and NOUTPUTS outputs to this extended-asm.  Upon return,
+   *ALLOWS_MEM will be TRUE iff the constraint allows the use of a
+   memory operand.  Similarly, *ALLOWS_REG will be TRUE iff the
+   constraint allows the use of a register operand.  And, *IS_INOUT
+   will be true if the operand is read-write, i.e., if it is used as
+   an input as well as an output.  If *CONSTRAINT_P is not in
+   canonical form, it will be made canonical.  (Note that `+' will be
+   rpelaced with `=' as part of this process.)
+
+   Returns TRUE if all went well; FALSE if an error occurred.  */
+
+bool
+parse_output_constraint (constraint_p, 
+                        operand_num,
+                        ninputs,
+                        noutputs,
+                        allows_mem, 
+                        allows_reg, 
+                        is_inout)
+     const char **constraint_p;
+     int operand_num;
+     int ninputs;
+     int noutputs;
+     bool *allows_mem;
+     bool *allows_reg;
+     bool *is_inout;
+{
+  const char *constraint = *constraint_p;
+  const char *p;
+
+  /* Assume the constraint doesn't allow the use of either a register
+     or memory.  */
+  *allows_mem = false;
+  *allows_reg = false;
+
+  /* Allow the `=' or `+' to not be at the beginning of the string,
+     since it wasn't explicitly documented that way, and there is a
+     large body of code that puts it last.  Swap the character to
+     the front, so as not to uglify any place else.  */
+  p = strchr (constraint, '=');
+  if (!p)
+    p = strchr (constraint, '+');
+
+  /* If the string doesn't contain an `=', issue an error
+     message.  */
+  if (!p)
+    {
+      error ("output operand constraint lacks `='");
+      return false;
+    }
+
+  /* If the constraint begins with `+', then the operand is both read
+     from and written to.  */
+  *is_inout = (*p == '+');
+
+  /* Make sure we can specify the matching operand.  */
+  if (*is_inout && operand_num > 9)
+    {
+      error ("output operand constraint %d contains `+'", 
+            operand_num);
+      return false;
+    }
+
+  /* Canonicalize the output constraint so that it begins with `='.  */
+  if (p != constraint || is_inout)
+    {
+      char *buf;
+      size_t c_len = strlen (constraint);
+
+      if (p != constraint)
+       warning ("output constraint `%c' for operand %d is not at the beginning",
+                *p, operand_num);
+
+      /* Make a copy of the constraint.  */
+      buf = alloca (c_len + 1);
+      strcpy (buf, constraint);
+      /* Swap the first character and the `=' or `+'.  */
+      buf[p - constraint] = buf[0];
+      /* Make sure the first character is an `='.  (Until we do this,
+        it might be a `+'.)  */
+      buf[0] = '=';
+      /* Replace the constraint with the canonicalized string.  */
+      *constraint_p = ggc_alloc_string (buf, c_len);
+      constraint = *constraint_p;
+    }
+
+  /* Loop through the constraint string.  */
+  for (p = constraint + 1; *p; ++p)
+    switch (*p)
+      {
+      case '+':
+      case '=':
+       error ("operand constraint contains '+' or '=' at illegal position.");
+       return false;
+       
+      case '%':
+       if (operand_num + 1 == ninputs + noutputs)
+         {
+           error ("`%%' constraint used with last operand");
+           return false;
+         }
+       break;
+
+      case 'V':  case 'm':  case 'o':
+       *allows_mem = true;
+       break;
+
+      case '?':  case '!':  case '*':  case '&':  case '#':
+      case 'E':  case 'F':  case 'G':  case 'H':
+      case 's':  case 'i':  case 'n':
+      case 'I':  case 'J':  case 'K':  case 'L':  case 'M':
+      case 'N':  case 'O':  case 'P':  case ',':
+       break;
+
+      case '0':  case '1':  case '2':  case '3':  case '4':
+      case '5':  case '6':  case '7':  case '8':  case '9':
+       error ("matching constraint not valid in output operand");
+       return false;
+
+      case '<':  case '>':
+       /* ??? Before flow, auto inc/dec insns are not supposed to exist,
+          excepting those that expand_call created.  So match memory
+          and hope.  */
+       *allows_mem = true;
+       break;
+
+      case 'g':  case 'X':
+       *allows_reg = true;
+       *allows_mem = true;
+       break;
+       
+      case 'p': case 'r':
+       *allows_reg = true;
+       break;
+
+      default:
+       if (!ISALPHA (*p))
+         break;
+       if (REG_CLASS_FROM_LETTER (*p) != NO_REGS)
+         *allows_reg = true;
+#ifdef EXTRA_CONSTRAINT
+       else
+         {
+           /* Otherwise we can't assume anything about the nature of
+              the constraint except that it isn't purely registers.
+              Treat it like "g" and hope for the best.  */
+           *allows_reg = true;
+           *allows_mem = true;
+         }
+#endif
+       break;
+      }
+
+  return true;
+}
+
 /* Generate RTL for an asm statement with arguments.
    STRING is the instruction template.
    OUTPUTS is a list of output arguments (lvalues); INPUTS a list of inputs.
@@ -1411,15 +1569,12 @@ expand_asm_operands (string, outputs, inputs, clobbers, vol, filename, line)
       tree val = TREE_VALUE (tail);
       tree type = TREE_TYPE (val);
       const char *constraint;
-      char *p;
-      int c_len;
-      int j;
-      int is_inout = 0;
-      int allows_reg = 0;
-      int allows_mem = 0;
+      bool is_inout;
+      bool allows_reg;
+      bool allows_mem;
 
       /* If there's an erroneous arg, emit no insn.  */
-      if (TREE_TYPE (val) == error_mark_node)
+      if (type == error_mark_node)
        return;
 
       /* Make sure constraint has `=' and does not have `+'.  Also, see
@@ -1429,119 +1584,17 @@ expand_asm_operands (string, outputs, inputs, clobbers, vol, filename, line)
 
       constraint = TREE_STRING_POINTER (TREE_PURPOSE (tail));
       output_constraints[i] = constraint;
-      c_len = strlen (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
-        large body of code that puts it last.  Swap the character to
-        the front, so as not to uglify any place else.  */
-      switch (c_len)
-       {
-       default:
-         if ((p = strchr (constraint, '=')) != NULL)
-           break;
-         if ((p = strchr (constraint, '+')) != NULL)
-           break;
-       case 0:
-         error ("output operand constraint lacks `='");
-         return;
-       }
-      j = p - constraint;
-      is_inout = *p == '+';
-
-      if (j || is_inout)
-       {
-         /* Have to throw away this constraint string and get a new one.  */
-         char *buf = alloca (c_len + 1);
-         buf[0] = '=';
-         if (j)
-           memcpy (buf + 1, constraint, j);
-         memcpy (buf + 1 + j, p + 1, c_len - j);  /* not -j-1 - copy null */
-         constraint = ggc_alloc_string (buf, c_len);
-         output_constraints[i] = constraint;
-
-         if (j)
-           warning (
-               "output constraint `%c' for operand %d is not at the beginning",
-               *p, i);
-       }
-
-      /* Make sure we can specify the matching operand.  */
-      if (is_inout && i > 9)
-       {
-         error ("output operand constraint %d contains `+'", i);
-         return;
-       }
-
-      for (j = 1; j < c_len; j++)
-       switch (constraint[j])
-         {
-         case '+':
-         case '=':
-           error ("operand constraint contains '+' or '=' at illegal position.");
-           return;
 
-         case '%':
-           if (i + 1 == ninputs + noutputs)
-             {
-               error ("`%%' constraint used with last operand");
-               return;
-             }
-           break;
-
-         case '?':  case '!':  case '*':  case '&':  case '#':
-         case 'E':  case 'F':  case 'G':  case 'H':
-         case 's':  case 'i':  case 'n':
-         case 'I':  case 'J':  case 'K':  case 'L':  case 'M':
-         case 'N':  case 'O':  case 'P':  case ',':
-           break;
-
-         case '0':  case '1':  case '2':  case '3':  case '4':
-         case '5':  case '6':  case '7':  case '8':  case '9':
-           error ("matching constraint not valid in output operand");
-           break;
-
-         case 'V':  case 'm':  case 'o':
-           allows_mem = 1;
-           break;
-
-         case '<':  case '>':
-          /* ??? Before flow, auto inc/dec insns are not supposed to exist,
-             excepting those that expand_call created.  So match memory
-            and hope.  */
-           allows_mem = 1;
-           break;
-
-         case 'g':  case 'X':
-           allows_reg = 1;
-           allows_mem = 1;
-           break;
-
-         case 'p': case 'r':
-           allows_reg = 1;
-           break;
-
-         default:
-           if (! ISALPHA (constraint[j]))
-             {
-               error ("invalid punctuation `%c' in constraint",
-                      constraint[j]);
-               return;
-             }
-           if (REG_CLASS_FROM_LETTER (constraint[j]) != NO_REGS)
-             allows_reg = 1;
-#ifdef EXTRA_CONSTRAINT
-           else
-             {
-               /* Otherwise we can't assume anything about the nature of
-                  the constraint except that it isn't purely registers.
-                  Treat it like "g" and hope for the best.  */
-               allows_reg = 1;
-               allows_mem = 1;
-             }
-#endif
-           break;
-         }
+      /* Try to parse the output constraint.  If that fails, there's
+        no point in going further.  */
+      if (!parse_output_constraint (&output_constraints[i],
+                                   i,
+                                   ninputs,
+                                   noutputs,
+                                   &allows_mem,
+                                   &allows_reg,
+                                   &is_inout))
+       return;
 
       /* If an output operand is not a decl or indirect ref and our constraint
         allows a register, make a temporary to act as an intermediate.
diff --git a/gcc/testsuite/g++.old-deja/g++.other/asm3.C b/gcc/testsuite/g++.old-deja/g++.other/asm3.C
new file mode 100644 (file)
index 0000000..d37de69
--- /dev/null
@@ -0,0 +1,11 @@
+// Build don't link:
+// Skip if not target: i?86-*-*
+// Special g++ Options: -O2
+
+typedef unsigned long long uint64;
+uint64 fstps(void)
+{
+  uint64 ret;
+  asm volatile("fstps %0" : "=m" (ret));
+  return ret;
+}
index 4becd00..edb344f 100644 (file)
@@ -2851,6 +2851,9 @@ extern int div_and_round_double           PARAMS ((enum tree_code, int,
 /* In stmt.c */
 extern void emit_nop                   PARAMS ((void));
 extern void expand_computed_goto       PARAMS ((tree));
+extern bool parse_output_constraint     PARAMS ((const char **,
+                                                int, int, int,
+                                                bool *, bool *, bool *));
 extern void expand_asm_operands                PARAMS ((tree, tree, tree, tree, int,
                                                 const char *, int));
 extern int any_pending_cleanups                PARAMS ((int));