* builtins.c (expand_builtin_synchronize): Use gimple_build_asm_vec.
authorrth <rth@138bc75d-0d04-0410-961f-82ee72b054a4>
Mon, 14 Sep 2009 20:17:24 +0000 (20:17 +0000)
committerrth <rth@138bc75d-0d04-0410-961f-82ee72b054a4>
Mon, 14 Sep 2009 20:17:24 +0000 (20:17 +0000)
* cfgbuild.c (make_edges): Handle asm goto.
* cfglayout.c (fixup_reorder_chain): Likewise.
* cfgrtl.c (patch_jump_insn): Likewise.
* gimple-pretty-print.c (dump_gimple_asm): Likewise.
* gimple.c (gimple_build_asm_1): Add and use nlabels parameter.
(gimple_build_asm_vec): Add and use labels parameter.
(gimple_build_asm): Remove.
(walk_gimple_asm): Walk labels too.
* gimple.def (GIMPLE_ASM): Update docs.
* gimple.h: Update decls.
(struct gimple_statement_asm): Change nc to use unsigned char;
add nl member.
(gimple_asm_nlabels): New.
(gimple_asm_label_op, gimple_asm_set_label_op): New.
* gimplify.c (gimplify_asm_expr): Copy labels from ASM_EXPR
into gimple_build_asm_vec.
* jump.c (mark_jump_label_asm): New.
(mark_jump_label): Use it.
(redirect_jump_1): Handle asm goto.
(invert_jump_1): Soft fail if X is null.
* recog.c (extract_asm_operands): New.
(asm_noperands): Use it; handle asm labels.
(decode_asm_operands): Use extract_asm_operands.
(asm_operand_ok): Properly handle empty string.
* reg-stack.c (get_asm_operands_in_out): Rename from
get_asm_operand_n_inputs; use extract_asm_operands; return both
inputs and outputs by reference; update all callers.
* rtl.def (ASM_OPERANDS): Add label vector as operand 6.
* rtl.h (ASM_OPERANDS_LABEL_VEC): New.
(ASM_OPERANDS_LABEL_LENGTH, ASM_OPERANDS_LABEL): New.
(ASM_OPERANDS_SOURCE_LOCATION): Renumber.
(extract_asm_operands): Declare.
* stmt.c (expand_asm_operands): Add and use labels parameter.
(check_unique_operand_names): Likewise.
(resolve_asm_operand_names, resolve_operand_name_1): Likewise.
(expand_asm_stmt): Handle asm labels.
* tree-cfg.c (make_gimple_asm_edges): New.
(make_edges): Use it.
(cleanup_dead_labels): Handle asm labels.
(is_ctrl_altering_stmt): Likewise.
(gimple_redirect_edge_and_branch): Likewise.
* tree.def (ASM_EXPR): Add 5th operand.
* tree.h (ASM_LABELS): New.
(resolve_asm_operand_names): Update decl.

* c-parser.c (c_parser_asm_statement): Parse asm goto.
(c_parser_asm_goto_operands): New.
* c-tree.h (build_asm_expr): Update decl.
* c-typeck.c (build_asm_expr): Add and use labels parameter.
* doc/extend.texi: Document asm goto.

gcc/ada/
* gcc-interface/trans.c (Pragma_to_gnu): Use build5 for ASM_EXPR.

gcc/cp/
* cp-tree.h (finish_asm_stmt): Update decl.
* parser.c (cp_parser_asm_definition): Parse asm goto.
(cp_parser_asm_label_list): New.
* pt.c (tsubst_copy_asm_operands): Don't recurse on labels.
(tsubst_expr): Handle asm labels.
* semantics.c (finish_asm_stmt): Add and use labels parameter.

gcc/testsuite/
* c-c++-common/asmgoto-1.c, c-c++-common/asmgoto-2.c,
c-c++-common/asmgoto-3.c, gcc.c-torture/compile/asmgoto-1.c,
gcc.dg/tree-ssa/asmgoto-1.c: New files.

git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@151701 138bc75d-0d04-0410-961f-82ee72b054a4

36 files changed:
gcc/ChangeLog
gcc/ada/ChangeLog
gcc/ada/gcc-interface/trans.c
gcc/builtins.c
gcc/c-parser.c
gcc/c-tree.h
gcc/c-typeck.c
gcc/cfgbuild.c
gcc/cfglayout.c
gcc/cfgrtl.c
gcc/cp/ChangeLog
gcc/cp/cp-tree.h
gcc/cp/parser.c
gcc/cp/pt.c
gcc/cp/semantics.c
gcc/doc/extend.texi
gcc/gimple-pretty-print.c
gcc/gimple.c
gcc/gimple.def
gcc/gimple.h
gcc/gimplify.c
gcc/jump.c
gcc/recog.c
gcc/reg-stack.c
gcc/rtl.def
gcc/rtl.h
gcc/stmt.c
gcc/testsuite/ChangeLog
gcc/testsuite/c-c++-common/asmgoto-1.c [new file with mode: 0644]
gcc/testsuite/c-c++-common/asmgoto-2.c [new file with mode: 0644]
gcc/testsuite/c-c++-common/asmgoto-3.c [new file with mode: 0644]
gcc/testsuite/gcc.c-torture/compile/asmgoto-1.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/tree-ssa/asmgoto-1.c [new file with mode: 0644]
gcc/tree-cfg.c
gcc/tree.def
gcc/tree.h

index cc04831..304f207 100644 (file)
@@ -1,4 +1,59 @@
 2009-09-14  Richard Henderson  <rth@redhat.com>
+            Jakub Jelinek  <jakub@redhat.com>
+
+       * builtins.c (expand_builtin_synchronize): Use gimple_build_asm_vec.
+       * cfgbuild.c (make_edges): Handle asm goto.
+       * cfglayout.c (fixup_reorder_chain): Likewise.
+       * cfgrtl.c (patch_jump_insn): Likewise.
+       * gimple-pretty-print.c (dump_gimple_asm): Likewise.
+       * gimple.c (gimple_build_asm_1): Add and use nlabels parameter.
+       (gimple_build_asm_vec): Add and use labels parameter.
+       (gimple_build_asm): Remove.
+       (walk_gimple_asm): Walk labels too.
+       * gimple.def (GIMPLE_ASM): Update docs.
+       * gimple.h: Update decls.
+       (struct gimple_statement_asm): Change nc to use unsigned char;
+       add nl member.
+       (gimple_asm_nlabels): New.
+       (gimple_asm_label_op, gimple_asm_set_label_op): New.
+       * gimplify.c (gimplify_asm_expr): Copy labels from ASM_EXPR
+       into gimple_build_asm_vec.
+       * jump.c (mark_jump_label_asm): New.
+       (mark_jump_label): Use it.
+       (redirect_jump_1): Handle asm goto.
+       (invert_jump_1): Soft fail if X is null.
+       * recog.c (extract_asm_operands): New.
+       (asm_noperands): Use it; handle asm labels.
+       (decode_asm_operands): Use extract_asm_operands.
+       (asm_operand_ok): Properly handle empty string.
+       * reg-stack.c (get_asm_operands_in_out): Rename from
+       get_asm_operand_n_inputs; use extract_asm_operands; return both
+       inputs and outputs by reference; update all callers.
+       * rtl.def (ASM_OPERANDS): Add label vector as operand 6.
+       * rtl.h (ASM_OPERANDS_LABEL_VEC): New.
+       (ASM_OPERANDS_LABEL_LENGTH, ASM_OPERANDS_LABEL): New.
+       (ASM_OPERANDS_SOURCE_LOCATION): Renumber.
+       (extract_asm_operands): Declare.
+       * stmt.c (expand_asm_operands): Add and use labels parameter.
+       (check_unique_operand_names): Likewise.
+       (resolve_asm_operand_names, resolve_operand_name_1): Likewise.
+       (expand_asm_stmt): Handle asm labels.
+       * tree-cfg.c (make_gimple_asm_edges): New.
+       (make_edges): Use it.
+       (cleanup_dead_labels): Handle asm labels.
+       (is_ctrl_altering_stmt): Likewise.
+       (gimple_redirect_edge_and_branch): Likewise.
+       * tree.def (ASM_EXPR): Add 5th operand.
+       * tree.h (ASM_LABELS): New.
+       (resolve_asm_operand_names): Update decl.
+
+       * c-parser.c (c_parser_asm_statement): Parse asm goto.
+       (c_parser_asm_goto_operands): New.
+       * c-tree.h (build_asm_expr): Update decl.
+       * c-typeck.c (build_asm_expr): Add and use labels parameter.
+       * doc/extend.texi: Document asm goto.
+
+2009-09-14  Richard Henderson  <rth@redhat.com>
 
        * except.h: Update declarations.
        (struct pointer_map_t): Forward declare.
index 110d735..cb38dab 100644 (file)
@@ -1,3 +1,7 @@
+2009-09-14  Richard Henderson  <rth@redhat.com>
+
+       * gcc-interface/trans.c (Pragma_to_gnu): Use build5 for ASM_EXPR.
+
 2009-09-14  Eric Botcazou  <ebotcazou@adacore.com>
 
        * exp_dbug.ads (Packed Array Encoding): Document the new encoding for
index 61a3aea..5bce21a 100644 (file)
@@ -1026,14 +1026,14 @@ Pragma_to_gnu (Node_Id gnat_node)
          asm_constraint = build_string (strlen (comment), comment);
          free (comment);
 #endif
-         gnu_expr = build4 (ASM_EXPR, void_type_node,
+         gnu_expr = build5 (ASM_EXPR, void_type_node,
                             asm_constraint,
                             NULL_TREE,
                             tree_cons
                             (build_tree_list (NULL_TREE,
                                               build_string (1, "g")),
                              gnu_expr, NULL_TREE),
-                            NULL_TREE);
+                            NULL_TREE, NULL_TREE);
          ASM_VOLATILE_P (gnu_expr) = 1;
          set_expr_location_from_node (gnu_expr, gnat_node);
          append_to_statement_list (gnu_expr, &gnu_result);
@@ -5088,9 +5088,9 @@ gnat_to_gnu (Node_Id gnat_node)
              TREE_VALUE (tail) = input;
            }
 
-         gnu_result = build4 (ASM_EXPR,  void_type_node,
+         gnu_result = build5 (ASM_EXPR,  void_type_node,
                               gnu_template, gnu_outputs,
-                              gnu_inputs, gnu_clobbers);
+                              gnu_inputs, gnu_clobbers, NULL_TREE);
          ASM_VOLATILE_P (gnu_result) = Is_Asm_Volatile (gnat_node);
        }
       else
index ee6417d..8ef9607 100644 (file)
@@ -6236,6 +6236,7 @@ static void
 expand_builtin_synchronize (void)
 {
   gimple x;
+  VEC (tree, gc) *v_clobbers;
 
 #ifdef HAVE_memory_barrier
   if (HAVE_memory_barrier)
@@ -6253,8 +6254,10 @@ expand_builtin_synchronize (void)
 
   /* If no explicit memory barrier instruction is available, create an
      empty asm stmt with a memory clobber.  */
-  x = gimple_build_asm ("", 0, 0, 1,
-                       tree_cons (NULL, build_string (6, "memory"), NULL));
+  v_clobbers = VEC_alloc (tree, gc, 1);
+  VEC_quick_push (tree, v_clobbers,
+                 tree_cons (NULL, build_string (6, "memory"), NULL));
+  x = gimple_build_asm_vec ("", NULL, NULL, v_clobbers, NULL);
   gimple_asm_set_volatile (x, true);
   expand_asm_stmt (x);
 }
index feec8a4..d9ea159 100644 (file)
@@ -903,6 +903,7 @@ static void c_parser_do_statement (c_parser *);
 static void c_parser_for_statement (c_parser *);
 static tree c_parser_asm_statement (c_parser *);
 static tree c_parser_asm_operands (c_parser *, bool);
