diff mbox series

asm inline

Message ID 6d90f6bb5a86b853173e3c980598b6e472fdc591.1539346564.git.segher@kernel.crashing.org
State New
Headers show
Series asm inline | expand

Commit Message

Segher Boessenkool Oct. 12, 2018, 12:21 p.m. UTC
The Linux kernel people want a feature that makes GCC pretend some
inline assembler code is tiny (while it would think it is huge), so
that such code will be inlined essentially always instead of
essentially never.

This patch lets you say "asm inline" instead of just "asm", with the
result that that inline assembler is always counted as minimum cost
for inlining.  It implements this for C and C++.

Tested on powerpc64-linux {-m32,-m64}.  Is this okay for trunk?

(I'll wait a bit committing this until kernel testing has finished,
either way).


Segher


2018-10-10  Segher Boessenkool  <segher@kernel.crashing.org>

	* doc/extend.texi (Using Assembly Language with C): Document asm inline.
	(Size of an asm): Fix typo.  Document asm inline.
	* gimple-pretty-print.c (dump_gimple_asm): Handle asm inline.
	* gimple.h (enum gf_mask): Add GF_ASM_INLINE.
	(gimple_asm_set_volatile): Fix typo.
	* gimple_asm_inline_p: New.
	* gimple_asm_set_inline: New.
	* gimplify.c (gimplify_asm_expr): Propagate the asm inline flag from
	tree to gimple.
	* ipa-icf-gimple.c (func_checker::compare_gimple_asm): Compare the
	gimple_asm_inline_p flag, too.
	* tree-core.h (tree_base): Document that protected_flag is ASM_INLINE_P
	in an ASM_EXPR.
	* tree-inline.c (estimate_num_insns): If gimple_asm_inline_p return
	a minimum size for an asm.
	* tree.h (ASM_INLINE_P): New.

c/
	* c-parser.c (c_parser_asm_statement): Detect the inline keyword
	after asm.  Pass a flag for it to build_asm_expr.
	* c-tree.h (build_asm_expr): Update declaration.
	* c-typeck.c (build_asm_stmt): Add is_inline parameter.  Use it to
	set ASM_INLINE_P.

cp/
	* cp-tree.h (finish_asm_stmt): Update declaration.
	* parser.c (cp_parser_asm_definition): Detect the inline keyword
	after asm.  Pass a flag for it to finish_asm_stmt.
	* pt.c (tsubst_expr): Pass the ASM_INLINE_P flag to finish_asm_stmt.
	* semantics.c (finish_asm_stmt): Add inline_p parameter.  Use it to
	set ASM_INLINE_P.

gcc/testsuite/
	* c-c++-common/torture/asm-inline.c: New testcase.

---
 gcc/c/c-parser.c                                | 16 ++++++--
 gcc/c/c-tree.h                                  |  3 +-
 gcc/c/c-typeck.c                                |  3 +-
 gcc/cp/cp-tree.h                                |  2 +-
 gcc/cp/parser.c                                 | 38 +++++++++++++-----
 gcc/cp/pt.c                                     |  2 +-
 gcc/cp/semantics.c                              |  3 +-
 gcc/doc/extend.texi                             | 10 ++++-
 gcc/gimple-pretty-print.c                       |  2 +
 gcc/gimple.h                                    | 24 ++++++++++-
 gcc/gimplify.c                                  |  1 +
 gcc/ipa-icf-gimple.c                            |  3 ++
 gcc/testsuite/c-c++-common/torture/asm-inline.c | 53 +++++++++++++++++++++++++
 gcc/tree-core.h                                 |  3 ++
 gcc/tree-inline.c                               |  3 ++
 gcc/tree.h                                      |  3 ++
 16 files changed, 147 insertions(+), 22 deletions(-)
 create mode 100644 gcc/testsuite/c-c++-common/torture/asm-inline.c

Comments

Jakub Jelinek Oct. 12, 2018, 12:38 p.m. UTC | #1
On Fri, Oct 12, 2018 at 12:21:06PM +0000, Segher Boessenkool wrote:
> The Linux kernel people want a feature that makes GCC pretend some
> inline assembler code is tiny (while it would think it is huge), so
> that such code will be inlined essentially always instead of
> essentially never.

Just a minor comment, shouldn't we accept both
asm volatile inline (...)
and
asm inline volatile (...)
ordering (probably still require that goto if present must be last)?

	Jakub
Alexander Monakov Oct. 12, 2018, 12:45 p.m. UTC | #2
On Fri, 12 Oct 2018, Segher Boessenkool wrote:

> The Linux kernel people want a feature that makes GCC pretend some
> inline assembler code is tiny (while it would think it is huge), so
> that such code will be inlined essentially always instead of
> essentially never.

I do apologize for being quite late for the party here, but given
that the main problem is too much of the asm body is accounted as
its cost, wouldn't it be cleaner to give asm writers a way to
write portions of asm template that GCC wouldn't count as its cost?

For instance by introducing special tokens like %[ %] that demarkate
portion of the asm that shouldn't be counted:

  asm (
   "mov r1, r2                              \
    %[                                      \
    .pushsection foo                        \
    %% this part is not counted as asm cost \
    ...                                     \
    ...                                     \ 
    .popsection                             \
    %]                                      \
    ");

The implementation on GCC side should be quite simple.

Alexander
Segher Boessenkool Oct. 12, 2018, 1:13 p.m. UTC | #3
On Fri, Oct 12, 2018 at 02:38:52PM +0200, Jakub Jelinek wrote:
> On Fri, Oct 12, 2018 at 12:21:06PM +0000, Segher Boessenkool wrote:
> > The Linux kernel people want a feature that makes GCC pretend some
> > inline assembler code is tiny (while it would think it is huge), so
> > that such code will be inlined essentially always instead of
> > essentially never.
> 
> Just a minor comment, shouldn't we accept both
> asm volatile inline (...)
> and
> asm inline volatile (...)
> ordering (probably still require that goto if present must be last)?

For C volatile is a type-qualifier, which makes the syntax more complex
if we would handle it as you suggest.  It can be done of course.

I followed the example of goto as you say, which cannot go before volatile
either.


Segher
Jakub Jelinek Oct. 12, 2018, 1:18 p.m. UTC | #4
On Fri, Oct 12, 2018 at 08:13:51AM -0500, Segher Boessenkool wrote:
> On Fri, Oct 12, 2018 at 02:38:52PM +0200, Jakub Jelinek wrote:
> > On Fri, Oct 12, 2018 at 12:21:06PM +0000, Segher Boessenkool wrote:
> > > The Linux kernel people want a feature that makes GCC pretend some
> > > inline assembler code is tiny (while it would think it is huge), so
> > > that such code will be inlined essentially always instead of
> > > essentially never.
> > 
> > Just a minor comment, shouldn't we accept both
> > asm volatile inline (...)
> > and
> > asm inline volatile (...)
> > ordering (probably still require that goto if present must be last)?
> 
> For C volatile is a type-qualifier, which makes the syntax more complex
> if we would handle it as you suggest.  It can be done of course.

You could just check after parsing asm if the next token is inline, handle
it then, and after parsing the type qualifiers if there wasn't any inline
keyword check for it again.
Don't feel strongly about it, but it might be getting hard for users to
remember the right ordering (of course, that can be partially helped with
useful fixit diagnostics).

	Jakub
