Message ID | 4C32F9BA0200007800009ABF@vpn.id2.novell.com |
---|---|
State | New |
Headers | show |
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
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
>>> 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
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
>>> 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
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 > > >
>>> 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
>>> 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
>>> 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
--- 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