From 2674fa47de9ecfee5b04f753582fb23041029f40 Mon Sep 17 00:00:00 2001 From: Jason Merrill Date: Fri, 16 Nov 2018 16:49:42 -0500 Subject: [PATCH] Implement P0479R5, [[likely]] and [[unlikely]]. [[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. gcc/ * gimplify.c (gimplify_case_label_expr): Handle hot/cold attributes. gcc/c-family/ * c-lex.c (c_common_has_attribute): Handle likely/unlikely. * c-attribs.c (attr_cold_hot_exclusions): Make public. gcc/cp/ * tree.c (handle_likeliness_attribute): New. (std_attribute_table): Add likely/unlikely. * cp-gimplify.c (lookup_hotness_attribute, remove_hotness_attribute) (process_stmt_hotness_attribute, first_stmt): New. (genericize_if_stmt): Check for duplicate predictions. * parser.c (cp_parser_statement): Call process_stmt_hotness_attribute. (cp_parser_label_for_labeled_statement): Apply attributes to case. * decl.c (finish_case_label): Give label in template type void. * pt.c (tsubst_expr) [CASE_LABEL_EXPR]: Copy attributes. [PREDICT_EXPR]: Handle. From-SVN: r266223 --- gcc/ChangeLog | 4 + gcc/c-family/ChangeLog | 5 ++ gcc/c-family/c-attribs.c | 2 +- gcc/c-family/c-common.h | 1 + gcc/c-family/c-lex.c | 4 +- gcc/cp/ChangeLog | 15 ++++ gcc/cp/cp-gimplify.c | 93 +++++++++++++++++++++++ gcc/cp/cp-tree.h | 2 + gcc/cp/decl.c | 2 +- gcc/cp/parser.c | 41 ++++++++-- gcc/cp/pt.c | 12 ++- gcc/cp/tree.c | 30 ++++++++ gcc/gimplify.c | 10 ++- gcc/testsuite/g++.dg/cpp2a/attr-likely1.C | 38 +++++++++ gcc/testsuite/g++.dg/cpp2a/attr-likely2.C | 12 +++ gcc/testsuite/g++.dg/cpp2a/attr-likely3.C | 8 ++ gcc/testsuite/g++.dg/cpp2a/attr-likely4.C | 19 +++++ gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C | 12 +++ 18 files changed, 296 insertions(+), 14 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp2a/attr-likely1.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/attr-likely2.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/attr-likely3.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/attr-likely4.C diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 713f1c367fb..4b87c43a2ea 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,7 @@ +2018-11-12 Jason Merrill + + * gimplify.c (gimplify_case_label_expr): Handle hot/cold attributes. + 2018-11-16 Michael Meissner * config/rs6000/constraints.md (wF constraint): Remove power9 diff --git a/gcc/c-family/ChangeLog b/gcc/c-family/ChangeLog index ed700f8deca..db465421051 100644 --- a/gcc/c-family/ChangeLog +++ b/gcc/c-family/ChangeLog @@ -1,3 +1,8 @@ +2018-11-16 Jason Merrill + + * c-lex.c (c_common_has_attribute): Handle likely/unlikely. + * c-attribs.c (attr_cold_hot_exclusions): Make public. + 2018-11-16 Jakub Jelinek PR middle-end/87854 diff --git a/gcc/c-family/c-attribs.c b/gcc/c-family/c-attribs.c index c9afa1f78f4..88b24cd6cd4 100644 --- a/gcc/c-family/c-attribs.c +++ b/gcc/c-family/c-attribs.c @@ -163,7 +163,7 @@ static const struct attribute_spec::exclusions attr_aligned_exclusions[] = ATTR_EXCL (NULL, false, false, false) }; -static const struct attribute_spec::exclusions attr_cold_hot_exclusions[] = +extern const struct attribute_spec::exclusions attr_cold_hot_exclusions[] = { ATTR_EXCL ("cold", true, true, true), ATTR_EXCL ("hot", true, true, true), diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h index 8eeeba75319..4aa61426114 100644 --- a/gcc/c-family/c-common.h +++ b/gcc/c-family/c-common.h @@ -1330,6 +1330,7 @@ extern int parse_tm_stmt_attr (tree, int); extern int tm_attr_to_mask (tree); extern tree tm_mask_to_attr (int); extern tree find_tm_attribute (tree); +extern const struct attribute_spec::exclusions attr_cold_hot_exclusions[]; /* A bitmap of flags to positional_argument. */ enum posargflags { diff --git a/gcc/c-family/c-lex.c b/gcc/c-family/c-lex.c index d979a970459..93a6f1660c4 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/ChangeLog b/gcc/cp/ChangeLog index b8a74376fb9..d0a344eadca 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,18 @@ +2018-11-16 Jason Merrill + + Implement P0479R5, [[likely]] and [[unlikely]]. + * tree.c (handle_likeliness_attribute): New. + (std_attribute_table): Add likely/unlikely. + * cp-gimplify.c (lookup_hotness_attribute, remove_hotness_attribute) + (process_stmt_hotness_attribute, first_stmt): New. + (genericize_if_stmt): Check for duplicate predictions. + * parser.c (cp_parser_statement): Call + process_stmt_hotness_attribute. + (cp_parser_label_for_labeled_statement): Apply attributes to case. + * 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-16 Nathan Sidwell Remove ovl_used, it is no longer needed diff --git a/gcc/cp/cp-gimplify.c b/gcc/cp/cp-gimplify.c index eb761b118a1..5cb54adf60f 100644 --- a/gcc/cp/cp-gimplify.c +++ b/gcc/cp/cp-gimplify.c @@ -34,6 +34,7 @@ along with GCC; see the file COPYING3. If not see #include "stringpool.h" #include "attribs.h" #include "asan.h" +#include "gcc-rich-location.h" /* Forward declarations. */ @@ -158,6 +159,26 @@ genericize_eh_spec_block (tree *stmt_p) TREE_NO_WARNING (TREE_OPERAND (*stmt_p, 1)) = true; } +/* Return the first non-compound statement in STMT. */ + +tree +first_stmt (tree stmt) +{ + switch (TREE_CODE (stmt)) + { + case STATEMENT_LIST: + if (tree_statement_list_node *p = STATEMENT_LIST_HEAD (stmt)) + return first_stmt (p->stmt); + return void_node; + + case BIND_EXPR: + return first_stmt (BIND_EXPR_BODY (stmt)); + + default: + return stmt; + } +} + /* Genericize an IF_STMT by turning it into a COND_EXPR. */ static void @@ -171,6 +192,24 @@ genericize_if_stmt (tree *stmt_p) then_ = THEN_CLAUSE (stmt); else_ = ELSE_CLAUSE (stmt); + if (then_ && else_) + { + tree ft = first_stmt (then_); + tree fe = first_stmt (else_); + br_predictor pr; + if (TREE_CODE (ft) == PREDICT_EXPR + && TREE_CODE (fe) == PREDICT_EXPR + && (pr = PREDICT_EXPR_PREDICTOR (ft)) == PREDICT_EXPR_PREDICTOR (fe) + && (pr == PRED_HOT_LABEL || pr == PRED_COLD_LABEL)) + { + gcc_rich_location richloc (EXPR_LOC_OR_LOC (ft, locus)); + richloc.add_range (EXPR_LOC_OR_LOC (fe, locus)); + warning_at (&richloc, OPT_Wattributes, + "both branches of % statement marked as %qs", + predictor_name (pr)); + } + } + if (!then_) then_ = build_empty_stmt (locus); if (!else_) @@ -2674,4 +2713,58 @@ cp_fold (tree x) return x; } +/* Look up either "hot" or "cold" in attribute list LIST. */ + +tree +lookup_hotness_attribute (tree list) +{ + for (; list; list = TREE_CHAIN (list)) + { + tree name = get_attribute_name (list); + if (is_attribute_p ("hot", name) + || is_attribute_p ("cold", name) + || is_attribute_p ("likely", name) + || is_attribute_p ("unlikely", name)) + break; + } + return list; +} + +/* Remove both "hot" and "cold" attributes from LIST. */ + +static tree +remove_hotness_attribute (tree list) +{ + list = remove_attribute ("hot", list); + list = remove_attribute ("cold", list); + list = remove_attribute ("likely", list); + list = remove_attribute ("unlikely", list); + return 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) + || is_attribute_p ("likely", name)); + tree pred = build_predict_expr (hot ? PRED_HOT_LABEL : PRED_COLD_LABEL, + hot ? TAKEN : NOT_TAKEN); + SET_EXPR_LOCATION (pred, input_location); + 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/cp-tree.h b/gcc/cp/cp-tree.h index a5c9e5b2080..edb94940098 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/cp/decl.c b/gcc/cp/decl.c index 7d63bbe7f1b..1eee29e0d59 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 92a4f96c6b7..215c5fb9983 100644 --- a/gcc/cp/parser.c +++ b/gcc/cp/parser.c @@ -10880,6 +10880,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 @@ -10901,12 +10902,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; @@ -10914,6 +10917,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; @@ -10923,15 +10927,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; @@ -10940,9 +10953,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; @@ -11001,12 +11016,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. */ @@ -11014,11 +11026,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); @@ -11131,7 +11148,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, @@ -11148,7 +11168,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"); @@ -11178,6 +11201,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)) diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index 66e8f6fed1f..a0d899f594b 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/cp/tree.c b/gcc/cp/tree.c index 10b2cfbdf02..97074dfab56 100644 --- a/gcc/cp/tree.c +++ b/gcc/cp/tree.c @@ -4403,6 +4403,32 @@ handle_no_unique_addr_attribute (tree* node, return NULL_TREE; } +/* The C++20 [[likely]] and [[unlikely]] attributes on labels map to the GNU + hot/cold attributes. */ + +static tree +handle_likeliness_attribute (tree *node, tree name, tree args, + int flags, bool *no_add_attrs) +{ + *no_add_attrs = true; + if (TREE_CODE (*node) == LABEL_DECL + || TREE_CODE (*node) == FUNCTION_DECL) + { + if (args) + warning (OPT_Wattributes, "%qE attribute takes no arguments", name); + tree bname = (is_attribute_p ("likely", name) + ? get_identifier ("hot") : get_identifier ("cold")); + if (TREE_CODE (*node) == FUNCTION_DECL) + warning (OPT_Wattributes, "ISO C++ %qE attribute does not apply to " + "functions; treating as %<[[gnu::%E]]%>", name, bname); + tree battr = build_tree_list (bname, NULL_TREE); + decl_attributes (node, battr, flags); + return NULL_TREE; + } + else + return error_mark_node; +} + /* Table of valid C++ attributes. */ const struct attribute_spec cxx_attribute_table[] = { @@ -4426,6 +4452,10 @@ const struct attribute_spec std_attribute_table[] = handle_nodiscard_attribute, NULL }, { "no_unique_address", 0, 0, true, false, false, false, handle_no_unique_addr_attribute, NULL }, + { "likely", 0, 0, false, false, false, false, + handle_likeliness_attribute, attr_cold_hot_exclusions }, + { "unlikely", 0, 0, false, false, false, false, + handle_likeliness_attribute, attr_cold_hot_exclusions }, { NULL, 0, 0, false, false, false, false, NULL, NULL } }; diff --git a/gcc/gimplify.c b/gcc/gimplify.c index 87082ad10d2..40fbaa2c523 100644 --- a/gcc/gimplify.c +++ b/gcc/gimplify.c @@ -2511,11 +2511,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 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(); + + 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..6c59610528e --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/attr-likely2.C @@ -0,0 +1,12 @@ +// { dg-do compile { target c++2a } } + +bool b; +int main() +{ + if (b) + [[likely, likely]] b; // { dg-warning "ignoring" } + else + [[unlikely]] [[likely]] b; // { dg-warning "ignoring" } + + [[likely, unlikely]] lab:; // { dg-warning "ignoring" } +} diff --git a/gcc/testsuite/g++.dg/cpp2a/attr-likely3.C b/gcc/testsuite/g++.dg/cpp2a/attr-likely3.C new file mode 100644 index 00000000000..bb1265ddb6e --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/attr-likely3.C @@ -0,0 +1,8 @@ +// { dg-do compile { target c++2a } } + +[[likely]] void f() { } // { dg-warning "function" } + +int main() +{ + f(); +} diff --git a/gcc/testsuite/g++.dg/cpp2a/attr-likely4.C b/gcc/testsuite/g++.dg/cpp2a/attr-likely4.C new file mode 100644 index 00000000000..bf0dc4c5d4e --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/attr-likely4.C @@ -0,0 +1,19 @@ +// { dg-do compile { target c++2a } } + +int a, b, c; + +void +__attribute__((noinline)) +bar() +{ + if (a == 123) + [[likely]] c = 5; // { dg-warning "both" } + else + [[likely]] b = 77; +} + +int main() +{ + bar (); + return 0; +} 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 -- 2.30.2