Segher Boessenkool Oct. 12, 2018, 1:54 p.m. UTC | #5
On Fri, Oct 12, 2018 at 03:45:41PM +0300, Alexander Monakov wrote:
> On Fri, 12 Oct 2018, Segher Boessenkool wrote:
> 
> > The Linux kernel people want a feature that makes GCC pretend some
> > inline assembler code is tiny (while it would think it is huge), so
> > that such code will be inlined essentially always instead of
> > essentially never.
> 
> I do apologize for being quite late for the party here, but given
> that the main problem is too much of the asm body is accounted as
> its cost, wouldn't it be cleaner to give asm writers a way to
> write portions of asm template that GCC wouldn't count as its cost?
> 
> For instance by introducing special tokens like %[ %] that demarkate
> portion of the asm that shouldn't be counted:

This potentially conflicts with targetm.asm_out.print_operand_punct_valid_p
(and it does in fact conflict for microblaze).

> The implementation on GCC side should be quite simple.

I don't agree at all.  Sure it is not very many lines to implement this,
but it will make the semantics of inline assembler even stranger than it
already is.

I also don't think it will be very useful.

The "count the number of instructions in an asm" thing in GCC is primarily
meant to generate *correct* code: if the asm does not do strange things
(assembler pseudo-ops, change object section, etc.), it can reliably count
an upper bound of the number of bytes this asm will take, and that matters
for things like keeping jump targets in range.

The inliner also uses this number (well, the estimated number of assembler
statments in fact, about the same as the number of machine instructions; not
the number of bytes taken by the code) to guide its decisions.  This is
pretty good in simple cases, but grossly wrong in other cases.

Making some parts of the asm template invisible to this counting does not
improve the estimated size very much IMO; my suggested solution is easier
to use, and cleaner (both in user code and in GCC code), and will result
in better results in the end I believe.


Segher
Alexander Monakov Oct. 12, 2018, 4:57 p.m. UTC | #6
On Fri, 12 Oct 2018, Segher Boessenkool wrote:
> > For instance by introducing special tokens like %[ %] that demarkate
> > portion of the asm that shouldn't be counted:
> 
> This potentially conflicts with targetm.asm_out.print_operand_punct_valid_p
> (and it does in fact conflict for microblaze).

As far as I can tell, microblaze.c:print_operand will handle it via

  default:
    output_operand_lossage ("unknown punctuation '%c'", letter);