+static tree c_parser_asm_goto_operands (c_parser *);
 static tree c_parser_asm_clobbers (c_parser *);
 static struct c_expr c_parser_expr_no_commas (c_parser *, struct c_expr *);
 static struct c_expr c_parser_conditional_expression (c_parser *,
@@ -4226,12 +4227,17 @@ c_parser_for_statement (c_parser *parser)
 
    asm-statement:
      asm type-qualifier[opt] ( asm-argument ) ;
+     asm type-qualifier[opt] goto ( asm-goto-argument ) ;
 
    asm-argument:
      asm-string-literal
      asm-string-literal : asm-operands[opt]
      asm-string-literal : asm-operands[opt] : asm-operands[opt]
-     asm-string-literal : asm-operands[opt] : asm-operands[opt] : asm-clobbers
+     asm-string-literal : asm-operands[opt] : asm-operands[opt] : asm-clobbers[opt]
+
+   asm-goto-argument:
+     asm-string-literal : : asm-operands[opt] : asm-clobbers[opt] \
+       : asm-goto-operands
 
    Qualifiers other than volatile are accepted in the syntax but
    warned for.  */
@@ -4239,9 +4245,11 @@ c_parser_for_statement (c_parser *parser)
 static tree
 c_parser_asm_statement (c_parser *parser)
 {
-  tree quals, str, outputs, inputs, clobbers, ret;
-  bool simple;
+  tree quals, str, outputs, inputs, clobbers, labels, ret;
+  bool simple, is_goto;
   location_t asm_loc = c_parser_peek_token (parser)->location;
+  int section, nsections;
+
   gcc_assert (c_parser_next_token_is_keyword (parser, RID_ASM));
   c_parser_consume_token (parser);
   if (c_parser_next_token_is_keyword (parser, RID_VOLATILE))
@@ -4261,85 +4269,96 @@ c_parser_asm_statement (c_parser *parser)
     }
   else
     quals = NULL_TREE;
+
+  is_goto = false;
+  if (c_parser_next_token_is_keyword (parser, RID_GOTO))
+    {
+      c_parser_consume_token (parser);
+      is_goto = true;
+    }
+
   /* ??? Follow the C++ parser rather than using the
      lex_untranslated_string kludge.  */
   parser->lex_untranslated_string = true;
+  ret = NULL;
+
   if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>"))
-    {
-      parser->lex_untranslated_string = false;
-      return NULL_TREE;
-    }
+    goto error;
+
   str = c_parser_asm_string_literal (parser);
   if (str == NULL_TREE)
-    {
-      parser->lex_untranslated_string = false;
-      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
-      return NULL_TREE;
-    }
-  if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN))
-    {
-      simple = true;
-      outputs = NULL_TREE;
-      inputs = NULL_TREE;
-      clobbers = NULL_TREE;
-      goto done_asm;
-    }
-  if (!c_parser_require (parser, CPP_COLON, "expected %<:%> or %<)%>"))
-    {
-      parser->lex_untranslated_string = false;
-      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
-      return NULL_TREE;
-    }
-  simple = false;
-  /* Parse outputs.  */
-  if (c_parser_next_token_is (parser, CPP_COLON)
-      || c_parser_next_token_is (parser, CPP_CLOSE_PAREN))
-    outputs = NULL_TREE;
-  else
-    outputs = c_parser_asm_operands (parser, false);
-  if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN))
-    {
-      inputs = NULL_TREE;
-      clobbers = NULL_TREE;
-      goto done_asm;
-    }
-  if (!c_parser_require (parser, CPP_COLON, "expected %<:%> or %<)%>"))
-    {
-      parser->lex_untranslated_string = false;
-      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
-      return NULL_TREE;
-    }
-  /* Parse inputs.  */
-  if (c_parser_next_token_is (parser, CPP_COLON)
-      || c_parser_next_token_is (parser, CPP_CLOSE_PAREN))
-    inputs = NULL_TREE;
-  else
-    inputs = c_parser_asm_operands (parser, true);
-  if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN))
-    {
-      clobbers = NULL_TREE;
-      goto done_asm;
-    }
-  if (!c_parser_require (parser, CPP_COLON, "expected %<:%> or %<)%>"))
-    {
-      parser->lex_untranslated_string = false;
-      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
-      return NULL_TREE;
+    goto error_close_paren;
+
+  simple = true;
+  outputs = NULL_TREE;
+  inputs = NULL_TREE;
+  clobbers = NULL_TREE;
+  labels = NULL_TREE;
+
+  if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN) && !is_goto)
+    goto done_asm;
+
+  /* Parse each colon-delimited section of operands.  */
+  nsections = 3 + is_goto;
+  for (section = 0; section < nsections; ++section)
+    {
+      if (!c_parser_require (parser, CPP_COLON,
+                            is_goto
+                            ? "expected %<:%>"
+                            : "expected %<:%> or %<)%>"))
+       goto error_close_paren;
+
+      /* Once past any colon, we're no longer a simple asm.  */
+      simple = false;
+
+      if ((!c_parser_next_token_is (parser, CPP_COLON)
+          && !c_parser_next_token_is (parser, CPP_CLOSE_PAREN))
+         || section == 3)
+       switch (section)
+         {
+         case 0:
+           /* For asm goto, we don't allow output operands, but reserve
+              the slot for a future extension that does allow them.  */
+           if (!is_goto)
+             outputs = c_parser_asm_operands (parser, false);
+           break;
+         case 1:
+           inputs = c_parser_asm_operands (parser, true);
+           break;
+         case 2:
+           clobbers = c_parser_asm_clobbers (parser);
+           break;
+         case 3:
+           labels = c_parser_asm_goto_operands (parser);
+           break;
+         default:
+           gcc_unreachable ();
+         }
+
+      if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN) && !is_goto)
+       goto done_asm;
     }
-  /* Parse clobbers.  */
-  clobbers = c_parser_asm_clobbers (parser);
+
  done_asm:
-  parser->lex_untranslated_string = false;
   if (!c_parser_require (parser, CPP_CLOSE_PAREN, "expected %<)%>"))
     {
       c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
-      return NULL_TREE;
+      goto error;
     }
+
   if (!c_parser_require (parser, CPP_SEMICOLON, "expected %<;%>"))
     c_parser_skip_to_end_of_block_or_statement (parser);
+
   ret = build_asm_stmt (quals, build_asm_expr (asm_loc, str, outputs, inputs,
-                                              clobbers, simple));
+                                              clobbers, labels, simple));
+
+ error:
+  parser->lex_untranslated_string = false;
   return ret;
+
+ error_close_paren:
+  c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
+  goto error;
 }
 
 /* Parse asm operands, a GNU extension.  If CONVERT_P (for inputs but
@@ -4441,6 +4460,45 @@ c_parser_asm_clobbers (c_parser *parser)
   return list;
 }
 
+/* Parse asm goto labels, a GNU extension.
+   asm-goto-operands:
+     identifier
+     asm-goto-operands , identifier
+*/
+
+static tree
+c_parser_asm_goto_operands (c_parser *parser)
+{
+  tree list = NULL_TREE;
+  while (true)
+    {
+      tree name, label;
+
+      if (c_parser_next_token_is (parser, CPP_NAME))
+       {
+         c_token *tok = c_parser_peek_token (parser);
+         name = tok->value;
+         label = lookup_label_for_goto (tok->location, name);
+         c_parser_consume_token (parser);
+         TREE_USED (label) = 1;
+       }
+      else
+       {
+         c_parser_error (parser, "expected identifier");
+         return NULL_TREE;
+       }
+
+      name = build_string (IDENTIFIER_LENGTH (name),
+                          IDENTIFIER_POINTER (name));
+      list = tree_cons (name, label, list);
+      if (c_parser_next_token_is (parser, CPP_COMMA))
+       c_parser_consume_token (parser);
+      else
+       return nreverse (list);
+    }
+}
+
 /* Parse an expression other than a compound expression; that is, an
    assignment expression (C90 6.3.16, C99 6.5.16).  If AFTER is not
    NULL then it is an Objective-C message expression which is the
index 5c1ccb5..c7490e4 100644 (file)
@@ -541,7 +541,7 @@ extern tree build_compound_literal (location_t, tree, tree, bool);
 extern void check_compound_literal_type (location_t, struct c_type_name *);
 extern tree c_start_case (location_t, location_t, tree);
 extern void c_finish_case (tree);
-extern tree build_asm_expr (location_t, tree, tree, tree, tree, bool);
+extern tree build_asm_expr (location_t, tree, tree, tree, tree, tree, bool);
 extern tree build_asm_stmt (tree, tree);
 extern int c_types_compatible_p (tree, tree);
 extern tree c_begin_compound_stmt (bool);
index abd7880..411b9ec 100644 (file)
@@ -7920,7 +7920,7 @@ build_asm_stmt (tree cv_qualifier, tree args)
    are subtly different.  We use a ASM_EXPR node to represent this.  */
 tree
 build_asm_expr (location_t loc, tree string, tree outputs, tree inputs,
-               tree clobbers, bool simple)
+               tree clobbers, tree labels, bool simple)
 {
   tree tail;
   tree args;
@@ -7934,7 +7934,7 @@ build_asm_expr (location_t loc, tree string, tree outputs, tree inputs,
   noutputs = list_length (outputs);
   oconstraints = (const char **) alloca (noutputs * sizeof (const char *));
 
-  string = resolve_asm_operand_names (string, outputs, inputs);
+  string = resolve_asm_operand_names (string, outputs, inputs, labels);
 
   /* Remove output conversions that change the type but not the mode.  */
   for (i = 0, tail = outputs; tail; ++i, tail = TREE_CHAIN (tail))
@@ -8004,7 +8004,11 @@ build_asm_expr (location_t loc, tree string, tree outputs, tree inputs,
       TREE_VALUE (tail) = input;
     }
 
-  args = build_stmt (loc, ASM_EXPR, string, outputs, inputs, clobbers);
+  /* ASMs with labels cannot have outputs.  This should have been
+     enforced by the parser.  */
+  gcc_assert (outputs == NULL || labels == NULL);
+
+  args = build_stmt (loc, ASM_EXPR, string, outputs, inputs, clobbers, labels);
 
   /* asm statements without outputs, including simple ones, are treated
      as volatile.  */
index 7d87a7a..b5ddadd 100644 (file)
@@ -303,6 +303,15 @@ make_edges (basic_block min, basic_block max, int update_p)
          else if (returnjump_p (insn))
            cached_make_edge (edge_cache, bb, EXIT_BLOCK_PTR, 0);
 
+         /* Recognize asm goto and do the right thing.  */
+         else if ((tmp = extract_asm_operands (PATTERN (insn))) != NULL)
+           {
+             int i, n = ASM_OPERANDS_LABEL_LENGTH (tmp);
+             for (i = 0; i < n; ++i)
+               make_label_edge (edge_cache, bb,
+                                XEXP (ASM_OPERANDS_LABEL (tmp, i), 0), 0);
+           }
+
          /* Otherwise, we have a plain conditional or unconditional jump.  */
          else
            {
index ca400a8..bc8ed8b 100644 (file)
@@ -848,6 +848,15 @@ fixup_reorder_chain (void)
                  continue;
                }
            }
+         else if (extract_asm_operands (PATTERN (bb_end_insn)) != NULL)
+           {
+             /* If the old fallthru is still next, nothing to do.  */
+             if (bb->aux == e_fall->dest
+                 || e_fall->dest == EXIT_BLOCK_PTR)
+               continue;
+
+             /* Otherwise we'll have to use the fallthru fixup below.  */
+           }
          else
            {
              /* Otherwise we have some return, switch or computed
index a7e93dd..4146b14 100644 (file)
@@ -956,6 +956,45 @@ patch_jump_insn (rtx insn, rtx old_label, basic_block new_bb)
          ++LABEL_NUSES (new_label);
        }
     }
+  else if ((tmp = extract_asm_operands (PATTERN (insn))) != NULL)
+    {
+      int i, n = ASM_OPERANDS_LABEL_LENGTH (tmp);
+      rtx new_label, note;
+
+      if (new_bb == EXIT_BLOCK_PTR)
+       return false;
+      new_label = block_label (new_bb);
+
+      for (i = 0; i < n; ++i)
+       {
+         rtx old_ref = ASM_OPERANDS_LABEL (tmp, i);
+         gcc_assert (GET_CODE (old_ref) == LABEL_REF);
+         if (XEXP (old_ref, 0) == old_label)
+           {
+             ASM_OPERANDS_LABEL (tmp, i)
+               = gen_rtx_LABEL_REF (Pmode, new_label);
+             --LABEL_NUSES (old_label);
+             ++LABEL_NUSES (new_label);
+           }
+       }
+
+      if (JUMP_LABEL (insn) == old_label)
+       {
+         JUMP_LABEL (insn) = new_label;
+         note = find_reg_note (insn, REG_LABEL_TARGET, new_label);
+         if (note)
+           remove_note (insn, note);
+       }
+      else
+       {
+         note = find_reg_note (insn, REG_LABEL_TARGET, old_label);
+         if (note)
+           remove_note (insn, note);
+         if (JUMP_LABEL (insn) != new_label
+             && !find_reg_note (insn, REG_LABEL_TARGET, new_label))
+           add_reg_note (insn, REG_LABEL_TARGET, new_label);
+       }
+    }
   else
     {
       /* ?? We may play the games with moving the named labels from
index 4a17a77..93b90b5 100644 (file)
@@ -1,4 +1,14 @@
 2009-09-14  Richard Henderson  <rth@redhat.com>
+            Jakub Jelinek  <jakub@redhat.com>
+
+       * cp-tree.h (finish_asm_stmt): Update decl.
+       * parser.c (cp_parser_asm_definition): Parse asm goto.
+       (cp_parser_asm_label_list): New.
+       * pt.c (tsubst_copy_asm_operands): Don't recurse on labels.
+       (tsubst_expr): Handle asm labels.
+       * semantics.c (finish_asm_stmt): Add and use labels parameter.
+
+2009-09-14  Richard Henderson  <rth@redhat.com>
 
        * except.c (init_exception_processing): Don't call
        default_init_unwind_resume_libfunc.
index c720a56..e8db635 100644 (file)
@@ -4820,7 +4820,8 @@ enum {
 extern tree begin_compound_stmt                        (unsigned int);
 
 extern void finish_compound_stmt               (tree);
-extern tree finish_asm_stmt                    (int, tree, tree, tree, tree);
+extern tree finish_asm_stmt                    (int, tree, tree, tree, tree,
+                                                tree);
 extern tree finish_label_stmt                  (tree);
 extern void finish_label_decl                  (tree);
 extern tree finish_parenthesized_expr          (tree);
index 14733b8..55effed 100644 (file)
@@ -1859,6 +1859,8 @@ static tree cp_parser_asm_operand_list
   (cp_parser *);
 static tree cp_parser_asm_clobber_list
   (cp_parser *);
+static tree cp_parser_asm_label_list
+  (cp_parser *);
 static tree cp_parser_attributes_opt
   (cp_parser *);
 static tree cp_parser_attribute_list
@@ -12531,7 +12533,10 @@ cp_parser_using_directive (cp_parser* parser)
                          : asm-operand-list [opt] ) ;
      asm volatile [opt] ( string-literal : asm-operand-list [opt]
                          : asm-operand-list [opt]
-                         : asm-operand-list [opt] ) ;  */
+                         : asm-clobber-list [opt] ) ;
+     asm volatile [opt] goto ( string-literal : : asm-operand-list [opt]
+                              : asm-clobber-list [opt]
+                              : asm-goto-list ) ;  */
 
 static void
 cp_parser_asm_definition (cp_parser* parser)
@@ -12540,11 +12545,14 @@ cp_parser_asm_definition (cp_parser* parser)
   tree outputs = NULL_TREE;
   tree inputs = NULL_TREE;
   tree clobbers = NULL_TREE;
+  tree labels = NULL_TREE;
   tree asm_stmt;
   bool volatile_p = false;
   bool extended_p = false;
   bool invalid_inputs_p = false;
   bool invalid_outputs_p = false;
+  bool goto_p = false;
+  const char *missing = NULL;
 
   /* Look for the `asm' keyword.  */
   cp_parser_require_keyword (parser, RID_ASM, "%<asm%>");
@@ -12557,6 +12565,15 @@ cp_parser_asm_definition (cp_parser* parser)
       /* Consume the token.  */
       cp_lexer_consume_token (parser->lexer);
     }
