[[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? 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 <ja...@redhat.com> + + * gimplify.c (gimplify_case_label_expr): Handle hot/cold attributes. + 2018-11-13 Alan Modra <amo...@gmail.com> * 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 <ja...@redhat.com> + + * c-lex.c (c_common_has_attribute): Handle likely/unlikely. + 2018-11-12 Jason Merrill <ja...@redhat.com> * 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 <ja...@redhat.com> + + 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 <ja...@redhat.com> Implement P0722R3, destroying operator delete. base-commit: 76b94d4ba654e9af1882865933343d11f5c3b18b -- 2.17.2