diff mbox

allow certain kinds of inputs to top level asm()-s

Message ID 4C32F9BA0200007800009ABF@vpn.id2.novell.com
State New
Headers show

Commit Message

Jan Beulich July 6, 2010, 7:39 a.m. UTC
This is so that use of symbols referenced in these asm()-s can be
properly tracked by the compiler, just like is the case for all other
asm()-s. I'm particularly looking forward to use this in the Linux
kernel.

gcc/
2010-07-06  Jan Beulich  <jbeulich@novell.com>

	* c-parser.c: New second parameter of
	c_parser_simple_asm_expr().
	Change c_parser_asm_operands()' second parameter's type to
	'int'.
	(c_parser_declaration_or_fndef): Adjust call to
	c_parser_simple_asm_expr().
	(c_parser_asm_definition): Obtain and pass inputs.
	(c_parser_simple_asm_expr): Adjust simple-asm-expr definition.
	New second parameter. Handle optional inputs if requested.
	(c_parser_asm_statement): Adjust calls to
	c_parser_asm_operands().
	(c_parser_asm_statement): Adjust comment. Change second
	parameter's type to 'int'. Call c_parser_expr_no_commas()
	instead of c_parser_expression() when 'mode' is negative.
	* cgraph.c (cgraph_add_asm_node): New parameters 'inputs' and
	'loc'. Call check_unique_operand_names(). Initialize new fields
	of 'node'.
	* cgraph.h (struct cgraph_asm_node): New members 'inputs' and
	'loc'.
	(cgraph_add_asm_node): New parameters 'inputs' and 'loc'.
	* cgraphunit.c (cgraph_output_pending_asms): Adjust call to
	assemble_asm().
	(process_function_and_variable_attributes): Process inputs of
	all asm nodes.
	(cgraph_output_in_order): Adjust call to assemble_asm().
	* lto-cgraph.c (output_cgraph): Call lto_output_toplevel_asms()
	instead of emitting top level asm-s here.
	(input_cgraph_1): Call lto_input_toplevel_asms() instead of
	reading in top level asm-s here.
	* lto-section-in.c (lto_section_name): Add name for asm-s'
	section.
	* lto-streamer-in.c (lto_input_toplevel_asms): New.
	* lto-streamer-out.c (lto_output_toplevel_asms): New.
	* lto-streamer.c (lto_get_section_name): Handle
	LTO_section_asm.
	* lto-streamer.h (LTO_minor_version): Bump.
	(enum lto_section_type): Add LTO_section_asm.
	(struct lto_asm_header): New.
	(lto_input_toplevel_asms): Declare.
	(lto_output_toplevel_asms): Declare.
	* output.h (assemble_asm): New second and third parameter.
	* stmt.c (check_unique_operand_names): No longer static.
	* tree.h (check_unique_operand_names): Declare.
	* varasm.c: Include pretty-print.h.
	(assemble_asm): New parameters 'inputs' and 'loc'. Handle
	inputs.

gcc/cp/
2010-07-06  Jan Beulich  <jbeulich@novell.com>

	* parser.c (cp_parser_asm_definition): Correct a comment.
	Process	optional inputs.

gcc/testsuite/
2010-07-06  Jan Beulich  <jbeulich@novell.com>

	* g++.dg/ext/asm-static-1.C: New.
	* gcc.dg/asm-static-1.c: New.
	* gcc.dg/asm-static-2.c: New.
	* gcc.dg/asm-static-3.c: New.
	* gcc.dg/asm-static-4.c: New.
This is so that use of symbols referenced in these asm()-s can be
properly tracked by the compiler, just like is the case for all other
asm()-s. I'm particularly looking forward to use this in the Linux
kernel.

gcc/
2010-07-06  Jan Beulich  <jbeulich@novell.com>

	* c-parser.c: New second parameter of
	c_parser_simple_asm_expr().
	Change c_parser_asm_operands()' second parameter's type to
	'int'.
	(c_parser_declaration_or_fndef): Adjust call to
	c_parser_simple_asm_expr().
	(c_parser_asm_definition): Obtain and pass inputs.
	(c_parser_simple_asm_expr): Adjust simple-asm-expr definition.
	New second parameter. Handle optional inputs if requested.
	(c_parser_asm_statement): Adjust calls to
	c_parser_asm_operands().
	(c_parser_asm_statement): Adjust comment. Change second
	parameter's type to 'int'. Call c_parser_expr_no_commas()
	instead of c_parser_expression() when 'mode' is negative.
	* cgraph.c (cgraph_add_asm_node): New parameters 'inputs' and
	'loc'. Call check_unique_operand_names(). Initialize new fields
	of 'node'.
	* cgraph.h (struct cgraph_asm_node): New members 'inputs' and
	'loc'.
	(cgraph_add_asm_node): New parameters 'inputs' and 'loc'.
	* cgraphunit.c (cgraph_output_pending_asms): Adjust call to
	assemble_asm().
	(process_function_and_variable_attributes): Process inputs of
	all asm nodes.
	(cgraph_output_in_order): Adjust call to assemble_asm().
	* lto-cgraph.c (output_cgraph): Call lto_output_toplevel_asms()
	instead of emitting top level asm-s here.
	(input_cgraph_1): Call lto_input_toplevel_asms() instead of
	reading in top level asm-s here.
	* lto-section-in.c (lto_section_name): Add name for asm-s'
	section.
	* lto-streamer-in.c (lto_input_toplevel_asms): New.
	* lto-streamer-out.c (lto_output_toplevel_asms): New.
	* lto-streamer.c (lto_get_section_name): Handle
	LTO_section_asm.
	* lto-streamer.h (LTO_minor_version): Bump.
	(enum lto_section_type): Add LTO_section_asm.
	(struct lto_asm_header): New.
	(lto_input_toplevel_asms): Declare.
	(lto_output_toplevel_asms): Declare.
	* output.h (assemble_asm): New second and third parameter.
	* stmt.c (check_unique_operand_names): No longer static.
	* tree.h (check_unique_operand_names): Declare.
	* varasm.c: Include pretty-print.h.
	(assemble_asm): New parameters 'inputs' and 'loc'. Handle
	inputs.

gcc/cp/
2010-07-06  Jan Beulich  <jbeulich@novell.com>

	* parser.c (cp_parser_asm_definition): Correct a comment.
	Process	optional inputs.

gcc/testsuite/
2010-07-06  Jan Beulich  <jbeulich@novell.com>

	* g++.dg/ext/asm-static-1.C: New.
	* gcc.dg/asm-static-1.c: New.
	* gcc.dg/asm-static-2.c: New.
	* gcc.dg/asm-static-3.c: New.
	* gcc.dg/asm-static-4.c: New.

--- 2010-06-30/gcc/c-parser.c	2010-06-30 08:40:05.000000000 +0200
+++ 2010-06-30/gcc/c-parser.c	2010-07-02 15:33:45.000000000 +0200
@@ -926,7 +926,7 @@ static struct c_declarator *c_parser_dir
 static struct c_arg_info *c_parser_parms_declarator (c_parser *, bool, tree);
 static struct c_arg_info *c_parser_parms_list_declarator (c_parser *, tree);
 static struct c_parm *c_parser_parameter_declaration (c_parser *, tree);
-static tree c_parser_simple_asm_expr (c_parser *);
+static tree c_parser_simple_asm_expr (c_parser *, tree *);
 static tree c_parser_attributes (c_parser *);
 static struct c_type_name *c_parser_type_name (c_parser *);
 static struct c_expr c_parser_initializer (c_parser *);
@@ -945,7 +945,7 @@ static void c_parser_while_statement (c_
 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_operands (c_parser *, int);
 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 *);