+  if (cp_parser_allow_gnu_extensions_p (parser)
+      && parser->in_function_body
+      && cp_lexer_next_token_is_keyword (parser->lexer, RID_GOTO))
+    {
+      /* Remember that we saw the `goto' keyword.  */
+      goto_p = true;
+      /* Consume the token.  */
+      cp_lexer_consume_token (parser->lexer);
+    }
   /* Look for the opening `('.  */
   if (!cp_parser_require (parser, CPP_OPEN_PAREN, "%<(%>"))
     return;
@@ -12581,6 +12598,7 @@ cp_parser_asm_definition (cp_parser* parser)
     {
       bool inputs_p = false;
       bool clobbers_p = false;
+      bool labels_p = false;
 
       /* The extended syntax was used.  */
       extended_p = true;
@@ -12596,7 +12614,8 @@ cp_parser_asm_definition (cp_parser* parser)
              && cp_lexer_next_token_is_not (parser->lexer,
                                             CPP_SCOPE)
              && cp_lexer_next_token_is_not (parser->lexer,
-                                            CPP_CLOSE_PAREN))
+                                            CPP_CLOSE_PAREN)
+             && !goto_p)
            outputs = cp_parser_asm_operand_list (parser);
 
            if (outputs == error_mark_node)
@@ -12618,6 +12637,8 @@ cp_parser_asm_definition (cp_parser* parser)
          if (cp_lexer_next_token_is_not (parser->lexer,
                                          CPP_COLON)
              && cp_lexer_next_token_is_not (parser->lexer,
+                                            CPP_SCOPE)
+             && cp_lexer_next_token_is_not (parser->lexer,
                                             CPP_CLOSE_PAREN))
            inputs = cp_parser_asm_operand_list (parser);
 
@@ -12632,16 +12653,41 @@ cp_parser_asm_definition (cp_parser* parser)
       if (clobbers_p
          || cp_lexer_next_token_is (parser->lexer, CPP_COLON))
        {
+         clobbers_p = true;
          /* Consume the `:' or `::'.  */
          cp_lexer_consume_token (parser->lexer);
          /* Parse the clobbers.  */
          if (cp_lexer_next_token_is_not (parser->lexer,
-                                         CPP_CLOSE_PAREN))
+                                         CPP_COLON)
+             && cp_lexer_next_token_is_not (parser->lexer,
+                                            CPP_CLOSE_PAREN))
            clobbers = cp_parser_asm_clobber_list (parser);
        }
+      else if (goto_p
+              && cp_lexer_next_token_is (parser->lexer, CPP_SCOPE))
+       /* The labels are coming next.  */
+       labels_p = true;
+
+      /* Look for labels.  */
+      if (labels_p
+         || (goto_p && cp_lexer_next_token_is (parser->lexer, CPP_COLON)))
+       {
+         labels_p = true;
+         /* Consume the `:' or `::'.  */
+         cp_lexer_consume_token (parser->lexer);
+         /* Parse the labels.  */
+         labels = cp_parser_asm_label_list (parser);
+       }
+
+      if (goto_p && !labels_p)
+       missing = clobbers_p ? "%<:%>" : "%<:%> or %<::%>";
     }
+  else if (goto_p)
+    missing = "%<:%> or %<::%>";
+
   /* Look for the closing `)'.  */
-  if (!cp_parser_require (parser, CPP_CLOSE_PAREN, "%<)%>"))
+  if (!cp_parser_require (parser, missing ? CPP_COLON : CPP_CLOSE_PAREN,
+                         missing ? missing : "%<)%>"))
     cp_parser_skip_to_closing_parenthesis (parser, true, false,
                                           /*consume_paren=*/true);
   cp_parser_require (parser, CPP_SEMICOLON, "%<;%>");
@@ -12652,7 +12698,7 @@ cp_parser_asm_definition (cp_parser* parser)
       if (parser->in_function_body)
        {
          asm_stmt = finish_asm_stmt (volatile_p, string, outputs,
-                                     inputs, clobbers);
+                                     inputs, clobbers, labels);
          /* If the extended syntax was not used, mark the ASM_EXPR.  */
          if (!extended_p)
            {
@@ -16866,6 +16912,49 @@ cp_parser_asm_clobber_list (cp_parser* parser)
   return clobbers;
 }
 