(as any non-letter punctuation other than ?@# ), so it seems unclear why the
port claims it as valid.

In any case, if we choose to pursue this solution at all, we should be able
to find a workable delimiter style.

> > The implementation on GCC side should be quite simple.
> 
> I don't agree at all.  Sure it is not very many lines to implement this,
> but it will make the semantics of inline assembler even stranger than it
> already is.
> 
> I also don't think it will be very useful.
> 
> The "count the number of instructions in an asm" thing in GCC is primarily
> meant to generate *correct* code: if the asm does not do strange things
> (assembler pseudo-ops, change object section, etc.), it can reliably count
> an upper bound of the number of bytes this asm will take, and that matters
> for things like keeping jump targets in range.

Please consider that as currently GCC over-estimates length of such asms,
branches around them are emitted as long-range jumps more often than needed,
which should be a problem we'd want to solve, because the whole reason this
is being raised is because such asms appear in hot code paths. My proposal
solves this aspect as well.

Alexander
Segher Boessenkool Oct. 12, 2018, 5:19 p.m. UTC | #7
On Fri, Oct 12, 2018 at 07:57:12PM +0300, Alexander Monakov wrote:
> > I don't agree at all.  Sure it is not very many lines to implement this,
> > but it will make the semantics of inline assembler even stranger than it
> > already is.
> > 
> > I also don't think it will be very useful.
> > 
> > The "count the number of instructions in an asm" thing in GCC is primarily
> > meant to generate *correct* code: if the asm does not do strange things
> > (assembler pseudo-ops, change object section, etc.), it can reliably count
> > an upper bound of the number of bytes this asm will take, and that matters
> > for things like keeping jump targets in range.
> 
> Please consider that as currently GCC over-estimates length of such asms,
> branches around them are emitted as long-range jumps more often than needed,
> which should be a problem we'd want to solve, because the whole reason this
> is being raised is because such asms appear in hot code paths. My proposal
> solves this aspect as well.

The compiler estimates the size of an asm for very different purposes than
for making inlining decisions.  It is pretty good for those other purposes,
but not great for inlining decisions.  Your proposal makes it not good for
*either*, has a handwavy definition that is hard to reason with, and has
other problems integrating with the existing GCC code as well.

Don't focus on how to implement this.  That is the easy part.  Focus on how
to define the actual exact semantics of it, and how it interacts with other
inline assembler features, and what it will mean for later changes to
nearby code.

Also prototype code actually using it.  See if there are unexpected
problems, if you can always predict what the behaviour will be, if it is
easy to get the behaviour you want.  Also see if there would be easier
ways to get equivalent results.


Segher
Alexander Monakov Oct. 12, 2018, 5:55 p.m. UTC | #8
On Fri, 12 Oct 2018, Segher Boessenkool wrote:
> > Please consider that as currently GCC over-estimates length of such asms,
> > branches around them are emitted as long-range jumps more often than needed,
> > which should be a problem we'd want to solve, because the whole reason this
> > is being raised is because such asms appear in hot code paths. My proposal
> > solves this aspect as well.
> 
> The compiler estimates the size of an asm for very different purposes than
> for making inlining decisions.  It is pretty good for those other purposes,
> but not great for inlining decisions.  Your proposal makes it not good for
> *either*, has a handwavy definition that is hard to reason with, and has
> other problems integrating with the existing GCC code as well.

No, I'm pretty sure that's an unfair assessment of my idea. Can I ask someone
to step in?

Alexander
Eric Gallager Oct. 28, 2018, 5:32 p.m. UTC | #9
On 10/12/18, Segher Boessenkool <segher@kernel.crashing.org> wrote:
> The Linux kernel people want a feature that makes GCC pretend some
> inline assembler code is tiny (while it would think it is huge), so
> that such code will be inlined essentially always instead of
> essentially never.
>
> This patch lets you say "asm inline" instead of just "asm", with the
> result that that inline assembler is always counted as minimum cost
> for inlining.  It implements this for C and C++.
>
> Tested on powerpc64-linux {-m32,-m64}.  Is this okay for trunk?
>
> (I'll wait a bit committing this until kernel testing has finished,
> either way).
>
>
> Segher
>
>
> 2018-10-10  Segher Boessenkool  <segher@kernel.crashing.org>
>
> 	* doc/extend.texi (Using Assembly Language with C): Document asm inline.
> 	(Size of an asm): Fix typo.  Document asm inline.
> 	* gimple-pretty-print.c (dump_gimple_asm): Handle asm inline.
> 	* gimple.h (enum gf_mask): Add GF_ASM_INLINE.
> 	(gimple_asm_set_volatile): Fix typo.
> 	* gimple_asm_inline_p: New.
> 	* gimple_asm_set_inline: New.
> 	* gimplify.c (gimplify_asm_expr): Propagate the asm inline flag from
> 	tree to gimple.
> 	* ipa-icf-gimple.c (func_checker::compare_gimple_asm): Compare the
> 	gimple_asm_inline_p flag, too.
> 	* tree-core.h (tree_base): Document that protected_flag is ASM_INLINE_P
> 	in an ASM_EXPR.
> 	* tree-inline.c (estimate_num_insns): If gimple_asm_inline_p return
> 	a minimum size for an asm.
> 	* tree.h (ASM_INLINE_P): New.
>
> c/
> 	* c-parser.c (c_parser_asm_statement): Detect the inline keyword
> 	after asm.  Pass a flag for it to build_asm_expr.
> 	* c-tree.h (build_asm_expr): Update declaration.
> 	* c-typeck.c (build_asm_stmt): Add is_inline parameter.  Use it to
> 	set ASM_INLINE_P.
>
> cp/
> 	* cp-tree.h (finish_asm_stmt): Update declaration.
> 	* parser.c (cp_parser_asm_definition): Detect the inline keyword
> 	after asm.  Pass a flag for it to finish_asm_stmt.
> 	* pt.c (tsubst_expr): Pass the ASM_INLINE_P flag to finish_asm_stmt.
> 	* semantics.c (finish_asm_stmt): Add inline_p parameter.  Use it to
> 	set ASM_INLINE_P.
>
> gcc/testsuite/
> 	* c-c++-common/torture/asm-inline.c: New testcase.
>
> ---
>  gcc/c/c-parser.c                                | 16 ++++++--
>  gcc/c/c-tree.h                                  |  3 +-
>  gcc/c/c-typeck.c                                |  3 +-
>  gcc/cp/cp-tree.h                                |  2 +-
>  gcc/cp/parser.c                                 | 38 +++++++++++++-----
>  gcc/cp/pt.c                                     |  2 +-
>  gcc/cp/semantics.c                              |  3 +-
>  gcc/doc/extend.texi                             | 10 ++++-
>  gcc/gimple-pretty-print.c                       |  2 +
>  gcc/gimple.h                                    | 24 ++++++++++-
>  gcc/gimplify.c                                  |  1 +
>  gcc/ipa-icf-gimple.c                            |  3 ++
>  gcc/testsuite/c-c++-common/torture/asm-inline.c | 53
> +++++++++++++++++++++++++
>  gcc/tree-core.h                                 |  3 ++
>  gcc/tree-inline.c                               |  3 ++
>  gcc/tree.h                                      |  3 ++
>  16 files changed, 147 insertions(+), 22 deletions(-)
>  create mode 100644 gcc/testsuite/c-c++-common/torture/asm-inline.c
>
> diff --git a/gcc/c/c-parser.c b/gcc/c/c-parser.c
> index 1f173fc..2a8c2dd 100644
> --- a/gcc/c/c-parser.c
> +++ b/gcc/c/c-parser.c
> @@ -6267,8 +6267,8 @@ c_parser_for_statement (c_parser *parser, bool ivdep,
> unsigned short unroll,
>     allowed.
>
>     asm-statement:
> -     asm type-qualifier[opt] ( asm-argument ) ;
> -     asm type-qualifier[opt] goto ( asm-goto-argument ) ;
> +     asm type-qualifier[opt] inline[opt] ( asm-argument ) ;
> +     asm type-qualifier[opt] inline[opt] goto ( asm-goto-argument ) ;
>
>     asm-argument:
>       asm-string-literal

Since this is touching c_parser_asm_statement() it seems relevant to
bug 55681; could you check to see how it interacts with some of the
cases listed in that bug?
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=55681

> @@ -6287,7 +6287,7 @@ static tree
>  c_parser_asm_statement (c_parser *parser)
>  {
>    tree quals, str, outputs, inputs, clobbers, labels, ret;
> -  bool simple, is_goto;
> +  bool simple, is_goto, is_inline;
>    location_t asm_loc = c_parser_peek_token (parser)->location;
>    int section, nsections;
>
> @@ -6311,6 +6311,13 @@ c_parser_asm_statement (c_parser *parser)
>    else
>      quals = NULL_TREE;
>
> +  is_inline = false;
> +  if (c_parser_next_token_is_keyword (parser, RID_INLINE))
> +    {
> +      c_parser_consume_token (parser);
> +      is_inline = true;
> +    }
> +
>    is_goto = false;
>    if (c_parser_next_token_is_keyword (parser, RID_GOTO))
>      {
> @@ -6393,7 +6400,8 @@ c_parser_asm_statement (c_parser *parser)
>      c_parser_skip_to_end_of_block_or_statement (parser);
>
>    ret = build_asm_stmt (quals, build_asm_expr (asm_loc, str, outputs,
> inputs,
> -					       clobbers, labels, simple));
> +					       clobbers, labels, simple,
> +					       is_inline));
>
>   error:
>    parser->lex_untranslated_string = false;
> diff --git a/gcc/c/c-tree.h b/gcc/c/c-tree.h
> index 017c01c..f5629300 100644
> --- a/gcc/c/c-tree.h
> +++ b/gcc/c/c-tree.h
> @@ -677,7 +677,8 @@ extern tree build_compound_literal (location_t, tree,
> tree, bool,
>  extern void check_compound_literal_type (location_t, struct c_type_name
> *);
>  extern tree c_start_case (location_t, location_t, tree, bool);
>  extern void c_finish_case (tree, tree);
> -extern tree build_asm_expr (location_t, tree, tree, tree, tree, tree,
> bool);
> +extern tree build_asm_expr (location_t, tree, tree, tree, tree, tree,
> bool,
> +			    bool);
>  extern tree build_asm_stmt (tree, tree);
>  extern int c_types_compatible_p (tree, tree);
>  extern tree c_begin_compound_stmt (bool);
> diff --git a/gcc/c/c-typeck.c b/gcc/c/c-typeck.c
> index 9d09b8d..e013100 100644
> --- a/gcc/c/c-typeck.c
> +++ b/gcc/c/c-typeck.c
> @@ -10064,7 +10064,7 @@ build_asm_stmt (tree cv_qualifier, tree args)
>     are subtly different.  We use a ASM_EXPR node to represent this.  */
>  tree
>  build_asm_expr (location_t loc, tree string, tree outputs, tree inputs,
> -		tree clobbers, tree labels, bool simple)
> +		tree clobbers, tree labels, bool simple, bool is_inline)
>  {
>    tree tail;
>    tree args;
> @@ -10182,6 +10182,7 @@ build_asm_expr (location_t loc, tree string, tree
> outputs, tree inputs,
>       as volatile.  */
>    ASM_INPUT_P (args) = simple;
>    ASM_VOLATILE_P (args) = (noutputs == 0);
> +  ASM_INLINE_P (args) = is_inline;
>
>    return args;
>  }
> diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
> index efbdad8..e2835a9 100644
> --- a/gcc/cp/cp-tree.h
> +++ b/gcc/cp/cp-tree.h
> @@ -6926,7 +6926,7 @@ extern tree begin_compound_stmt			(unsigned int);
>
>  extern void finish_compound_stmt		(tree);
>  extern tree finish_asm_stmt			(int, tree, tree, tree, tree,
> -						 tree);
> +						 tree, bool);
>  extern tree finish_label_stmt			(tree);
>  extern void finish_label_decl			(tree);
>  extern cp_expr finish_parenthesized_expr	(cp_expr);
> diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
> index 032108a..c74d17a 100644
> --- a/gcc/cp/parser.c
> +++ b/gcc/cp/parser.c
> @@ -19216,16 +19216,20 @@ cp_parser_using_directive (cp_parser* parser)
>     GNU Extension:
>
>     asm-definition:
> -     asm volatile [opt] ( string-literal ) ;
> -     asm volatile [opt] ( string-literal : asm-operand-list [opt] ) ;
> -     asm volatile [opt] ( string-literal : asm-operand-list [opt]
> -			  : asm-operand-list [opt] ) ;
> -     asm volatile [opt] ( string-literal : asm-operand-list [opt]
> -			  : asm-operand-list [opt]
> -			  : asm-clobber-list [opt] ) ;
> -     asm volatile [opt] goto ( string-literal : : asm-operand-list [opt]
> -			       : asm-clobber-list [opt]
> -			       : asm-goto-list ) ;  */
> +     asm volatile [opt] inline [opt] ( string-literal ) ;
> +     asm volatile [opt] inline [opt] ( string-literal
> +				       : asm-operand-list [opt] ) ;
> +     asm volatile [opt] inline [opt] ( string-literal
> +				       : asm-operand-list [opt]
> +				       : asm-operand-list [opt] ) ;
> +     asm volatile [opt] inline [opt] ( string-literal
> +				       : asm-operand-list [opt]
> +				       : asm-operand-list [opt]
> +				       : asm-clobber-list [opt] ) ;
> +     asm volatile [opt] inline [opt] goto ( string-literal
> +					    : : asm-operand-list [opt]
> +					    : asm-clobber-list [opt]
> +					    : asm-goto-list ) ;  */
>
>  static void
>  cp_parser_asm_definition (cp_parser* parser)
> @@ -19241,6 +19245,7 @@ cp_parser_asm_definition (cp_parser* parser)
>    bool invalid_inputs_p = false;
>    bool invalid_outputs_p = false;
>    bool goto_p = false;
> +  bool inline_p = false;
>    required_token missing = RT_NONE;
>
>    /* Look for the `asm' keyword.  */
> @@ -19262,6 +19267,17 @@ cp_parser_asm_definition (cp_parser* parser)
>        /* Consume the token.  */
>        cp_lexer_consume_token (parser->lexer);
>      }
> +  /* See if the next token is `inline'.  */
> +  if (cp_parser_allow_gnu_extensions_p (parser)
> +      && parser->in_function_body
> +      && cp_lexer_next_token_is_keyword (parser->lexer, RID_INLINE))
> +    {
> +      /* Remember that we saw the `inline' keyword.  */
> +      inline_p = true;
> +      /* Consume the token.  */
> +      cp_lexer_consume_token (parser->lexer);
> +    }
> +  /* See if the next token is `goto'.  */
>    if (cp_parser_allow_gnu_extensions_p (parser)
>        && parser->in_function_body
>        && cp_lexer_next_token_is_keyword (parser->lexer, RID_GOTO))
> @@ -19397,7 +19413,7 @@ cp_parser_asm_definition (cp_parser* parser)
>        if (parser->in_function_body)
>  	{
>  	  asm_stmt = finish_asm_stmt (volatile_p, string, outputs,
> -				      inputs, clobbers, labels);
> +				      inputs, clobbers, labels, inline_p);
>  	  /* If the extended syntax was not used, mark the ASM_EXPR.  */
>  	  if (!extended_p)
>  	    {
> diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
> index b8b6545..4bdb3aa 100644
> --- a/gcc/cp/pt.c
> +++ b/gcc/cp/pt.c
> @@ -17008,7 +17008,7 @@ tsubst_expr (tree t, tree args, tsubst_flags_t
> complain, tree in_decl,
>  	tree labels = tsubst_copy_asm_operands (ASM_LABELS (t), args,
>  						complain, in_decl);
>  	tmp = finish_asm_stmt (ASM_VOLATILE_P (t), string, outputs, inputs,
> -			       clobbers, labels);
> +			       clobbers, labels, ASM_INLINE_P (t));
>  	tree asm_expr = tmp;
>  	if (TREE_CODE (asm_expr) == CLEANUP_POINT_EXPR)
>  	  asm_expr = TREE_OPERAND (asm_expr, 0);
> diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c
> index c7f53d1..fa792bd 100644
> --- a/gcc/cp/semantics.c
> +++ b/gcc/cp/semantics.c
> @@ -1485,7 +1485,7 @@ finish_compound_stmt (tree stmt)
>
>  tree
>  finish_asm_stmt (int volatile_p, tree string, tree output_operands,
> -		 tree input_operands, tree clobbers, tree labels)
> +		 tree input_operands, tree clobbers, tree labels, bool inline_p)
>  {
>    tree r;
>    tree t;
> @@ -1639,6 +1639,7 @@ finish_asm_stmt (int volatile_p, tree string, tree
> output_operands,
>  		  output_operands, input_operands,
>  		  clobbers, labels);
>    ASM_VOLATILE_P (r) = volatile_p || noutputs == 0;
> +  ASM_INLINE_P (r) = inline_p;
>    r = maybe_cleanup_point_expr_void (r);
>    return add_stmt (r);
>  }
> diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
> index 0d9b99f..3ca45d9 100644
> --- a/gcc/doc/extend.texi
> +++ b/gcc/doc/extend.texi
> @@ -8188,6 +8188,9 @@ The extended form is preferred for mixing C and
> assembly language
>  within a function, but to include assembly language at
>  top level you must use basic @code{asm}.
>
> +You can use @code{asm inline} instead of @code{asm} to have the assembler
> +code counted as mimimum size for inlining purposes; @pxref{Size of an
> asm}.
> +
>  You can also use the @code{asm} keyword to override the assembler name
>  for a C symbol, or to place a C variable in a specific register.
>
> @@ -9821,7 +9824,7 @@ does this by counting the number of instructions in
> the pattern of the
>  @code{asm} and multiplying that by the length of the longest
>  instruction supported by that processor.  (When working out the number
>  of instructions, it assumes that any occurrence of a newline or of
> -whatever statement separator character is supported by the assembler --
> +whatever statement separator character is supported by the assembler ---
>  typically @samp{;} --- indicates the end of an instruction.)
>
>  Normally, GCC's estimate is adequate to ensure that correct
> @@ -9832,6 +9835,11 @@ space in the object file than is needed for a single
> instruction.
>  If this happens then the assembler may produce a diagnostic saying that
>  a label is unreachable.
>
> +@cindex @code{asm inline}
> +This size is also used for inlining decisions.  If you use @code{asm
> inline}
> +instead of just @code{asm}, then for inlining purposes the size of the asm
> +is taken as the minimum size.
> +
>  @node Alternate Keywords
>  @section Alternate Keywords
>  @cindex alternate keywords
> diff --git a/gcc/gimple-pretty-print.c b/gcc/gimple-pretty-print.c
> index 83e2273..1a00fa3 100644
> --- a/gcc/gimple-pretty-print.c
> +++ b/gcc/gimple-pretty-print.c
> @@ -2019,6 +2019,8 @@ dump_gimple_asm (pretty_printer *buffer, gasm *gs, int
> spc, dump_flags_t flags)
>        pp_string (buffer, "__asm__");
>        if (gimple_asm_volatile_p (gs))
>  	pp_string (buffer, " __volatile__");
> +      if (gimple_asm_inline_p (gs))
> +	pp_string (buffer, " __inline__");
>        if (gimple_asm_nlabels (gs))
>  	pp_string (buffer, " goto");
>        pp_string (buffer, "(\"");
> diff --git a/gcc/gimple.h b/gcc/gimple.h
> index a5dda93..8a58e07 100644
> --- a/gcc/gimple.h
> +++ b/gcc/gimple.h
> @@ -137,6 +137,7 @@ enum gimple_rhs_class
>  enum gf_mask {
>      GF_ASM_INPUT		= 1 << 0,
>      GF_ASM_VOLATILE		= 1 << 1,
> +    GF_ASM_INLINE		= 1 << 2,
>      GF_CALL_FROM_THUNK		= 1 << 0,
>      GF_CALL_RETURN_SLOT_OPT	= 1 << 1,
>      GF_CALL_TAILCALL		= 1 << 2,
> @@ -3911,7 +3912,7 @@ gimple_asm_volatile_p (const gasm *asm_stmt)
>  }
>
>
> -/* If VOLATLE_P is true, mark asm statement ASM_STMT as volatile.  */
> +/* If VOLATILE_P is true, mark asm statement ASM_STMT as volatile.  */
>
>  static inline void
>  gimple_asm_set_volatile (gasm *asm_stmt, bool volatile_p)
> @@ -3923,6 +3924,27 @@ gimple_asm_set_volatile (gasm *asm_stmt, bool
> volatile_p)
>  }
>
>
> +/* Return true ASM_STMT ASM_STMT is an asm statement marked inline.  */
> +
> +static inline bool
> +gimple_asm_inline_p (const gasm *asm_stmt)
> +{
> +  return (asm_stmt->subcode & GF_ASM_INLINE) != 0;
> +}
> +
> +
> +/* If INLINE_P is true, mark asm statement ASM_STMT as inline.  */
> +
> +static inline void
> +gimple_asm_set_inline (gasm *asm_stmt, bool inline_p)
> +{
> +  if (inline_p)
> +    asm_stmt->subcode |= GF_ASM_INLINE;
> +  else
> +    asm_stmt->subcode &= ~GF_ASM_INLINE;
> +}
> +
> +
>  /* If INPUT_P is true, mark asm ASM_STMT as an ASM_INPUT.  */
>
>  static inline void
> diff --git a/gcc/gimplify.c b/gcc/gimplify.c
> index 509fc2f..10b80f2 100644
> --- a/gcc/gimplify.c
> +++ b/gcc/gimplify.c
> @@ -6315,6 +6315,7 @@ gimplify_asm_expr (tree *expr_p, gimple_seq *pre_p,
> gimple_seq *post_p)
>
>        gimple_asm_set_volatile (stmt, ASM_VOLATILE_P (expr) || noutputs ==
> 0);
>        gimple_asm_set_input (stmt, ASM_INPUT_P (expr));
> +      gimple_asm_set_inline (stmt, ASM_INLINE_P (expr));
>
>        gimplify_seq_add_stmt (pre_p, stmt);
>      }
> diff --git a/gcc/ipa-icf-gimple.c b/gcc/ipa-icf-gimple.c
> index ba39ea3..5361139 100644
> --- a/gcc/ipa-icf-gimple.c
> +++ b/gcc/ipa-icf-gimple.c
> @@ -993,6 +993,9 @@ func_checker::compare_gimple_asm (const gasm *g1, const
> gasm *g2)
>    if (gimple_asm_input_p (g1) != gimple_asm_input_p (g2))
>      return false;
>
> +  if (gimple_asm_inline_p (g1) != gimple_asm_inline_p (g2))
> +    return false;
> +
>    if (gimple_asm_ninputs (g1) != gimple_asm_ninputs (g2))
>      return false;
>
> diff --git a/gcc/testsuite/c-c++-common/torture/asm-inline.c
> b/gcc/testsuite/c-c++-common/torture/asm-inline.c
> new file mode 100644
> index 0000000..dea8965
> --- /dev/null
> +++ b/gcc/testsuite/c-c++-common/torture/asm-inline.c
> @@ -0,0 +1,53 @@
> +/* { dg-do compile } */
> +/* -O0 does no inlining, and -O3 does it too aggressively for this test:
> */
> +/* { dg-skip-if "" { *-*-* } { "-O0" "-O3" } { "" } }
> +/* The normal asm is not inlined:  */
> +/* { dg-final { scan-assembler-times
> "w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w" 2 } } */
> +/* But the asm inline is inlined:  */
> +/* { dg-final { scan-assembler-times
> "x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x" 8 } } */
> +
> +static void f(void)
> +{
> +  asm ("w\nw\nw\nw\nw\nw\nw\nw\nw\nw\nw\nw\nw\nw\nw\n"
> +       "w\nw\nw\nw\nw\nw\nw\nw\nw\nw\nw\nw\nw\nw\nw");
> +}
> +
> +int f0(void) { f(); return 0; }
> +int f1(void) { f(); return 1; }
> +int f2(void) { f(); return 2; }
> +int f3(void) { f(); return 3; }
> +
> +static void fg(void)
> +{
> +  asm goto("w\nw\nw\nw\nw\nw\nw\nw\nw\nw\nw\nw\nw\nw\nw\n"
> +	   "w\nw\nw\nw\nw\nw\nw\nw\nw\nw\nw\nw\nw\nw\nw" :::: q);
> +  q: ;
> +}
> +
> +int fg0(void) { fg(); return 0; }
> +int fg1(void) { fg(); return 1; }
> +int fg2(void) { fg(); return 2; }
> +int fg3(void) { fg(); return 3; }
> +
> +static void g(void)
> +{
> +  asm inline("x\nx\nx\nx\nx\nx\nx\nx\nx\nx\nx\nx\nx\nx\nx\n"
> +	     "x\nx\nx\nx\nx\nx\nx\nx\nx\nx\nx\nx\nx\nx\nx");
> +}
> +
> +int g0(void) { g(); return 0; }
> +int g1(void) { g(); return 1; }
> +int g2(void) { g(); return 2; }
> +int g3(void) { g(); return 3; }
> +
> +static void gg(void)
> +{
> +  asm inline goto("x\nx\nx\nx\nx\nx\nx\nx\nx\nx\nx\nx\nx\nx\nx\n"
> +		  "x\nx\nx\nx\nx\nx\nx\nx\nx\nx\nx\nx\nx\nx\nx" :::: q);
> +  q: ;
> +}
> +
> +int gg0(void) { gg(); return 0; }
> +int gg1(void) { gg(); return 1; }
> +int gg2(void) { gg(); return 2; }
> +int gg3(void) { gg(); return 3; }
> diff --git a/gcc/tree-core.h b/gcc/tree-core.h
> index 7df5c40..a99ca5d 100644
> --- a/gcc/tree-core.h
> +++ b/gcc/tree-core.h
> @@ -1151,6 +1151,9 @@ struct GTY(()) tree_base {
>         OMP_CLAUSE_LINEAR_VARIABLE_STRIDE in
>  	   OMP_CLAUSE_LINEAR
>
> +       ASM_INLINE_P in
> +	   ASM_EXPR
> +
>     side_effects_flag:
>
>         TREE_SIDE_EFFECTS in
> diff --git a/gcc/tree-inline.c b/gcc/tree-inline.c
> index 9134253..5d48edb 100644
> --- a/gcc/tree-inline.c
> +++ b/gcc/tree-inline.c
> @@ -4108,6 +4108,9 @@ estimate_num_insns (gimple *stmt, eni_weights
> *weights)
>  	   with very long asm statements.  */
>  	if (count > 1000)
>  	  count = 1000;
> +	/* If this asm is asm inline, count anything as minimum size.  */
> +	if (gimple_asm_inline_p (as_a <gasm *> (stmt)))
> +	  count = !!count;
>  	return MAX (1, count);
>        }
>
> diff --git a/gcc/tree.h b/gcc/tree.h
> index 2e45f9d..160b3a7 100644
> --- a/gcc/tree.h
> +++ b/gcc/tree.h
> @@ -1245,6 +1245,9 @@ extern tree maybe_wrap_with_location (tree,
> location_t);
>     ASM_OPERAND with no operands.  */
>  #define ASM_INPUT_P(NODE) (ASM_EXPR_CHECK (NODE)->base.static_flag)
>  #define ASM_VOLATILE_P(NODE) (ASM_EXPR_CHECK (NODE)->base.public_flag)
> +/* Nonzero if we want to consider this asm as minimum length and cost
> +   for inlining decisions.  */
> +#define ASM_INLINE_P(NODE) (ASM_EXPR_CHECK (NODE)->base.protected_flag)
>
>  /* COND_EXPR accessors.  */
>  #define COND_EXPR_COND(NODE)	(TREE_OPERAND (COND_EXPR_CHECK (NODE), 0))
> --
> 1.8.3.1
>
>
Segher Boessenkool Oct. 28, 2018, 7:56 p.m. UTC | #10
On Sun, Oct 28, 2018 at 01:32:09PM -0400, Eric Gallager wrote:
> Since this is touching c_parser_asm_statement() it seems relevant to
> bug 55681; could you check to see how it interacts with some of the
> cases listed in that bug?
> https://gcc.gnu.org/bugzilla/show_bug.cgi?id=55681