@@ -1273,7 +1273,7 @@ c_parser_declaration_or_fndef (c_parser 
 	     function definition.  */
 	  fndef_ok = false;
 	  if (c_parser_next_token_is_keyword (parser, RID_ASM))
-	    asm_name = c_parser_simple_asm_expr (parser);
+	    asm_name = c_parser_simple_asm_expr (parser, NULL);
 	  if (c_parser_next_token_is_keyword (parser, RID_ATTRIBUTE))
 	    postfix_attrs = c_parser_attributes (parser);
 	  if (c_parser_next_token_is (parser, CPP_EQ))
@@ -1406,9 +1406,12 @@ c_parser_declaration_or_fndef (c_parser 
 static void
 c_parser_asm_definition (c_parser *parser)
 {
-  tree asm_str = c_parser_simple_asm_expr (parser);
+  location_t loc = c_parser_peek_token (parser)->location;
+  tree inputs = NULL_TREE;
+  tree asm_str = c_parser_simple_asm_expr (parser, &inputs);
+
   if (asm_str)
-    cgraph_add_asm_node (asm_str);
+    cgraph_add_asm_node (asm_str, inputs, loc);
   c_parser_skip_until_found (parser, CPP_SEMICOLON, "expected %<;%>");
 }
 
@@ -2910,10 +2913,11 @@ c_parser_asm_string_literal (c_parser *p
 
    simple-asm-expr:
      asm ( asm-string-literal )
+     asm ( asm-string-literal : asm-operands )
 */
 
 static tree
-c_parser_simple_asm_expr (c_parser *parser)
+c_parser_simple_asm_expr (c_parser *parser, tree *inputsp)
 {
   tree str;
   gcc_assert (c_parser_next_token_is_keyword (parser, RID_ASM));
@@ -2927,6 +2931,9 @@ c_parser_simple_asm_expr (c_parser *pars
       return NULL_TREE;
     }
   str = c_parser_asm_string_literal (parser);
+  if (inputsp && !c_parser_next_token_is (parser, CPP_CLOSE_PAREN)
+      && c_parser_require (parser, CPP_COLON, "expected %<:%> or %<)%>"))
+      *inputsp = c_parser_asm_operands (parser, -1);
   parser->lex_untranslated_string = false;
   if (!c_parser_require (parser, CPP_CLOSE_PAREN, "expected %<)%>"))
     {
@@ -4513,10 +4520,10 @@ c_parser_asm_statement (c_parser *parser
 	    /* 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);
+	      outputs = c_parser_asm_operands (parser, 0);
 	    break;
 	  case 1:
-	    inputs = c_parser_asm_operands (parser, true);
+	    inputs = c_parser_asm_operands (parser, 1);
 	    break;
 	  case 2:
 	    clobbers = c_parser_asm_clobbers (parser);
@@ -4554,9 +4561,10 @@ c_parser_asm_statement (c_parser *parser
   goto error;
 }
 
-/* Parse asm operands, a GNU extension.  If CONVERT_P (for inputs but
-   not outputs), apply the default conversion of functions and arrays
-   to pointers.
+/* Parse asm operands, a GNU extension.  If MODE is non-zero (for inputs
+   but not outputs), apply the default conversion of functions and arrays
+   to pointers.  If MODE is negative (for inputs of asm definitions),
+   don't allow comma expressions.
 
    asm-operands:
      asm-operand
@@ -4568,7 +4576,7 @@ c_parser_asm_statement (c_parser *parser
 */
 
 static tree
-c_parser_asm_operands (c_parser *parser, bool convert_p)
+c_parser_asm_operands (c_parser *parser, int mode)
 {
   tree list = NULL_TREE;
   location_t loc;
@@ -4607,9 +4615,12 @@ c_parser_asm_operands (c_parser *parser,
 	  return NULL_TREE;
 	}
       loc = c_parser_peek_token (parser)->location;
-      expr = c_parser_expression (parser);
+      if (mode >= 0)
+	expr = c_parser_expression (parser);
+      else
+	expr = c_parser_expr_no_commas(parser, NULL);
       mark_exp_read (expr.value);
-      if (convert_p)
+      if (mode)
 	expr = default_function_array_conversion (loc, expr);
       expr.value = c_fully_fold (expr.value, false, NULL);
       parser->lex_untranslated_string = true;
--- 2010-06-30/gcc/cgraph.c	2010-06-30 08:40:00.000000000 +0200
+++ 2010-06-30/gcc/cgraph.c	2010-07-02 15:33:45.000000000 +0200
@@ -1971,12 +1971,15 @@ change_decl_assembler_name (tree decl, t
 /* Add a top-level asm statement to the list.  */
 
 struct cgraph_asm_node *
-cgraph_add_asm_node (tree asm_str)
+cgraph_add_asm_node (tree asm_str, tree inputs, location_t loc)
 {
   struct cgraph_asm_node *node;
 
   node = ggc_alloc_cleared_cgraph_asm_node ();
   node->asm_str = asm_str;
+  node->inputs = check_unique_operand_names (NULL_TREE, inputs, NULL_TREE)
+		 ? inputs : NULL_TREE;
+  node->loc = loc;
   node->order = cgraph_order++;
   node->next = NULL;
   if (cgraph_asm_nodes == NULL)
--- 2010-06-30/gcc/cgraph.h	2010-06-30 08:40:00.000000000 +0200
+++ 2010-06-30/gcc/cgraph.h	2010-07-02 15:33:45.000000000 +0200
@@ -508,6 +508,10 @@ struct GTY(()) cgraph_asm_node {
   struct cgraph_asm_node *next;
   /* String for this asm node.  */
   tree asm_str;
+  /* Inputs for this asm node (optional).  */
+  tree inputs;
+  /* Source location of the asm().  */
+  location_t loc;
   /* Ordering of all cgraph nodes.  */
   int order;
 };
@@ -582,7 +586,7 @@ struct cgraph_node * cgraph_clone_node (
 void cgraph_redirect_edge_callee (struct cgraph_edge *, struct cgraph_node *);
 void cgraph_make_edge_direct (struct cgraph_edge *, struct cgraph_node *);
 
-struct cgraph_asm_node *cgraph_add_asm_node (tree);
+struct cgraph_asm_node *cgraph_add_asm_node (tree, tree, location_t);
 
 bool cgraph_function_possibly_inlined_p (tree);
 void cgraph_unnest_node (struct cgraph_node *);
--- 2010-06-30/gcc/cgraphunit.c	2010-06-30 08:40:05.000000000 +0200
+++ 2010-06-30/gcc/cgraphunit.c	2010-07-02 15:33:45.000000000 +0200
@@ -903,7 +903,7 @@ cgraph_output_pending_asms (void)
     return;
 
   for (can = cgraph_asm_nodes; can; can = can->next)
-    assemble_asm (can->asm_str);
+    assemble_asm (can->asm_str, can->inputs, can->loc);
   cgraph_asm_nodes = NULL;
 }
 
@@ -964,6 +964,7 @@ process_function_and_variable_attributes
 {
   struct cgraph_node *node;
   struct varpool_node *vnode;
+  struct cgraph_asm_node *anode;
 
   for (node = cgraph_nodes; node != first; node = node->next)
     {
@@ -999,6 +1000,47 @@ process_function_and_variable_attributes
 	    varpool_mark_needed_node (vnode);
 	}
     }
+  for (anode = cgraph_asm_nodes; anode; anode = anode->next)
+    if (anode->inputs)
+      {
+	tree node = anode->inputs;
+
+	for (; node && node != error_mark_node; node = TREE_CHAIN (node))
+	  {
+	    tree value = TREE_VALUE (node);
+
+	    while (value && value != error_mark_node)
+	      {
+		switch (TREE_CODE (value))
+		  {
+		    case INTEGER_CST:
+		      value = NULL_TREE;
+		      break;
+
+		    CASE_CONVERT:
+		    case ADDR_EXPR:
+		    case FDESC_EXPR:
+		    case POINTER_PLUS_EXPR:
+		    case COMPONENT_REF:
+		      value = TREE_OPERAND (value, 0);
+		      break;
+
+		    case FUNCTION_DECL:
+		    case VAR_DECL:
+		      mark_decl_referenced (value);
+		      value = NULL_TREE;
+		      break;
+
+		    default:
+		      error_at (EXPR_LOCATION (value),
+				"expression not supported as file scope"
+				" asm() input");
+		      value = NULL_TREE;
+		      break;
+		  }
+	      }
+	  }
+      }
 }
 
 /* Process CGRAPH_NODES_NEEDED queue, analyze each function (and transitively
@@ -1810,7 +1852,8 @@ cgraph_output_in_order (void)
 	  break;
 
 	case ORDER_ASM:
-	  assemble_asm (nodes[i].u.a->asm_str);
+	  assemble_asm (nodes[i].u.a->asm_str, nodes[i].u.a->inputs,
+			nodes[i].u.a->loc);
 	  break;
 
 	case ORDER_UNDEFINED:
--- 2010-06-30/gcc/cp/parser.c	2010-06-30 08:39:35.000000000 +0200
+++ 2010-06-30/gcc/cp/parser.c	2010-07-02 15:33:45.000000000 +0200
@@ -13537,6 +13537,7 @@ cp_parser_asm_definition (cp_parser* par
   bool invalid_inputs_p = false;
   bool invalid_outputs_p = false;
   bool goto_p = false;
+  location_t loc = cp_lexer_peek_token (parser->lexer)->location;
   required_token missing = RT_NONE;
 
   /* Look for the `asm' keyword.  */
@@ -13618,7 +13619,7 @@ cp_parser_asm_definition (cp_parser* par
 	{
 	  /* Consume the `:' or `::'.  */
 	  cp_lexer_consume_token (parser->lexer);
-	  /* Parse the output-operands.  */
+	  /* Parse the input-operands.  */
 	  if (cp_lexer_next_token_is_not (parser->lexer,
 					  CPP_COLON)
 	      && cp_lexer_next_token_is_not (parser->lexer,
@@ -13669,6 +13670,19 @@ cp_parser_asm_definition (cp_parser* par
     }
   else if (goto_p)
     missing = RT_COLON_SCOPE;
+  else if (cp_parser_allow_gnu_extensions_p (parser)
+	   && ! parser->in_function_body
+	   && cp_lexer_next_token_is (parser->lexer, CPP_COLON))
+    {
+      /* Consume the `:'.  */
+      cp_lexer_consume_token (parser->lexer);
+
+      /* Parse the input-operands.  */
+      inputs = cp_parser_asm_operand_list (parser);
+
+      if (inputs == error_mark_node)
+	invalid_inputs_p = true;
+    }
 
   /* Look for the closing `)'.  */
   if (!cp_parser_require (parser, missing ? CPP_COLON : CPP_CLOSE_PAREN,
@@ -13695,7 +13709,33 @@ cp_parser_asm_definition (cp_parser* par
 	    }
 	}
       else
-	cgraph_add_asm_node (string);
+	{
+	  tree input = inputs;
+
+	  while (input && input != error_mark_node)
+	    {
+	      tree operand = decay_conversion (TREE_VALUE (input));
+
+	      /* If the type of the operand hasn't been determined (e.g.,
+		 because it involves an overloaded function), then issue
+		 an error message.  There's no context available to
+		 resolve the overloading.  */
+	      if (TREE_TYPE (operand) == unknown_type_node)
+		{
+		  error ("type of asm operand %qE could not be determined",
+			 TREE_VALUE (input));
+		  operand = error_mark_node;
+		}
+
+	      if (!cxx_mark_addressable (operand))
+		operand = error_mark_node;
+
+	      TREE_VALUE (input) = operand;
+
+	      input = TREE_CHAIN (input);
+	    }
+	  cgraph_add_asm_node (string, inputs, loc);
+	}
     }
 }
 
--- 2010-06-30/gcc/lto-cgraph.c	2010-06-30 08:40:05.000000000 +0200
+++ 2010-06-30/gcc/lto-cgraph.c	2010-07-02 15:33:45.000000000 +0200
@@ -840,7 +840,6 @@ output_cgraph (cgraph_node_set set, varp
   int i, n_nodes;
   lto_cgraph_encoder_t encoder;
   lto_varpool_encoder_t varpool_encoder;
-  struct cgraph_asm_node *can;
 
   if (flag_wpa)
     output_cgraph_opt_summary ();
@@ -876,19 +875,8 @@ output_cgraph (cgraph_node_set set, varp
 
   lto_output_uleb128_stream (ob->main_stream, 0);
 
-  /* Emit toplevel asms.  */
-  for (can = cgraph_asm_nodes; can; can = can->next)
-    {
-      int len = TREE_STRING_LENGTH (can->asm_str);
-      lto_output_uleb128_stream (ob->main_stream, len);
-      for (i = 0; i < len; ++i)
-	lto_output_1_stream (ob->main_stream,
-			     TREE_STRING_POINTER (can->asm_str)[i]);
-    }
-
-  lto_output_uleb128_stream (ob->main_stream, 0);
-
   lto_destroy_simple_output_block (ob);
+  lto_output_toplevel_asms ();
   output_varpool (set, vset);
   output_refs (set, vset, encoder, varpool_encoder);
 }
@@ -1229,7 +1217,6 @@ input_cgraph_1 (struct lto_file_decl_dat
   VEC(cgraph_node_ptr, heap) *nodes = NULL;
   struct cgraph_node *node;
   unsigned i;
-  unsigned HOST_WIDE_INT len;
 
   tag = (enum LTO_cgraph_tags) lto_input_uleb128 (ib);
   while (tag)
@@ -1250,18 +1237,7 @@ input_cgraph_1 (struct lto_file_decl_dat
       tag = (enum LTO_cgraph_tags) lto_input_uleb128 (ib);
     }
 
-  /* Input toplevel asms.  */
-  len = lto_input_uleb128 (ib);
-  while (len)
-    {
-      char *str = (char *)xmalloc (len + 1);
-      for (i = 0; i < len; ++i)
-	str[i] = lto_input_1_unsigned (ib);
-      cgraph_add_asm_node (build_string (len, str));
-      free (str);
-
-      len = lto_input_uleb128 (ib);
-    }
+  lto_input_toplevel_asms (file_data);
 
   for (i = 0; VEC_iterate (cgraph_node_ptr, nodes, i, node); i++)
     {
--- 2010-06-30/gcc/lto-section-in.c	2010-06-08 17:20:08.000000000 +0200
+++ 2010-06-30/gcc/lto-section-in.c	2010-07-02 15:33:45.000000000 +0200
@@ -52,6 +52,7 @@ const char *lto_section_name[LTO_N_SECTI
   "function_body",
   "static_initializer",
   "cgraph",
+  "asm",
   "varpool",
   "refs",
   "jump_funcs"
--- 2010-06-30/gcc/lto-streamer-in.c	2010-06-30 08:40:05.000000000 +0200
+++ 2010-06-30/gcc/lto-streamer-in.c	2010-07-02 15:33:45.000000000 +0200
@@ -2731,6 +2731,52 @@ lto_input_tree (struct lto_input_block *
 }
 
 
+/* Input toplevel asms.  */
+
+void
+lto_input_toplevel_asms (struct lto_file_decl_data *file_data)
+{
+  size_t len;
+  const char *data = lto_get_section_data (file_data, LTO_section_asm,
+					   NULL, &len);
+  const struct lto_asm_header *header = (const struct lto_asm_header *) data;
+  int32_t string_offset;
+  struct data_in *data_in;
+  struct lto_input_block ib;
+  tree str;
+
+  if (! data)
+    return;
+
+  string_offset = sizeof (*header) + header->main_size;
+
+  LTO_INIT_INPUT_BLOCK (ib,
+			data + sizeof (*header),
+			0,
+			header->main_size);
+
+  data_in = lto_data_in_create (file_data, data + string_offset,
+				header->string_size, NULL);
+
+  /* Make sure the file was generated by the exact same compiler.  */
+  lto_check_version (header->lto_header.major_version,
+		     header->lto_header.minor_version);
+
+  while ((str = input_string_cst (data_in, &ib)))
+    {
+      tree inputs = lto_input_tree (&ib, data_in);
+      location_t loc = lto_input_location (&ib, data_in);
+
+      cgraph_add_asm_node (str, inputs, loc);
+    }
+
+  clear_line_info (data_in);
+  lto_data_in_delete (data_in);
+
+  lto_free_section_data (file_data, LTO_section_asm, NULL, data, len);
+}
+
+
 /* Initialization for the LTO reader.  */
 
 void
--- 2010-06-30/gcc/lto-streamer-out.c	2010-06-30 08:40:05.000000000 +0200
+++ 2010-06-30/gcc/lto-streamer-out.c	2010-07-02 15:33:45.000000000 +0200
@@ -2006,6 +2006,65 @@ output_unreferenced_globals (cgraph_node
 }
 
 
+/* Emit toplevel asms.  */
+
+void
+lto_output_toplevel_asms (void)
+{
+  struct output_block *ob;
+  struct cgraph_asm_node *can;
+  char *section_name;
+  struct lto_output_stream *header_stream;
+  struct lto_asm_header header;
+
+  if (! cgraph_asm_nodes)
+    return;
+
+  ob = create_output_block (LTO_section_asm);
+
+  /* Make string 0 be a NULL string.  */
+  lto_output_1_stream (ob->string_stream, 0);
+
+  for (can = cgraph_asm_nodes; can; can = can->next)
+    {
+      output_string_cst (ob, ob->main_stream, can->asm_str);
+      lto_output_tree (ob, can->inputs, false);
+      lto_output_location (ob, can->loc);
+    }
+
+  lto_output_uleb128_stream (ob->main_stream, 0);
+
+  section_name = lto_get_section_name (LTO_section_asm, NULL);
+  lto_begin_section (section_name, !flag_wpa);
+  free (section_name);
+
+  /* The entire header stream is computed here.  */
+  memset (&header, 0, sizeof (struct lto_function_header));
+
+  /* Write the header.  */
+  header.lto_header.major_version = LTO_major_version;
+  header.lto_header.minor_version = LTO_minor_version;
+  header.lto_header.section_type = LTO_section_asm;
+
+  header.main_size = ob->main_stream->total_size;
+  header.string_size = ob->string_stream->total_size;
+
+  header_stream = XCNEW (struct lto_output_stream);
+  lto_output_data_stream (header_stream, &header, sizeof (header));
+  lto_write_stream (header_stream);
+  free (header_stream);
+
+  /* Put all of the gimple and the string table out the asm file as a
+     block of text.  */
+  lto_write_stream (ob->main_stream);
+  lto_write_stream (ob->string_stream);
+
+  lto_end_section ();
+
+  destroy_output_block (ob);
+}
+
+
 /* Copy the function body of NODE without deserializing. */
 
 static void
--- 2010-06-30/gcc/lto-streamer.c	2010-06-30 08:40:05.000000000 +0200
+++ 2010-06-30/gcc/lto-streamer.c	2010-07-02 15:33:45.000000000 +0200
@@ -160,6 +160,9 @@ lto_get_section_name (int section_type, 
     case LTO_section_cgraph:
       return concat (LTO_SECTION_NAME_PREFIX, ".cgraph", NULL);
 
+    case LTO_section_asm:
+      return concat (LTO_SECTION_NAME_PREFIX, ".asm", NULL);
+
     case LTO_section_varpool:
       return concat (LTO_SECTION_NAME_PREFIX, ".vars", NULL);
 
--- 2010-06-30/gcc/lto-streamer.h	2010-06-30 08:40:05.000000000 +0200
+++ 2010-06-30/gcc/lto-streamer.h	2010-07-02 15:33:45.000000000 +0200
@@ -140,7 +140,7 @@ along with GCC; see the file COPYING3.  
 #define LTO_SECTION_NAME_PREFIX         ".gnu.lto_"
 
 #define LTO_major_version 1
-#define LTO_minor_version 0
+#define LTO_minor_version 1
 
 typedef unsigned char	lto_decl_flags_t;
 
@@ -254,6 +254,7 @@ enum lto_section_type
   LTO_section_function_body,
   LTO_section_static_initializer,
   LTO_section_cgraph,
+  LTO_section_asm,
   LTO_section_varpool,
   LTO_section_refs,
   LTO_section_jump_functions,
@@ -440,6 +441,23 @@ struct lto_decl_header
 };
 
 
+/* Structure describing top level asm()s.  */
+struct lto_asm_header
+{
+  /* The header for all types of sections. */
+  struct lto_header lto_header;
+
+  /* Size compressed or 0 if not compressed.  */
+  int32_t compressed_size;
+
+  /* Size of region for expressions, decls, types, etc. */
+  int32_t main_size;
+
+  /* Size of the string table.  */
+  int32_t string_size;
+};
+
+
 /* Statistics gathered during LTO, WPA and LTRANS.  */
 struct lto_stats_d
 {
@@ -844,6 +862,7 @@ extern void lto_input_function_body (str
 				     const char *);
 extern void lto_input_constructors_and_inits (struct lto_file_decl_data *,
 					      const char *);
+extern void lto_input_toplevel_asms (struct lto_file_decl_data *);
 extern void lto_init_reader (void);
 extern struct data_in *lto_data_in_create (struct lto_file_decl_data *,
 				    const char *, unsigned,
@@ -856,6 +875,7 @@ extern void lto_register_decl_definition
 extern struct output_block *create_output_block (enum lto_section_type);
 extern void destroy_output_block (struct output_block *);
 extern void lto_output_tree (struct output_block *, tree, bool);
+extern void lto_output_toplevel_asms (void);
 extern void produce_asm (struct output_block *ob, tree fn);
 
 
--- 2010-06-30/gcc/output.h	2010-06-30 08:40:05.000000000 +0200
+++ 2010-06-30/gcc/output.h	2010-07-02 15:33:45.000000000 +0200
@@ -186,7 +186,7 @@ extern void default_assemble_visibility 
 
 /* Output a string of literal assembler code
    for an `asm' keyword used between functions.  */
-extern void assemble_asm (tree);
+extern void assemble_asm (tree, tree, location_t);
 
 /* Output assembler code for the constant pool of a function and associated
    with defining the name of the function.  DECL describes the function.
--- 2010-06-30/gcc/stmt.c	2010-06-30 08:40:05.000000000 +0200
+++ 2010-06-30/gcc/stmt.c	2010-07-02 15:33:45.000000000 +0200
@@ -112,7 +112,6 @@ static int n_occurrences (int, const cha
 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, tree);
 static char *resolve_operand_name_1 (char *, tree, tree, tree);
 static void expand_null_return_1 (void);
 static void expand_value_return (rtx);
@@ -1227,7 +1226,7 @@ check_operand_nalternatives (tree output
    are identifiers, and so have been canonicalized by get_identifier,
    so all we need are pointer comparisons.  */
 
-static bool
+bool
 check_unique_operand_names (tree outputs, tree inputs, tree labels)
 {
   tree i, j;
--- 2010-06-30/gcc/testsuite/g++.dg/ext/asm-static-1.C	1970-01-01 01:00:00.000000000 +0100
+++ 2010-06-30/gcc/testsuite/g++.dg/ext/asm-static-1.C	2010-07-02 15:33:45.000000000 +0200
@@ -0,0 +1,21 @@
+/* Check compilation of file scope asm() with input oprand(s). */
+/* { dg-do compile } */
+
+extern int a[];
+extern void f(int*);
+extern struct s {
+	int i, a[2];
+} s;
+static inline void inl(void)
+{
+	f(a);
+}
+
+__asm__ (".long %c0" : "i" (f));
+__asm__ (".long %c0" : "i" (a));
+__asm__ (".long %c0" : "i" (inl));
+__asm__ (".long %c0" : "i" (&s.i));
+__asm__ (".long %c0" : "i" (s.a));
+__asm__ (".long %c0, %c1, %c2" : "i" (-1), "i" ((long)f), "i" (a + 1));
+__asm__ (".long %c[arr], %c[cnst], %c[fn]"
+	 : [cnst] "i" (-1), [fn] "i" ((long)f), [arr] "i" (a + 1));
--- 2010-06-30/gcc/testsuite/gcc.dg/asm-static-1.c	1970-01-01 01:00:00.000000000 +0100
+++ 2010-06-30/gcc/testsuite/gcc.dg/asm-static-1.c	2010-07-02 15:33:45.000000000 +0200
@@ -0,0 +1,21 @@
+/* Check compilation of file scope asm() with input oprand(s). */
+/* { dg-do compile } */
+
+extern int a[];
+extern void f(int*);
+extern struct s {
+	int i, a[2];
+} s;
+static __inline__ void inl(void)
+{
+	f(a);
+}
+
+__asm__ (".long %c0" : "i" (f));
+__asm__ (".long %c0" : "i" (a));
+__asm__ (".long %c0" : "i" (inl));
+__asm__ (".long %c0" : "i" (&s.i));
+__asm__ (".long %c0" : "i" (s.a));
+__asm__ (".long %c0, %c1, %c2" : "i" (-1), "i" ((long)f), "i" (a + 1));
+__asm__ (".long %c[arr], %c[cnst], %c[fn]"
+	 : [cnst] "i" (-1), [fn] "i" ((long)f), [arr] "i" (a + 1));
--- 2010-06-30/gcc/testsuite/gcc.dg/asm-static-2.c	1970-01-01 01:00:00.000000000 +0100
+++ 2010-06-30/gcc/testsuite/gcc.dg/asm-static-2.c	2010-07-02 15:33:45.000000000 +0200
@@ -0,0 +1,8 @@
+/* Check for parse time errors. */
+/* { dg-do compile } */
+
+extern int a[];
+
+__asm__ (".long %c0" :: "i" (0)); /* { dg-error "expected string literal" } */
+__asm__ (".long %c0" : "=g" (a[0]): "0" (0)); /* { dg-error "expected .?\\)" } */
+__asm__ (".long %c[n]" : [n] "i" (0), [n] "i" (0)); /* { dg-error "duplicate.*operand name" } */
--- 2010-06-30/gcc/testsuite/gcc.dg/asm-static-3.c	1970-01-01 01:00:00.000000000 +0100
+++ 2010-06-30/gcc/testsuite/gcc.dg/asm-static-3.c	2010-07-02 15:33:45.000000000 +0200
@@ -0,0 +1,7 @@
+/* Check for errors detected before code generation. */
+/* { dg-do compile } */
+
+extern int a[];
+extern void f(int*);
+
+__asm__ (".long %c0" : "i" ((long)a - (long)f)); /* { dg-error "expression not supported" } */
--- 2010-06-30/gcc/testsuite/gcc.dg/asm-static-4.c	1970-01-01 01:00:00.000000000 +0100
+++ 2010-06-30/gcc/testsuite/gcc.dg/asm-static-4.c	2010-07-02 15:33:45.000000000 +0200
@@ -0,0 +1,8 @@
+/* Check for errors detected during code generation. */
+/* { dg-do compile } */
+
+__asm__ (".long %0" : "r" (0)); /* { dg-warning "invalid.*constraint ignored" } */
+__asm__ (".long %c1" : "i" (0)); /* { dg-error "operand number out of range" } */
+__asm__ (".long %!0" : "i" (0)); /* { dg-error "invalid.*%-code" } */
+__asm__ (".long %c[n" : "i" (0)); /* { dg-error "missing close brace" } */
+__asm__ (".long %c[n]" : "i" (0)); /* { dg-error "undefined named.*operand" } */
--- 2010-06-30/gcc/tree.h	2010-06-30 13:19:57.000000000 +0200
+++ 2010-06-30/gcc/tree.h	2010-07-02 15:33:45.000000000 +0200
@@ -5258,6 +5258,7 @@ extern bool parse_input_constraint (cons
 				    const char * const *, bool *, bool *);
 extern void expand_asm_stmt (gimple);
 extern tree resolve_asm_operand_names (tree, tree, tree, tree);
+extern bool check_unique_operand_names (tree, tree, tree);
 extern void expand_case (gimple);
 extern void expand_decl (tree);
 #ifdef HARD_CONST
--- 2010-06-30/gcc/varasm.c	2010-06-30 08:40:05.000000000 +0200
+++ 2010-06-30/gcc/varasm.c	2010-07-02 15:33:45.000000000 +0200
@@ -52,6 +52,7 @@ along with GCC; see the file COPYING3.  
 #include "cfglayout.h"
 #include "basic-block.h"
 #include "tree-iterator.h"
+#include "pretty-print.h"
 
 #ifdef XCOFF_DEBUGGING_INFO
 #include "xcoffout.h"		/* Needed for external data
@@ -1557,14 +1558,111 @@ make_decl_rtl_for_debug (tree decl)
    for an `asm' keyword used between functions.  */
 
 void
-assemble_asm (tree string)
+assemble_asm (tree string, tree inputs, location_t loc)
 {
   app_enable ();
 
   if (TREE_CODE (string) == ADDR_EXPR)
     string = TREE_OPERAND (string, 0);
 
-  fprintf (asm_out_file, "\t%s\n", TREE_STRING_POINTER (string));
+  if (inputs)
+    {
+      const char *p = TREE_STRING_POINTER (string);
+      char c;
+
+      putc ('\t', asm_out_file);
+
+      while ((c = *p++))
+	{
+	  tree node;
+
+	  if (c != '%')
+	    {
+	      putc (c, asm_out_file);
+	      continue;
+	    }
+
+	  c = *p;
+	  if (!ISDIGIT(c))
+	    switch (c)
+	      {
+		case 'c': case 'a':
+		  /* For now simply ignore (but also don't require) these.  */
+		  c = *++p;
+		  break;
+		case '%':
+		  ++p;
+		  putc ('%', asm_out_file);
+		  continue;
+	      }
+	  if (c == '[')
+	    {
+	      const char *end = strchr (++p, ']');
+	      char *q;
+
+	      if (! end)
+		{
+		  error_at (loc,
+			    "missing close brace for named asm() operand");
+		  break;
+		}
+	      q = xstrndup (p, end - p);
+
+	      for (node = inputs; node && node != error_mark_node; )
+		{
+		  tree name = TREE_PURPOSE (TREE_PURPOSE (node));
+
+		  if (name && ! strcmp (TREE_STRING_POINTER (name), q))
+		    break;
+		  node = TREE_CHAIN (node);
+		}
+	      if (! node)
+		error_at (loc, "undefined named asm() operand %qs",
+			  identifier_to_locale (q));
+	      free (q);
+	      p = end + 1;
+	    }
+	  else if (ISDIGIT(c))
+	    {
+	      char *endp;
+	      unsigned long opnum = strtoul (p, &endp, 10);
+
+	      for (node = inputs; opnum-- && node && node != error_mark_node; )
+		node = TREE_CHAIN (node);
+
+	      if (! node)
+		error_at (loc, "asm() operand number out of range");
+	      p = endp;
+	    }
+	  else
+	    {
+	      error_at (loc, 
+			"unsupported or invalid asm() %%-code"
+			" in this context");
+	      break;
+	    }
+
+	  if (node && node != error_mark_node)
+	    {
+	      tree val = TREE_VALUE (node);
+
+	      string = TREE_VALUE (TREE_PURPOSE (node));
+	      gcc_assert (TREE_CODE (string) == STRING_CST);
+	      if (strcmp (TREE_STRING_POINTER (string), "i"))
+		warning_at (loc, 0,
+			    "unsupported or invalid asm() constraint"
+			    " ignored - \"i\" assumed");
+
+	      output_addr_const (asm_out_file,
+				 expand_expr (val, NULL_RTX, VOIDmode,
+					      EXPAND_INITIALIZER));
+	    }
+	}
+
+      putc ('\n', asm_out_file);
+    }
+  else
+    fprintf (asm_out_file, "\t%s\n", TREE_STRING_POINTER (string));
 }
 
 /* Record an element in the table of global destructors.  SYMBOL is

Comments

Steven Bosscher July 6, 2010, 7:50 a.m. UTC | #1
On Tue, Jul 6, 2010 at 9:39 AM, Jan Beulich <JBeulich@novell.com> wrote:
> This is so that use of symbols referenced in these asm()-s can be
> properly tracked by the compiler, just like is the case for all other
> asm()-s. I'm particularly looking forward to use this in the Linux
> kernel.

Hello,

I think that this also needs a few changes in doc/extend.texi...?

Ciao!
Steven
Jakub Jelinek July 6, 2010, 8:12 a.m. UTC | #2
On Tue, Jul 06, 2010 at 08:39:06AM +0100, Jan Beulich wrote:
> This is so that use of symbols referenced in these asm()-s can be
> properly tracked by the compiler, just like is the case for all other
> asm()-s. I'm particularly looking forward to use this in the Linux
> kernel.

I'd say it is a bad idea to have in this case the input arguments after
first :, there should be two :'s before those.  You can of course for
toplevel asms require that there are no output operands (like we already do
require e.g. for asm goto).

Also note that address of symbols with "i" constraint might not work on all
targets, certainly won't work on many targets with -fpic/-fPIC (so the
testcase needs to be guarded with { target !fpic } or something similar).
It might not even work on some targets where -fpic/-fPIC is the default.

	Jakub
Jan Beulich July 6, 2010, 8:21 a.m. UTC | #3
>>> On 06.07.10 at 10:12, Jakub Jelinek <jakub@redhat.com> wrote:
> On Tue, Jul 06, 2010 at 08:39:06AM +0100, Jan Beulich wrote:
>> This is so that use of symbols referenced in these asm()-s can be
>> properly tracked by the compiler, just like is the case for all other
>> asm()-s. I'm particularly looking forward to use this in the Linux
>> kernel.
> 
> I'd say it is a bad idea to have in this case the input arguments after
> first :, there should be two :'s before those.  You can of course for
> toplevel asms require that there are no output operands (like we already do
> require e.g. for asm goto).

I tend to disagree (and intentionally coded it the way it is) - there is,
afaics, no possible way for top level asm()-s to have outputs. I'm not
very familiar with asm goto-s yet, but would suspect they can
theoretically have outputs.

> Also note that address of symbols with "i" constraint might not work on all
> targets, certainly won't work on many targets with -fpic/-fPIC (so the
> testcase needs to be guarded with { target !fpic } or something similar).
> It might not even work on some targets where -fpic/-fPIC is the default.

Hmm, that wouldn't be good - the construct ought to work regardless
of -fPIC imo. I have to admit that I don't see why you think this
wouldn't work, so any insight would be appreciated.

Jan
Jakub Jelinek July 6, 2010, 8:37 a.m. UTC | #4
On Tue, Jul 06, 2010 at 09:21:56AM +0100, Jan Beulich wrote:
> >>> On 06.07.10 at 10:12, Jakub Jelinek <jakub@redhat.com> wrote:
> > On Tue, Jul 06, 2010 at 08:39:06AM +0100, Jan Beulich wrote:
> >> This is so that use of symbols referenced in these asm()-s can be
> >> properly tracked by the compiler, just like is the case for all other
> >> asm()-s. I'm particularly looking forward to use this in the Linux
> >> kernel.
> > 
> > I'd say it is a bad idea to have in this case the input arguments after
> > first :, there should be two :'s before those.  You can of course for
> > toplevel asms require that there are no output operands (like we already do
> > require e.g. for asm goto).
> 
> I tend to disagree (and intentionally coded it the way it is) - there is,
> afaics, no possible way for top level asm()-s to have outputs. I'm not
> very familiar with asm goto-s yet, but would suspect they can
> theoretically have outputs.

Yes, toplevel asms can't have outputs, sure, but if the first set of
operands means something different between toplevel asms and other asms,
that's just going to cause confusion.

> > Also note that address of symbols with "i" constraint might not work on all
> > targets, certainly won't work on many targets with -fpic/-fPIC (so the
> > testcase needs to be guarded with { target !fpic } or something similar).
> > It might not even work on some targets where -fpic/-fPIC is the default.
> 
> Hmm, that wouldn't be good - the construct ought to work regardless
> of -fPIC imo. I have to admit that I don't see why you think this
> wouldn't work, so any insight would be appreciated.

How could it possibly work?
If you have say:
int i;
asm (".section .rodata; foobar: .long %0; .previous" : : "i" (&i));
you would need to emit code to get the &i value (and, the value would be in
a register, not constant).  That's because for -fpic you need there is an
extra indirection, you need to read i@GOT value from memory and that's the
&i value.
"i" constraint requires:
        case 'i':
          if (CONSTANT_P (op) && (! flag_pic || LEGITIMATE_PIC_OPERAND_P (op)))
            result = 1;
          break;
and it is target's choice what is LEGITIMATE_PIC_OPERAND_P.  "n" allows just
constant integers.
Sure, you could use "X" constraint in that case, do the checking on what
operand is valid in toplevel asms separately and just let the asm writer
deal with -fpic etc. - the user would be responsible for adding @GOT and all
other kinds of special target stuff to actually read the value.  But then
using say .long %0 definitely won't work in many cases, you'll need actual
code to read the value.

	Jakub
Jan Beulich July 6, 2010, 8:45 a.m. UTC | #5
>>> On 06.07.10 at 10:37, Jakub Jelinek <jakub@redhat.com> wrote:
> "i" constraint requires:
>         case 'i':
>           if (CONSTANT_P (op) && (! flag_pic || LEGITIMATE_PIC_OPERAND_P 
> (op)))
>             result = 1;
>           break;
> and it is target's choice what is LEGITIMATE_PIC_OPERAND_P.  "n" allows just
> constant integers.

Ah, okay, I should have payed closer attention to this. Will fix the
testcases then as you suggested.

Thanks, Jan
Richard Biener July 6, 2010, 10:08 a.m. UTC | #6
On Tue, Jul 6, 2010 at 9:39 AM, Jan Beulich <JBeulich@novell.com> wrote:
> This is so that use of symbols referenced in these asm()-s can be
> properly tracked by the compiler, just like is the case for all other
> asm()-s. I'm particularly looking forward to use this in the Linux
> kernel.

Can you split up the patch please?  The LTO parts look like a useful
cleanup not related to the core patch.

Thanks,
Richard.

> gcc/
> 2010-07-06  Jan Beulich  <jbeulich@novell.com>
>
>        * c-parser.c: New second parameter of
>        c_parser_simple_asm_expr().
>        Change c_parser_asm_operands()' second parameter's type to
>        'int'.
>        (c_parser_declaration_or_fndef): Adjust call to
>        c_parser_simple_asm_expr().
>        (c_parser_asm_definition): Obtain and pass inputs.
>        (c_parser_simple_asm_expr): Adjust simple-asm-expr definition.
>        New second parameter. Handle optional inputs if requested.
>        (c_parser_asm_statement): Adjust calls to
>        c_parser_asm_operands().
>        (c_parser_asm_statement): Adjust comment. Change second
>        parameter's type to 'int'. Call c_parser_expr_no_commas()
>        instead of c_parser_expression() when 'mode' is negative.
>        * cgraph.c (cgraph_add_asm_node): New parameters 'inputs' and
>        'loc'. Call check_unique_operand_names(). Initialize new fields
>        of 'node'.
>        * cgraph.h (struct cgraph_asm_node): New members 'inputs' and
>        'loc'.
>        (cgraph_add_asm_node): New parameters 'inputs' and 'loc'.
>        * cgraphunit.c (cgraph_output_pending_asms): Adjust call to
>        assemble_asm().
>        (process_function_and_variable_attributes): Process inputs of
>        all asm nodes.
>        (cgraph_output_in_order): Adjust call to assemble_asm().
>        * lto-cgraph.c (output_cgraph): Call lto_output_toplevel_asms()
>        instead of emitting top level asm-s here.
>        (input_cgraph_1): Call lto_input_toplevel_asms() instead of
>        reading in top level asm-s here.
>        * lto-section-in.c (lto_section_name): Add name for asm-s'
>        section.
>        * lto-streamer-in.c (lto_input_toplevel_asms): New.
>        * lto-streamer-out.c (lto_output_toplevel_asms): New.
>        * lto-streamer.c (lto_get_section_name): Handle
>        LTO_section_asm.
>        * lto-streamer.h (LTO_minor_version): Bump.
>        (enum lto_section_type): Add LTO_section_asm.
>        (struct lto_asm_header): New.
>        (lto_input_toplevel_asms): Declare.
>        (lto_output_toplevel_asms): Declare.
>        * output.h (assemble_asm): New second and third parameter.
>        * stmt.c (check_unique_operand_names): No longer static.
>        * tree.h (check_unique_operand_names): Declare.
>        * varasm.c: Include pretty-print.h.
>        (assemble_asm): New parameters 'inputs' and 'loc'. Handle
>        inputs.
>
> gcc/cp/
> 2010-07-06  Jan Beulich  <jbeulich@novell.com>
>
>        * parser.c (cp_parser_asm_definition): Correct a comment.
>        Process optional inputs.
>
> gcc/testsuite/
> 2010-07-06  Jan Beulich  <jbeulich@novell.com>
>
>        * g++.dg/ext/asm-static-1.C: New.
>        * gcc.dg/asm-static-1.c: New.
>        * gcc.dg/asm-static-2.c: New.
>        * gcc.dg/asm-static-3.c: New.
>        * gcc.dg/asm-static-4.c: New.
>
> --- 2010-06-30/gcc/c-parser.c   2010-06-30 08:40:05.000000000 +0200
> +++ 2010-06-30/gcc/c-parser.c   2010-07-02 15:33:45.000000000 +0200
> @@ -926,7 +926,7 @@ static struct c_declarator *c_parser_dir
>  static struct c_arg_info *c_parser_parms_declarator (c_parser *, bool, tree);
>  static struct c_arg_info *c_parser_parms_list_declarator (c_parser *, tree);
>  static struct c_parm *c_parser_parameter_declaration (c_parser *, tree);
> -static tree c_parser_simple_asm_expr (c_parser *);
> +static tree c_parser_simple_asm_expr (c_parser *, tree *);
>  static tree c_parser_attributes (c_parser *);
>  static struct c_type_name *c_parser_type_name (c_parser *);
>  static struct c_expr c_parser_initializer (c_parser *);
> @@ -945,7 +945,7 @@ static void c_parser_while_statement (c_
>  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_operands (c_parser *, int);
>  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 *);
> @@ -1273,7 +1273,7 @@ c_parser_declaration_or_fndef (c_parser
>             function definition.  */
>          fndef_ok = false;
>          if (c_parser_next_token_is_keyword (parser, RID_ASM))
> -           asm_name = c_parser_simple_asm_expr (parser);
> +           asm_name = c_parser_simple_asm_expr (parser, NULL);
>          if (c_parser_next_token_is_keyword (parser, RID_ATTRIBUTE))
>            postfix_attrs = c_parser_attributes (parser);
>          if (c_parser_next_token_is (parser, CPP_EQ))
> @@ -1406,9 +1406,12 @@ c_parser_declaration_or_fndef (c_parser
>  static void
>  c_parser_asm_definition (c_parser *parser)
>  {
> -  tree asm_str = c_parser_simple_asm_expr (parser);
> +  location_t loc = c_parser_peek_token (parser)->location;
> +  tree inputs = NULL_TREE;
> +  tree asm_str = c_parser_simple_asm_expr (parser, &inputs);
> +
>   if (asm_str)
> -    cgraph_add_asm_node (asm_str);
> +    cgraph_add_asm_node (asm_str, inputs, loc);
>   c_parser_skip_until_found (parser, CPP_SEMICOLON, "expected %<;%>");
>  }
>
> @@ -2910,10 +2913,11 @@ c_parser_asm_string_literal (c_parser *p
>
>    simple-asm-expr:
>      asm ( asm-string-literal )
> +     asm ( asm-string-literal : asm-operands )
>  */
>
>  static tree
> -c_parser_simple_asm_expr (c_parser *parser)
> +c_parser_simple_asm_expr (c_parser *parser, tree *inputsp)
>  {
>   tree str;
>   gcc_assert (c_parser_next_token_is_keyword (parser, RID_ASM));
> @@ -2927,6 +2931,9 @@ c_parser_simple_asm_expr (c_parser *pars
>       return NULL_TREE;
>     }
>   str = c_parser_asm_string_literal (parser);
> +  if (inputsp && !c_parser_next_token_is (parser, CPP_CLOSE_PAREN)
> +      && c_parser_require (parser, CPP_COLON, "expected %<:%> or %<)%>"))
> +      *inputsp = c_parser_asm_operands (parser, -1);
>   parser->lex_untranslated_string = false;
>   if (!c_parser_require (parser, CPP_CLOSE_PAREN, "expected %<)%>"))
>     {
> @@ -4513,10 +4520,10 @@ c_parser_asm_statement (c_parser *parser
>            /* 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);
> +             outputs = c_parser_asm_operands (parser, 0);
>            break;
>          case 1:
> -           inputs = c_parser_asm_operands (parser, true);
> +           inputs = c_parser_asm_operands (parser, 1);
>            break;
>          case 2:
>            clobbers = c_parser_asm_clobbers (parser);
> @@ -4554,9 +4561,10 @@ c_parser_asm_statement (c_parser *parser
>   goto error;
>  }
>
> -/* Parse asm operands, a GNU extension.  If CONVERT_P (for inputs but
> -   not outputs), apply the default conversion of functions and arrays
> -   to pointers.
> +/* Parse asm operands, a GNU extension.  If MODE is non-zero (for inputs
> +   but not outputs), apply the default conversion of functions and arrays
> +   to pointers.  If MODE is negative (for inputs of asm definitions),
> +   don't allow comma expressions.
>
>    asm-operands:
>      asm-operand
> @@ -4568,7 +4576,7 @@ c_parser_asm_statement (c_parser *parser
>  */
>
>  static tree
> -c_parser_asm_operands (c_parser *parser, bool convert_p)
> +c_parser_asm_operands (c_parser *parser, int mode)
>  {
>   tree list = NULL_TREE;
>   location_t loc;
> @@ -4607,9 +4615,12 @@ c_parser_asm_operands (c_parser *parser,
>          return NULL_TREE;
>        }
>       loc = c_parser_peek_token (parser)->location;
> -      expr = c_parser_expression (parser);
> +      if (mode >= 0)
> +       expr = c_parser_expression (parser);
> +      else
> +       expr = c_parser_expr_no_commas(parser, NULL);
>       mark_exp_read (expr.value);
> -      if (convert_p)
> +      if (mode)
>        expr = default_function_array_conversion (loc, expr);
>       expr.value = c_fully_fold (expr.value, false, NULL);
>       parser->lex_untranslated_string = true;
> --- 2010-06-30/gcc/cgraph.c     2010-06-30 08:40:00.000000000 +0200
> +++ 2010-06-30/gcc/cgraph.c     2010-07-02 15:33:45.000000000 +0200
> @@ -1971,12 +1971,15 @@ change_decl_assembler_name (tree decl, t
>  /* Add a top-level asm statement to the list.  */
>
>  struct cgraph_asm_node *
> -cgraph_add_asm_node (tree asm_str)
> +cgraph_add_asm_node (tree asm_str, tree inputs, location_t loc)
>  {
>   struct cgraph_asm_node *node;
>
>   node = ggc_alloc_cleared_cgraph_asm_node ();
>   node->asm_str = asm_str;
> +  node->inputs = check_unique_operand_names (NULL_TREE, inputs, NULL_TREE)
> +                ? inputs : NULL_TREE;
> +  node->loc = loc;
>   node->order = cgraph_order++;
>   node->next = NULL;
>   if (cgraph_asm_nodes == NULL)
> --- 2010-06-30/gcc/cgraph.h     2010-06-30 08:40:00.000000000 +0200
> +++ 2010-06-30/gcc/cgraph.h     2010-07-02 15:33:45.000000000 +0200
> @@ -508,6 +508,10 @@ struct GTY(()) cgraph_asm_node {
>   struct cgraph_asm_node *next;
>   /* String for this asm node.  */
>   tree asm_str;
> +  /* Inputs for this asm node (optional).  */
> +  tree inputs;
> +  /* Source location of the asm().  */
> +  location_t loc;
>   /* Ordering of all cgraph nodes.  */
>   int order;
>  };
> @@ -582,7 +586,7 @@ struct cgraph_node * cgraph_clone_node (
>  void cgraph_redirect_edge_callee (struct cgraph_edge *, struct cgraph_node *);
>  void cgraph_make_edge_direct (struct cgraph_edge *, struct cgraph_node *);
>
> -struct cgraph_asm_node *cgraph_add_asm_node (tree);
> +struct cgraph_asm_node *cgraph_add_asm_node (tree, tree, location_t);
>
>  bool cgraph_function_possibly_inlined_p (tree);
>  void cgraph_unnest_node (struct cgraph_node *);
> --- 2010-06-30/gcc/cgraphunit.c 2010-06-30 08:40:05.000000000 +0200
> +++ 2010-06-30/gcc/cgraphunit.c 2010-07-02 15:33:45.000000000 +0200
> @@ -903,7 +903,7 @@ cgraph_output_pending_asms (void)
>     return;
>
>   for (can = cgraph_asm_nodes; can; can = can->next)
> -    assemble_asm (can->asm_str);
> +    assemble_asm (can->asm_str, can->inputs, can->loc);
>   cgraph_asm_nodes = NULL;
>  }
>
> @@ -964,6 +964,7 @@ process_function_and_variable_attributes
>  {
>   struct cgraph_node *node;
>   struct varpool_node *vnode;
> +  struct cgraph_asm_node *anode;
>
>   for (node = cgraph_nodes; node != first; node = node->next)
>     {
> @@ -999,6 +1000,47 @@ process_function_and_variable_attributes
>            varpool_mark_needed_node (vnode);
>        }
>     }
> +  for (anode = cgraph_asm_nodes; anode; anode = anode->next)
> +    if (anode->inputs)
> +      {
> +       tree node = anode->inputs;
> +
> +       for (; node && node != error_mark_node; node = TREE_CHAIN (node))
> +         {
> +           tree value = TREE_VALUE (node);
> +
> +           while (value && value != error_mark_node)
> +             {
> +               switch (TREE_CODE (value))
> +                 {
> +                   case INTEGER_CST:
> +                     value = NULL_TREE;
> +                     break;
> +
> +                   CASE_CONVERT:
> +                   case ADDR_EXPR:
> +                   case FDESC_EXPR:
> +                   case POINTER_PLUS_EXPR:
> +                   case COMPONENT_REF:
> +                     value = TREE_OPERAND (value, 0);
> +                     break;
> +
> +                   case FUNCTION_DECL:
> +                   case VAR_DECL:
> +                     mark_decl_referenced (value);
> +                     value = NULL_TREE;
> +                     break;
> +
> +                   default:
> +                     error_at (EXPR_LOCATION (value),
> +                               "expression not supported as file scope"
> +                               " asm() input");
> +                     value = NULL_TREE;
> +                     break;
> +                 }
> +             }
> +         }
> +      }
>  }
>
>  /* Process CGRAPH_NODES_NEEDED queue, analyze each function (and transitively
> @@ -1810,7 +1852,8 @@ cgraph_output_in_order (void)
>          break;
>
>        case ORDER_ASM:
> -         assemble_asm (nodes[i].u.a->asm_str);
> +         assemble_asm (nodes[i].u.a->asm_str, nodes[i].u.a->inputs,
> +                       nodes[i].u.a->loc);
>          break;
>
>        case ORDER_UNDEFINED:
> --- 2010-06-30/gcc/cp/parser.c  2010-06-30 08:39:35.000000000 +0200
> +++ 2010-06-30/gcc/cp/parser.c  2010-07-02 15:33:45.000000000 +0200
> @@ -13537,6 +13537,7 @@ cp_parser_asm_definition (cp_parser* par
>   bool invalid_inputs_p = false;
>   bool invalid_outputs_p = false;
>   bool goto_p = false;
> +  location_t loc = cp_lexer_peek_token (parser->lexer)->location;
>   required_token missing = RT_NONE;
>
>   /* Look for the `asm' keyword.  */
> @@ -13618,7 +13619,7 @@ cp_parser_asm_definition (cp_parser* par
>        {
>          /* Consume the `:' or `::'.  */
>          cp_lexer_consume_token (parser->lexer);
> -         /* Parse the output-operands.  */
> +         /* Parse the input-operands.  */
>          if (cp_lexer_next_token_is_not (parser->lexer,
>                                          CPP_COLON)
>              && cp_lexer_next_token_is_not (parser->lexer,
> @@ -13669,6 +13670,19 @@ cp_parser_asm_definition (cp_parser* par
>     }
>   else if (goto_p)
>     missing = RT_COLON_SCOPE;
> +  else if (cp_parser_allow_gnu_extensions_p (parser)
> +          && ! parser->in_function_body
> +          && cp_lexer_next_token_is (parser->lexer, CPP_COLON))
> +    {
> +      /* Consume the `:'.  */
> +      cp_lexer_consume_token (parser->lexer);
> +
> +      /* Parse the input-operands.  */
> +      inputs = cp_parser_asm_operand_list (parser);
> +
> +      if (inputs == error_mark_node)
> +       invalid_inputs_p = true;
> +    }
>
>   /* Look for the closing `)'.  */
>   if (!cp_parser_require (parser, missing ? CPP_COLON : CPP_CLOSE_PAREN,
> @@ -13695,7 +13709,33 @@ cp_parser_asm_definition (cp_parser* par
>            }
>        }
>       else
> -       cgraph_add_asm_node (string);
> +       {
> +         tree input = inputs;
> +
> +         while (input && input != error_mark_node)
> +           {
> +             tree operand = decay_conversion (TREE_VALUE (input));
> +
> +             /* If the type of the operand hasn't been determined (e.g.,
> +                because it involves an overloaded function), then issue
> +                an error message.  There's no context available to
> +                resolve the overloading.  */
> +             if (TREE_TYPE (operand) == unknown_type_node)
> +               {
> +                 error ("type of asm operand %qE could not be determined",
> +                        TREE_VALUE (input));
> +                 operand = error_mark_node;
> +               }
> +
> +             if (!cxx_mark_addressable (operand))
> +               operand = error_mark_node;
> +
> +             TREE_VALUE (input) = operand;
> +
> +             input = TREE_CHAIN (input);
> +           }
> +         cgraph_add_asm_node (string, inputs, loc);
> +       }
>     }
>  }
>
> --- 2010-06-30/gcc/lto-cgraph.c 2010-06-30 08:40:05.000000000 +0200
> +++ 2010-06-30/gcc/lto-cgraph.c 2010-07-02 15:33:45.000000000 +0200
> @@ -840,7 +840,6 @@ output_cgraph (cgraph_node_set set, varp
>   int i, n_nodes;
>   lto_cgraph_encoder_t encoder;
>   lto_varpool_encoder_t varpool_encoder;
> -  struct cgraph_asm_node *can;
>
>   if (flag_wpa)
>     output_cgraph_opt_summary ();
> @@ -876,19 +875,8 @@ output_cgraph (cgraph_node_set set, varp
>
>   lto_output_uleb128_stream (ob->main_stream, 0);
>
> -  /* Emit toplevel asms.  */
> -  for (can = cgraph_asm_nodes; can; can = can->next)
> -    {
> -      int len = TREE_STRING_LENGTH (can->asm_str);
> -      lto_output_uleb128_stream (ob->main_stream, len);
> -      for (i = 0; i < len; ++i)
> -       lto_output_1_stream (ob->main_stream,
> -                            TREE_STRING_POINTER (can->asm_str)[i]);
> -    }
> -
> -  lto_output_uleb128_stream (ob->main_stream, 0);
> -
>   lto_destroy_simple_output_block (ob);
> +  lto_output_toplevel_asms ();
>   output_varpool (set, vset);
>   output_refs (set, vset, encoder, varpool_encoder);
>  }
> @@ -1229,7 +1217,6 @@ input_cgraph_1 (struct lto_file_decl_dat
>   VEC(cgraph_node_ptr, heap) *nodes = NULL;
>   struct cgraph_node *node;
>   unsigned i;
> -  unsigned HOST_WIDE_INT len;
>
>   tag = (enum LTO_cgraph_tags) lto_input_uleb128 (ib);
>   while (tag)
> @@ -1250,18 +1237,7 @@ input_cgraph_1 (struct lto_file_decl_dat
>       tag = (enum LTO_cgraph_tags) lto_input_uleb128 (ib);
>     }
>
> -  /* Input toplevel asms.  */
> -  len = lto_input_uleb128 (ib);
> -  while (len)
> -    {
> -      char *str = (char *)xmalloc (len + 1);
> -      for (i = 0; i < len; ++i)
> -       str[i] = lto_input_1_unsigned (ib);
> -      cgraph_add_asm_node (build_string (len, str));
> -      free (str);
> -
> -      len = lto_input_uleb128 (ib);
> -    }
> +  lto_input_toplevel_asms (file_data);
>
>   for (i = 0; VEC_iterate (cgraph_node_ptr, nodes, i, node); i++)
>     {
> --- 2010-06-30/gcc/lto-section-in.c     2010-06-08 17:20:08.000000000 +0200
> +++ 2010-06-30/gcc/lto-section-in.c     2010-07-02 15:33:45.000000000 +0200
> @@ -52,6 +52,7 @@ const char *lto_section_name[LTO_N_SECTI
>   "function_body",
>   "static_initializer",
>   "cgraph",
> +  "asm",
>   "varpool",
>   "refs",
>   "jump_funcs"
> --- 2010-06-30/gcc/lto-streamer-in.c    2010-06-30 08:40:05.000000000 +0200
> +++ 2010-06-30/gcc/lto-streamer-in.c    2010-07-02 15:33:45.000000000 +0200
> @@ -2731,6 +2731,52 @@ lto_input_tree (struct lto_input_block *
>  }
>
>
> +/* Input toplevel asms.  */
> +
> +void
> +lto_input_toplevel_asms (struct lto_file_decl_data *file_data)
> +{
> +  size_t len;
> +  const char *data = lto_get_section_data (file_data, LTO_section_asm,
> +                                          NULL, &len);
> +  const struct lto_asm_header *header = (const struct lto_asm_header *) data;
> +  int32_t string_offset;
> +  struct data_in *data_in;
> +  struct lto_input_block ib;
> +  tree str;
> +
> +  if (! data)
> +    return;
> +
> +  string_offset = sizeof (*header) + header->main_size;
> +
> +  LTO_INIT_INPUT_BLOCK (ib,
> +                       data + sizeof (*header),
> +                       0,
> +                       header->main_size);
> +
> +  data_in = lto_data_in_create (file_data, data + string_offset,
> +                               header->string_size, NULL);
> +
> +  /* Make sure the file was generated by the exact same compiler.  */
> +  lto_check_version (header->lto_header.major_version,
> +                    header->lto_header.minor_version);
> +
> +  while ((str = input_string_cst (data_in, &ib)))
> +    {
> +      tree inputs = lto_input_tree (&ib, data_in);
> +      location_t loc = lto_input_location (&ib, data_in);
> +
> +      cgraph_add_asm_node (str, inputs, loc);
> +    }
> +
> +  clear_line_info (data_in);
> +  lto_data_in_delete (data_in);
> +
> +  lto_free_section_data (file_data, LTO_section_asm, NULL, data, len);
> +}
> +
> +
>  /* Initialization for the LTO reader.  */
>
>  void
> --- 2010-06-30/gcc/lto-streamer-out.c   2010-06-30 08:40:05.000000000 +0200
> +++ 2010-06-30/gcc/lto-streamer-out.c   2010-07-02 15:33:45.000000000 +0200
> @@ -2006,6 +2006,65 @@ output_unreferenced_globals (cgraph_node
>  }
>
>
> +/* Emit toplevel asms.  */
> +
> +void
> +lto_output_toplevel_asms (void)
> +{
> +  struct output_block *ob;
> +  struct cgraph_asm_node *can;
> +  char *section_name;
> +  struct lto_output_stream *header_stream;
> +  struct lto_asm_header header;
> +
> +  if (! cgraph_asm_nodes)
> +    return;
> +
> +  ob = create_output_block (LTO_section_asm);
> +
> +  /* Make string 0 be a NULL string.  */
> +  lto_output_1_stream (ob->string_stream, 0);
> +
> +  for (can = cgraph_asm_nodes; can; can = can->next)
> +    {
> +      output_string_cst (ob, ob->main_stream, can->asm_str);
> +      lto_output_tree (ob, can->inputs, false);
> +      lto_output_location (ob, can->loc);
> +    }
> +
> +  lto_output_uleb128_stream (ob->main_stream, 0);
> +
> +  section_name = lto_get_section_name (LTO_section_asm, NULL);
> +  lto_begin_section (section_name, !flag_wpa);
> +  free (section_name);
> +
> +  /* The entire header stream is computed here.  */
> +  memset (&header, 0, sizeof (struct lto_function_header));
> +
> +  /* Write the header.  */
> +  header.lto_header.major_version = LTO_major_version;
> +  header.lto_header.minor_version = LTO_minor_version;
> +  header.lto_header.section_type = LTO_section_asm;
> +
> +  header.main_size = ob->main_stream->total_size;
> +  header.string_size = ob->string_stream->total_size;
> +
> +  header_stream = XCNEW (struct lto_output_stream);
> +  lto_output_data_stream (header_stream, &header, sizeof (header));
> +  lto_write_stream (header_stream);
> +  free (header_stream);
> +
> +  /* Put all of the gimple and the string table out the asm file as a
> +     block of text.  */
> +  lto_write_stream (ob->main_stream);
> +  lto_write_stream (ob->string_stream);
> +
> +  lto_end_section ();
> +
> +  destroy_output_block (ob);
> +}
> +
> +
>  /* Copy the function body of NODE without deserializing. */
>
>  static void
> --- 2010-06-30/gcc/lto-streamer.c       2010-06-30 08:40:05.000000000 +0200
> +++ 2010-06-30/gcc/lto-streamer.c       2010-07-02 15:33:45.000000000 +0200
> @@ -160,6 +160,9 @@ lto_get_section_name (int section_type,
>     case LTO_section_cgraph:
>       return concat (LTO_SECTION_NAME_PREFIX, ".cgraph", NULL);
>
> +    case LTO_section_asm:
> +      return concat (LTO_SECTION_NAME_PREFIX, ".asm", NULL);
> +
>     case LTO_section_varpool:
>       return concat (LTO_SECTION_NAME_PREFIX, ".vars", NULL);
>
> --- 2010-06-30/gcc/lto-streamer.h       2010-06-30 08:40:05.000000000 +0200
> +++ 2010-06-30/gcc/lto-streamer.h       2010-07-02 15:33:45.000000000 +0200
> @@ -140,7 +140,7 @@ along with GCC; see the file COPYING3.
>  #define LTO_SECTION_NAME_PREFIX         ".gnu.lto_"
>
>  #define LTO_major_version 1
> -#define LTO_minor_version 0
> +#define LTO_minor_version 1
>
>  typedef unsigned char  lto_decl_flags_t;
>
> @@ -254,6 +254,7 @@ enum lto_section_type
>   LTO_section_function_body,
>   LTO_section_static_initializer,
>   LTO_section_cgraph,
> +  LTO_section_asm,
>   LTO_section_varpool,
>   LTO_section_refs,
>   LTO_section_jump_functions,
> @@ -440,6 +441,23 @@ struct lto_decl_header
>  };
>
>
> +/* Structure describing top level asm()s.  */
> +struct lto_asm_header
> +{
> +  /* The header for all types of sections. */
> +  struct lto_header lto_header;
> +
> +  /* Size compressed or 0 if not compressed.  */
> +  int32_t compressed_size;
> +
> +  /* Size of region for expressions, decls, types, etc. */
> +  int32_t main_size;
> +
> +  /* Size of the string table.  */
> +  int32_t string_size;
> +};
> +
> +
>  /* Statistics gathered during LTO, WPA and LTRANS.  */
>  struct lto_stats_d
>  {
> @@ -844,6 +862,7 @@ extern void lto_input_function_body (str
>                                     const char *);
>  extern void lto_input_constructors_and_inits (struct lto_file_decl_data *,
>                                              const char *);
> +extern void lto_input_toplevel_asms (struct lto_file_decl_data *);
>  extern void lto_init_reader (void);
>  extern struct data_in *lto_data_in_create (struct lto_file_decl_data *,
>                                    const char *, unsigned,
> @@ -856,6 +875,7 @@ extern void lto_register_decl_definition
>  extern struct output_block *create_output_block (enum lto_section_type);
>  extern void destroy_output_block (struct output_block *);
>  extern void lto_output_tree (struct output_block *, tree, bool);
> +extern void lto_output_toplevel_asms (void);
>  extern void produce_asm (struct output_block *ob, tree fn);
>
>
> --- 2010-06-30/gcc/output.h     2010-06-30 08:40:05.000000000 +0200
> +++ 2010-06-30/gcc/output.h     2010-07-02 15:33:45.000000000 +0200
> @@ -186,7 +186,7 @@ extern void default_assemble_visibility
>
>  /* Output a string of literal assembler code
>    for an `asm' keyword used between functions.  */
> -extern void assemble_asm (tree);
> +extern void assemble_asm (tree, tree, location_t);
>
>  /* Output assembler code for the constant pool of a function and associated
>    with defining the name of the function.  DECL describes the function.
> --- 2010-06-30/gcc/stmt.c       2010-06-30 08:40:05.000000000 +0200
> +++ 2010-06-30/gcc/stmt.c       2010-07-02 15:33:45.000000000 +0200
> @@ -112,7 +112,6 @@ static int n_occurrences (int, const cha
>  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, tree);
>  static char *resolve_operand_name_1 (char *, tree, tree, tree);
>  static void expand_null_return_1 (void);
>  static void expand_value_return (rtx);
> @@ -1227,7 +1226,7 @@ check_operand_nalternatives (tree output
>    are identifiers, and so have been canonicalized by get_identifier,
>    so all we need are pointer comparisons.  */
>
> -static bool
> +bool
>  check_unique_operand_names (tree outputs, tree inputs, tree labels)
>  {
>   tree i, j;
> --- 2010-06-30/gcc/testsuite/g++.dg/ext/asm-static-1.C  1970-01-01 01:00:00.000000000 +0100
> +++ 2010-06-30/gcc/testsuite/g++.dg/ext/asm-static-1.C  2010-07-02 15:33:45.000000000 +0200
> @@ -0,0 +1,21 @@
> +/* Check compilation of file scope asm() with input oprand(s). */
> +/* { dg-do compile } */
> +
> +extern int a[];
> +extern void f(int*);
> +extern struct s {
> +       int i, a[2];
> +} s;
> +static inline void inl(void)
> +{
> +       f(a);
> +}
> +
> +__asm__ (".long %c0" : "i" (f));
> +__asm__ (".long %c0" : "i" (a));
> +__asm__ (".long %c0" : "i" (inl));
> +__asm__ (".long %c0" : "i" (&s.i));
> +__asm__ (".long %c0" : "i" (s.a));
> +__asm__ (".long %c0, %c1, %c2" : "i" (-1), "i" ((long)f), "i" (a + 1));
> +__asm__ (".long %c[arr], %c[cnst], %c[fn]"
> +        : [cnst] "i" (-1), [fn] "i" ((long)f), [arr] "i" (a + 1));
> --- 2010-06-30/gcc/testsuite/gcc.dg/asm-static-1.c      1970-01-01 01:00:00.000000000 +0100
> +++ 2010-06-30/gcc/testsuite/gcc.dg/asm-static-1.c      2010-07-02 15:33:45.000000000 +0200
> @@ -0,0 +1,21 @@
> +/* Check compilation of file scope asm() with input oprand(s). */
> +/* { dg-do compile } */
> +
> +extern int a[];
> +extern void f(int*);
> +extern struct s {
> +       int i, a[2];
> +} s;
> +static __inline__ void inl(void)
> +{
> +       f(a);
> +}
> +
> +__asm__ (".long %c0" : "i" (f));
> +__asm__ (".long %c0" : "i" (a));
> +__asm__ (".long %c0" : "i" (inl));
> +__asm__ (".long %c0" : "i" (&s.i));
> +__asm__ (".long %c0" : "i" (s.a));
> +__asm__ (".long %c0, %c1, %c2" : "i" (-1), "i" ((long)f), "i" (a + 1));
> +__asm__ (".long %c[arr], %c[cnst], %c[fn]"
> +        : [cnst] "i" (-1), [fn] "i" ((long)f), [arr] "i" (a + 1));
> --- 2010-06-30/gcc/testsuite/gcc.dg/asm-static-2.c      1970-01-01 01:00:00.000000000 +0100
> +++ 2010-06-30/gcc/testsuite/gcc.dg/asm-static-2.c      2010-07-02 15:33:45.000000000 +0200
> @@ -0,0 +1,8 @@
> +/* Check for parse time errors. */
> +/* { dg-do compile } */
> +
> +extern int a[];
> +
> +__asm__ (".long %c0" :: "i" (0)); /* { dg-error "expected string literal" } */
> +__asm__ (".long %c0" : "=g" (a[0]): "0" (0)); /* { dg-error "expected .?\\)" } */
> +__asm__ (".long %c[n]" : [n] "i" (0), [n] "i" (0)); /* { dg-error "duplicate.*operand name" } */
> --- 2010-06-30/gcc/testsuite/gcc.dg/asm-static-3.c      1970-01-01 01:00:00.000000000 +0100
> +++ 2010-06-30/gcc/testsuite/gcc.dg/asm-static-3.c      2010-07-02 15:33:45.000000000 +0200
> @@ -0,0 +1,7 @@
> +/* Check for errors detected before code generation. */
> +/* { dg-do compile } */
> +
> +extern int a[];
> +extern void f(int*);
> +
> +__asm__ (".long %c0" : "i" ((long)a - (long)f)); /* { dg-error "expression not supported" } */
> --- 2010-06-30/gcc/testsuite/gcc.dg/asm-static-4.c      1970-01-01 01:00:00.000000000 +0100
> +++ 2010-06-30/gcc/testsuite/gcc.dg/asm-static-4.c      2010-07-02 15:33:45.000000000 +0200
> @@ -0,0 +1,8 @@
> +/* Check for errors detected during code generation. */
> +/* { dg-do compile } */
> +
> +__asm__ (".long %0" : "r" (0)); /* { dg-warning "invalid.*constraint ignored" } */
> +__asm__ (".long %c1" : "i" (0)); /* { dg-error "operand number out of range" } */
> +__asm__ (".long %!0" : "i" (0)); /* { dg-error "invalid.*%-code" } */
> +__asm__ (".long %c[n" : "i" (0)); /* { dg-error "missing close brace" } */
> +__asm__ (".long %c[n]" : "i" (0)); /* { dg-error "undefined named.*operand" } */
> --- 2010-06-30/gcc/tree.h       2010-06-30 13:19:57.000000000 +0200
> +++ 2010-06-30/gcc/tree.h       2010-07-02 15:33:45.000000000 +0200
> @@ -5258,6 +5258,7 @@ extern bool parse_input_constraint (cons
>                                    const char * const *, bool *, bool *);
>  extern void expand_asm_stmt (gimple);
>  extern tree resolve_asm_operand_names (tree, tree, tree, tree);
> +extern bool check_unique_operand_names (tree, tree, tree);
>  extern void expand_case (gimple);
>  extern void expand_decl (tree);
>  #ifdef HARD_CONST
> --- 2010-06-30/gcc/varasm.c     2010-06-30 08:40:05.000000000 +0200
> +++ 2010-06-30/gcc/varasm.c     2010-07-02 15:33:45.000000000 +0200
> @@ -52,6 +52,7 @@ along with GCC; see the file COPYING3.
>  #include "cfglayout.h"
>  #include "basic-block.h"
>  #include "tree-iterator.h"
> +#include "pretty-print.h"
>
>  #ifdef XCOFF_DEBUGGING_INFO
>  #include "xcoffout.h"          /* Needed for external data
> @@ -1557,14 +1558,111 @@ make_decl_rtl_for_debug (tree decl)
>    for an `asm' keyword used between functions.  */
>
>  void
> -assemble_asm (tree string)
> +assemble_asm (tree string, tree inputs, location_t loc)
>  {
>   app_enable ();
>
>   if (TREE_CODE (string) == ADDR_EXPR)
>     string = TREE_OPERAND (string, 0);
>
> -  fprintf (asm_out_file, "\t%s\n", TREE_STRING_POINTER (string));
> +  if (inputs)
> +    {
> +      const char *p = TREE_STRING_POINTER (string);
> +      char c;
> +
> +      putc ('\t', asm_out_file);
> +
> +      while ((c = *p++))
> +       {
> +         tree node;
> +
> +         if (c != '%')
> +           {
> +             putc (c, asm_out_file);
> +             continue;
> +           }
> +
> +         c = *p;
> +         if (!ISDIGIT(c))
> +           switch (c)
> +             {
> +               case 'c': case 'a':
> +                 /* For now simply ignore (but also don't require) these.  */
> +                 c = *++p;
> +                 break;
> +               case '%':
> +                 ++p;
> +                 putc ('%', asm_out_file);
> +                 continue;
> +             }
> +         if (c == '[')
> +           {
> +             const char *end = strchr (++p, ']');
> +             char *q;
> +
> +             if (! end)
> +               {
> +                 error_at (loc,
> +                           "missing close brace for named asm() operand");
> +                 break;
> +               }
> +             q = xstrndup (p, end - p);
> +
> +             for (node = inputs; node && node != error_mark_node; )
> +               {
> +                 tree name = TREE_PURPOSE (TREE_PURPOSE (node));
> +
> +                 if (name && ! strcmp (TREE_STRING_POINTER (name), q))
> +                   break;
> +                 node = TREE_CHAIN (node);
> +               }
> +             if (! node)
> +               error_at (loc, "undefined named asm() operand %qs",
> +                         identifier_to_locale (q));
> +             free (q);
> +             p = end + 1;
> +           }
> +         else if (ISDIGIT(c))
> +           {
> +             char *endp;
> +             unsigned long opnum = strtoul (p, &endp, 10);
> +
> +             for (node = inputs; opnum-- && node && node != error_mark_node; )
> +               node = TREE_CHAIN (node);
> +
> +             if (! node)
> +               error_at (loc, "asm() operand number out of range");
> +             p = endp;
> +           }
> +         else
> +           {
> +             error_at (loc,
> +                       "unsupported or invalid asm() %%-code"
> +                       " in this context");
> +             break;
> +           }
> +
> +         if (node && node != error_mark_node)
> +           {
> +             tree val = TREE_VALUE (node);
> +
> +             string = TREE_VALUE (TREE_PURPOSE (node));
> +             gcc_assert (TREE_CODE (string) == STRING_CST);
> +             if (strcmp (TREE_STRING_POINTER (string), "i"))
> +               warning_at (loc, 0,
> +                           "unsupported or invalid asm() constraint"
> +                           " ignored - \"i\" assumed");
> +
> +             output_addr_const (asm_out_file,
> +                                expand_expr (val, NULL_RTX, VOIDmode,
> +                                             EXPAND_INITIALIZER));
> +           }
> +       }
> +
> +      putc ('\n', asm_out_file);
> +    }
> +  else
> +    fprintf (asm_out_file, "\t%s\n", TREE_STRING_POINTER (string));
>  }
>
>  /* Record an element in the table of global destructors.  SYMBOL is
>
>
>
Jan Beulich July 6, 2010, 11:27 a.m. UTC | #7
>>> On 06.07.10 at 12:08, Richard Guenther <richard.guenther@gmail.com> wrote:
> On Tue, Jul 6, 2010 at 9:39 AM, Jan Beulich <JBeulich@novell.com> wrote:
>> This is so that use of symbols referenced in these asm()-s can be
>> properly tracked by the compiler, just like is the case for all other
>> asm()-s. I'm particularly looking forward to use this in the Linux
>> kernel.
> 
> Can you split up the patch please?  The LTO parts look like a useful
> cleanup not related to the core patch.

Not really - the code movement was actually necessary for the
added functionality.

Jan
Jan Beulich July 6, 2010, noon UTC | #8
>>> On 06.07.10 at 09:50, Steven Bosscher <stevenb.gcc@gmail.com> wrote:
> On Tue, Jul 6, 2010 at 9:39 AM, Jan Beulich <JBeulich@novell.com> wrote:
>> This is so that use of symbols referenced in these asm()-s can be
>> properly tracked by the compiler, just like is the case for all other
>> asm()-s. I'm particularly looking forward to use this in the Linux
>> kernel.
> 
> I think that this also needs a few changes in doc/extend.texi...?

I would have done so if I would be able to locate the documentation
for top level asm()-s in the first place. If you can point me at this, I'll
happily add a respective paragraph, but I wouldn't really see fit for
adding documentation of the base feature in this patch (nor am I
sure I would be writing this up properly).

Jan
Jan Beulich July 6, 2010, 12:12 p.m. UTC | #9
>>> On 06.07.10 at 10:37, Jakub Jelinek <jakub@redhat.com> wrote:
> On Tue, Jul 06, 2010 at 09:21:56AM +0100, Jan Beulich wrote:
>> >>> On 06.07.10 at 10:12, Jakub Jelinek <jakub@redhat.com> wrote:
>> > Also note that address of symbols with "i" constraint might not work on all
>> > targets, certainly won't work on many targets with -fpic/-fPIC (so the
>> > testcase needs to be guarded with { target !fpic } or something similar).
>> > It might not even work on some targets where -fpic/-fPIC is the default.
>> 
>> Hmm, that wouldn't be good - the construct ought to work regardless
>> of -fPIC imo. I have to admit that I don't see why you think this
>> wouldn't work, so any insight would be appreciated.
> 
> How could it possibly work?
> If you have say:
> int i;
> asm (".section .rodata; foobar: .long %0; .previous" : : "i" (&i));
> you would need to emit code to get the &i value (and, the value would be in
> a register, not constant).  That's because for -fpic you need there is an
> extra indirection, you need to read i@GOT value from memory and that's the
> &i value.
> "i" constraint requires:
>         case 'i':
>           if (CONSTANT_P (op) && (! flag_pic || LEGITIMATE_PIC_OPERAND_P 
> (op)))
>             result = 1;
>           break;
> and it is target's choice what is LEGITIMATE_PIC_OPERAND_P.  "n" allows just
> constant integers.
> Sure, you could use "X" constraint in that case, do the checking on what
> operand is valid in toplevel asms separately and just let the asm writer
> deal with -fpic etc. - the user would be responsible for adding @GOT and all
> other kinds of special target stuff to actually read the value.  But then
> using say .long %0 definitely won't work in many cases, you'll need actual
> code to read the value.

Indeed I would expect the @GOT (or whatever) to be provided by
the programmer. But I agree that the test case isn't valid with -fPIC,
and hence I'll change it accordingly for a second submission (once it
is clear what other adjustments I may need to do).

Thanks, Jan
diff mbox

Patch

--- 2010-06-30/gcc/c-parser.c	2010-06-30 08:40:05.000000000 +0200
+++ 2010-06-30/gcc/c-parser.c	2010-07-02 15:33:45.000000000 +0200
@@ -926,7 +926,7 @@  static struct c_declarator *c_parser_dir
 static struct c_arg_info *c_parser_parms_declarator (c_parser *, bool, tree);
 static struct c_arg_info *c_parser_parms_list_declarator (c_parser *, tree);
 static struct c_parm *c_parser_parameter_declaration (c_parser *, tree);
-static tree c_parser_simple_asm_expr (c_parser *);
+static tree c_parser_simple_asm_expr (c_parser *, tree *);
 static tree c_parser_attributes (c_parser *);
 static struct c_type_name *c_parser_type_name (c_parser *);
 static struct c_expr c_parser_initializer (c_parser *);
@@ -945,7 +945,7 @@  static void c_parser_while_statement (c_
 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_operands (c_parser *, int);
 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 *);
@@ -1273,7 +1273,7 @@  c_parser_declaration_or_fndef (c_parser 
 	     function definition.  */
 	  fndef_ok = false;
 	  if (c_parser_next_token_is_keyword (parser, RID_ASM))
-	    asm_name = c_parser_simple_asm_expr (parser);
+	    asm_name = c_parser_simple_asm_expr (parser, NULL);
 	  if (c_parser_next_token_is_keyword (parser, RID_ATTRIBUTE))
 	    postfix_attrs = c_parser_attributes (parser);
 	  if (c_parser_next_token_is (parser, CPP_EQ))
@@ -1406,9 +1406,12 @@  c_parser_declaration_or_fndef (c_parser 
 static void
 c_parser_asm_definition (c_parser *parser)
 {
-  tree asm_str = c_parser_simple_asm_expr (parser);
+  location_t loc = c_parser_peek_token (parser)->location;
+  tree inputs = NULL_TREE;
+  tree asm_str = c_parser_simple_asm_expr (parser, &inputs);
+
   if (asm_str)
-    cgraph_add_asm_node (asm_str);
+    cgraph_add_asm_node (asm_str, inputs, loc);
   c_parser_skip_until_found (parser, CPP_SEMICOLON, "expected %<;%>");
 }
 
@@ -2910,10 +2913,11 @@  c_parser_asm_string_literal (c_parser *p
 
    simple-asm-expr:
      asm ( asm-string-literal )
+     asm ( asm-string-literal : asm-operands )
 */
 
 static tree
-c_parser_simple_asm_expr (c_parser *parser)
+c_parser_simple_asm_expr (c_parser *parser, tree *inputsp)
 {
   tree str;
   gcc_assert (c_parser_next_token_is_keyword (parser, RID_ASM));
@@ -2927,6 +2931,9 @@  c_parser_simple_asm_expr (c_parser *pars
       return NULL_TREE;
     }
   str = c_parser_asm_string_literal (parser);
+  if (inputsp && !c_parser_next_token_is (parser, CPP_CLOSE_PAREN)
+      && c_parser_require (parser, CPP_COLON, "expected %<:%> or %<)%>"))
+      *inputsp = c_parser_asm_operands (parser, -1);
   parser->lex_untranslated_string = false;
   if (!c_parser_require (parser, CPP_CLOSE_PAREN, "expected %<)%>"))
     {
@@ -4513,10 +4520,10 @@  c_parser_asm_statement (c_parser *parser
 	    /* 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);
+	      outputs = c_parser_asm_operands (parser, 0);
 	    break;
 	  case 1:
-	    inputs = c_parser_asm_operands (parser, true);
+	    inputs = c_parser_asm_operands (parser, 1);
 	    break;
 	  case 2:
 	    clobbers = c_parser_asm_clobbers (parser);
@@ -4554,9 +4561,10 @@  c_parser_asm_statement (c_parser *parser
   goto error;
 }
 
-/* Parse asm operands, a GNU extension.  If CONVERT_P (for inputs but
-   not outputs), apply the default conversion of functions and arrays
-   to pointers.
+/* Parse asm operands, a GNU extension.  If MODE is non-zero (for inputs
+   but not outputs), apply the default conversion of functions and arrays
+   to pointers.  If MODE is negative (for inputs of asm definitions),
+   don't allow comma expressions.
 
    asm-operands:
      asm-operand
@@ -4568,7 +4576,7 @@  c_parser_asm_statement (c_parser *parser
 */
 
 static tree
-c_parser_asm_operands (c_parser *parser, bool convert_p)
+c_parser_asm_operands (c_parser *parser, int mode)
 {
   tree list = NULL_TREE;
   location_t loc;
@@ -4607,9 +4615,12 @@  c_parser_asm_operands (c_parser *parser,
 	  return NULL_TREE;
 	}
       loc = c_parser_peek_token (parser)->location;
-      expr = c_parser_expression (parser);
+      if (mode >= 0)
+	expr = c_parser_expression (parser);
+      else
+	expr = c_parser_expr_no_commas(parser, NULL);
       mark_exp_read (expr.value);
-      if (convert_p)
+      if (mode)
 	expr = default_function_array_conversion (loc, expr);
       expr.value = c_fully_fold (expr.value, false, NULL);
       parser->lex_untranslated_string = true;
--- 2010-06-30/gcc/cgraph.c	2010-06-30 08:40:00.000000000 +0200
+++ 2010-06-30/gcc/cgraph.c	2010-07-02 15:33:45.000000000 +0200
@@ -1971,12 +1971,15 @@  change_decl_assembler_name (tree decl, t
 /* Add a top-level asm statement to the list.  */
 
 struct cgraph_asm_node *
-cgraph_add_asm_node (tree asm_str)
+cgraph_add_asm_node (tree asm_str, tree inputs, location_t loc)
 {
   struct cgraph_asm_node *node;
 
   node = ggc_alloc_cleared_cgraph_asm_node ();
   node->asm_str = asm_str;
+  node->inputs = check_unique_operand_names (NULL_TREE, inputs, NULL_TREE)
+		 ? inputs : NULL_TREE;
+  node->loc = loc;
   node->order = cgraph_order++;
   node->next = NULL;
   if (cgraph_asm_nodes == NULL)
--- 2010-06-30/gcc/cgraph.h	2010-06-30 08:40:00.000000000 +0200
+++ 2010-06-30/gcc/cgraph.h	2010-07-02 15:33:45.000000000 +0200
@@ -508,6 +508,10 @@  struct GTY(()) cgraph_asm_node {
   struct cgraph_asm_node *next;
   /* String for this asm node.  */
   tree asm_str;
+  /* Inputs for this asm node (optional).  */
+  tree inputs;
+  /* Source location of the asm().  */
+  location_t loc;
   /* Ordering of all cgraph nodes.  */
   int order;
 };
@@ -582,7 +586,7 @@  struct cgraph_node * cgraph_clone_node (
 void cgraph_redirect_edge_callee (struct cgraph_edge *, struct cgraph_node *);
 void cgraph_make_edge_direct (struct cgraph_edge *, struct cgraph_node *);
 
-struct cgraph_asm_node *cgraph_add_asm_node (tree);
+struct cgraph_asm_node *cgraph_add_asm_node (tree, tree, location_t);
 
 bool cgraph_function_possibly_inlined_p (tree);
 void cgraph_unnest_node (struct cgraph_node *);
--- 2010-06-30/gcc/cgraphunit.c	2010-06-30 08:40:05.000000000 +0200
+++ 2010-06-30/gcc/cgraphunit.c	2010-07-02 15:33:45.000000000 +0200
@@ -903,7 +903,7 @@  cgraph_output_pending_asms (void)
     return;
 
   for (can = cgraph_asm_nodes; can; can = can->next)
-    assemble_asm (can->asm_str);
+    assemble_asm (can->asm_str, can->inputs, can->loc);
   cgraph_asm_nodes = NULL;
 }
 
@@ -964,6 +964,7 @@  process_function_and_variable_attributes
 {
   struct cgraph_node *node;
   struct varpool_node *vnode;
+  struct cgraph_asm_node *anode;
 
   for (node = cgraph_nodes; node != first; node = node->next)
     {
@@ -999,6 +1000,47 @@  process_function_and_variable_attributes
 	    varpool_mark_needed_node (vnode);
 	}
     }
+  for (anode = cgraph_asm_nodes; anode; anode = anode->next)
+    if (anode->inputs)
+      {
+	tree node = anode->inputs;
+
+	for (; node && node != error_mark_node; node = TREE_CHAIN (node))
+	  {
+	    tree value = TREE_VALUE (node);
+
+	    while (value && value != error_mark_node)
+	      {
+		switch (TREE_CODE (value))
+		  {
+		    case INTEGER_CST:
+		      value = NULL_TREE;
+		      break;
+
+		    CASE_CONVERT:
+		    case ADDR_EXPR:
+		    case FDESC_EXPR:
+		    case POINTER_PLUS_EXPR:
+		    case COMPONENT_REF:
+		      value = TREE_OPERAND (value, 0);
+		      break;
+
+		    case FUNCTION_DECL:
+		    case VAR_DECL:
+		      mark_decl_referenced (value);
+		      value = NULL_TREE;
+		      break;
+
+		    default:
+		      error_at (EXPR_LOCATION (value),
+				"expression not supported as file scope"
+				" asm() input");
+		      value = NULL_TREE;
+		      break;
+		  }
+	      }
+	  }
+      }
 }
 
 /* Process CGRAPH_NODES_NEEDED queue, analyze each function (and transitively
@@ -1810,7 +1852,8 @@  cgraph_output_in_order (void)
 	  break;
 
 	case ORDER_ASM:
-	  assemble_asm (nodes[i].u.a->asm_str);
+	  assemble_asm (nodes[i].u.a->asm_str, nodes[i].u.a->inputs,
+			nodes[i].u.a->loc);
 	  break;
 
 	case ORDER_UNDEFINED:
--- 2010-06-30/gcc/cp/parser.c	2010-06-30 08:39:35.000000000 +0200
+++ 2010-06-30/gcc/cp/parser.c	2010-07-02 15:33:45.000000000 +0200
@@ -13537,6 +13537,7 @@  cp_parser_asm_definition (cp_parser* par
   bool invalid_inputs_p = false;
   bool invalid_outputs_p = false;
   bool goto_p = false;
+  location_t loc = cp_lexer_peek_token (parser->lexer)->location;
   required_token missing = RT_NONE;
 
   /* Look for the `asm' keyword.  */
@@ -13618,7 +13619,7 @@  cp_parser_asm_definition (cp_parser* par
 	{
 	  /* Consume the `:' or `::'.  */
 	  cp_lexer_consume_token (parser->lexer);
-	  /* Parse the output-operands.  */
+	  /* Parse the input-operands.  */
 	  if (cp_lexer_next_token_is_not (parser->lexer,
 					  CPP_COLON)
 	      && cp_lexer_next_token_is_not (parser->lexer,
@@ -13669,6 +13670,19 @@  cp_parser_asm_definition (cp_parser* par
     }
   else if (goto_p)
     missing = RT_COLON_SCOPE;
+  else if (cp_parser_allow_gnu_extensions_p (parser)
+	   && ! parser->in_function_body
+	   && cp_lexer_next_token_is (parser->lexer, CPP_COLON))
+    {
+      /* Consume the `:'.  */
+      cp_lexer_consume_token (parser->lexer);
+
+      /* Parse the input-operands.  */
+      inputs = cp_parser_asm_operand_list (parser);
+
+      if (inputs == error_mark_node)
+	invalid_inputs_p = true;
+    }
 
   /* Look for the closing `)'.  */
   if (!cp_parser_require (parser, missing ? CPP_COLON : CPP_CLOSE_PAREN,
@@ -13695,7 +13709,33 @@  cp_parser_asm_definition (cp_parser* par
 	    }
 	}
       else
-	cgraph_add_asm_node (string);
+	{
+	  tree input = inputs;
+
+	  while (input && input != error_mark_node)
+	    {
+	      tree operand = decay_conversion (TREE_VALUE (input));
+
+	      /* If the type of the operand hasn't been determined (e.g.,
+		 because it involves an overloaded function), then issue
+		 an error message.  There's no context available to
+		 resolve the overloading.  */
+	      if (TREE_TYPE (operand) == unknown_type_node)
+		{
+		  error ("type of asm operand %qE could not be determined",
+			 TREE_VALUE (input));
+		  operand = error_mark_node;
+		}
+
+	      if (!cxx_mark_addressable (operand))
+		operand = error_mark_node;
+
+	      TREE_VALUE (input) = operand;
+
+	      input = TREE_CHAIN (input);
+	    }
+	  cgraph_add_asm_node (string, inputs, loc);
+	}
     }
 }
 
--- 2010-06-30/gcc/lto-cgraph.c	2010-06-30 08:40:05.000000000 +0200
+++ 2010-06-30/gcc/lto-cgraph.c	2010-07-02 15:33:45.000000000 +0200
@@ -840,7 +840,6 @@  output_cgraph (cgraph_node_set set, varp
   int i, n_nodes;
   lto_cgraph_encoder_t encoder;
   lto_varpool_encoder_t varpool_encoder;
-  struct cgraph_asm_node *can;
 
   if (flag_wpa)
     output_cgraph_opt_summary ();
@@ -876,19 +875,8 @@  output_cgraph (cgraph_node_set set, varp
 
   lto_output_uleb128_stream (ob->main_stream, 0);
 
-  /* Emit toplevel asms.  */
-  for (can = cgraph_asm_nodes; can; can = can->next)
-    {
-      int len = TREE_STRING_LENGTH (can->asm_str);
-      lto_output_uleb128_stream (ob->main_stream, len);
-      for (i = 0; i < len; ++i)
-	lto_output_1_stream (ob->main_stream,
-			     TREE_STRING_POINTER (can->asm_str)[i]);
-    }
-
-  lto_output_uleb128_stream (ob->main_stream, 0);
-
   lto_destroy_simple_output_block (ob);
+  lto_output_toplevel_asms ();
   output_varpool (set, vset);
   output_refs (set, vset, encoder, varpool_encoder);
 }
@@ -1229,7 +1217,6 @@  input_cgraph_1 (struct lto_file_decl_dat
   VEC(cgraph_node_ptr, heap) *nodes = NULL;
   struct cgraph_node *node;
   unsigned i;
-  unsigned HOST_WIDE_INT len;
 
   tag = (enum LTO_cgraph_tags) lto_input_uleb128 (ib);
   while (tag)
@@ -1250,18 +1237,7 @@  input_cgraph_1 (struct lto_file_decl_dat
       tag = (enum LTO_cgraph_tags) lto_input_uleb128 (ib);
     }
 
-  /* Input toplevel asms.  */
-  len = lto_input_uleb128 (ib);
-  while (len)
-    {
-      char *str = (char *)xmalloc (len + 1);
-      for (i = 0; i < len; ++i)
-	str[i] = lto_input_1_unsigned (ib);
-      cgraph_add_asm_node (build_string (len, str));
-      free (str);
-
-      len = lto_input_uleb128 (ib);
-    }
+  lto_input_toplevel_asms (file_data);
 
   for (i = 0; VEC_iterate (cgraph_node_ptr, nodes, i, node); i++)
     {
--- 2010-06-30/gcc/lto-section-in.c	2010-06-08 17:20:08.000000000 +0200
+++ 2010-06-30/gcc/lto-section-in.c	2010-07-02 15:33:45.000000000 +0200
@@ -52,6 +52,7 @@  const char *lto_section_name[LTO_N_SECTI
   "function_body",
   "static_initializer",
   "cgraph",
+  "asm",
   "varpool",
   "refs",
   "jump_funcs"
--- 2010-06-30/gcc/lto-streamer-in.c	2010-06-30 08:40:05.000000000 +0200
+++ 2010-06-30/gcc/lto-streamer-in.c	2010-07-02 15:33:45.000000000 +0200
@@ -2731,6 +2731,52 @@  lto_input_tree (struct lto_input_block *
 }
 
 
+/* Input toplevel asms.  */
+
+void
+lto_input_toplevel_asms (struct lto_file_decl_data *file_data)
+{
+  size_t len;
+  const char *data = lto_get_section_data (file_data, LTO_section_asm,
+					   NULL, &len);
+  const struct lto_asm_header *header = (const struct lto_asm_header *) data;
+  int32_t string_offset;
+  struct data_in *data_in;
+  struct lto_input_block ib;
+  tree str;
+
+  if (! data)
+    return;
+
+  string_offset = sizeof (*header) + header->main_size;
+
+  LTO_INIT_INPUT_BLOCK (ib,
+			data + sizeof (*header),
+			0,
+			header->main_size);
+
+  data_in = lto_data_in_create (file_data, data + string_offset,
+				header->string_size, NULL);
+
+  /* Make sure the file was generated by the exact same compiler.  */
+  lto_check_version (header->lto_header.major_version,
+		     header->lto_header.minor_version);
+
+  while ((str = input_string_cst (data_in, &ib)))
+    {
+      tree inputs = lto_input_tree (&ib, data_in);
+      location_t loc = lto_input_location (&ib, data_in);
+
+      cgraph_add_asm_node (str, inputs, loc);
+    }
+
+  clear_line_info (data_in);
+  lto_data_in_delete (data_in);
+
+  lto_free_section_data (file_data, LTO_section_asm, NULL, data, len);
+}
+
+
 /* Initialization for the LTO reader.  */
 
 void
--- 2010-06-30/gcc/lto-streamer-out.c	2010-06-30 08:40:05.000000000 +0200
+++ 2010-06-30/gcc/lto-streamer-out.c	2010-07-02 15:33:45.000000000 +0200
@@ -2006,6 +2006,65 @@  output_unreferenced_globals (cgraph_node
 }
 
 
+/* Emit toplevel asms.  */
+
+void
+lto_output_toplevel_asms (void)
+{
+  struct output_block *ob;
+  struct cgraph_asm_node *can;
+  char *section_name;
+  struct lto_output_stream *header_stream;
+  struct lto_asm_header header;
+
+  if (! cgraph_asm_nodes)
+    return;
+
+  ob = create_output_block (LTO_section_asm);
+
+  /* Make string 0 be a NULL string.  */
+  lto_output_1_stream (ob->string_stream, 0);
+
+  for (can = cgraph_asm_nodes; can; can = can->next)
+    {
+      output_string_cst (ob, ob->main_stream, can->asm_str);
+      lto_output_tree (ob, can->inputs, false);
+      lto_output_location (ob, can->loc);
+    }
+
+  lto_output_uleb128_stream (ob->main_stream, 0);
+
+  section_name = lto_get_section_name (LTO_section_asm, NULL);
+  lto_begin_section (section_name, !flag_wpa);
+  free (section_name);
+
+  /* The entire header stream is computed here.  */
+  memset (&header, 0, sizeof (struct lto_function_header));
+
+  /* Write the header.  */
+  header.lto_header.major_version = LTO_major_version;
+  header.lto_header.minor_version = LTO_minor_version;
+  header.lto_header.section_type = LTO_section_asm;
+
+  header.main_size = ob->main_stream->total_size;
+  header.string_size = ob->string_stream->total_size;
+
+  header_stream = XCNEW (struct lto_output_stream);
+  lto_output_data_stream (header_stream, &header, sizeof (header));
+  lto_write_stream (header_stream);
+  free (header_stream);
+
+  /* Put all of the gimple and the string table out the asm file as a
+     block of text.  */
+  lto_write_stream (ob->main_stream);
+  lto_write_stream (ob->string_stream);
+
+  lto_end_section ();
+
+  destroy_output_block (ob);
+}
+
+
 /* Copy the function body of NODE without deserializing. */
 
 static void
--- 2010-06-30/gcc/lto-streamer.c	2010-06-30 08:40:05.000000000 +0200
+++ 2010-06-30/gcc/lto-streamer.c	2010-07-02 15:33:45.000000000 +0200
@@ -160,6 +160,9 @@  lto_get_section_name (int section_type, 
     case LTO_section_cgraph:
       return concat (LTO_SECTION_NAME_PREFIX, ".cgraph", NULL);
 
+    case LTO_section_asm:
+      return concat (LTO_SECTION_NAME_PREFIX, ".asm", NULL);
+
     case LTO_section_varpool:
       return concat (LTO_SECTION_NAME_PREFIX, ".vars", NULL);
 
--- 2010-06-30/gcc/lto-streamer.h	2010-06-30 08:40:05.000000000 +0200
+++ 2010-06-30/gcc/lto-streamer.h	2010-07-02 15:33:45.000000000 +0200
@@ -140,7 +140,7 @@  along with GCC; see the file COPYING3.  
 #define LTO_SECTION_NAME_PREFIX         ".gnu.lto_"
 
 #define LTO_major_version 1
-#define LTO_minor_version 0
+#define LTO_minor_version 1
 
 typedef unsigned char	lto_decl_flags_t;
 
@@ -254,6 +254,7 @@  enum lto_section_type
   LTO_section_function_body,
   LTO_section_static_initializer,
   LTO_section_cgraph,
+  LTO_section_asm,
   LTO_section_varpool,
   LTO_section_refs,
   LTO_section_jump_functions,
@@ -440,6 +441,23 @@  struct lto_decl_header
 };
 
 
+/* Structure describing top level asm()s.  */
+struct lto_asm_header
+{
+  /* The header for all types of sections. */
+  struct lto_header lto_header;
+
+  /* Size compressed or 0 if not compressed.  */
+  int32_t compressed_size;
+
+  /* Size of region for expressions, decls, types, etc. */
+  int32_t main_size;
+
+  /* Size of the string table.  */
+  int32_t string_size;
+};
+
+
 /* Statistics gathered during LTO, WPA and LTRANS.  */
 struct lto_stats_d
 {
@@ -844,6 +862,7 @@  extern void lto_input_function_body (str
 				     const char *);
 extern void lto_input_constructors_and_inits (struct lto_file_decl_data *,
 					      const char *);
+extern void lto_input_toplevel_asms (struct lto_file_decl_data *);
 extern void lto_init_reader (void);
 extern struct data_in *lto_data_in_create (struct lto_file_decl_data *,
 				    const char *, unsigned,
@@ -856,6 +875,7 @@  extern void lto_register_decl_definition
 extern struct output_block *create_output_block (enum lto_section_type);
 extern void destroy_output_block (struct output_block *);
 extern void lto_output_tree (struct output_block *, tree, bool);
+extern void lto_output_toplevel_asms (void);
 extern void produce_asm (struct output_block *ob, tree fn);
 
 
--- 2010-06-30/gcc/output.h	2010-06-30 08:40:05.000000000 +0200
+++ 2010-06-30/gcc/output.h	2010-07-02 15:33:45.000000000 +0200
@@ -186,7 +186,7 @@  extern void default_assemble_visibility 
 
 /* Output a string of literal assembler code
    for an `asm' keyword used between functions.  */
-extern void assemble_asm (tree);
+extern void assemble_asm (tree, tree, location_t);
 
 /* Output assembler code for the constant pool of a function and associated
    with defining the name of the function.  DECL describes the function.
--- 2010-06-30/gcc/stmt.c	2010-06-30 08:40:05.000000000 +0200
+++ 2010-06-30/gcc/stmt.c	2010-07-02 15:33:45.000000000 +0200
@@ -112,7 +112,6 @@  static int n_occurrences (int, const cha
 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, tree);
 static char *resolve_operand_name_1 (char *, tree, tree, tree);
 static void expand_null_return_1 (void);
 static void expand_value_return (rtx);
@@ -1227,7 +1226,7 @@  check_operand_nalternatives (tree output
    are identifiers, and so have been canonicalized by get_identifier,
    so all we need are pointer comparisons.  */
 
-static bool
+bool
 check_unique_operand_names (tree outputs, tree inputs, tree labels)
 {
   tree i, j;
--- 2010-06-30/gcc/testsuite/g++.dg/ext/asm-static-1.C	1970-01-01 01:00:00.000000000 +0100
+++ 2010-06-30/gcc/testsuite/g++.dg/ext/asm-static-1.C	2010-07-02 15:33:45.000000000 +0200
@@ -0,0 +1,21 @@ 
+/* Check compilation of file scope asm() with input oprand(s). */
+/* { dg-do compile } */
+
+extern int a[];
+extern void f(int*);
+extern struct s {
+	int i, a[2];
+} s;
+static inline void inl(void)
+{
+	f(a);
+}
+
+__asm__ (".long %c0" : "i" (f));
+__asm__ (".long %c0" : "i" (a));
+__asm__ (".long %c0" : "i" (inl));
+__asm__ (".long %c0" : "i" (&s.i));
+__asm__ (".long %c0" : "i" (s.a));
+__asm__ (".long %c0, %c1, %c2" : "i" (-1), "i" ((long)f), "i" (a + 1));
+__asm__ (".long %c[arr], %c[cnst], %c[fn]"
+	 : [cnst] "i" (-1), [fn] "i" ((long)f), [arr] "i" (a + 1));
--- 2010-06-30/gcc/testsuite/gcc.dg/asm-static-1.c	1970-01-01 01:00:00.000000000 +0100
+++ 2010-06-30/gcc/testsuite/gcc.dg/asm-static-1.c	2010-07-02 15:33:45.000000000 +0200
@@ -0,0 +1,21 @@ 
+/* Check compilation of file scope asm() with input oprand(s). */
+/* { dg-do compile } */
+
+extern int a[];
+extern void f(int*);
+extern struct s {
+	int i, a[2];
+} s;
+static __inline__ void inl(void)
+{
+	f(a);
+}
+
+__asm__ (".long %c0" : "i" (f));
+__asm__ (".long %c0" : "i" (a));
+__asm__ (".long %c0" : "i" (inl));
+__asm__ (".long %c0" : "i" (&s.i));
+__asm__ (".long %c0" : "i" (s.a));
+__asm__ (".long %c0, %c1, %c2" : "i" (-1), "i" ((long)f), "i" (a + 1));
+__asm__ (".long %c[arr], %c[cnst], %c[fn]"
+	 : [cnst] "i" (-1), [fn] "i" ((long)f), [arr] "i" (a + 1));
--- 2010-06-30/gcc/testsuite/gcc.dg/asm-static-2.c	1970-01-01 01:00:00.000000000 +0100
+++ 2010-06-30/gcc/testsuite/gcc.dg/asm-static-2.c	2010-07-02 15:33:45.000000000 +0200
@@ -0,0 +1,8 @@ 
+/* Check for parse time errors. */
+/* { dg-do compile } */
+
+extern int a[];
+
+__asm__ (".long %c0" :: "i" (0)); /* { dg-error "expected string literal" } */
+__asm__ (".long %c0" : "=g" (a[0]): "0" (0)); /* { dg-error "expected .?\\)" } */
+__asm__ (".long %c[n]" : [n] "i" (0), [n] "i" (0)); /* { dg-error "duplicate.*operand name" } */
--- 2010-06-30/gcc/testsuite/gcc.dg/asm-static-3.c	1970-01-01 01:00:00.000000000 +0100
+++ 2010-06-30/gcc/testsuite/gcc.dg/asm-static-3.c	2010-07-02 15:33:45.000000000 +0200
@@ -0,0 +1,7 @@ 
+/* Check for errors detected before code generation. */
+/* { dg-do compile } */
+
+extern int a[];
+extern void f(int*);
+
+__asm__ (".long %c0" : "i" ((long)a - (long)f)); /* { dg-error "expression not supported" } */
--- 2010-06-30/gcc/testsuite/gcc.dg/asm-static-4.c	1970-01-01 01:00:00.000000000 +0100
+++ 2010-06-30/gcc/testsuite/gcc.dg/asm-static-4.c	2010-07-02 15:33:45.000000000 +0200
@@ -0,0 +1,8 @@ 
+/* Check for errors detected during code generation. */
+/* { dg-do compile } */
+
+__asm__ (".long %0" : "r" (0)); /* { dg-warning "invalid.*constraint ignored" } */
+__asm__ (".long %c1" : "i" (0)); /* { dg-error "operand number out of range" } */
+__asm__ (".long %!0" : "i" (0)); /* { dg-error "invalid.*%-code" } */
+__asm__ (".long %c[n" : "i" (0)); /* { dg-error "missing close brace" } */
+__asm__ (".long %c[n]" : "i" (0)); /* { dg-error "undefined named.*operand" } */
--- 2010-06-30/gcc/tree.h	2010-06-30 13:19:57.000000000 +0200
+++ 2010-06-30/gcc/tree.h	2010-07-02 15:33:45.000000000 +0200
@@ -5258,6 +5258,7 @@  extern bool parse_input_constraint (cons
 				    const char * const *, bool *, bool *);
 extern void expand_asm_stmt (gimple);
 extern tree resolve_asm_operand_names (tree, tree, tree, tree);
+extern bool check_unique_operand_names (tree, tree, tree);
 extern void expand_case (gimple);
 extern void expand_decl (tree);
 #ifdef HARD_CONST
--- 2010-06-30/gcc/varasm.c	2010-06-30 08:40:05.000000000 +0200
+++ 2010-06-30/gcc/varasm.c	2010-07-02 15:33:45.000000000 +0200
@@ -52,6 +52,7 @@  along with GCC; see the file COPYING3.  
 #include "cfglayout.h"
 #include "basic-block.h"
 #include "tree-iterator.h"
+#include "pretty-print.h"
 
 #ifdef XCOFF_DEBUGGING_INFO
 #include "xcoffout.h"		/* Needed for external data
@@ -1557,14 +1558,111 @@  make_decl_rtl_for_debug (tree decl)
    for an `asm' keyword used between functions.  */
 
 void
-assemble_asm (tree string)
+assemble_asm (tree string, tree inputs, location_t loc)
 {
   app_enable ();
 
   if (TREE_CODE (string) == ADDR_EXPR)
     string = TREE_OPERAND (string, 0);
 
-  fprintf (asm_out_file, "\t%s\n", TREE_STRING_POINTER (string));
+  if (inputs)
+    {
+      const char *p = TREE_STRING_POINTER (string);
+      char c;
+
+      putc ('\t', asm_out_file);
+
+      while ((c = *p++))
+	{
+	  tree node;
+
+	  if (c != '%')
+	    {
+	      putc (c, asm_out_file);
+	      continue;
+	    }
+
+	  c = *p;
+	  if (!ISDIGIT(c))
+	    switch (c)
+	      {
+		case 'c': case 'a':
+		  /* For now simply ignore (but also don't require) these.  */
+		  c = *++p;
+		  break;
+		case '%':
+		  ++p;
+		  putc ('%', asm_out_file);
+		  continue;
+	      }
+	  if (c == '[')
+	    {
+	      const char *end = strchr (++p, ']');
+	      char *q;
+
+	      if (! end)
+		{
+		  error_at (loc,
+			    "missing close brace for named asm() operand");
+		  break;
+		}
+	      q = xstrndup (p, end - p);
+
+	      for (node = inputs; node && node != error_mark_node; )
+		{
+		  tree name = TREE_PURPOSE (TREE_PURPOSE (node));
+
+		  if (name && ! strcmp (TREE_STRING_POINTER (name), q))
+		    break;
+		  node = TREE_CHAIN (node);
+		}
+	      if (! node)
+		error_at (loc, "undefined named asm() operand %qs",
+			  identifier_to_locale (q));
+	      free (q);
+	      p = end + 1;
+	    }
+	  else if (ISDIGIT(c))
+	    {
+	      char *endp;
+	      unsigned long opnum = strtoul (p, &endp, 10);
+
+	      for (node = inputs; opnum-- && node && node != error_mark_node; )
+		node = TREE_CHAIN (node);
+
+	      if (! node)
+		error_at (loc, "asm() operand number out of range");
+	      p = endp;
+	    }
+	  else
+	    {
+	      error_at (loc, 
+			"unsupported or invalid asm() %%-code"
+			" in this context");
+	      break;
+	    }
+
+	  if (node && node != error_mark_node)
+	    {
+	      tree val = TREE_VALUE (node);
+
+	      string = TREE_VALUE (TREE_PURPOSE (node));
+	      gcc_assert (TREE_CODE (string) == STRING_CST);
+	      if (strcmp (TREE_STRING_POINTER (string), "i"))
+		warning_at (loc, 0,
+			    "unsupported or invalid asm() constraint"
+			    " ignored - \"i\" assumed");
+
+	      output_addr_const (asm_out_file,
+				 expand_expr (val, NULL_RTX, VOIDmode,
+					      EXPAND_INITIALIZER));
+	    }
+	}
+
+      putc ('\n', asm_out_file);
+    }
+  else
+    fprintf (asm_out_file, "\t%s\n", TREE_STRING_POINTER (string));
 }
 
 /* Record an element in the table of global destructors.  SYMBOL is