+/* Parse an asm-label-list.
+
+   asm-label-list:
+     identifier
+     asm-label-list , identifier
+
+   Returns a TREE_LIST, indicating the labels in the order that they
+   appeared.  The TREE_VALUE of each node is a label.  */
+
+static tree
+cp_parser_asm_label_list (cp_parser* parser)
+{
+  tree labels = NULL_TREE;
+
+  while (true)
+    {
+      tree identifier, label, name;
+
+      /* Look for the identifier.  */
+      identifier = cp_parser_identifier (parser);
+      if (!error_operand_p (identifier))
+        {
+         label = lookup_label (identifier);
+         if (TREE_CODE (label) == LABEL_DECL)
+           {
+             TREE_USED (label) = 1;
+             check_goto (label);
+             name = build_string (IDENTIFIER_LENGTH (identifier),
+                                  IDENTIFIER_POINTER (identifier));
+             labels = tree_cons (name, label, labels);
+           }
+       }
+      /* If the next token is not a `,', then the list is
+        complete.  */
+      if (cp_lexer_next_token_is_not (parser->lexer, CPP_COMMA))
+       break;
+      /* Consume the `,' token.  */
+      cp_lexer_consume_token (parser->lexer);
+    }
+
+  return nreverse (labels);
+}
+
 /* Parse an (optional) series of attributes.
 
    attributes:
index 5d48b1f..9f094a3 100644 (file)
@@ -10818,7 +10818,7 @@ tsubst_copy_asm_operands (tree t, tree args, tsubst_flags_t complain,
   if (purpose)
     purpose = RECUR (purpose);
   value = TREE_VALUE (t);
-  if (value)
+  if (value && TREE_CODE (value) != LABEL_DECL)
     value = RECUR (value);
   chain = TREE_CHAIN (t);
   if (chain && chain != void_type_node)
@@ -11210,7 +11210,8 @@ tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl,
         RECUR (ASM_STRING (t)),
         tsubst_copy_asm_operands (ASM_OUTPUTS (t), args, complain, in_decl),
         tsubst_copy_asm_operands (ASM_INPUTS (t), args, complain, in_decl),
-        tsubst_copy_asm_operands (ASM_CLOBBERS (t), args, complain, in_decl));
+        tsubst_copy_asm_operands (ASM_CLOBBERS (t), args, complain, in_decl),
+        tsubst_copy_asm_operands (ASM_LABELS (t), args, complain, in_decl));
       {
        tree asm_expr = tmp;
        if (TREE_CODE (asm_expr) == CLEANUP_POINT_EXPR)
index 477140c..6b741b3 100644 (file)
@@ -1200,12 +1200,13 @@ finish_compound_stmt (tree stmt)
 }
 
 /* Finish an asm-statement, whose components are a STRING, some
-   OUTPUT_OPERANDS, some INPUT_OPERANDS, and some CLOBBERS.  Also note
-   whether the asm-statement should be considered volatile.  */
+   OUTPUT_OPERANDS, some INPUT_OPERANDS, some CLOBBERS and some
+   LABELS.  Also note whether the asm-statement should be
+   considered volatile.  */
 
 tree
 finish_asm_stmt (int volatile_p, tree string, tree output_operands,
-                tree input_operands, tree clobbers)
+                tree input_operands, tree clobbers, tree labels)
 {
   tree r;
   tree t;
@@ -1223,7 +1224,7 @@ finish_asm_stmt (int volatile_p, tree string, tree output_operands,
       oconstraints = (const char **) alloca (noutputs * sizeof (char *));
 
       string = resolve_asm_operand_names (string, output_operands,
-                                         input_operands);
+                                         input_operands, labels);
 
       for (i = 0, t = output_operands; t; t = TREE_CHAIN (t), ++i)
        {
@@ -1309,7 +1310,7 @@ finish_asm_stmt (int volatile_p, tree string, tree output_operands,
 
   r = build_stmt (input_location, ASM_EXPR, string,
                  output_operands, input_operands,
-                 clobbers);
+                 clobbers, labels);
   ASM_VOLATILE_P (r) = volatile_p || noutputs == 0;
   r = maybe_cleanup_point_expr_void (r);
   return add_stmt (r);
index 92f26e5..22d9f6e 100644 (file)
@@ -5251,7 +5251,7 @@ and most Unix assemblers do.
 Speaking of labels, jumps from one @code{asm} to another are not
 supported.  The compiler's optimizers do not know about these jumps, and
 therefore they cannot take account of them when deciding how to
-optimize.
+optimize.  @xref{Extended asm with goto}.
 
 @cindex macros containing @code{asm}
 Usually the most convenient way to use these @code{asm} instructions is to
@@ -5350,6 +5350,94 @@ For reasons similar to those described above, it is not possible to give
 an assembler instruction access to the condition code left by previous
 instructions.
 
+@anchor{Extended asm with goto}
+As of GCC version 4.5, @code{asm goto} may be used to have the assembly
+jump to one or more C labels.  In this form, a fifth section after the
+clobber list contains a list of all C labels to which the assembly may jump.
+Each label operand is implicitly self-named.  The @code{asm} is also assumed
+to fall through to the next statement.
+
+This form of @code{asm} is restricted to not have outputs.  This is due
+to a internal restriction in the compiler that control transfer instructions
+cannot have outputs.  This restriction on @code{asm goto} may be lifted
+in some future version of the compiler.  In the mean time, @code{asm goto}
+may include a memory clobber, and so leave outputs in memory.
+
+@smallexample
+int frob(int x)
+@{
+  int y;
+  asm goto ("frob %%r5, %1; jc %l[error]; mov (%2), %%r5"
+            : : "r"(x), "r"(&y) : "r5", "memory" : error);
+  return y;
+ error:
+  return -1;
+@}
+@end smallexample
+
+In this (inefficient) example, the @code{frob} instruction sets the
+carry bit to indicate an error.  The @code{jc} instruction detects
+this and branches to the @code{error} label.  Finally, the output 
+of the @code{frob} instruction (@code{%r5}) is stored into the memory
+for variable @code{y}, which is later read by the @code{return} statement.
+
+@smallexample
+void doit(void)
+@{
+  int i = 0;
+  asm goto ("mfsr %%r1, 123; jmp %%r1;"
+            ".pushsection doit_table;"
+           ".long %l0, %l1, %l2, %l3;"
+           ".popsection"
+           : : : "r1" : label1, label2, label3, label4);
+  __builtin_unreachable ();
+
+ label1:
+  f1();
+  return;
+ label2:
+  f2();
+  return;
+ label3:
+  i = 1;
+ label4:
+  f3(i);
+@}
+@end smallexample
+
+In this (also inefficient) example, the @code{mfsr} instruction reads
+an address from some out-of-band machine register, and the following
+@code{jmp} instruction branches to that address.  The address read by
+the @code{mfsr} instruction is assumed to have been previously set via
+some application-specific mechanism to be one of the four values stored
+in the @code{doit_table} section.  Finally, the @code{asm} is followed
+by a call to @code{__builtin_unreachable} to indicate that the @code{asm}
+does not in fact fall through.
+
+@smallexample
+#define TRACE1(NUM)                         \
+  do @{                                      \
+    asm goto ("0: nop;"                     \
+              ".pushsection trace_table;"   \
+              ".long 0b, %l0;"              \
+              ".popsection"                 \
+              : : : : trace#NUM);           \
+    if (0) @{ trace#NUM: trace(); @}          \
+  @} while (0)
+#define TRACE  TRACE1(__COUNTER__)
+@end smallexample
+
+In this example (which in fact inspired the @code{asm goto} feature)
+we want on rare occasions to call the @code{trace} function; on other
+occasions we'd like to keep the overhead to the absolute minimum.
+The normal code path consists of a single @code{nop} instruction.
+However, we record the address of this @code{nop} together with the
+address of a label that calls the @code{trace} function.  This allows
+the @code{nop} instruction to be patched at runtime to be an 
+unconditional branch to the stored label.  It is assumed that an
+optimizing compiler will move the labeled block out of line, to
+optimize the fall through path from the @code{asm}.
+
 If you are writing a header file that should be includable in ISO C
 programs, write @code{__asm__} instead of @code{asm}.  @xref{Alternate
 Keywords}.
index 8903c66..3d3134b 100644 (file)
@@ -1101,89 +1101,151 @@ dump_gimple_omp_return (pretty_printer *buffer, gimple gs, int spc, int flags)
 static void
 dump_gimple_asm (pretty_printer *buffer, gimple gs, int spc, int flags)
 {
-  unsigned int i;
+  unsigned int i, n, f, fields;
 
   if (flags & TDF_RAW)
-    dump_gimple_fmt (buffer, spc, flags, "%G <%+STRING <%n%s%n>", gs,
-                     gimple_asm_string (gs));
+    {
+      dump_gimple_fmt (buffer, spc, flags, "%G <%+STRING <%n%s%n>", gs,
+                       gimple_asm_string (gs));
+
+      n = gimple_asm_noutputs (gs);
+      if (n)
+       {
+         newline_and_indent (buffer, spc + 2);
+         pp_string (buffer, "OUTPUT: ");
+         for (i = 0; i < n; i++)
+           {
+             dump_generic_node (buffer, gimple_asm_output_op (gs, i),
+                                spc, flags, false);
+             if (i < n - 1)
+               pp_string (buffer, ", ");
+           }
+       }
+
+      n = gimple_asm_ninputs (gs);
+      if (n)
+       {
+         newline_and_indent (buffer, spc + 2);
+         pp_string (buffer, "INPUT: ");
+         for (i = 0; i < n; i++)
+           {
+             dump_generic_node (buffer, gimple_asm_input_op (gs, i),
+                                spc, flags, false);
+             if (i < n - 1)
+               pp_string (buffer, ", ");
+           }
+       }
+
+      n = gimple_asm_nclobbers (gs);
+      if (n)
+       {
+         newline_and_indent (buffer, spc + 2);
+         pp_string (buffer, "CLOBBER: ");
+         for (i = 0; i < n; i++)
+           {
+             dump_generic_node (buffer, gimple_asm_clobber_op (gs, i),
+                                spc, flags, false);
+             if (i < n - 1)
+               pp_string (buffer, ", ");
+           }
+       }
+
+      n = gimple_asm_nlabels (gs);
+      if (n)
+       {
+         newline_and_indent (buffer, spc + 2);
+         pp_string (buffer, "LABEL: ");
+         for (i = 0; i < n; i++)
+           {
+             dump_generic_node (buffer, gimple_asm_label_op (gs, i),
+                                spc, flags, false);
+             if (i < n - 1)
+               pp_string (buffer, ", ");
+           }
+       }
+
+      newline_and_indent (buffer, spc);
+      pp_character (buffer, '>');
+    }
   else
     {
       pp_string (buffer, "__asm__");
       if (gimple_asm_volatile_p (gs))
        pp_string (buffer, " __volatile__");
+      if (gimple_asm_nlabels (gs))
+       pp_string (buffer, " goto");
       pp_string (buffer, "(\"");
       pp_string (buffer, gimple_asm_string (gs));
       pp_string (buffer, "\"");
-    }
 
-  if (gimple_asm_ninputs (gs)
-     || gimple_asm_noutputs (gs) 
-     || gimple_asm_nclobbers (gs))
-    {
-      if (gimple_asm_noutputs (gs))
-        {
-          if (flags & TDF_RAW)
-            {
-              newline_and_indent (buffer, spc + 2);
-              pp_string (buffer, "OUTPUT: ");
-            }
-          else
-            pp_string (buffer, " : ");
-        }
+      if (gimple_asm_nlabels (gs))
+       fields = 4;
+      else if (gimple_asm_nclobbers (gs))
+       fields = 3;
+      else if (gimple_asm_ninputs (gs))
+       fields = 2;
+      else if (gimple_asm_noutputs (gs))
+       fields = 1;
+      else
+       fields = 0;
 
-      for (i = 0; i < gimple_asm_noutputs (gs); i++)
-        {
-          dump_generic_node (buffer, gimple_asm_output_op (gs, i), spc, flags,
-                             false);
-          if ( i < gimple_asm_noutputs (gs) -1)
-            pp_string (buffer, ", ");
-        }
+      for (f = 0; f < fields; ++f)
+       {
+         pp_string (buffer, " : ");
 
-      if (gimple_asm_ninputs (gs))
-        {
-          if (flags & TDF_RAW)
-            {
-              newline_and_indent (buffer, spc + 2);
-              pp_string (buffer, "INPUT: ");
-            }
-          else
-            pp_string (buffer, " : ");
-        }
+         switch (f)
+           {
+           case 0:
+             n = gimple_asm_noutputs (gs);
+             for (i = 0; i < n; i++)
+               {
+                 dump_generic_node (buffer, gimple_asm_output_op (gs, i),
+                                    spc, flags, false);
+                 if (i < n - 1)
+                   pp_string (buffer, ", ");
+               }
+             break;
 
-      for (i = 0; i < gimple_asm_ninputs (gs); i++)
-        {
-          dump_generic_node (buffer, gimple_asm_input_op (gs, i), spc, flags,
-                             false);
-          if (i < gimple_asm_ninputs (gs) -1)
-            pp_string (buffer, " : ");
-        }
+           case 1:
+             n = gimple_asm_ninputs (gs);
+             for (i = 0; i < n; i++)
+               {
+                 dump_generic_node (buffer, gimple_asm_input_op (gs, i),
+                                    spc, flags, false);
+                 if (i < n - 1)
+                   pp_string (buffer, ", ");
+               }
+             break;
 
-      if (gimple_asm_nclobbers (gs))
-        {
-          if (flags & TDF_RAW)
-            {
-              newline_and_indent (buffer, spc + 2);
-              pp_string (buffer, "CLOBBER: ");
-            }
-          else
-            pp_string (buffer, " : ");
-        }
+           case 2:
+             n = gimple_asm_nclobbers (gs);
+             for (i = 0; i < n; i++)
+               {
+                 dump_generic_node (buffer, gimple_asm_clobber_op (gs, i),
+                                    spc, flags, false);
+                 if (i < n - 1)
+                   pp_string (buffer, ", ");
+               }
+             break;
 
-      for (i = 0; i < gimple_asm_nclobbers (gs); i++)
-        {
-          dump_generic_node (buffer, gimple_asm_clobber_op (gs, i), spc, flags,
-                             false);
-          if ( i < gimple_asm_nclobbers (gs) -1)
-            pp_string (buffer, ", ");
-        }
-    }
-  if (flags & TDF_RAW)
-    {
-      newline_and_indent (buffer, spc);
-      pp_character (buffer, '>');
+           case 3:
+             n = gimple_asm_nlabels (gs);
+             for (i = 0; i < n; i++)
+               {
+                 dump_generic_node (buffer, gimple_asm_label_op (gs, i),
+                                    spc, flags, false);
+                 if (i < n - 1)
+                   pp_string (buffer, ", ");
+               }
+             break;
+
+           default:
+             gcc_unreachable ();
+           }
+       }
+
+      pp_string (buffer, ");");
     }
-  else
-    pp_string (buffer, ");");
 }
 
 
index 33daafc..425463c 100644 (file)
@@ -507,17 +507,22 @@ gimple_build_bind (tree vars, gimple_seq body, tree block)
 
 static inline gimple
 gimple_build_asm_1 (const char *string, unsigned ninputs, unsigned noutputs, 
-                    unsigned nclobbers)
+                    unsigned nclobbers, unsigned nlabels)
 {
   gimple p;
   int size = strlen (string);
 
+  /* ASMs with labels cannot have outputs.  This should have been
+     enforced by the front end.  */
+  gcc_assert (nlabels == 0 || noutputs == 0);
+
   p = gimple_build_with_ops (GIMPLE_ASM, ERROR_MARK,
-                            ninputs + noutputs + nclobbers);
+                            ninputs + noutputs + nclobbers + nlabels);
 
   p->gimple_asm.ni = ninputs;
   p->gimple_asm.no = noutputs;
   p->gimple_asm.nc = nclobbers;
+  p->gimple_asm.nl = nlabels;
   p->gimple_asm.string = ggc_alloc_string (string, size);
 
 #ifdef GATHER_STATISTICS
@@ -535,11 +540,13 @@ gimple_build_asm_1 (const char *string, unsigned ninputs, unsigned noutputs,
    NCLOBBERS is the number of clobbered registers.
    INPUTS is a vector of the input register parameters.
    OUTPUTS is a vector of the output register parameters.
-   CLOBBERS is a vector of the clobbered register parameters.  */
+   CLOBBERS is a vector of the clobbered register parameters.
+   LABELS is a vector of destination labels.  */
 
 gimple
 gimple_build_asm_vec (const char *string, VEC(tree,gc)* inputs, 
-                      VEC(tree,gc)* outputs, VEC(tree,gc)* clobbers)
+                      VEC(tree,gc)* outputs, VEC(tree,gc)* clobbers,
+                     VEC(tree,gc)* labels)
 {
   gimple p;
   unsigned i;
@@ -547,7 +554,8 @@ gimple_build_asm_vec (const char *string, VEC(tree,gc)* inputs,
   p = gimple_build_asm_1 (string,
                           VEC_length (tree, inputs),
                           VEC_length (tree, outputs), 
-                          VEC_length (tree, clobbers));
+                          VEC_length (tree, clobbers),
+                         VEC_length (tree, labels));
   
   for (i = 0; i < VEC_length (tree, inputs); i++)
     gimple_asm_set_input_op (p, i, VEC_index (tree, inputs, i));
@@ -558,39 +566,8 @@ gimple_build_asm_vec (const char *string, VEC(tree,gc)* inputs,
   for (i = 0; i < VEC_length (tree, clobbers); i++)
     gimple_asm_set_clobber_op (p, i, VEC_index (tree, clobbers, i));
   
-  return p;
-}
-
-/* Build a GIMPLE_ASM statement.
-
-   STRING is the assembly code.
-   NINPUT is the number of register inputs.
-   NOUTPUT is the number of register outputs.
-   NCLOBBERS is the number of clobbered registers.
-   ... are trees for each input, output and clobbered register.  */
-
-gimple
-gimple_build_asm (const char *string, unsigned ninputs, unsigned noutputs, 
-                 unsigned nclobbers, ...)
-{
-  gimple p;
-  unsigned i;
-  va_list ap;
-  
-  p = gimple_build_asm_1 (string, ninputs, noutputs, nclobbers);
-  
-  va_start (ap, nclobbers);
-
-  for (i = 0; i < ninputs; i++)
-    gimple_asm_set_input_op (p, i, va_arg (ap, tree));
-
-  for (i = 0; i < noutputs; i++)
-    gimple_asm_set_output_op (p, i, va_arg (ap, tree));
-
-  for (i = 0; i < nclobbers; i++)
-    gimple_asm_set_clobber_op (p, i, va_arg (ap, tree));
-
-  va_end (ap);
+  for (i = 0; i < VEC_length (tree, labels); i++)
+    gimple_asm_set_label_op (p, i, VEC_index (tree, labels, i));
   
   return p;
 }
@@ -1230,10 +1207,10 @@ static tree
 walk_gimple_asm (gimple stmt, walk_tree_fn callback_op,
                 struct walk_stmt_info *wi)
 {
-  tree ret;
+  tree ret, op;
   unsigned noutputs;
   const char **oconstraints;
-  unsigned i;
+  unsigned i, n;
   const char *constraint;
   bool allows_mem, allows_reg, is_inout;
 
@@ -1245,7 +1222,7 @@ walk_gimple_asm (gimple stmt, walk_tree_fn callback_op,
 
   for (i = 0; i < noutputs; i++)
     {
-      tree op = gimple_asm_output_op (stmt, i);
+      op = gimple_asm_output_op (stmt, i);
       constraint = TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (op)));
       oconstraints[i] = constraint;
       parse_output_constraint (&constraint, i, 0, 0, &allows_mem, &allows_reg,
@@ -1257,18 +1234,19 @@ walk_gimple_asm (gimple stmt, walk_tree_fn callback_op,
        return ret;
     }
 
-  for (i = 0; i < gimple_asm_ninputs (stmt); i++)
+  n = gimple_asm_ninputs (stmt);
+  for (i = 0; i < n; i++)
     {
-      tree op = gimple_asm_input_op (stmt, i);
+      op = gimple_asm_input_op (stmt, i);
       constraint = TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (op)));
       parse_input_constraint (&constraint, 0, 0, noutputs, 0,
                              oconstraints, &allows_mem, &allows_reg);
       if (wi)
-       wi->val_only = (allows_reg || !allows_mem);
-
-      /* Although input "m" is not really a LHS, we need a lvalue.  */
-      if (wi)
-       wi->is_lhs = !wi->val_only;
+       {
+         wi->val_only = (allows_reg || !allows_mem);
+          /* Although input "m" is not really a LHS, we need a lvalue.  */
+         wi->is_lhs = !wi->val_only;
+       }
       ret = walk_tree (&TREE_VALUE (op), callback_op, wi, NULL);
       if (ret)
        return ret;
@@ -1280,6 +1258,15 @@ walk_gimple_asm (gimple stmt, walk_tree_fn callback_op,
       wi->val_only = true;
     }
 
+  n = gimple_asm_nlabels (stmt);
+  for (i = 0; i < n; i++)
+    {
+      op = gimple_asm_label_op (stmt, i);
+      ret = walk_tree (&TREE_VALUE (op), callback_op, wi, NULL);
+      if (ret)
+       return ret;
+    }
+
   return NULL_TREE;
 }
 
index 603d97e..d736dd7 100644 (file)
@@ -106,7 +106,8 @@ DEFGSCODE(GIMPLE_ASSIGN, "gimple_assign", GSS_WITH_MEM_OPS)
    STRING is the string containing the assembly statements.
    I1 ... IN are the N input operands.
    O1 ... OM are the M output operands.
-   C1 ... CP are the P clobber operands.  */
+   C1 ... CP are the P clobber operands.
+   L1 ... LQ are the Q label operands.  */
 DEFGSCODE(GIMPLE_ASM, "gimple_asm", GSS_ASM)
 
 /* GIMPLE_CALL <FN, LHS, ARG1, ..., ARGN[, CHAIN]> represents function
index b539623..e1e3b65 100644 (file)
@@ -553,10 +553,11 @@ struct GTY(()) gimple_statement_asm
   const char *string;
 
   /* [ WORD 10 ]
-       Number of inputs, outputs and clobbers.  */
+       Number of inputs, outputs, clobbers, labels.  */
   unsigned char ni;
   unsigned char no;
-  unsigned short nc;
+  unsigned char nc;
+  unsigned char nl;
 
   /* [ WORD 11 ]
      Operand vector.  NOTE!  This must always be the last field
@@ -792,9 +793,8 @@ gimple gimple_build_label (tree label);
 gimple gimple_build_goto (tree dest);
 gimple gimple_build_nop (void);
 gimple gimple_build_bind (tree, gimple_seq, tree);
-gimple gimple_build_asm (const char *, unsigned, unsigned, unsigned, ...);
 gimple gimple_build_asm_vec (const char *, VEC(tree,gc) *, VEC(tree,gc) *,
-                             VEC(tree,gc) *);
+                             VEC(tree,gc) *, VEC(tree,gc) *);
 gimple gimple_build_catch (tree, gimple_seq);
 gimple gimple_build_eh_filter (tree, gimple_seq);
 gimple gimple_build_eh_must_not_throw (tree);
@@ -2614,6 +2614,14 @@ gimple_asm_nclobbers (const_gimple gs)
   return gs->gimple_asm.nc;
 }
 
+/* Return the number of label operands for GIMPLE_ASM GS.  */
+
+static inline unsigned
+gimple_asm_nlabels (const_gimple gs)
+{
+  GIMPLE_CHECK (gs, GIMPLE_ASM);
+  return gs->gimple_asm.nl;
+}
 
 /* Return input operand INDEX of GIMPLE_ASM GS.  */
 
@@ -2703,6 +2711,26 @@ gimple_asm_set_clobber_op (gimple gs, unsigned index, tree clobber_op)
   gimple_set_op (gs, index + gs->gimple_asm.ni + gs->gimple_asm.no, clobber_op);
 }
 
+/* Return label operand INDEX of GIMPLE_ASM GS.  */
+
+static inline tree
+gimple_asm_label_op (const_gimple gs, unsigned index)
+{
+  GIMPLE_CHECK (gs, GIMPLE_ASM);
+  gcc_assert (index <= gs->gimple_asm.nl);
+  return gimple_op (gs, index + gs->gimple_asm.ni + gs->gimple_asm.nc);
+}
+
+/* Set LABEL_OP to be label operand INDEX in GIMPLE_ASM GS.  */
+
+static inline void
+gimple_asm_set_label_op (gimple gs, unsigned index, tree label_op)
+{
+  GIMPLE_CHECK (gs, GIMPLE_ASM);
+  gcc_assert (index <= gs->gimple_asm.nl);
+  gcc_assert (TREE_CODE (label_op) == TREE_LIST);
+  gimple_set_op (gs, index + gs->gimple_asm.ni + gs->gimple_asm.nc, label_op);
+}
 
 /* Return the string representing the assembly instruction in
    GIMPLE_ASM GS.  */
index 381e611..c0cab20 100644 (file)
@@ -4765,13 +4765,14 @@ gimplify_asm_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p)
   VEC(tree, gc) *inputs;
   VEC(tree, gc) *outputs;
   VEC(tree, gc) *clobbers;
+  VEC(tree, gc) *labels;
   tree link_next;
   
   expr = *expr_p;
   noutputs = list_length (ASM_OUTPUTS (expr));
   oconstraints = (const char **) alloca ((noutputs) * sizeof (const char *));
 
-  inputs = outputs = clobbers = NULL;
+  inputs = outputs = clobbers = labels = NULL;
 
   ret = GS_ALL_DONE;
   link_next = NULL_TREE;
@@ -4953,13 +4954,16 @@ gimplify_asm_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p)
     }
   
   for (link = ASM_CLOBBERS (expr); link; ++i, link = TREE_CHAIN (link))