I wasn't aware of this PR.  Thanks!


Segher
diff mbox series

Patch

diff --git a/gcc/c/c-parser.c b/gcc/c/c-parser.c
index 1f173fc..2a8c2dd 100644
--- a/gcc/c/c-parser.c
+++ b/gcc/c/c-parser.c
@@ -6267,8 +6267,8 @@  c_parser_for_statement (c_parser *parser, bool ivdep, unsigned short unroll,
    allowed.
 
    asm-statement:
-     asm type-qualifier[opt] ( asm-argument ) ;
-     asm type-qualifier[opt] goto ( asm-goto-argument ) ;
+     asm type-qualifier[opt] inline[opt] ( asm-argument ) ;
+     asm type-qualifier[opt] inline[opt] goto ( asm-goto-argument ) ;
 
    asm-argument:
      asm-string-literal
@@ -6287,7 +6287,7 @@  static tree
 c_parser_asm_statement (c_parser *parser)
 {
   tree quals, str, outputs, inputs, clobbers, labels, ret;
-  bool simple, is_goto;
+  bool simple, is_goto, is_inline;
   location_t asm_loc = c_parser_peek_token (parser)->location;
   int section, nsections;
 
@@ -6311,6 +6311,13 @@  c_parser_asm_statement (c_parser *parser)
   else
     quals = NULL_TREE;
 
+  is_inline = false;
+  if (c_parser_next_token_is_keyword (parser, RID_INLINE))
+    {
+      c_parser_consume_token (parser);
+      is_inline = true;
+    }
+
   is_goto = false;
   if (c_parser_next_token_is_keyword (parser, RID_GOTO))
     {
@@ -6393,7 +6400,8 @@  c_parser_asm_statement (c_parser *parser)
     c_parser_skip_to_end_of_block_or_statement (parser);
 
   ret = build_asm_stmt (quals, build_asm_expr (asm_loc, str, outputs, inputs,
-					       clobbers, labels, simple));
+					       clobbers, labels, simple,
+					       is_inline));
 
  error:
   parser->lex_untranslated_string = false;
diff --git a/gcc/c/c-tree.h b/gcc/c/c-tree.h
index 017c01c..f5629300 100644
--- a/gcc/c/c-tree.h
+++ b/gcc/c/c-tree.h
@@ -677,7 +677,8 @@  extern tree build_compound_literal (location_t, tree, tree, bool,
 extern void check_compound_literal_type (location_t, struct c_type_name *);
 extern tree c_start_case (location_t, location_t, tree, bool);
 extern void c_finish_case (tree, tree);
-extern tree build_asm_expr (location_t, tree, tree, tree, tree, tree, bool);
+extern tree build_asm_expr (location_t, tree, tree, tree, tree, tree, bool,
+			    bool);
 extern tree build_asm_stmt (tree, tree);
 extern int c_types_compatible_p (tree, tree);
 extern tree c_begin_compound_stmt (bool);
diff --git a/gcc/c/c-typeck.c b/gcc/c/c-typeck.c
index 9d09b8d..e013100 100644
--- a/gcc/c/c-typeck.c
+++ b/gcc/c/c-typeck.c
@@ -10064,7 +10064,7 @@  build_asm_stmt (tree cv_qualifier, tree args)
    are subtly different.  We use a ASM_EXPR node to represent this.  */
 tree
 build_asm_expr (location_t loc, tree string, tree outputs, tree inputs,
-		tree clobbers, tree labels, bool simple)
+		tree clobbers, tree labels, bool simple, bool is_inline)
 {
   tree tail;
   tree args;
@@ -10182,6 +10182,7 @@  build_asm_expr (location_t loc, tree string, tree outputs, tree inputs,
      as volatile.  */
   ASM_INPUT_P (args) = simple;
   ASM_VOLATILE_P (args) = (noutputs == 0);
+  ASM_INLINE_P (args) = is_inline;
 
   return args;
 }
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index efbdad8..e2835a9 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -6926,7 +6926,7 @@  extern tree begin_compound_stmt			(unsigned int);
 
 extern void finish_compound_stmt		(tree);
 extern tree finish_asm_stmt			(int, tree, tree, tree, tree,
-						 tree);
+						 tree, bool);
 extern tree finish_label_stmt			(tree);
 extern void finish_label_decl			(tree);
 extern cp_expr finish_parenthesized_expr	(cp_expr);
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 032108a..c74d17a 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -19216,16 +19216,20 @@  cp_parser_using_directive (cp_parser* parser)
    GNU Extension:
 
    asm-definition:
-     asm volatile [opt] ( string-literal ) ;
-     asm volatile [opt] ( string-literal : asm-operand-list [opt] ) ;
-     asm volatile [opt] ( string-literal : asm-operand-list [opt]
-			  : asm-operand-list [opt] ) ;
-     asm volatile [opt] ( string-literal : asm-operand-list [opt]
-			  : asm-operand-list [opt]
-			  : asm-clobber-list [opt] ) ;
-     asm volatile [opt] goto ( string-literal : : asm-operand-list [opt]
-			       : asm-clobber-list [opt]
-			       : asm-goto-list ) ;  */
+     asm volatile [opt] inline [opt] ( string-literal ) ;
+     asm volatile [opt] inline [opt] ( string-literal
+				       : asm-operand-list [opt] ) ;
+     asm volatile [opt] inline [opt] ( string-literal
+				       : asm-operand-list [opt]
+				       : asm-operand-list [opt] ) ;
+     asm volatile [opt] inline [opt] ( string-literal
+				       : asm-operand-list [opt]
+				       : asm-operand-list [opt]
+				       : asm-clobber-list [opt] ) ;
+     asm volatile [opt] inline [opt] goto ( string-literal
+					    : : asm-operand-list [opt]
+					    : asm-clobber-list [opt]
+					    : asm-goto-list ) ;  */
 
 static void
 cp_parser_asm_definition (cp_parser* parser)
@@ -19241,6 +19245,7 @@  cp_parser_asm_definition (cp_parser* parser)
   bool invalid_inputs_p = false;
   bool invalid_outputs_p = false;
   bool goto_p = false;
+  bool inline_p = false;
   required_token missing = RT_NONE;
 
   /* Look for the `asm' keyword.  */
@@ -19262,6 +19267,17 @@  cp_parser_asm_definition (cp_parser* parser)
       /* Consume the token.  */
       cp_lexer_consume_token (parser->lexer);
     }
