On 11/13/18 5:43 AM, Jason Merrill wrote:
> [[likely]] and [[unlikely]] are equivalent to the GNU hot/cold attributes,
> except that they can be applied to arbitrary statements as well as labels;
> this is most likely to be useful for marking if/else branches as likely or
> unlikely. Conveniently, PREDICT_EXPR fits the bill nicely as a
> representation.
>
> I also had to fix marking case labels as hot/cold, which didn't work before.
> Which then required me to force __attribute ((fallthrough)) to apply to the
> statement rather than the label.
>
> Tested x86_64-pc-linux-gnu. Does this seem like a sane implementation
> approach to people with more experience with PREDICT_EXPR?
Hi.
In general it makes sense to implement it the same way. Question is how much
should the hold/cold attribute should be close to __builtin_expect.
Let me present few examples and differences that I see:
1) ./xgcc -B. -O2 -fdump-tree-profile_estimate=/dev/stdout /tmp/test1.C
;; Function foo (_Z3foov, funcdef_no=0, decl_uid=2301, cgraph_uid=1,
symbol_order=3)
Predictions for bb 2
first match heuristics: 90.00%
combined heuristics: 90.00%
__builtin_expect heuristics of edge 2->3: 90.00%
As seen here __builtin_expect is stronger as it's first match heuristics and
has probability == 90%.
;; Function bar (_Z3barv, funcdef_no=1, decl_uid=2303, cgraph_uid=2,
symbol_order=4)
Predictions for bb 2
DS theory heuristics: 74.48%
combined heuristics: 74.48%
opcode values nonequal (on trees) heuristics of edge 2->3: 34.00%
hot label heuristics of edge 2->3: 85.00%
Here we combine hot label prediction with the opcode one, resulting in quite
poor result 75%.
So maybe cold/hot prediction cal also happen first match.
2) ./xgcc -B. -O2 -fdump-tree-profile_estimate=/dev/stdout /tmp/test2.C
...
foo ()
{
...
switch (_3) <default: <L3> [3.33%], case 3: <L0> [3.33%], case 42: <L1>
[3.33%], case 333: <L2> [90.00%]>
while:
bar ()
{
switch (a.1_1) <default: <L3> [25.00%], case 3: <L0> [25.00%], case 42: <L1>
[25.00%], case 333: <L2> [25.00%]>
...
Note that support for __builtin_expect was enhanced in this stage1. I can
definitely cover also situations when one uses
hot/cold for labels. So definitely place for improvement.
3) last example where one can use the attribute for function decl, resulting in:
__attribute__((hot, noinline))
foo ()
{
..
Hope it's desired? If so I would cover that with a test-case in test-suite.
Jason can you please point to C++ specification of the attributes?
Would you please consider an error diagnostics for situations written in
test4.C?
Such situation is then silently ignored in profile_estimate pass:
Predictions for bb 2
hot label heuristics of edge 2->4 (edge pair duplicate): 85.00%
hot label heuristics of edge 2->3 (edge pair duplicate): 85.00%
...
Thanks,
Martin
>
> gcc/
> * gimplify.c (gimplify_case_label_expr): Handle hot/cold attributes.
> gcc/c-family/
> * c-lex.c (c_common_has_attribute): Handle likely/unlikely.
> gcc/cp/
> * parser.c (cp_parser_std_attribute): Handle likely/unlikely.
> (cp_parser_statement): Call process_stmt_hotness_attribute.
> (cp_parser_label_for_labeled_statement): Apply attributes to case.
> * cp-gimplify.c (lookup_hotness_attribute, remove_hotness_attribute)
> (process_stmt_hotness_attribute): New.
> * decl.c (finish_case_label): Give label in template type void.
> * pt.c (tsubst_expr) [CASE_LABEL_EXPR]: Copy attributes.
> [PREDICT_EXPR]: Handle.
> ---
> gcc/cp/cp-tree.h | 2 +
> gcc/c-family/c-lex.c | 4 +-
> gcc/cp/cp-gimplify.c | 42 +++++++++++++++++++++
> gcc/cp/decl.c | 2 +-
> gcc/cp/parser.c | 45 +++++++++++++++++++----
> gcc/cp/pt.c | 12 +++++-
> gcc/gimplify.c | 10 ++++-
> gcc/testsuite/g++.dg/cpp2a/attr-likely1.C | 38 +++++++++++++++++++
> gcc/testsuite/g++.dg/cpp2a/attr-likely2.C | 10 +++++
> gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C | 12 ++++++
> gcc/ChangeLog | 4 ++
> gcc/c-family/ChangeLog | 4 ++
> gcc/cp/ChangeLog | 12 ++++++
> 13 files changed, 184 insertions(+), 13 deletions(-)
> create mode 100644 gcc/testsuite/g++.dg/cpp2a/attr-likely1.C
> create mode 100644 gcc/testsuite/g++.dg/cpp2a/attr-likely2.C
>
> diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
> index c4d79c0cf7f..c55352ec5ff 100644
> --- a/gcc/cp/cp-tree.h
> +++ b/gcc/cp/cp-tree.h
> @@ -7541,6 +7541,8 @@ extern bool cxx_omp_disregard_value_expr (tree,
> bool);
> extern void cp_fold_function (tree);
> extern tree cp_fully_fold (tree);
> extern void clear_fold_cache (void);
> +extern tree lookup_hotness_attribute (tree);
> +extern tree process_stmt_hotness_attribute (tree);
>
> /* in name-lookup.c */
> extern tree strip_using_decl (tree);
> diff --git a/gcc/c-family/c-lex.c b/gcc/c-family/c-lex.c
> index 28a820a2a3d..3cc015083e0 100644
> --- a/gcc/c-family/c-lex.c
> +++ b/gcc/c-family/c-lex.c
> @@ -356,7 +356,9 @@ c_common_has_attribute (cpp_reader *pfile)
> || is_attribute_p ("nodiscard", attr_name)
> || is_attribute_p ("fallthrough", attr_name))
> result = 201603;
> - else if (is_attribute_p ("no_unique_address", attr_name))
> + else if (is_attribute_p ("no_unique_address", attr_name)
> + || is_attribute_p ("likely", attr_name)
> + || is_attribute_p ("unlikely", attr_name))
> result = 201803;
> if (result)
> attr_name = NULL_TREE;
> diff --git a/gcc/cp/cp-gimplify.c b/gcc/cp/cp-gimplify.c
> index eb761b118a1..f8212187162 100644
> --- a/gcc/cp/cp-gimplify.c
> +++ b/gcc/cp/cp-gimplify.c
> @@ -2674,4 +2674,46 @@ cp_fold (tree x)
> return x;
> }
>
> +/* Look up either "hot" or "cold" in attribute list LIST. */
> +
> +tree
> +lookup_hotness_attribute (tree list)
> +{
> + tree attr = lookup_attribute ("hot", list);
> + if (attr)
> + return attr;
> + return lookup_attribute ("cold", list);
> +}
> +
> +/* Remove both "hot" and "cold" attributes from LIST. */
> +
> +static tree
> +remove_hotness_attribute (tree list)
> +{
> + return remove_attribute ("hot", remove_attribute ("cold", list));
> +}
> +
> +/* If [[likely]] or [[unlikely]] appear on this statement, turn it into a
> + PREDICT_EXPR. */
> +
> +tree
> +process_stmt_hotness_attribute (tree std_attrs)
> +{
> + if (std_attrs == error_mark_node)
> + return std_attrs;
> + if (tree attr = lookup_hotness_attribute (std_attrs))
> + {
> + tree name = get_attribute_name (attr);
> + bool hot = is_attribute_p ("hot", name);
> + tree pred = build_predict_expr (hot ? PRED_HOT_LABEL : PRED_COLD_LABEL,
> + hot ? TAKEN : NOT_TAKEN);
> + add_stmt (pred);
> + if (tree other = lookup_hotness_attribute (TREE_CHAIN (attr)))
> + warning (OPT_Wattributes, "ignoring attribute %qE after earlier %qE",
> + get_attribute_name (other), name);
> + std_attrs = remove_hotness_attribute (std_attrs);
> + }
> + return std_attrs;
> +}
> +
> #include "gt-cp-cp-gimplify.h"
> diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
> index 42994055d5f..73f27038e5c 100644
> --- a/gcc/cp/decl.c
> +++ b/gcc/cp/decl.c
> @@ -3624,7 +3624,7 @@ finish_case_label (location_t loc, tree low_value, tree
> high_value)
>
> /* For templates, just add the case label; we'll do semantic
> analysis at instantiation-time. */
> - label = build_decl (loc, LABEL_DECL, NULL_TREE, NULL_TREE);
> + label = build_decl (loc, LABEL_DECL, NULL_TREE, void_type_node);
> return add_stmt (build_case_label (low_value, high_value, label));
> }
>
> diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
> index 0428f6dda90..bdc35fa0cb8 100644
> --- a/gcc/cp/parser.c
> +++ b/gcc/cp/parser.c
> @@ -10875,6 +10875,7 @@ cp_parser_statement (cp_parser* parser, tree
> in_statement_expr,
> /* Peek at the next token. */
> token = cp_lexer_peek_token (parser->lexer);
> /* Remember the location of the first token in the statement. */
> + cp_token *statement_token = token;
> statement_location = token->location;
> add_debug_begin_stmt (statement_location);
> /* If this is a keyword, then that will often determine what kind of
> @@ -10896,12 +10897,14 @@ cp_parser_statement (cp_parser* parser, tree
> in_statement_expr,
>
> case RID_IF:
> case RID_SWITCH:
> + std_attrs = process_stmt_hotness_attribute (std_attrs);
> statement = cp_parser_selection_statement (parser, if_p, chain);
> break;
>
> case RID_WHILE:
> case RID_DO:
> case RID_FOR:
> + std_attrs = process_stmt_hotness_attribute (std_attrs);
> statement = cp_parser_iteration_statement (parser, if_p, false, 0);
> break;
>
> @@ -10909,6 +10912,7 @@ cp_parser_statement (cp_parser* parser, tree
> in_statement_expr,
> case RID_CONTINUE:
> case RID_RETURN:
> case RID_GOTO:
> + std_attrs = process_stmt_hotness_attribute (std_attrs);
> statement = cp_parser_jump_statement (parser);
> break;
>
> @@ -10918,15 +10922,24 @@ cp_parser_statement (cp_parser* parser, tree
> in_statement_expr,
> case RID_AT_FINALLY:
> case RID_AT_SYNCHRONIZED:
> case RID_AT_THROW:
> + std_attrs = process_stmt_hotness_attribute (std_attrs);
> statement = cp_parser_objc_statement (parser);
> break;
>
> case RID_TRY:
> + std_attrs = process_stmt_hotness_attribute (std_attrs);
> statement = cp_parser_try_block (parser);
> break;
>
> case RID_NAMESPACE:
> /* This must be a namespace alias definition. */
> + if (std_attrs != NULL_TREE)
> + {
> + /* Attributes should be parsed as part of the the
> + declaration, so let's un-parse them. */
> + saved_tokens.rollback();
> + std_attrs = NULL_TREE;
> + }
> cp_parser_declaration_statement (parser);
> return;
>
> @@ -10935,9 +10948,11 @@ cp_parser_statement (cp_parser* parser, tree
> in_statement_expr,
> case RID_SYNCHRONIZED:
> case RID_ATOMIC_NOEXCEPT:
> case RID_ATOMIC_CANCEL:
> + std_attrs = process_stmt_hotness_attribute (std_attrs);
> statement = cp_parser_transaction (parser, token);
> break;
> case RID_TRANSACTION_CANCEL:
> + std_attrs = process_stmt_hotness_attribute (std_attrs);
> statement = cp_parser_transaction_cancel (parser);
> break;
>
> @@ -10996,12 +11011,9 @@ cp_parser_statement (cp_parser* parser, tree
> in_statement_expr,
> if (cp_lexer_next_token_is_not (parser->lexer, CPP_SEMICOLON))
> {
> if (std_attrs != NULL_TREE)
> - {
> - /* Attributes should be parsed as part of the the
> - declaration, so let's un-parse them. */
> - saved_tokens.rollback();
> - std_attrs = NULL_TREE;
> - }
> + /* Attributes should be parsed as part of the declaration,
> + so let's un-parse them. */
> + saved_tokens.rollback();
>
> cp_parser_parse_tentatively (parser);
> /* Try to parse the declaration-statement. */
> @@ -11009,11 +11021,16 @@ cp_parser_statement (cp_parser* parser, tree
> in_statement_expr,
> /* If that worked, we're done. */
> if (cp_parser_parse_definitely (parser))
> return;
> + /* It didn't work, restore the post-attribute position. */
> + if (std_attrs)
> + cp_lexer_set_token_position (parser->lexer, statement_token);
> }
> /* All preceding labels have been parsed at this point. */
> if (loc_after_labels != NULL)
> *loc_after_labels = statement_location;
>
> + std_attrs = process_stmt_hotness_attribute (std_attrs);
> +
> /* Look for an expression-statement instead. */
> statement = cp_parser_expression_statement (parser, in_statement_expr);
>
> @@ -11126,7 +11143,10 @@ cp_parser_label_for_labeled_statement (cp_parser*
> parser, tree attributes)
> {
> tree l = finish_case_label (token->location, expr, expr_hi);
> if (l && TREE_CODE (l) == CASE_LABEL_EXPR)
> - FALLTHROUGH_LABEL_P (CASE_LABEL (l)) = fallthrough_p;
> + {
> + label = CASE_LABEL (l);
> + FALLTHROUGH_LABEL_P (label) = fallthrough_p;
> + }
> }
> else
> error_at (token->location,
> @@ -11143,7 +11163,10 @@ cp_parser_label_for_labeled_statement (cp_parser*
> parser, tree attributes)
> {
> tree l = finish_case_label (token->location, NULL_TREE, NULL_TREE);
> if (l && TREE_CODE (l) == CASE_LABEL_EXPR)
> - FALLTHROUGH_LABEL_P (CASE_LABEL (l)) = fallthrough_p;
> + {
> + label = CASE_LABEL (l);
> + FALLTHROUGH_LABEL_P (label) = fallthrough_p;
> + }
> }
> else
> error_at (token->location, "case label not within a switch statement");
> @@ -11173,6 +11196,8 @@ cp_parser_label_for_labeled_statement (cp_parser*
> parser, tree attributes)
> cp_parser_parse_tentatively (parser);
> attrs = cp_parser_gnu_attributes_opt (parser);
> if (attrs == NULL_TREE
> + /* And fallthrough always binds to the expression-statement. */
> + || attribute_fallthrough_p (attrs)
> || cp_lexer_next_token_is_not (parser->lexer, CPP_SEMICOLON))
> cp_parser_abort_tentative_parse (parser);
> else if (!cp_parser_parse_definitely (parser))
> @@ -25528,6 +25553,10 @@ cp_parser_std_attribute (cp_parser *parser, tree
> attr_ns)
> else if (is_attribute_p ("optimize_for_synchronized", attr_id))
> TREE_PURPOSE (attribute)
> = get_identifier ("transaction_callable");
> + else if (is_attribute_p ("likely", attr_id))
> + TREE_PURPOSE (attribute) = get_identifier ("hot");
> + else if (is_attribute_p ("unlikely", attr_id))
> + TREE_PURPOSE (attribute) = get_identifier ("cold");
> /* Transactional Memory attributes are GNU attributes. */
> else if (tm_attr_to_mask (attr_id))
> TREE_PURPOSE (attribute) = attr_id;
> diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
> index 0c33c8e1527..dfd9a436465 100644
> --- a/gcc/cp/pt.c
> +++ b/gcc/cp/pt.c
> @@ -17175,12 +17175,17 @@ tsubst_expr (tree t, tree args, tsubst_flags_t
> complain, tree in_decl,
>
> case CASE_LABEL_EXPR:
> {
> + tree decl = CASE_LABEL (t);
> tree low = RECUR (CASE_LOW (t));
> tree high = RECUR (CASE_HIGH (t));
> tree l = finish_case_label (EXPR_LOCATION (t), low, high);
> if (l && TREE_CODE (l) == CASE_LABEL_EXPR)
> - FALLTHROUGH_LABEL_P (CASE_LABEL (l))
> - = FALLTHROUGH_LABEL_P (CASE_LABEL (t));
> + {
> + tree label = CASE_LABEL (l);
> + FALLTHROUGH_LABEL_P (label) = FALLTHROUGH_LABEL_P (decl);
> + if (DECL_ATTRIBUTES (decl) != NULL_TREE)
> + cplus_decl_attributes (&label, DECL_ATTRIBUTES (decl), 0);
> + }
> }
> break;
>
> @@ -17731,6 +17736,9 @@ tsubst_expr (tree t, tree args, tsubst_flags_t
> complain, tree in_decl,
> RECUR (TREE_OPERAND (t, 1)),
> RECUR (TREE_OPERAND (t, 2))));
>
> + case PREDICT_EXPR:
> + RETURN (add_stmt (copy_node (t)));
> +
> default:
> gcc_assert (!STATEMENT_CODE_P (TREE_CODE (t)));
>
> diff --git a/gcc/gimplify.c b/gcc/gimplify.c
> index d7cb7840a5d..7fd0ab297f2 100644
> --- a/gcc/gimplify.c
> +++ b/gcc/gimplify.c
> @@ -2504,11 +2504,19 @@ gimplify_case_label_expr (tree *expr_p, gimple_seq
> *pre_p)
> if (ctxp->case_labels.exists ())
> break;
>
> - label_stmt = gimple_build_label (CASE_LABEL (*expr_p));
> + tree label = CASE_LABEL (*expr_p);
> + label_stmt = gimple_build_label (label);
> gimple_set_location (label_stmt, EXPR_LOCATION (*expr_p));
> ctxp->case_labels.safe_push (*expr_p);
> gimplify_seq_add_stmt (pre_p, label_stmt);
>
> + if (lookup_attribute ("cold", DECL_ATTRIBUTES (label)))
> + gimple_seq_add_stmt (pre_p, gimple_build_predict (PRED_COLD_LABEL,
> + NOT_TAKEN));
> + else if (lookup_attribute ("hot", DECL_ATTRIBUTES (label)))
> + gimple_seq_add_stmt (pre_p, gimple_build_predict (PRED_HOT_LABEL,
> + TAKEN));
> +
> return GS_ALL_DONE;
> }
>
> diff --git a/gcc/testsuite/g++.dg/cpp2a/attr-likely1.C
> b/gcc/testsuite/g++.dg/cpp2a/attr-likely1.C
> new file mode 100644
> index 00000000000..43de249bd5a
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp2a/attr-likely1.C
> @@ -0,0 +1,38 @@
> +// { dg-do compile { target c++2a } }
> +// { dg-additional-options -fdump-tree-gimple }
> +// { dg-final { scan-tree-dump-times "hot label" 5 "gimple" } }
> +// { dg-final { scan-tree-dump-times "cold label" 3 "gimple" } }
> +
> +bool b;
> +
> +template <class T> int f()
> +{
> + if (b)
> + [[likely]] return 0;
> + else
> + [[unlikely]] flabel: return 1;
> + switch (b)
> + {
> + [[likely]] case true: break;
> + };
> + return 1;
> +}
> +
> +int main()
> +{
> + if (b)
> + [[likely]] return 0;
> + else if (b)
> + [[unlikely]] elabel:
> + return 1;
> + else
> + [[likely]] b = false;
> +
> + f<int>();
> +
> + switch (b)
> + {
> + [[likely]] case true: break;
> + [[unlikely]] case false: break;
> + };
> +}
> diff --git a/gcc/testsuite/g++.dg/cpp2a/attr-likely2.C
> b/gcc/testsuite/g++.dg/cpp2a/attr-likely2.C
> new file mode 100644
> index 00000000000..3c951828c95
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp2a/attr-likely2.C
> @@ -0,0 +1,10 @@
> +// { dg-do compile { target c++2a } }
> +
> +bool b;
> +int main()
> +{
> + if (b)
> + [[likely, likely]] b; // { dg-warning "ignoring" }
> + else
> + [[likely]] [[unlikely]] b; // { dg-warning "ignoring" }
> +}
> diff --git a/gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C
> b/gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C
> index dba77179b82..b80cc342364 100644
> --- a/gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C
> +++ b/gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C
> @@ -456,6 +456,18 @@
> # error "__has_cpp_attribute(no_unique_address) != 201803"
> # endif
>
> +# if ! __has_cpp_attribute(likely)
> +# error "__has_cpp_attribute(likely)"
> +# elif __has_cpp_attribute(likely) != 201803
> +# error "__has_cpp_attribute(likely) != 201803"
> +# endif
> +
> +# if ! __has_cpp_attribute(unlikely)
> +# error "__has_cpp_attribute(unlikely)"
> +# elif __has_cpp_attribute(unlikely) != 201803
> +# error "__has_cpp_attribute(unlikely) != 201803"
> +# endif
> +
> #else
> # error "__has_cpp_attribute"
> #endif
> diff --git a/gcc/ChangeLog b/gcc/ChangeLog
> index 3f98c9001f4..52961030239 100644
> --- a/gcc/ChangeLog
> +++ b/gcc/ChangeLog
> @@ -1,3 +1,7 @@
> +2018-11-12 Jason Merrill <[email protected]>
> +
> + * gimplify.c (gimplify_case_label_expr): Handle hot/cold attributes.
> +
> 2018-11-13 Alan Modra <[email protected]>
>
> * config/rs6000/rs6000.c (rs6000_secondary_reload_inner): Negate
> diff --git a/gcc/c-family/ChangeLog b/gcc/c-family/ChangeLog
> index 165f9b7efcc..910dd628e87 100644
> --- a/gcc/c-family/ChangeLog
> +++ b/gcc/c-family/ChangeLog
> @@ -1,3 +1,7 @@
> +2018-11-11 Jason Merrill <[email protected]>
> +
> + * c-lex.c (c_common_has_attribute): Handle likely/unlikely.
> +
> 2018-11-12 Jason Merrill <[email protected]>
>
> * c-cppbuiltin.c (c_cpp_builtins): Define
> diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog
> index 5497a0829e3..0da4b720e0e 100644
> --- a/gcc/cp/ChangeLog
> +++ b/gcc/cp/ChangeLog
> @@ -1,3 +1,15 @@
> +2018-11-12 Jason Merrill <[email protected]>
> +
> + Implement P0479R5, [[likely]] and [[unlikely]].
> + * parser.c (cp_parser_std_attribute): Handle likely/unlikely.
> + (cp_parser_statement): Call process_stmt_hotness_attribute.
> + (cp_parser_label_for_labeled_statement): Apply attributes to case.
> + * cp-gimplify.c (lookup_hotness_attribute, remove_hotness_attribute)
> + (process_stmt_hotness_attribute): New.
> + * decl.c (finish_case_label): Give label in template type void.
> + * pt.c (tsubst_expr) [CASE_LABEL_EXPR]: Copy attributes.
> + [PREDICT_EXPR]: Handle.
> +
> 2018-11-12 Jason Merrill <[email protected]>
>
> Implement P0722R3, destroying operator delete.
>
> base-commit: 76b94d4ba654e9af1882865933343d11f5c3b18b
>
int a, b, c;
void
__attribute__((noinline))
foo()
{
if (__builtin_expect (a == 123, 1))
c = 5;
}
void
__attribute__((noinline))
bar()
{
if (a == 123)
[[likely]] c = 5;
}
int main()
{
foo ();
bar ();
return 0;
}
int a, b, c;
void
foo ()
{
switch (__builtin_expect(a, 333))
{
case 3: __builtin_puts("a"); break;
case 42: __builtin_puts("e"); break;
case 333: __builtin_puts("i"); break;
}
}
void
bar ()
{
switch (a)
{
case 3: __builtin_puts("a"); break;
case 42: __builtin_puts("e"); break;
[[likely]] case 333: __builtin_puts("i"); break;
}
}
int main()
{
foo ();
bar ();
return 0;
}
int a, b, c;
void
__attribute__((noinline))
[[likely]]
foo()
{
if (__builtin_expect (a == 123, 1))
c = 5;
}
void
__attribute__((noinline))
bar()
{
if (a == 123)
[[likely]] c = 5;
}
int main(int argc, char **argv)
{
if (argc)
foo ();
else
bar ();
return 0;
}
int a, b, c;
void
__attribute__((noinline))
bar()
{
if (a == 123)
[[likely]] c = 5;
else
[[likely]] b = 77;
}
int main()
{
bar ();
return 0;
}