-      VEC_safe_push (tree, gc, clobbers, link);
+    VEC_safe_push (tree, gc, clobbers, link);
+
+  for (link = ASM_LABELS (expr); link; ++i, link = TREE_CHAIN (link))
+    VEC_safe_push (tree, gc, labels, link);
 
   /* Do not add ASMs with errors to the gimple IL stream.  */
   if (ret != GS_ERROR)
     {
       stmt = gimple_build_asm_vec (TREE_STRING_POINTER (ASM_STRING (expr)),
-                                  inputs, outputs, clobbers);
+                                  inputs, outputs, clobbers, labels);
 
       gimple_asm_set_volatile (stmt, ASM_VOLATILE_P (expr));
       gimple_asm_set_input (stmt, ASM_INPUT_P (expr));
index 28a9b0f..a12d040 100644 (file)
@@ -68,6 +68,7 @@ along with GCC; see the file COPYING3.  If not see
 static void init_label_info (rtx);
 static void mark_all_labels (rtx);
 static void mark_jump_label_1 (rtx, rtx, bool, bool);
+static void mark_jump_label_asm (rtx, rtx);
 static void redirect_exp_1 (rtx *, rtx, rtx, rtx);
 static int invert_exp_1 (rtx, rtx);
 static int returnjump_p_1 (rtx *, void *);
@@ -1006,8 +1007,12 @@ sets_cc0_p (const_rtx x)
 void
 mark_jump_label (rtx x, rtx insn, int in_mem)
 {
-  mark_jump_label_1 (x, insn, in_mem != 0,
-                    (insn != NULL && x == PATTERN (insn) && JUMP_P (insn)));
+  rtx asmop = extract_asm_operands (x);
+  if (asmop)
+    mark_jump_label_asm (asmop, insn);
+  else
+    mark_jump_label_1 (x, insn, in_mem != 0,
+                      (insn != NULL && x == PATTERN (insn) && JUMP_P (insn)));
 }
 
 /* Worker function for mark_jump_label.  IN_MEM is TRUE when X occurs
@@ -1145,6 +1150,22 @@ mark_jump_label_1 (rtx x, rtx insn, bool in_mem, bool is_target)
     }
 }
 
+/* Worker function for mark_jump_label.  Handle asm insns specially.
+   In particular, output operands need not be considered so we can
+   avoid re-scanning the replicated asm_operand.  Also, the asm_labels
+   need to be considered targets.  */
+
+static void
+mark_jump_label_asm (rtx asmop, rtx insn)
+{
+  int i;
+
+  for (i = ASM_OPERANDS_INPUT_LENGTH (asmop) - 1; i >= 0; --i)
+    mark_jump_label_1 (ASM_OPERANDS_INPUT (asmop, i), insn, false, false);
+
+  for (i = ASM_OPERANDS_LABEL_LENGTH (asmop) - 1; i >= 0; --i)
+    mark_jump_label_1 (ASM_OPERANDS_LABEL (asmop, i), insn, false, true);
+}
 \f
 /* Delete insn INSN from the chain of insns and update label ref counts
    and delete insns now unreachable.
@@ -1386,9 +1407,17 @@ int
 redirect_jump_1 (rtx jump, rtx nlabel)
 {
   int ochanges = num_validated_changes ();
-  rtx *loc;
+  rtx *loc, asmop;
 
-  if (GET_CODE (PATTERN (jump)) == PARALLEL)
+  asmop = extract_asm_operands (PATTERN (jump));
+  if (asmop)
+    {
+      if (nlabel == NULL)
+       return 0;
+      gcc_assert (ASM_OPERANDS_LABEL_LENGTH (asmop) == 1);
+      loc = &ASM_OPERANDS_LABEL (asmop, 0);
+    }
+  else if (GET_CODE (PATTERN (jump)) == PARALLEL)
     loc = &XVECEXP (PATTERN (jump), 0, 0);
   else
     loc = &PATTERN (jump);
@@ -1514,7 +1543,8 @@ invert_jump_1 (rtx jump, rtx nlabel)
   int ok;
 
   ochanges = num_validated_changes ();
-  gcc_assert (x);
+  if (x == NULL)
+    return 0;
   ok = invert_exp_1 (SET_SRC (x), jump);
   gcc_assert (ok);
   
index bfca43b..6874d6c 100644 (file)
@@ -1373,6 +1373,42 @@ comparison_operator (rtx op, enum machine_mode mode)
          && COMPARISON_P (op));
 }
 \f
+/* If BODY is an insn body that uses ASM_OPERANDS, return it.  */
+
+rtx
+extract_asm_operands (rtx body)
+{
+  rtx tmp;
+  switch (GET_CODE (body))
+    {
+    case ASM_OPERANDS:
+      return body;
+
+    case SET:
+      /* Single output operand: BODY is (set OUTPUT (asm_operands ...)).  */
+      tmp = SET_SRC (body);
+      if (GET_CODE (tmp) == ASM_OPERANDS)
+       return tmp;
+      break;
+
+    case PARALLEL:
+      tmp = XVECEXP (body, 0, 0);
+      if (GET_CODE (tmp) == ASM_OPERANDS)
+       return tmp;
+      if (GET_CODE (tmp) == SET)
+       {
+         tmp = SET_SRC (tmp);
+         if (GET_CODE (tmp) == ASM_OPERANDS)
+           return tmp;
+       }
+      break;
+
+    default:
+      break;
+    }
+  return NULL;
+}
+
 /* If BODY is an insn body that uses ASM_OPERANDS,
    return the number of operands (both input and output) in the insn.
    Otherwise return -1.  */
@@ -1380,26 +1416,22 @@ comparison_operator (rtx op, enum machine_mode mode)
 int
 asm_noperands (const_rtx body)
 {
-  switch (GET_CODE (body))
+  rtx asm_op = extract_asm_operands (CONST_CAST_RTX (body));
+  int n_sets = 0;
+
+  if (asm_op == NULL)
+    return -1;
+
+  if (GET_CODE (body) == SET)
+    n_sets = 1;
+  else if (GET_CODE (body) == PARALLEL)
     {
-    case ASM_OPERANDS:
-      /* No output operands: return number of input operands.  */
-      return ASM_OPERANDS_INPUT_LENGTH (body);
-    case SET:
-      if (GET_CODE (SET_SRC (body)) == ASM_OPERANDS)
-       /* Single output operand: BODY is (set OUTPUT (asm_operands ...)).  */
-       return ASM_OPERANDS_INPUT_LENGTH (SET_SRC (body)) + 1;
-      else
-       return -1;
-    case PARALLEL:
-      if (GET_CODE (XVECEXP (body, 0, 0)) == SET
-         && GET_CODE (SET_SRC (XVECEXP (body, 0, 0))) == ASM_OPERANDS)
+      int i;
+      if (GET_CODE (XVECEXP (body, 0, 0)) == SET)
        {
          /* Multiple output operands, or 1 output plus some clobbers:
-            body is [(set OUTPUT (asm_operands ...))... (clobber (reg ...))...].  */
-         int i;
-         int n_sets;
-
+            body is 
+            [(set OUTPUT (asm_operands ...))... (clobber (reg ...))...].  */
          /* Count backwards through CLOBBERs to determine number of SETs.  */
          for (i = XVECLEN (body, 0); i > 0; i--)
            {
@@ -1425,30 +1457,23 @@ asm_noperands (const_rtx body)
              /* If these ASM_OPERANDS rtx's came from different original insns
                 then they aren't allowed together.  */
              if (ASM_OPERANDS_INPUT_VEC (SET_SRC (elt))
-                 != ASM_OPERANDS_INPUT_VEC (SET_SRC (XVECEXP (body, 0, 0))))
+                 != ASM_OPERANDS_INPUT_VEC (asm_op))
                return -1;
            }
-         return (ASM_OPERANDS_INPUT_LENGTH (SET_SRC (XVECEXP (body, 0, 0)))
-                 + n_sets);
        }
-      else if (GET_CODE (XVECEXP (body, 0, 0)) == ASM_OPERANDS)
+      else
        {
          /* 0 outputs, but some clobbers:
             body is [(asm_operands ...) (clobber (reg ...))...].  */
-         int i;
-
          /* Make sure all the other parallel things really are clobbers.  */
          for (i = XVECLEN (body, 0) - 1; i > 0; i--)
            if (GET_CODE (XVECEXP (body, 0, i)) != CLOBBER)
              return -1;
-
-         return ASM_OPERANDS_INPUT_LENGTH (XVECEXP (body, 0, 0));
        }
-      else
-       return -1;
-    default:
-      return -1;
     }