+  /* See if the next token is `inline'.  */
+  if (cp_parser_allow_gnu_extensions_p (parser)
+      && parser->in_function_body
+      && cp_lexer_next_token_is_keyword (parser->lexer, RID_INLINE))
+    {
+      /* Remember that we saw the `inline' keyword.  */
+      inline_p = true;
+      /* Consume the token.  */
+      cp_lexer_consume_token (parser->lexer);
+    }
+  /* See if the next token is `goto'.  */
   if (cp_parser_allow_gnu_extensions_p (parser)
       && parser->in_function_body
       && cp_lexer_next_token_is_keyword (parser->lexer, RID_GOTO))
@@ -19397,7 +19413,7 @@  cp_parser_asm_definition (cp_parser* parser)
       if (parser->in_function_body)
 	{
 	  asm_stmt = finish_asm_stmt (volatile_p, string, outputs,
-				      inputs, clobbers, labels);
+				      inputs, clobbers, labels, inline_p);
 	  /* If the extended syntax was not used, mark the ASM_EXPR.  */
 	  if (!extended_p)
 	    {
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index b8b6545..4bdb3aa 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -17008,7 +17008,7 @@  tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl,
 	tree labels = tsubst_copy_asm_operands (ASM_LABELS (t), args,
 						complain, in_decl);
 	tmp = finish_asm_stmt (ASM_VOLATILE_P (t), string, outputs, inputs,
-			       clobbers, labels);
+			       clobbers, labels, ASM_INLINE_P (t));
 	tree asm_expr = tmp;
 	if (TREE_CODE (asm_expr) == CLEANUP_POINT_EXPR)
 	  asm_expr = TREE_OPERAND (asm_expr, 0);
diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c
index c7f53d1..fa792bd 100644
--- a/gcc/cp/semantics.c
+++ b/gcc/cp/semantics.c
@@ -1485,7 +1485,7 @@  finish_compound_stmt (tree stmt)
 
 tree
 finish_asm_stmt (int volatile_p, tree string, tree output_operands,
-		 tree input_operands, tree clobbers, tree labels)
+		 tree input_operands, tree clobbers, tree labels, bool inline_p)
 {
   tree r;
   tree t;
@@ -1639,6 +1639,7 @@  finish_asm_stmt (int volatile_p, tree string, tree output_operands,
 		  output_operands, input_operands,
 		  clobbers, labels);
   ASM_VOLATILE_P (r) = volatile_p || noutputs == 0;
+  ASM_INLINE_P (r) = inline_p;
   r = maybe_cleanup_point_expr_void (r);
   return add_stmt (r);
 }
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index 0d9b99f..3ca45d9 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -8188,6 +8188,9 @@  The extended form is preferred for mixing C and assembly language
 within a function, but to include assembly language at
 top level you must use basic @code{asm}.
 
+You can use @code{asm inline} instead of @code{asm} to have the assembler
+code counted as mimimum size for inlining purposes; @pxref{Size of an asm}.
+
 You can also use the @code{asm} keyword to override the assembler name
 for a C symbol, or to place a C variable in a specific register.
 
