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. It is certainly not very useful in PIC code, at least not with some extra care.
gcc/ 2011-09-30 Jan Beulich <jbeul...@suse.com> * c-parser.c (c_parser_simple_asm_expr): Add new second parameter 'inputsp'. Process inputs if caller indicates they are allowed. Adjust calls to c_parser_asm_operands(). (c_parser_asm_operands): Change type of second parameter from 'bool' to 'int'. Call c_parser_expression() only for non-negative 'mode', and c_parser_expr_no_commas() otherwise. (c_parser_declaration_or_fndef): Pass NULL as new second argument to c_parser_simple_asm_expr(). (c_parser_asm_definition): New local variables 'loc' and 'inputs'. Adjust calls to c_parser_simple_asm_expr() and cgraph_add_asm_node(). (c_parser_simple_asm_expr): * cgraph.c (cgraph_add_asm_node): Call check_unique_operand_names() to validate input operands. Store inputs and location. * cgraph.h (struct cgraph_asm_node): Add 'inputs' and 'loc'. (cgraph_add_asm_node): New second and third parameters. * cgraphunit.c (cgraph_output_pending_asms): Pass new second and third arguments to assemble_asm(). (process_function_and_variable_attributes): New local variable 'anode'. Process list starting from 'cgraph_asm_nodes'. (cgraph_output_in_order): Pass new second and third arguments to assemble_asm(). * cp/parser.c (enum required_token): Add RT_COLON_NO_OUTPUT. (cp_parser_asm_definition): New local variable 'loc'. Correct a comment. Parse and process input operands if permitted. (cp_parser_required_error): Handle new case RT_COLON_NO_OUTPUT. * lto-streamer-in.c (lto_input_toplevel_asms): Pass new second and third arguments to cgraph_add_asm_node(). * lto-streamer-out.c (lto_output_toplevel_asms): Also output inputs and location. * output.h (assemble_asm): New second and third parameters. * stmt.c (check_unique_operand_names): Remove static declaration and make global. * tree.h (check_unique_operand_names): Declare. * varasm.c: Include pretty-print.h. (assemble_asm): New parameters 'inputs' and 'loc'. Process inputs if provided. gcc/testsuite/ 2011-09-30 Jan Beulich <jbeul...@suse.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. --- 2011-09-29.orig/gcc/c-parser.c 2011-09-28 10:56:01.000000000 +0200 +++ 2011-09-29/gcc/c-parser.c 2011-09-29 15:07:29.000000000 +0200 @@ -1131,7 +1131,7 @@ static struct c_arg_info *c_parser_parms static struct c_arg_info *c_parser_parms_list_declarator (c_parser *, tree, 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 *); @@ -1150,7 +1150,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 *); @@ -1623,7 +1623,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)) @@ -1782,9 +1782,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 %<;%>"); } @@ -3330,10 +3333,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)); @@ -3347,6 +3351,11 @@ 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 %<)%>") + && c_parser_require (parser, CPP_COLON, + "expected %<:%> (no output operands allowed here)")) + *inputsp = c_parser_asm_operands (parser, -1); parser->lex_untranslated_string = false; if (!c_parser_require (parser, CPP_CLOSE_PAREN, "expected %<)%>")) { @@ -5050,10 +5059,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); @@ -5091,9 +5100,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 @@ -5105,7 +5115,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; @@ -5144,9 +5154,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; --- 2011-09-29.orig/gcc/cgraph.c 2011-09-28 10:56:01.000000000 +0200 +++ 2011-09-29/gcc/cgraph.c 2011-09-29 15:07:29.000000000 +0200 @@ -1990,12 +1990,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) --- 2011-09-29.orig/gcc/cgraph.h 2011-09-28 10:56:01.000000000 +0200 +++ 2011-09-29/gcc/cgraph.h 2011-09-29 15:07:29.000000000 +0200 @@ -430,6 +430,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; }; @@ -510,7 +514,7 @@ void cgraph_redirect_edge_callee (struct void cgraph_make_edge_direct (struct cgraph_edge *, struct cgraph_node *); bool cgraph_only_called_directly_p (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 *); --- 2011-09-29.orig/gcc/cgraphunit.c 2011-09-28 10:56:01.000000000 +0200 +++ 2011-09-29/gcc/cgraphunit.c 2011-09-29 15:07:29.000000000 +0200 @@ -819,7 +819,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; } @@ -972,6 +972,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) { @@ -1052,6 +1053,47 @@ process_function_and_variable_attributes } process_common_attributes (decl); } + 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 @@ -1965,7 +2007,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: --- 2011-09-29.orig/gcc/cp/parser.c 2011-09-28 10:53:33.000000000 +0200 +++ 2011-09-29/gcc/cp/parser.c 2011-09-29 15:07:29.000000000 +0200 @@ -139,6 +139,7 @@ typedef enum required_token { RT_MULT, /* '*' */ RT_COMPL, /* '~' */ RT_COLON, /* ':' */ + RT_COLON_NO_OUTPUT, /* ':' with "no output" remark */ RT_COLON_SCOPE, /* ':' or '::' */ RT_CLOSE_PAREN, /* ')' */ RT_COMMA_CLOSE_PAREN, /* ',' or ')' */ @@ -14282,6 +14283,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. */ @@ -14363,7 +14365,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, @@ -14414,6 +14416,34 @@ 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) + || cp_lexer_next_token_is (parser->lexer, CPP_SCOPE))) + { + if (cp_lexer_next_token_is (parser->lexer, CPP_COLON)) + { + /* Consume the `:'. */ + cp_lexer_consume_token (parser->lexer); + if (! cp_lexer_next_token_is (parser->lexer, CPP_COLON)) + { + invalid_outputs_p = true; + missing = RT_COLON_NO_OUTPUT; + } + } + + if (! invalid_outputs_p) + { + /* Consume the `:' or `::'. */ + 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, @@ -14440,7 +14470,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); + } } } @@ -21344,6 +21400,9 @@ cp_parser_required_error (cp_parser *par case RT_COLON: cp_parser_error (parser, "expected %<:%>"); return; + case RT_COLON_NO_OUTPUT: + cp_parser_error (parser, "expected %<:%> (no output operands allowed here)"); + return; case RT_COLON_SCOPE: cp_parser_error (parser, "expected %<:%> or %<::%>"); return; --- 2011-09-29.orig/gcc/lto-streamer-in.c 2011-09-29 15:07:23.000000000 +0200 +++ 2011-09-29/gcc/lto-streamer-in.c 2011-09-29 15:07:29.000000000 +0200 @@ -1173,7 +1173,12 @@ lto_input_toplevel_asms (struct lto_file header->lto_header.minor_version); while ((str = streamer_read_string_cst (data_in, &ib))) - cgraph_add_asm_node (str); + { + 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); --- 2011-09-29.orig/gcc/lto-streamer-out.c 2011-09-29 15:07:23.000000000 +0200 +++ 2011-09-29/gcc/lto-streamer-out.c 2011-09-29 15:07:29.000000000 +0200 @@ -954,7 +954,11 @@ lto_output_toplevel_asms (void) streamer_write_char_stream (ob->string_stream, 0); for (can = cgraph_asm_nodes; can; can = can->next) - streamer_write_string_cst (ob, ob->main_stream, can->asm_str); + { + streamer_write_string_cst (ob, ob->main_stream, can->asm_str); + lto_output_tree (ob, can->inputs, false); + lto_output_location (ob, can->loc); + } streamer_write_string_cst (ob, ob->main_stream, NULL_TREE); --- 2011-09-29.orig/gcc/output.h 2011-09-28 10:56:01.000000000 +0200 +++ 2011-09-29/gcc/output.h 2011-09-29 15:07:29.000000000 +0200 @@ -188,7 +188,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. --- 2011-09-29.orig/gcc/stmt.c 2011-09-28 10:56:01.000000000 +0200 +++ 2011-09-29/gcc/stmt.c 2011-09-29 15:07:29.000000000 +0200 @@ -113,7 +113,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); @@ -1250,7 +1249,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; --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ 2011-09-29/gcc/testsuite/g++.dg/ext/asm-static-1.C 2011-09-29 15:07:29.000000000 +0200 @@ -0,0 +1,26 @@ +/* Check compilation of file scope asm() with input oprand(s). */ +/* { dg-do compile } */ +/* { dg-options "-fno-PIC" { target fpic } } */ + +extern int aaa[]; +extern void fff(int*); +extern struct s { + int i, a[2]; +} sss; +static inline void iii(void) +{ +} + +__asm__ (".long %c0" : : "i" (fff)); +__asm__ (".long %c0" : : "i" (aaa)); +__asm__ (".long %c0" : : "i" (iii)); +__asm__ (".long %c0" : : "i" (&sss.i)); +__asm__ (".long %c0" : : "i" (sss.a)); +__asm__ (".long %c0, %c1, %c2" :: "i" (-1), "i" ((long)fff), "i" (aaa + 1)); +__asm__ (".long %c[arr], %c[cnst], %c[fn]" + :: [cnst] "i" (-1), [fn] "i" ((long)fff), [arr] "i" (aaa + 1)); + +/* { dg-final { scan-assembler "long.*aaa" } } */ +/* { dg-final { scan-assembler "long.*fff" } } */ +/* { dg-final { scan-assembler "long.*iii" } } */ +/* { dg-final { scan-assembler "long.*sss" } } */ --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ 2011-09-29/gcc/testsuite/gcc.dg/asm-static-1.c 2011-09-29 15:07:29.000000000 +0200 @@ -0,0 +1,26 @@ +/* Check compilation of file scope asm() with input oprand(s). */ +/* { dg-do compile } */ +/* { dg-options "-fno-PIC" { target fpic } } */ + +extern int aaa[]; +extern void fff(int*); +extern struct s { + int i, a[2]; +} sss; +static __inline__ void iii(void) +{ +} + +__asm__ (".long %c0" :: "i" (fff)); +__asm__ (".long %c0" :: "i" (aaa)); +__asm__ (".long %c0" :: "i" (iii)); +__asm__ (".long %c0" :: "i" (&sss.i)); +__asm__ (".long %c0" :: "i" (sss.a)); +__asm__ (".long %c0, %c1, %c2" :: "i" (-1), "i" ((long)fff), "i" (aaa + 1)); +__asm__ (".long %c[arr], %c[cnst], %c[fn]" + :: [cnst] "i" (-1), [fn] "i" ((long)fff), [arr] "i" (aaa + 1)); + +/* { dg-final { scan-assembler "long.*aaa" } } */ +/* { dg-final { scan-assembler "long.*fff" } } */ +/* { dg-final { scan-assembler "long.*iii" } } */ +/* { dg-final { scan-assembler "long.*sss" } } */ --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ 2011-09-29/gcc/testsuite/gcc.dg/asm-static-2.c 2011-09-29 15:07:29.000000000 +0200 @@ -0,0 +1,9 @@ +/* Check for parse time errors. */ +/* { dg-do compile } */ + +extern int a[]; + +__asm__ (".long %c0" : "i" (0)); /* { dg-error "expected .?:.*no output operand" } */ +__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" } */ --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ 2011-09-29/gcc/testsuite/gcc.dg/asm-static-3.c 2011-09-29 15:07:29.000000000 +0200 @@ -0,0 +1,8 @@ +/* Check for errors detected before code generation. */ +/* { dg-do compile } */ +/* { dg-options "-fno-PIC" { target fpic } } */ + +extern int a[]; +extern void f(int*); + +__asm__ (".long %c0" :: "i" ((long)a - (long)f)); /* { dg-error "expression not supported" } */ --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ 2011-09-29/gcc/testsuite/gcc.dg/asm-static-4.c 2011-09-29 15:07:29.000000000 +0200 @@ -0,0 +1,9 @@ +/* Check for errors detected during code generation. */ +/* { dg-do compile } */ +/* { dg-options "-fno-PIC" { target fpic } } */ + +__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" } */ --- 2011-09-29.orig/gcc/tree.h 2011-09-28 10:56:01.000000000 +0200 +++ 2011-09-29/gcc/tree.h 2011-09-29 15:07:29.000000000 +0200 @@ -5624,6 +5624,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 bool expand_switch_using_bit_tests_p (tree, tree, unsigned int, unsigned int); extern void expand_case (gimple); --- 2011-09-29.orig/gcc/varasm.c 2011-09-28 10:56:01.000000000 +0200 +++ 2011-09-29/gcc/varasm.c 2011-09-29 15:07:29.000000000 +0200 @@ -54,6 +54,7 @@ along with GCC; see the file COPYING3. #include "basic-block.h" #include "tree-iterator.h" #include "pointer-set.h" +#include "pretty-print.h" #ifdef XCOFF_DEBUGGING_INFO #include "xcoffout.h" /* Needed for external data @@ -1352,14 +1353,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