+
+  return (ASM_OPERANDS_INPUT_LENGTH (asm_op)
+         + ASM_OPERANDS_LABEL_LENGTH (asm_op) + n_sets);
 }
 
 /* Assuming BODY is an insn body that uses ASM_OPERANDS,
@@ -1466,28 +1491,19 @@ decode_asm_operands (rtx body, rtx *operands, rtx **operand_locs,
                     const char **constraints, enum machine_mode *modes,
                     location_t *loc)
 {
-  int i;
-  int noperands;
-  rtx asmop = 0;
+  int noperands, nbase = 0, n, i;
+  rtx asmop;
 
-  if (GET_CODE (body) == SET && GET_CODE (SET_SRC (body)) == ASM_OPERANDS)
+  switch (GET_CODE (body))
     {
-      asmop = SET_SRC (body);
-      /* Single output operand: BODY is (set OUTPUT (asm_operands ....)).  */
-
-      noperands = ASM_OPERANDS_INPUT_LENGTH (asmop) + 1;
+    case ASM_OPERANDS:
+      /* Zero output asm: BODY is (asm_operands ...).  */
+      asmop = body;
+      break;
 
-      for (i = 1; i < noperands; i++)
-       {
-         if (operand_locs)
-           operand_locs[i] = &ASM_OPERANDS_INPUT (asmop, i - 1);
-         if (operands)
-           operands[i] = ASM_OPERANDS_INPUT (asmop, i - 1);
-         if (constraints)
-           constraints[i] = ASM_OPERANDS_INPUT_CONSTRAINT (asmop, i - 1);
-         if (modes)
-           modes[i] = ASM_OPERANDS_INPUT_MODE (asmop, i - 1);
-       }
+    case SET:
+      /* Single output asm: BODY is (set OUTPUT (asm_operands ...)).  */
+      asmop = SET_SRC (body);
 
       /* The output is in the SET.
         Its constraint is in the ASM_OPERANDS itself.  */
@@ -1499,93 +1515,70 @@ decode_asm_operands (rtx body, rtx *operands, rtx **operand_locs,
        constraints[0] = ASM_OPERANDS_OUTPUT_CONSTRAINT (asmop);
       if (modes)
        modes[0] = GET_MODE (SET_DEST (body));
-    }
-  else if (GET_CODE (body) == ASM_OPERANDS)
-    {
-      asmop = body;
-      /* No output operands: BODY is (asm_operands ....).  */
-
-      noperands = ASM_OPERANDS_INPUT_LENGTH (asmop);
-
-      /* The input operands are found in the 1st element vector.  */
-      /* Constraints for inputs are in the 2nd element vector.  */
-      for (i = 0; i < noperands; i++)
-       {
-         if (operand_locs)
-           operand_locs[i] = &ASM_OPERANDS_INPUT (asmop, i);
-         if (operands)
-           operands[i] = ASM_OPERANDS_INPUT (asmop, i);
-         if (constraints)
-           constraints[i] = ASM_OPERANDS_INPUT_CONSTRAINT (asmop, i);
-         if (modes)
-           modes[i] = ASM_OPERANDS_INPUT_MODE (asmop, i);
-       }
-    }
-  else if (GET_CODE (body) == PARALLEL
-          && GET_CODE (XVECEXP (body, 0, 0)) == SET
-          && GET_CODE (SET_SRC (XVECEXP (body, 0, 0))) == ASM_OPERANDS)
-    {
-      int nparallel = XVECLEN (body, 0); /* Includes CLOBBERs.  */
-      int nin;
-      int nout = 0;            /* Does not include CLOBBERs.  */
-
-      asmop = SET_SRC (XVECEXP (body, 0, 0));
-      nin = ASM_OPERANDS_INPUT_LENGTH (asmop);
+      nbase = 1;
+      break;
 
-      /* At least one output, plus some CLOBBERs.  */
+    case PARALLEL:
+      {
+       int nparallel = XVECLEN (body, 0); /* Includes CLOBBERs.  */
 
-      /* The outputs are in the SETs.
-        Their constraints are in the ASM_OPERANDS itself.  */
-      for (i = 0; i < nparallel; i++)
-       {
-         if (GET_CODE (XVECEXP (body, 0, i)) == CLOBBER)
-           break;              /* Past last SET */
+       asmop = XVECEXP (body, 0, 0);
+       if (GET_CODE (asmop) == SET)
+         {
+           asmop = SET_SRC (asmop);
 
-         if (operands)
-           operands[i] = SET_DEST (XVECEXP (body, 0, i));
-         if (operand_locs)
-           operand_locs[i] = &SET_DEST (XVECEXP (body, 0, i));
-         if (constraints)
-           constraints[i] = XSTR (SET_SRC (XVECEXP (body, 0, i)), 1);
-         if (modes)
-           modes[i] = GET_MODE (SET_DEST (XVECEXP (body, 0, i)));
-         nout++;
-       }
+           /* At least one output, plus some CLOBBERs.  The outputs are in
+              the SETs.  Their constraints are in the ASM_OPERANDS itself.  */
+           for (i = 0; i < nparallel; i++)
+             {
+               if (GET_CODE (XVECEXP (body, 0, i)) == CLOBBER)
+                 break;                /* Past last SET */
+               if (operands)
+                 operands[i] = SET_DEST (XVECEXP (body, 0, i));
+               if (operand_locs)
+                 operand_locs[i] = &SET_DEST (XVECEXP (body, 0, i));
+               if (constraints)
+                 constraints[i] = XSTR (SET_SRC (XVECEXP (body, 0, i)), 1);
+               if (modes)
+                 modes[i] = GET_MODE (SET_DEST (XVECEXP (body, 0, i)));
+             }
+           nbase = i;
+         }
+       break;
+      }
 
-      for (i = 0; i < nin; i++)
-       {
-         if (operand_locs)
-           operand_locs[i + nout] = &ASM_OPERANDS_INPUT (asmop, i);
-         if (operands)
-           operands[i + nout] = ASM_OPERANDS_INPUT (asmop, i);
-         if (constraints)
-           constraints[i + nout] = ASM_OPERANDS_INPUT_CONSTRAINT (asmop, i);
-         if (modes)
-           modes[i + nout] = ASM_OPERANDS_INPUT_MODE (asmop, i);
-       }
+    default:
+      gcc_unreachable ();
     }