@@ -9821,7 +9824,7 @@  does this by counting the number of instructions in the pattern of the
 @code{asm} and multiplying that by the length of the longest
 instruction supported by that processor.  (When working out the number
 of instructions, it assumes that any occurrence of a newline or of
-whatever statement separator character is supported by the assembler --
+whatever statement separator character is supported by the assembler ---
 typically @samp{;} --- indicates the end of an instruction.)
 
 Normally, GCC's estimate is adequate to ensure that correct
@@ -9832,6 +9835,11 @@  space in the object file than is needed for a single instruction.
 If this happens then the assembler may produce a diagnostic saying that
 a label is unreachable.
 
+@cindex @code{asm inline}
+This size is also used for inlining decisions.  If you use @code{asm inline}
+instead of just @code{asm}, then for inlining purposes the size of the asm
+is taken as the minimum size.
+
 @node Alternate Keywords
 @section Alternate Keywords
 @cindex alternate keywords
diff --git a/gcc/gimple-pretty-print.c b/gcc/gimple-pretty-print.c
index 83e2273..1a00fa3 100644
--- a/gcc/gimple-pretty-print.c
+++ b/gcc/gimple-pretty-print.c
@@ -2019,6 +2019,8 @@  dump_gimple_asm (pretty_printer *buffer, gasm *gs, int spc, dump_flags_t flags)
       pp_string (buffer, "__asm__");
       if (gimple_asm_volatile_p (gs))
 	pp_string (buffer, " __volatile__");
+      if (gimple_asm_inline_p (gs))
+	pp_string (buffer, " __inline__");
       if (gimple_asm_nlabels (gs))
 	pp_string (buffer, " goto");
       pp_string (buffer, "(\"");
diff --git a/gcc/gimple.h b/gcc/gimple.h
index a5dda93..8a58e07 100644
--- a/gcc/gimple.h
+++ b/gcc/gimple.h
@@ -137,6 +137,7 @@  enum gimple_rhs_class
 enum gf_mask {
     GF_ASM_INPUT		= 1 << 0,
     GF_ASM_VOLATILE		= 1 << 1,
+    GF_ASM_INLINE		= 1 << 2,
     GF_CALL_FROM_THUNK		= 1 << 0,
     GF_CALL_RETURN_SLOT_OPT	= 1 << 1,
     GF_CALL_TAILCALL		= 1 << 2,
@@ -3911,7 +3912,7 @@  gimple_asm_volatile_p (const gasm *asm_stmt)
 }
 
 