-  else if (GET_CODE (body) == PARALLEL
-          && GET_CODE (XVECEXP (body, 0, 0)) == ASM_OPERANDS)
-    {
-      /* No outputs, but some CLOBBERs.  */
-
-      int nin;
 
-      asmop = XVECEXP (body, 0, 0);
-      nin = ASM_OPERANDS_INPUT_LENGTH (asmop);
+  noperands = (ASM_OPERANDS_INPUT_LENGTH (asmop)
+              + ASM_OPERANDS_LABEL_LENGTH (asmop) + nbase);
 
-      for (i = 0; i < nin; i++)
-       {
-         if (operand_locs)
-           operand_locs[i] = &ASM_OPERANDS_INPUT (asmop, i);
-         if (operands)
-           operands[i] = ASM_OPERANDS_INPUT (asmop, i);
-         if (constraints)
-           constraints[i] = ASM_OPERANDS_INPUT_CONSTRAINT (asmop, i);
-         if (modes)
-           modes[i] = ASM_OPERANDS_INPUT_MODE (asmop, i);
-       }
+  n = ASM_OPERANDS_INPUT_LENGTH (asmop);
+  for (i = 0; i < n; i++)
+    {
+      if (operand_locs)
+       operand_locs[nbase + i] = &ASM_OPERANDS_INPUT (asmop, i);
+      if (operands)
+       operands[nbase + i] = ASM_OPERANDS_INPUT (asmop, i);
+      if (constraints)
+       constraints[nbase + i] = ASM_OPERANDS_INPUT_CONSTRAINT (asmop, i);
+      if (modes)
+       modes[nbase + i] = ASM_OPERANDS_INPUT_MODE (asmop, i);
+    }
+  nbase += n;
 
+  n = ASM_OPERANDS_LABEL_LENGTH (asmop);
+  for (i = 0; i < n; i++)
+    {
+      if (operand_locs)
+       operand_locs[nbase + i] = &ASM_OPERANDS_LABEL (asmop, i);
+      if (operands)
+       operands[nbase + i] = ASM_OPERANDS_LABEL (asmop, i);
+      if (constraints)
+       constraints[nbase + i] = "";
+      if (modes)
+       modes[nbase + i] = Pmode;
     }
 
   if (loc)
@@ -1605,6 +1598,11 @@ asm_operand_ok (rtx op, const char *constraint, const char **constraints)
   /* Use constrain_operands after reload.  */
   gcc_assert (!reload_completed);
 
+  /* Empty constraint string is the same as "X,...,X", i.e. X for as
+     many alternatives as required to match the other operands.  */
+  if (*constraint == '\0')
+    return 1;
+
   while (*constraint)
     {
       char c = *constraint;
index ff09ad2..7e4ba6c 100644 (file)
@@ -254,7 +254,7 @@ static void pop_stack (stack, int);
 static rtx *get_true_reg (rtx *);
 
 static int check_asm_stack_operands (rtx);
-static int get_asm_operand_n_inputs (rtx);
+static void get_asm_operands_in_out (rtx, int *, int *);
 static rtx stack_result (tree);
 static void replace_reg (rtx *, int);
 static void remove_regno_note (rtx, enum reg_note, unsigned int);
@@ -480,8 +480,7 @@ check_asm_stack_operands (rtx insn)
 
   preprocess_constraints ();
 
-  n_inputs = get_asm_operand_n_inputs (body);
-  n_outputs = recog_data.n_operands - n_inputs;
+  get_asm_operands_in_out (body, &n_outputs, &n_inputs);
 
   if (alt < 0)
     {
@@ -645,24 +644,15 @@ check_asm_stack_operands (rtx insn)
    N_INPUTS and N_OUTPUTS are pointers to ints into which the results are
    placed.  */
 
-static int
-get_asm_operand_n_inputs (rtx body)
+static void
+get_asm_operands_in_out (rtx body, int *pout, int *pin)
 {
-  switch (GET_CODE (body))
-    {
-    case SET:
-      gcc_assert (GET_CODE (SET_SRC (body)) == ASM_OPERANDS);
-      return ASM_OPERANDS_INPUT_LENGTH (SET_SRC (body));
-      
-    case ASM_OPERANDS:
-      return ASM_OPERANDS_INPUT_LENGTH (body);
-      
-    case PARALLEL:
-      return get_asm_operand_n_inputs (XVECEXP (body, 0, 0));
-      
-    default:
-      gcc_unreachable ();
-    }
+  rtx asmop = extract_asm_operands (body);
+
+  *pin = ASM_OPERANDS_INPUT_LENGTH (asmop);
+  *pout = (recog_data.n_operands
+          - ASM_OPERANDS_INPUT_LENGTH (asmop)
+          - ASM_OPERANDS_LABEL_LENGTH (asmop));
 }
 
 /* If current function returns its result in an fp stack register,
@@ -2034,8 +2024,7 @@ subst_asm_stack_regs (rtx insn, stack regstack)
 
   preprocess_constraints ();
 
-  n_inputs = get_asm_operand_n_inputs (body);
-  n_outputs = recog_data.n_operands - n_inputs;
+  get_asm_operands_in_out (body, &n_outputs, &n_inputs);
 
   gcc_assert (alt >= 0);
 
index d1c0793..2aa76b1 100644 (file)
@@ -187,8 +187,9 @@ DEF_RTL_EXPR(ASM_INPUT, "asm_input", "si", RTX_EXTRA)
    5th is a vector of modes and constraints for the input operands.
      Each element is an ASM_INPUT containing a constraint string
      and whose mode indicates the mode of the input operand.
-   6th is the source line number.  */
-DEF_RTL_EXPR(ASM_OPERANDS, "asm_operands", "ssiEEi", RTX_EXTRA)
+   6th is a vector of labels that may be branched to by the asm.
+   7th is the source line number.  */
+DEF_RTL_EXPR(ASM_OPERANDS, "asm_operands", "ssiEEEi", RTX_EXTRA)
 
 /* A machine-specific operation.
    1st operand is a vector of operands being used by the operation so that
index d3acebe..925246f 100644 (file)
--- a/gcc/rtl.h
+++ b/gcc/rtl.h
@@ -1188,7 +1188,10 @@ do {                                                                     \
   XSTR (XCVECEXP (RTX, 4, N, ASM_OPERANDS), 0)
 #define ASM_OPERANDS_INPUT_MODE(RTX, N)  \
   GET_MODE (XCVECEXP (RTX, 4, N, ASM_OPERANDS))
-#define ASM_OPERANDS_SOURCE_LOCATION(RTX) XCUINT (RTX, 5, ASM_OPERANDS)
+#define ASM_OPERANDS_LABEL_VEC(RTX) XCVEC (RTX, 5, ASM_OPERANDS)
+#define ASM_OPERANDS_LABEL_LENGTH(RTX) XCVECLEN (RTX, 5, ASM_OPERANDS)
+#define ASM_OPERANDS_LABEL(RTX, N) XCVECEXP (RTX, 5, N, ASM_OPERANDS)
+#define ASM_OPERANDS_SOURCE_LOCATION(RTX) XCUINT (RTX, 6, ASM_OPERANDS)
 #define ASM_INPUT_SOURCE_LOCATION(RTX) XCUINT (RTX, 1, ASM_INPUT)
 
 /* 1 if RTX is a mem that is statically allocated in read-only memory.  */
@@ -1926,6 +1929,7 @@ extern bool resize_reg_info (void);
 extern void free_reg_info (void);
 
 /* recog.c */
+extern rtx extract_asm_operands (rtx);
 extern int asm_noperands (const_rtx);
 extern const char *decode_asm_operands (rtx, rtx *, rtx **, const char **,
                                        enum machine_mode *, location_t *);
index 23fdd08..42f22b5 100644 (file)
@@ -110,8 +110,8 @@ static int n_occurrences (int, const char *);
 static bool tree_conflicts_with_clobbers_p (tree, HARD_REG_SET *);
 static void expand_nl_goto_receiver (void);
 static bool check_operand_nalternatives (tree, tree);
-static bool check_unique_operand_names (tree, tree);
-static char *resolve_operand_name_1 (char *, tree, tree);
+static bool check_unique_operand_names (tree, tree, tree);
+static char *resolve_operand_name_1 (char *, tree, tree, tree);
 static void expand_null_return_1 (void);
 static void expand_value_return (rtx);
 static int estimate_case_costs (case_node_ptr);
@@ -633,12 +633,13 @@ tree_conflicts_with_clobbers_p (tree t, HARD_REG_SET *clobbered_regs)
 
 static void
 expand_asm_operands (tree string, tree outputs, tree inputs,
-                    tree clobbers, int vol, location_t locus)
+                    tree clobbers, tree labels, int vol, location_t locus)
 {
-  rtvec argvec, constraintvec;
+  rtvec argvec, constraintvec, labelvec;
   rtx body;
   int ninputs = list_length (inputs);
   int noutputs = list_length (outputs);
+  int nlabels = list_length (labels);
   int ninout;
   int nclobbers;
   HARD_REG_SET clobbered_regs;
@@ -661,7 +662,7 @@ expand_asm_operands (tree string, tree outputs, tree inputs,
   if (! check_operand_nalternatives (outputs, inputs))
     return;
 
-  string = resolve_asm_operand_names (string, outputs, inputs);
+  string = resolve_asm_operand_names (string, outputs, inputs, labels);
 
   /* Collect constraints.  */
   i = 0;
@@ -845,12 +846,13 @@ expand_asm_operands (tree string, tree outputs, tree inputs,
 
   argvec = rtvec_alloc (ninputs);
   constraintvec = rtvec_alloc (ninputs);
+  labelvec = rtvec_alloc (nlabels);
 
   body = gen_rtx_ASM_OPERANDS ((noutputs == 0 ? VOIDmode
                                : GET_MODE (output_rtx[0])),
                               ggc_strdup (TREE_STRING_POINTER (string)),
                               empty_string, 0, argvec, constraintvec,
-                              locus);
+                              labelvec, locus);
 
   MEM_VOLATILE_P (body) = vol;
 
@@ -959,6 +961,11 @@ expand_asm_operands (tree string, tree outputs, tree inputs,
        = gen_rtx_ASM_INPUT (inout_mode[i], ggc_strdup (buffer));
     }
 
+  /* Copy labels to the vector.  */
+  for (i = 0, tail = labels; i < nlabels; ++i, tail = TREE_CHAIN (tail))
+    ASM_OPERANDS_LABEL (body, i)
+      = gen_rtx_LABEL_REF (Pmode, label_rtx (TREE_VALUE (tail)));
+
   generating_concat_p = old_generating_concat_p;
 
   /* Now, for each output, construct an rtx
@@ -966,18 +973,21 @@ expand_asm_operands (tree string, tree outputs, tree inputs,
                               ARGVEC CONSTRAINTS OPNAMES))
      If there is more than one, put them inside a PARALLEL.  */
 
-  if (noutputs == 1 && nclobbers == 0)
+  if (nlabels > 0 && nclobbers == 0)
     {
-      ASM_OPERANDS_OUTPUT_CONSTRAINT (body) = ggc_strdup (constraints[0]);
-      emit_insn (gen_rtx_SET (VOIDmode, output_rtx[0], body));
+      gcc_assert (noutputs == 0);
+      emit_jump_insn (body);
     }
-
   else if (noutputs == 0 && nclobbers == 0)
     {
       /* No output operands: put in a raw ASM_OPERANDS rtx.  */
       emit_insn (body);
     }
-
+  else if (noutputs == 1 && nclobbers == 0)
+    {
+      ASM_OPERANDS_OUTPUT_CONSTRAINT (body) = ggc_strdup (constraints[0]);
+      emit_insn (gen_rtx_SET (VOIDmode, output_rtx[0], body));
+    }
   else
     {
       rtx obody = body;
@@ -998,7 +1008,7 @@ expand_asm_operands (tree string, tree outputs, tree inputs,
                           (GET_MODE (output_rtx[i]),
                            ggc_strdup (TREE_STRING_POINTER (string)),
                            ggc_strdup (constraints[i]),
-                           i, argvec, constraintvec, locus));
+                           i, argvec, constraintvec, labelvec, locus));
 
          MEM_VOLATILE_P (SET_SRC (XVECEXP (body, 0, i))) = vol;
        }
@@ -1062,7 +1072,10 @@ expand_asm_operands (tree string, tree outputs, tree inputs,
            = gen_rtx_CLOBBER (VOIDmode, clobbered_reg);
        }
 
-      emit_insn (body);
+      if (nlabels > 0)
+       emit_jump_insn (body);
+      else
+       emit_insn (body);
     }
 
   /* For any outputs that needed reloading into registers, spill them
@@ -1083,7 +1096,7 @@ expand_asm_stmt (gimple stmt)
   tree *o;
   size_t i, n;
   const char *s;
-  tree str, out, in, cl;
+  tree str, out, in, cl, labels;
 
   /* Meh... convert the gimple asm operands into real tree lists.
      Eventually we should make all routines work on the vectors instead
@@ -1094,10 +1107,7 @@ expand_asm_stmt (gimple stmt)
     {
       t = out = gimple_asm_output_op (stmt, 0);
       for (i = 1; i < n; i++)
-       {
-         TREE_CHAIN (t) = gimple_asm_output_op (stmt, i);
-         t = gimple_asm_output_op (stmt, i);
-       }
+       t = TREE_CHAIN (t) = gimple_asm_output_op (stmt, i);
     }
 
   in = NULL_TREE;
@@ -1106,10 +1116,7 @@ expand_asm_stmt (gimple stmt)
     {
       t = in = gimple_asm_input_op (stmt, 0);
       for (i = 1; i < n; i++)
-       {
-         TREE_CHAIN (t) = gimple_asm_input_op (stmt, i);
-         t = gimple_asm_input_op (stmt, i);
-       }
+       t = TREE_CHAIN (t) = gimple_asm_input_op (stmt, i);
     }
 
   cl = NULL_TREE;
@@ -1118,10 +1125,16 @@ expand_asm_stmt (gimple stmt)
     {
       t = cl = gimple_asm_clobber_op (stmt, 0);
       for (i = 1; i < n; i++)
-       {
-         TREE_CHAIN (t) = gimple_asm_clobber_op (stmt, i);
-         t = gimple_asm_clobber_op (stmt, i);
-       }
+       t = TREE_CHAIN (t) = gimple_asm_clobber_op (stmt, i);
+    }
+
+  labels = NULL_TREE;
+  n = gimple_asm_nlabels (stmt);
+  if (n > 0)
+    {
+      t = labels = gimple_asm_label_op (stmt, 0);
+      for (i = 1; i < n; i++)
+       t = TREE_CHAIN (t) = gimple_asm_label_op (stmt, i);
     }
 
   s = gimple_asm_string (stmt);
@@ -1144,8 +1157,8 @@ expand_asm_stmt (gimple stmt)
 
   /* Generate the ASM_OPERANDS insn; store into the TREE_VALUEs of
      OUTPUTS some trees for where the values were actually stored.  */
-  expand_asm_operands (str, outputs, in, cl, gimple_asm_volatile_p (stmt),
-                      input_location);
+  expand_asm_operands (str, outputs, in, cl, labels,
+                      gimple_asm_volatile_p (stmt), input_location);
 
   /* Copy all the intermediate outputs into the specified outputs.  */
   for (i = 0, tail = outputs; tail; tail = TREE_CHAIN (tail), i++)
@@ -1210,7 +1223,7 @@ check_operand_nalternatives (tree outputs, tree inputs)
    so all we need are pointer comparisons.  */
 
 static bool
-check_unique_operand_names (tree outputs, tree inputs)
+check_unique_operand_names (tree outputs, tree inputs, tree labels)
 {
   tree i, j;
 
@@ -1239,6 +1252,20 @@ check_unique_operand_names (tree outputs, tree inputs)
          goto failure;
     }
 
+  for (i = labels; i ; i = TREE_CHAIN (i))
+    {
+      tree i_name = TREE_PURPOSE (i);
+      if (! i_name)
+       continue;
+
+      for (j = TREE_CHAIN (i); j ; j = TREE_CHAIN (j))
+       if (simple_cst_equal (i_name, TREE_PURPOSE (j)))
+         goto failure;
+      for (j = inputs; j ; j = TREE_CHAIN (j))
+       if (simple_cst_equal (i_name, TREE_PURPOSE (TREE_PURPOSE (j))))
+         goto failure;
+    }
+
   return true;
 
  failure:
@@ -1252,14 +1279,14 @@ check_unique_operand_names (tree outputs, tree inputs)
    STRING and in the constraints to those numbers.  */
 
 tree
-resolve_asm_operand_names (tree string, tree outputs, tree inputs)
+resolve_asm_operand_names (tree string, tree outputs, tree inputs, tree labels)
 {
   char *buffer;
   char *p;
   const char *c;
   tree t;
 
-  check_unique_operand_names (outputs, inputs);
+  check_unique_operand_names (outputs, inputs, labels);
 
   /* Substitute [<name>] in input constraint strings.  There should be no
      named operands in output constraints.  */
@@ -1270,7 +1297,7 @@ resolve_asm_operand_names (tree string, tree outputs, tree inputs)
        {
          p = buffer = xstrdup (c);
          while ((p = strchr (p, '[')) != NULL)
-           p = resolve_operand_name_1 (p, outputs, inputs);
+           p = resolve_operand_name_1 (p, outputs, inputs, NULL);
          TREE_VALUE (TREE_PURPOSE (t))
            = build_string (strlen (buffer), buffer);
          free (buffer);
@@ -1313,7 +1340,7 @@ resolve_asm_operand_names (tree string, tree outputs, tree inputs)
              continue;
            }
 
-         p = resolve_operand_name_1 (p, outputs, inputs);
+         p = resolve_operand_name_1 (p, outputs, inputs, labels);
        }
 
       string = build_string (strlen (buffer), buffer);
@@ -1329,53 +1356,49 @@ resolve_asm_operand_names (tree string, tree outputs, tree inputs)
    balance of the string after substitution.  */
 
 static char *
-resolve_operand_name_1 (char *p, tree outputs, tree inputs)
+resolve_operand_name_1 (char *p, tree outputs, tree inputs, tree labels)
 {
   char *q;
   int op;
   tree t;
-  size_t len;
 
   /* Collect the operand name.  */
-  q = strchr (p, ']');
+  q = strchr (++p, ']');
   if (!q)
     {
       error ("missing close brace for named operand");
       return strchr (p, '\0');
     }
-  len = q - p - 1;
+  *q = '\0';
 
   /* Resolve the name to a number.  */
   for (op = 0, t = outputs; t ; t = TREE_CHAIN (t), op++)
     {
       tree name = TREE_PURPOSE (TREE_PURPOSE (t));
-      if (name)
-       {
-         const char *c = TREE_STRING_POINTER (name);
-         if (strncmp (c, p + 1, len) == 0 && c[len] == '\0')
-           goto found;
-       }
+      if (name && strcmp (TREE_STRING_POINTER (name), p) == 0)
+       goto found;
     }
   for (t = inputs; t ; t = TREE_CHAIN (t), op++)
     {
       tree name = TREE_PURPOSE (TREE_PURPOSE (t));
-      if (name)
-       {
-         const char *c = TREE_STRING_POINTER (name);
-         if (strncmp (c, p + 1, len) == 0 && c[len] == '\0')
-           goto found;
-       }
+      if (name && strcmp (TREE_STRING_POINTER (name), p) == 0)
+       goto found;
+    }
+  for (t = labels; t ; t = TREE_CHAIN (t), op++)
+    {
+      tree name = TREE_PURPOSE (t);
+      if (name && strcmp (TREE_STRING_POINTER (name), p) == 0)
+       goto found;
     }
 
-  *q = '\0';
-  error ("undefined named operand %qs", identifier_to_locale (p + 1));
+  error ("undefined named operand %qs", identifier_to_locale (p));
   op = 0;
- found:
 
+ found:
   /* Replace the name with the number.  Unfortunately, not all libraries
      get the return value of sprintf correct, so search for the end of the
      generated string by hand.  */
-  sprintf (p, "%d", op);
+  sprintf (--p, "%d", op);
   p = strchr (p, '\0');
 
   /* Verify the no extra buffer space assumption.  */
index db86cf6..5649e20 100644 (file)
@@ -1,5 +1,11 @@
 2009-09-14  Richard Henderson  <rth@redhat.com>
 
+       * c-c++-common/asmgoto-1.c, c-c++-common/asmgoto-2.c,
+       c-c++-common/asmgoto-3.c, gcc.c-torture/compile/asmgoto-1.c,
+       gcc.dg/tree-ssa/asmgoto-1.c: New files.
+
+2009-09-14  Richard Henderson  <rth@redhat.com>
+
        * g++.dg/eh/builtin1.C: Update resx pattern match.
        * g++.dg/eh/builtin2.C, g++.dg/eh/builtin3.C: Likewise.
 
diff --git a/gcc/testsuite/c-c++-common/asmgoto-1.c b/gcc/testsuite/c-c++-common/asmgoto-1.c
new file mode 100644 (file)
index 0000000..9c729fd
--- /dev/null
@@ -0,0 +1,15 @@
+/* { dg-do compile } */
+/* { dg-options "" } */
+
+void
+foo (void)
+{
+  int i = 0;
+  asm ("" : : : "memory");
+  asm ("" : : : );
+  asm ("" : : "r" (i));
+  asm ("" : : );
+  asm ("" : "=r" (i));
+  asm ("" : );
+  asm ("");
+}
diff --git a/gcc/testsuite/c-c++-common/asmgoto-2.c b/gcc/testsuite/c-c++-common/asmgoto-2.c
new file mode 100644 (file)
index 0000000..5bf4572
--- /dev/null
@@ -0,0 +1,20 @@
+/* { dg-do compile } */
+/* { dg-options "" } */
+
+void
+foo (void)
+{
+  __label__ lab;
+  int i = 0;
+  asm goto ("" : : : : lab);
+  asm goto ("" : "=r" (i) : : : lab);  /* { dg-error "expected" } */
+  asm goto ("" : : : : );      /* { dg-error "expected" } */
+  asm goto ("" : : : "memory");        /* { dg-error "expected" } */
+  asm goto ("" : : : );                /* { dg-error "expected" } */
+  asm goto ("" : : "r" (i));   /* { dg-error "expected" } */
+  asm goto ("" : : );          /* { dg-error "expected" } */
+  asm goto ("" : "=r" (i));    /* { dg-error "expected" } */
+  asm goto ("" : );            /* { dg-error "expected" } */
+  asm goto ("");               /* { dg-error "expected" } */
+  lab:;
+}
diff --git a/gcc/testsuite/c-c++-common/asmgoto-3.c b/gcc/testsuite/c-c++-common/asmgoto-3.c
new file mode 100644 (file)
index 0000000..5224429
--- /dev/null
@@ -0,0 +1,10 @@
+/* { dg-do compile } */
+/* { dg-options "-Wunused" } */
+
+int foo ()
+{
+  asm goto ("" : : : : label);
+  return 1;
+ label:
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.c-torture/compile/asmgoto-1.c b/gcc/testsuite/gcc.c-torture/compile/asmgoto-1.c
new file mode 100644 (file)
index 0000000..cc34610
--- /dev/null
@@ -0,0 +1,30 @@
+void fn (void);
+
+void
+foo (void *x, unsigned long y)
+{
+  asm goto ("": : : : lab);
+lab:
+  fn ();
+}
+
+static void
+bar (unsigned long x)
+{
+  foo (0, x);
+}
+
+static void
+baz (unsigned long x)
+{
+  if (x > 8192)
+    bar (x);
+  else
+    ({ __here: (unsigned long) &&__here; });
+}
+
+void
+test (void)
+{
+  baz (16384);
+}
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/asmgoto-1.c b/gcc/testsuite/gcc.dg/tree-ssa/asmgoto-1.c
new file mode 100644 (file)
index 0000000..1d08067
--- /dev/null
@@ -0,0 +1,95 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+
+extern void XYZZY (void);
+typedef unsigned long __kernel_size_t;
+typedef __kernel_size_t size_t;
+typedef unsigned gfp_t;
+struct per_cpu_pageset { } __attribute__ ((__aligned__ ((1 << (6)))));
+struct zone { struct per_cpu_pageset *pageset[64]; }
+zone_flags_t; typedef struct pglist_data { struct zone node_zones[4]; } pg_data_t;
+extern struct pglist_data *first_online_pgdat (void);
+extern struct zone *next_zone (struct zone *zone);
+extern volatile int per_cpu__x86_cpu_to_node_map[];
+struct kmem_cache { int size; };
+extern struct kmem_cache kmalloc_caches[(12 + 2)];
+struct tracepoint { void **funcs; } __attribute__ ((aligned (32)));
+extern struct tracepoint __tracepoint_kmalloc_node;
+void *__kmalloc_node (size_t size, gfp_t flags, int node);
+
+static inline int
+cpu_to_node (int cpu)
+{
+  return per_cpu__x86_cpu_to_node_map[cpu];
+}
+
+static inline void
+trace_kmalloc_node (unsigned long call_site, const void *ptr,
+                   size_t bytes_req, size_t bytes_alloc, gfp_t gfp_flags,
+                   int node)
+{
+  asm goto ("" : : : : trace_label);
+  if (0)
+    {
+         void **it_func;
+    trace_label:
+         asm ("" : "=r"(it_func) : "0"(&__tracepoint_kmalloc_node.funcs));
+    }
+};
+
+static inline __attribute__ ((always_inline)) int
+kmalloc_index (size_t size)
+{
+  if (size <= 64)
+    return 6;
+  return -1;
+}
+
+static inline __attribute__ ((always_inline)) struct kmem_cache *
+kmalloc_slab (size_t size)
+{
+  int index = kmalloc_index (size);
+  if (index == 0)
+    return ((void *) 0);
+  return &kmalloc_caches[index];
+}
+
+static inline __attribute__ ((always_inline)) void *
+kmalloc_node (size_t size, gfp_t flags, int node)
+{
+  void *ret;
+  if (__builtin_constant_p (size) && size <= (2 * ((1UL) << 12))
+      && !(flags & ((gfp_t) 0x01u)))
+    {
+      struct kmem_cache *s = kmalloc_slab (size);
+      if (!s)
+       return ((void *) 16);
+      trace_kmalloc_node (({ __here:(unsigned long) &&__here;}),
+                         ret, size, s->size, flags, node);
+    }
+  return __kmalloc_node (size, flags, node);
+}
+
+int
+process_zones (int cpu)
+{
+  struct zone *zone, *dzone;
+  int node = cpu_to_node (cpu);
+  for (zone = (first_online_pgdat ())->node_zones;
+       zone; zone = next_zone (zone))
+      {
+       ((zone)->pageset[(cpu)]) =
+         kmalloc_node (sizeof (struct per_cpu_pageset),
+                       (((gfp_t) 0x10u) | ((gfp_t) 0x40u) | ((gfp_t) 0x80u)),
+                       node);
+       if (!((zone)->pageset[(cpu)]))
+         goto bad;
+      }
+  return 0;
+bad:
+  XYZZY ();
+  return -12;
+}
+
+/* { dg-final { scan-tree-dump-times "XYZZY" 1 "optimized" } } */
+/* { dg-final { cleanup-tree-dump "optimized" } } */
index fb24cc4..f596c75 100644 (file)
@@ -99,6 +99,7 @@ static void make_edges (void);
 static void make_cond_expr_edges (basic_block);
 static void make_gimple_switch_edges (basic_block);
 static void make_goto_expr_edges (basic_block);
+static void make_gimple_asm_edges (basic_block);
 static unsigned int locus_map_hash (const void *);
 static int locus_map_eq (const void *, const void *);
 static void assign_discriminator (location_t, basic_block);
@@ -572,6 +573,11 @@ make_edges (void)
              fallthru = true;
              break;
 
+           case GIMPLE_ASM:
+             make_gimple_asm_edges (bb);
+             fallthru = true;
+             break;
+
            case GIMPLE_OMP_PARALLEL:
            case GIMPLE_OMP_TASK:
            case GIMPLE_OMP_FOR:
@@ -593,13 +599,11 @@ make_edges (void)
              fallthru = false;
              break;
 
-
             case GIMPLE_OMP_ATOMIC_LOAD:
             case GIMPLE_OMP_ATOMIC_STORE:
                fallthru = true;
                break;
 
-
            case GIMPLE_OMP_RETURN:
              /* In the case of a GIMPLE_OMP_SECTION, the edge will go
                 somewhere other than the next block.  This will be
@@ -1011,6 +1015,23 @@ make_goto_expr_edges (basic_block bb)
   make_abnormal_goto_edges (bb, false);
 }
 
+/* Create edges for an asm statement with labels at block BB.  */
+
+static void
+make_gimple_asm_edges (basic_block bb)
+{
+  gimple stmt = last_stmt (bb);
+  location_t stmt_loc = gimple_location (stmt);
+  int i, n = gimple_asm_nlabels (stmt);
+
+  for (i = 0; i < n; ++i)
+    {
+      tree label = TREE_VALUE (gimple_asm_label_op (stmt, i));
+      basic_block label_bb = label_to_block (label);
+      make_edge (bb, label_bb, 0);
+      assign_discriminator (stmt_loc, label_bb);
+    }
+}
 
 /*---------------------------------------------------------------------------
                               Flowgraph analysis
@@ -1188,6 +1209,19 @@ cleanup_dead_labels (void)
            break;
          }
 
+       case GIMPLE_ASM:
+         {
+           int i, n = gimple_asm_nlabels (stmt);
+
+           for (i = 0; i < n; ++i)
+             {
+               tree cons = gimple_asm_label_op (stmt, i);
+               tree label = main_block_label (TREE_VALUE (cons));
+               TREE_VALUE (cons) = label;
+             }
+           break;
+         }
+
        /* We have to handle gotos until they're removed, and we don't
           remove them until after we've created the CFG edges.  */
        case GIMPLE_GOTO:
@@ -1195,8 +1229,8 @@ cleanup_dead_labels (void)
            {
              tree new_dest = main_block_label (gimple_goto_dest (stmt));
              gimple_goto_set_dest (stmt, new_dest);
-             break;
            }
+         break;
 
        default:
          break;
@@ -2821,6 +2855,11 @@ is_ctrl_altering_stmt (gimple t)
         fallthru to the next statement as well.  */
       return true;
 
+    case GIMPLE_ASM:
+      if (gimple_asm_nlabels (t) > 0)
+       return true;
+      break;
+
     CASE_GIMPLE_OMP:
       /* OpenMP directives alter control flow.  */
       return true;
@@ -5184,9 +5223,22 @@ gimple_redirect_edge_and_branch (edge e, basic_block dest)
                  CASE_LABEL (elt) = label;
              }
          }
+      }
+      break;
 