-/* If VOLATLE_P is true, mark asm statement ASM_STMT as volatile.  */
+/* If VOLATILE_P is true, mark asm statement ASM_STMT as volatile.  */
 
 static inline void
 gimple_asm_set_volatile (gasm *asm_stmt, bool volatile_p)
@@ -3923,6 +3924,27 @@  gimple_asm_set_volatile (gasm *asm_stmt, bool volatile_p)
 }
 
 
+/* Return true ASM_STMT ASM_STMT is an asm statement marked inline.  */
+
+static inline bool
+gimple_asm_inline_p (const gasm *asm_stmt)
+{
+  return (asm_stmt->subcode & GF_ASM_INLINE) != 0;
+}
+
+
+/* If INLINE_P is true, mark asm statement ASM_STMT as inline.  */
+
+static inline void
+gimple_asm_set_inline (gasm *asm_stmt, bool inline_p)
+{
+  if (inline_p)
+    asm_stmt->subcode |= GF_ASM_INLINE;
+  else
+    asm_stmt->subcode &= ~GF_ASM_INLINE;
+}
+
+
 /* If INPUT_P is true, mark asm ASM_STMT as an ASM_INPUT.  */
 
 static inline void
diff --git a/gcc/gimplify.c b/gcc/gimplify.c
index 509fc2f..10b80f2 100644
--- a/gcc/gimplify.c
+++ b/gcc/gimplify.c
@@ -6315,6 +6315,7 @@  gimplify_asm_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p)
 
       gimple_asm_set_volatile (stmt, ASM_VOLATILE_P (expr) || noutputs == 0);
       gimple_asm_set_input (stmt, ASM_INPUT_P (expr));
+      gimple_asm_set_inline (stmt, ASM_INLINE_P (expr));
 
       gimplify_seq_add_stmt (pre_p, stmt);
     }
diff --git a/gcc/ipa-icf-gimple.c b/gcc/ipa-icf-gimple.c
index ba39ea3..5361139 100644
--- a/gcc/ipa-icf-gimple.c
+++ b/gcc/ipa-icf-gimple.c
@@ -993,6 +993,9 @@  func_checker::compare_gimple_asm (const gasm *g1, const gasm *g2)
   if (gimple_asm_input_p (g1) != gimple_asm_input_p (g2))
     return false;
 
+  if (gimple_asm_inline_p (g1) != gimple_asm_inline_p (g2))
+    return false;
+
   if (gimple_asm_ninputs (g1) != gimple_asm_ninputs (g2))
     return false;
 
diff --git a/gcc/testsuite/c-c++-common/torture/asm-inline.c b/gcc/testsuite/c-c++-common/torture/asm-inline.c
new file mode 100644
index 0000000..dea8965
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/torture/asm-inline.c
@@ -0,0 +1,53 @@ 
+/* { dg-do compile } */
+/* -O0 does no inlining, and -O3 does it too aggressively for this test:  */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-O3" } { "" } }
+/* The normal asm is not inlined:  */
+/* { dg-final { scan-assembler-times "w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w" 2 } } */
+/* But the asm inline is inlined:  */
+/* { dg-final { scan-assembler-times "x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x" 8 } } */
+
+static void f(void)
+{
+  asm ("w\nw\nw\nw\nw\nw\nw\nw\nw\nw\nw\nw\nw\nw\nw\n"
+       "w\nw\nw\nw\nw\nw\nw\nw\nw\nw\nw\nw\nw\nw\nw");
+}
+
+int f0(void) { f(); return 0; }
+int f1(void) { f(); return 1; }
+int f2(void) { f(); return 2; }
+int f3(void) { f(); return 3; }
+
+static void fg(void)
+{
+  asm goto("w\nw\nw\nw\nw\nw\nw\nw\nw\nw\nw\nw\nw\nw\nw\n"
+	   "w\nw\nw\nw\nw\nw\nw\nw\nw\nw\nw\nw\nw\nw\nw" :::: q);
+  q: ;
+}
+
+int fg0(void) { fg(); return 0; }
+int fg1(void) { fg(); return 1; }
+int fg2(void) { fg(); return 2; }
+int fg3(void) { fg(); return 3; }
+
+static void g(void)
+{
+  asm inline("x\nx\nx\nx\nx\nx\nx\nx\nx\nx\nx\nx\nx\nx\nx\n"
+	     "x\nx\nx\nx\nx\nx\nx\nx\nx\nx\nx\nx\nx\nx\nx");
+}
+
+int g0(void) { g(); return 0; }
+int g1(void) { g(); return 1; }
+int g2(void) { g(); return 2; }
+int g3(void) { g(); return 3; }
+
+static void gg(void)
+{
+  asm inline goto("x\nx\nx\nx\nx\nx\nx\nx\nx\nx\nx\nx\nx\nx\nx\n"
+		  "x\nx\nx\nx\nx\nx\nx\nx\nx\nx\nx\nx\nx\nx\nx" :::: q);
+  q: ;
+}
+
+int gg0(void) { gg(); return 0; }
+int gg1(void) { gg(); return 1; }
+int gg2(void) { gg(); return 2; }
+int gg3(void) { gg(); return 3; }
diff --git a/gcc/tree-core.h b/gcc/tree-core.h
index 7df5c40..a99ca5d 100644
--- a/gcc/tree-core.h
+++ b/gcc/tree-core.h
@@ -1151,6 +1151,9 @@  struct GTY(()) tree_base {
        OMP_CLAUSE_LINEAR_VARIABLE_STRIDE in
 	   OMP_CLAUSE_LINEAR
 
+       ASM_INLINE_P in
+	   ASM_EXPR
+
    side_effects_flag:
 
        TREE_SIDE_EFFECTS in
diff --git a/gcc/tree-inline.c b/gcc/tree-inline.c
index 9134253..5d48edb 100644
--- a/gcc/tree-inline.c
+++ b/gcc/tree-inline.c
@@ -4108,6 +4108,9 @@  estimate_num_insns (gimple *stmt, eni_weights *weights)
 	   with very long asm statements.  */
 	if (count > 1000)
 	  count = 1000;
+	/* If this asm is asm inline, count anything as minimum size.  */
+	if (gimple_asm_inline_p (as_a <gasm *> (stmt)))
+	  count = !!count;
 	return MAX (1, count);
       }
 
diff --git a/gcc/tree.h b/gcc/tree.h
index 2e45f9d..160b3a7 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -1245,6 +1245,9 @@  extern tree maybe_wrap_with_location (tree, location_t);
    ASM_OPERAND with no operands.  */
 #define ASM_INPUT_P(NODE) (ASM_EXPR_CHECK (NODE)->base.static_flag)
 #define ASM_VOLATILE_P(NODE) (ASM_EXPR_CHECK (NODE)->base.public_flag)
+/* Nonzero if we want to consider this asm as minimum length and cost
+   for inlining decisions.  */
+#define ASM_INLINE_P(NODE) (ASM_EXPR_CHECK (NODE)->base.protected_flag)
 
 /* COND_EXPR accessors.  */
 #define COND_EXPR_COND(NODE)	(TREE_OPERAND (COND_EXPR_CHECK (NODE), 0))