-       break;
+    case GIMPLE_ASM:
+      {
+       int i, n = gimple_asm_nlabels (stmt);
+       tree label = gimple_block_label (dest);
+
+       for (i = 0; i < n; ++i)
+         {
+           tree cons = gimple_asm_label_op (stmt, i);
+           if (label_to_block (TREE_VALUE (cons)) == e->dest)
+             TREE_VALUE (cons) = label;
+         }
       }
+      break;
 
     case GIMPLE_RETURN:
       gsi_remove (&gsi, true);
index 71987ef..c1ba96a 100644 (file)
@@ -875,8 +875,9 @@ DEFTREECODE (CASE_LABEL_EXPR, "case_label_expr", tcc_statement, 3)
 /* Used to represent an inline assembly statement.  ASM_STRING returns a
    STRING_CST for the instruction (e.g., "mov x, y"). ASM_OUTPUTS,
    ASM_INPUTS, and ASM_CLOBBERS represent the outputs, inputs, and clobbers
-   for the statement.  */
-DEFTREECODE (ASM_EXPR, "asm_expr", tcc_statement, 4)
+   for the statement.  ASM_LABELS, if present, indicates various destinations
+   for the asm; labels cannot be combined with outputs.  */
+DEFTREECODE (ASM_EXPR, "asm_expr", tcc_statement, 5)
 
 /* Variable references for SSA analysis.  New SSA names are created every
    time a variable is assigned a new value.  The SSA builder uses SSA_NAME
index b9404c7..7065048 100644 (file)
@@ -1626,6 +1626,7 @@ extern void protected_set_expr_location (tree, location_t);
 #define ASM_OUTPUTS(NODE)       TREE_OPERAND (ASM_EXPR_CHECK (NODE), 1)
 #define ASM_INPUTS(NODE)        TREE_OPERAND (ASM_EXPR_CHECK (NODE), 2)
 #define ASM_CLOBBERS(NODE)      TREE_OPERAND (ASM_EXPR_CHECK (NODE), 3)
+#define ASM_LABELS(NODE)       TREE_OPERAND (ASM_EXPR_CHECK (NODE), 4)
 /* Nonzero if we want to create an ASM_INPUT instead of an
    ASM_OPERAND with no operands.  */
 #define ASM_INPUT_P(NODE) (ASM_EXPR_CHECK (NODE)->base.static_flag)
@@ -5087,7 +5088,7 @@ extern bool parse_output_constraint (const char **, int, int, int,
 extern bool parse_input_constraint (const char **, int, int, int, int,
                                    const char * const *, bool *, bool *);
 extern void expand_asm_stmt (gimple);
-extern tree resolve_asm_operand_names (tree, tree, tree);
+extern tree resolve_asm_operand_names (tree, tree, tree, tree);
 extern void expand_case (gimple);
 extern void expand_decl (tree);
 #ifdef HARD_CONST