From b8fd7909c04a29e82fb3ebace161801f949ad4f5 Mon Sep 17 00:00:00 2001 From: Jason Merrill Date: Sun, 4 Oct 2015 15:17:19 -0400 Subject: [PATCH] Implement N4514, C++ Extensions for Transactional Memory. gcc/ * builtins.def (BUILT_IN_ABORT): Add transaction_pure attribute. gcc/c-family/ * c-common.c (c_common_reswords): Add C++ TM TS keywords. (c_common_attribute_table): Add transaction_safe_dynamic. transaction_safe now affects type identity. (handle_tm_attribute): Handle transaction_safe_dynamic. * c-common.h (enum rid): Add RID_ATOMIC_NOEXCEPT, RID_ATOMIC_CANCEL, RID_SYNCHRONIZED. (OBJC_IS_CXX_KEYWORD): Add RID_SYNCHRONIZED. (D_TRANSMEM): New. * c-cppbuiltin.c (c_cpp_builtins): Define __cpp_transactional_memory. * c-pretty-print.c (pp_c_attributes_display): Don't print transaction_safe in C++. gcc/c/ * c-parser.c (c_lex_one_token): Handle @synchronized. * c-decl.c (match_builtin_function_types): A declaration of a built-in can change whether the function is transaction_safe. gcc/cp/ * cp-tree.h (struct cp_declarator): Add tx_qualifier field. (BCS_NORMAL, BCS_TRANSACTION): New enumerators. * lex.c (init_reswords): Limit TM kewords to -fgnu-tm. * parser.c (cp_lexer_get_preprocessor_token): Fix @synchronized. (make_call_declarator): Take tx_qualifier. (cp_parser_tx_qualifier_opt): New. (cp_parser_lambda_declarator_opt): Use it. (cp_parser_direct_declarator): Likewise. (cp_parser_statement): Handle atomic_noexcept, atomic_cancel. (cp_parser_compound_statement): Change in_try parameter to bcs_flags. (cp_parser_std_attribute): Map optimize_for_synchronized to transaction_callable. (cp_parser_transaction): Take the token. Handle atomic_noexcept. * lambda.c (maybe_add_lambda_conv_op): Handle transaction-safety. * call.c (enum conversion_kind): Add ck_tsafe. (standard_conversion): Handle transaction-safety conversion. (convert_like_real, resolve_address_of_overloaded_function): Likewise. (check_methods): Diagnose transaction_safe_dynamic on non-virtual function. (look_for_tm_attr_overrides): Don't inherit transaction_safe_dynamic. * cvt.c (tx_safe_fn_type_p, tx_unsafe_fn_variant) (can_convert_tx_safety): New. * typeck.c (composite_pointer_type): Handle transaction-safety. * name-lookup.h (enum scope_kind): Add sk_transaction. * name-lookup.c (begin_scope): Handle it. * semantics.c (begin_compound_stmt): Pass it. * decl.c (check_previous_goto_1): Check it. (struct named_label_entry): Add in_transaction_scope. (poplevel_named_label_1): Set it. (check_goto): Check it. (duplicate_decls): A specialization can be transaction_safe independently of its template. (grokdeclarator): Handle tx-qualifier. * rtti.c (ptr_initializer): Handle transaction-safe. * search.c (check_final_overrider): Check transaction_safe_dynamic. Don't check transaction_safe. * mangle.c (write_function_type): Mangle transaction_safe here. (write_CV_qualifiers_for_type): Not here. (write_type): Preserve transaction_safe when stripping attributes. * error.c (dump_type_suffix): Print transaction_safe. libiberty/ * cp-demangle.c (d_cv_qualifiers): Dx means transaction_safe. (cplus_demangle_type): Let d_cv_qualifiers handle it. (d_dump, d_make_comp, has_return_type, d_encoding) (d_count_templates_scopes, d_print_comp_inner) (d_print_mod_list, d_print_mod, d_print_function_type) (is_ctor_or_dtor): Handle DEMANGLE_COMPONENT_TRANSACTION_SAFE. From-SVN: r228462 --- gcc/ChangeLog | 4 + gcc/builtins.def | 2 +- gcc/c-family/ChangeLog | 15 ++++ gcc/c-family/c-common.c | 28 ++++++- gcc/c-family/c-common.h | 6 +- gcc/c-family/c-cppbuiltin.c | 4 + gcc/c-family/c-pretty-print.c | 4 + gcc/c/ChangeLog | 6 ++ gcc/c/c-decl.c | 14 +++- gcc/c/c-parser.c | 1 + gcc/cp/ChangeLog | 44 +++++++++++ gcc/cp/call.c | 17 +++++ gcc/cp/class.c | 23 +++++- gcc/cp/cp-tree.h | 9 ++- gcc/cp/cvt.c | 33 ++++++++ gcc/cp/decl.c | 52 +++++++++++-- gcc/cp/error.c | 8 ++ gcc/cp/lambda.c | 5 +- gcc/cp/lex.c | 2 + gcc/cp/mangle.c | 12 ++- gcc/cp/name-lookup.c | 1 + gcc/cp/name-lookup.h | 1 + gcc/cp/parser.c | 123 +++++++++++++++++++++++------- gcc/cp/rtti.c | 5 ++ gcc/cp/search.c | 22 +++++- gcc/cp/semantics.c | 9 ++- gcc/cp/typeck.c | 16 ++++ gcc/testsuite/g++.dg/tm/composite1.C | 14 ++++ gcc/testsuite/g++.dg/tm/dynamic1.C | 13 ++++ gcc/testsuite/g++.dg/tm/dynamic2.C | 17 +++++ gcc/testsuite/g++.dg/tm/eh1.C | 10 +++ gcc/testsuite/g++.dg/tm/eh2.C | 14 ++++ gcc/testsuite/g++.dg/tm/eh4.C | 21 +++++ gcc/testsuite/g++.dg/tm/inherit1.C | 11 +++ gcc/testsuite/g++.dg/tm/inherit2.C | 33 ++++++++ gcc/testsuite/g++.dg/tm/jump1.C | 23 ++++++ gcc/testsuite/g++.dg/tm/keyword1.C | 9 +++ gcc/testsuite/g++.dg/tm/lambda1.C | 10 +++ gcc/testsuite/g++.dg/tm/lambda2.C | 9 +++ gcc/testsuite/g++.dg/tm/macro1.C | 5 ++ gcc/testsuite/g++.dg/tm/mangle1.C | 18 +++++ gcc/testsuite/g++.dg/tm/noexcept-7.C | 7 ++ gcc/testsuite/g++.dg/tm/overload1.C | 6 ++ gcc/testsuite/g++.dg/tm/overload2.C | 9 +++ gcc/testsuite/g++.dg/tm/pretty-print1.C | 6 ++ gcc/testsuite/g++.dg/tm/static_cast1.C | 9 +++ gcc/testsuite/g++.dg/tm/sync1.C | 15 ++++ gcc/testsuite/g++.dg/tm/sync2.C | 21 +++++ gcc/testsuite/g++.dg/tm/template-3.C | 15 ++++ gcc/testsuite/g++.dg/tm/template-4.C | 13 ++++ gcc/testsuite/g++.dg/tm/template-5.C | 12 +++ gcc/testsuite/g++.dg/tm/unsafe1.C | 15 ++++ gcc/testsuite/g++.dg/tm/unsafe2.C | 13 ++++ gcc/testsuite/lib/g++.exp | 4 + gcc/tree.c | 2 + gcc/tree.h | 3 + include/demangle.h | 2 + libiberty/ChangeLog | 9 +++ libiberty/cp-demangle.c | 36 +++++++-- libiberty/testsuite/demangle-expected | 3 + libstdc++-v3/libsupc++/cxxabi.h | 3 +- libstdc++-v3/libsupc++/pbase_type_info.cc | 13 +++- 62 files changed, 833 insertions(+), 56 deletions(-) create mode 100644 gcc/testsuite/g++.dg/tm/composite1.C create mode 100644 gcc/testsuite/g++.dg/tm/dynamic1.C create mode 100644 gcc/testsuite/g++.dg/tm/dynamic2.C create mode 100644 gcc/testsuite/g++.dg/tm/eh1.C create mode 100644 gcc/testsuite/g++.dg/tm/eh2.C create mode 100644 gcc/testsuite/g++.dg/tm/eh4.C create mode 100644 gcc/testsuite/g++.dg/tm/inherit1.C create mode 100644 gcc/testsuite/g++.dg/tm/inherit2.C create mode 100644 gcc/testsuite/g++.dg/tm/jump1.C create mode 100644 gcc/testsuite/g++.dg/tm/keyword1.C create mode 100644 gcc/testsuite/g++.dg/tm/lambda1.C create mode 100644 gcc/testsuite/g++.dg/tm/lambda2.C create mode 100644 gcc/testsuite/g++.dg/tm/macro1.C create mode 100644 gcc/testsuite/g++.dg/tm/mangle1.C create mode 100644 gcc/testsuite/g++.dg/tm/noexcept-7.C create mode 100644 gcc/testsuite/g++.dg/tm/overload1.C create mode 100644 gcc/testsuite/g++.dg/tm/overload2.C create mode 100644 gcc/testsuite/g++.dg/tm/pretty-print1.C create mode 100644 gcc/testsuite/g++.dg/tm/static_cast1.C create mode 100644 gcc/testsuite/g++.dg/tm/sync1.C create mode 100644 gcc/testsuite/g++.dg/tm/sync2.C create mode 100644 gcc/testsuite/g++.dg/tm/template-3.C create mode 100644 gcc/testsuite/g++.dg/tm/template-4.C create mode 100644 gcc/testsuite/g++.dg/tm/template-5.C create mode 100644 gcc/testsuite/g++.dg/tm/unsafe1.C create mode 100644 gcc/testsuite/g++.dg/tm/unsafe2.C diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 4ba93a2..0d740a2 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,7 @@ +2015-10-04 Jason Merrill + + * builtins.def (BUILT_IN_ABORT): Add transaction_pure attribute. + 2015-10-04 Uros Bizjak * config/i386/i386.c (ix86_nsaved_regs): Use GENERAL_REGNO_P to diff --git a/gcc/builtins.def b/gcc/builtins.def index f7ac4a8..2cb8251 100644 --- a/gcc/builtins.def +++ b/gcc/builtins.def @@ -729,7 +729,7 @@ DEF_GCC_BUILTIN (BUILT_IN_UMULL_OVERFLOW, "umull_overflow", BT_FN_BOOL_UL DEF_GCC_BUILTIN (BUILT_IN_UMULLL_OVERFLOW, "umulll_overflow", BT_FN_BOOL_ULONGLONG_ULONGLONG_ULONGLONGPTR, ATTR_NOTHROW_LEAF_LIST) /* Category: miscellaneous builtins. */ -DEF_LIB_BUILTIN (BUILT_IN_ABORT, "abort", BT_FN_VOID, ATTR_NORETURN_NOTHROW_LEAF_LIST) +DEF_LIB_BUILTIN (BUILT_IN_ABORT, "abort", BT_FN_VOID, ATTR_TMPURE_NORETURN_NOTHROW_LEAF_LIST) DEF_LIB_BUILTIN (BUILT_IN_ABS, "abs", BT_FN_INT_INT, ATTR_CONST_NOTHROW_LEAF_LIST) DEF_GCC_BUILTIN (BUILT_IN_AGGREGATE_INCOMING_ADDRESS, "aggregate_incoming_address", BT_FN_PTR_VAR, ATTR_LEAF_LIST) DEF_EXT_LIB_BUILTIN (BUILT_IN_ALLOCA, "alloca", BT_FN_PTR_SIZE, ATTR_MALLOC_NOTHROW_LEAF_LIST) diff --git a/gcc/c-family/ChangeLog b/gcc/c-family/ChangeLog index 78cc2bd..c75d489 100644 --- a/gcc/c-family/ChangeLog +++ b/gcc/c-family/ChangeLog @@ -1,3 +1,18 @@ +2015-10-04 Jason Merrill + + Implement N4514, C++ Extensions for Transactional Memory. + * c-common.c (c_common_reswords): Add C++ TM TS keywords. + (c_common_attribute_table): Add transaction_safe_dynamic. + transaction_safe now affects type identity. + (handle_tm_attribute): Handle transaction_safe_dynamic. + * c-common.h (enum rid): Add RID_ATOMIC_NOEXCEPT, + RID_ATOMIC_CANCEL, RID_SYNCHRONIZED. + (OBJC_IS_CXX_KEYWORD): Add RID_SYNCHRONIZED. + (D_TRANSMEM): New. + * c-cppbuiltin.c (c_cpp_builtins): Define __cpp_transactional_memory. + * c-pretty-print.c (pp_c_attributes_display): Don't print + transaction_safe in C++. + 2015-10-02 Marek Polacek * c.opt (Wduplicated-cond): Don't enable by -Wall anymore. diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c index f38378d..4b64a44 100644 --- a/gcc/c-family/c-common.c +++ b/gcc/c-family/c-common.c @@ -594,6 +594,12 @@ const struct c_common_resword c_common_reswords[] = { "wchar_t", RID_WCHAR, D_CXXONLY }, { "while", RID_WHILE, 0 }, + /* C++ transactional memory. */ + { "synchronized", RID_SYNCHRONIZED, D_CXX_OBJC | D_TRANSMEM }, + { "atomic_noexcept", RID_ATOMIC_NOEXCEPT, D_CXXONLY | D_TRANSMEM }, + { "atomic_cancel", RID_ATOMIC_CANCEL, D_CXXONLY | D_TRANSMEM }, + { "atomic_commit", RID_TRANSACTION_ATOMIC, D_CXXONLY | D_TRANSMEM }, + /* Concepts-related keywords */ { "concept", RID_CONCEPT, D_CXX_CONCEPTS_FLAGS | D_CXXWARN }, { "requires", RID_REQUIRES, D_CXX_CONCEPTS_FLAGS | D_CXXWARN }, @@ -609,7 +615,6 @@ const struct c_common_resword c_common_reswords[] = { "protocol", RID_AT_PROTOCOL, D_OBJC }, { "selector", RID_AT_SELECTOR, D_OBJC }, { "finally", RID_AT_FINALLY, D_OBJC }, - { "synchronized", RID_AT_SYNCHRONIZED, D_OBJC }, { "optional", RID_AT_OPTIONAL, D_OBJC }, { "required", RID_AT_REQUIRED, D_OBJC }, { "property", RID_AT_PROPERTY, D_OBJC }, @@ -728,8 +733,10 @@ const struct attribute_spec c_common_attribute_table[] = { "transaction_callable", 0, 0, false, true, false, handle_tm_attribute, false }, { "transaction_unsafe", 0, 0, false, true, false, - handle_tm_attribute, false }, + handle_tm_attribute, true }, { "transaction_safe", 0, 0, false, true, false, + handle_tm_attribute, true }, + { "transaction_safe_dynamic", 0, 0, true, false, false, handle_tm_attribute, false }, { "transaction_may_cancel_outer", 0, 0, false, true, false, handle_tm_attribute, false }, @@ -9136,6 +9143,23 @@ handle_tm_attribute (tree *node, tree name, tree args, } break; + case FUNCTION_DECL: + { + /* transaction_safe_dynamic goes on the FUNCTION_DECL, but we also + want to set transaction_safe on the type. */ + gcc_assert (is_attribute_p ("transaction_safe_dynamic", name)); + if (!TYPE_P (DECL_CONTEXT (*node))) + error_at (DECL_SOURCE_LOCATION (*node), + "% may only be specified for " + "a virtual function"); + *no_add_attrs = false; + decl_attributes (&TREE_TYPE (*node), + build_tree_list (get_identifier ("transaction_safe"), + NULL_TREE), + 0); + break; + } + case POINTER_TYPE: { enum tree_code subcode = TREE_CODE (TREE_TYPE (*node)); diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h index ff4530f..d5fb4998 100644 --- a/gcc/c-family/c-common.h +++ b/gcc/c-family/c-common.h @@ -154,6 +154,9 @@ enum rid /* C++ concepts */ RID_CONCEPT, RID_REQUIRES, + /* C++ transactional memory. */ + RID_ATOMIC_NOEXCEPT, RID_ATOMIC_CANCEL, RID_SYNCHRONIZED, + /* Cilk Plus keywords. */ RID_CILK_SPAWN, RID_CILK_SYNC, RID_CILK_FOR, @@ -246,7 +249,7 @@ enum rid is found elsewhere, it follows the rules of the C/C++ language. */ #define OBJC_IS_CXX_KEYWORD(rid) \ - (rid == RID_CLASS \ + (rid == RID_CLASS || rid == RID_SYNCHRONIZED \ || rid == RID_PUBLIC || rid == RID_PROTECTED || rid == RID_PRIVATE \ || rid == RID_TRY || rid == RID_THROW || rid == RID_CATCH) @@ -391,6 +394,7 @@ extern machine_mode c_default_pointer_mode; #define D_CXX_OBJC 0x100 /* In Objective C, and C++, but not C. */ #define D_CXXWARN 0x200 /* In C warn with -Wcxx-compat. */ #define D_CXX_CONCEPTS 0x400 /* In C++, only with concepts. */ +#define D_TRANSMEM 0X800 /* C++ transactional memory TS. */ #define D_CXX_CONCEPTS_FLAGS D_CXXONLY | D_CXX_CONCEPTS diff --git a/gcc/c-family/c-cppbuiltin.c b/gcc/c-family/c-cppbuiltin.c index b222a9f..e109e82 100644 --- a/gcc/c-family/c-cppbuiltin.c +++ b/gcc/c-family/c-cppbuiltin.c @@ -877,6 +877,10 @@ c_cpp_builtins (cpp_reader *pfile) /* Use a value smaller than the 201507 specified in the TS, since we don't yet support extended auto. */ cpp_define (pfile, "__cpp_concepts=201500"); + if (flag_tm) + /* Use a value smaller than the 201505 specified in + the TS, since we don't yet support atomic_cancel. */ + cpp_define (pfile, "__cpp_transactional_memory=210500"); if (flag_sized_deallocation) cpp_define (pfile, "__cpp_sized_deallocation=201309"); } diff --git a/gcc/c-family/c-pretty-print.c b/gcc/c-family/c-pretty-print.c index e2809cf..c61c41f 100644 --- a/gcc/c-family/c-pretty-print.c +++ b/gcc/c-family/c-pretty-print.c @@ -802,6 +802,10 @@ pp_c_attributes_display (c_pretty_printer *pp, tree a) as = lookup_attribute_spec (TREE_PURPOSE (a)); if (!as || as->affects_type_identity == false) continue; + if (c_dialect_cxx () + && !strcmp ("transaction_safe", as->name)) + /* In C++ transaction_safe is printed at the end of the declarator. */ + continue; if (is_first) { pp_c_ws_string (pp, "__attribute__"); diff --git a/gcc/c/ChangeLog b/gcc/c/ChangeLog index c6eab4e..e0f169d 100644 --- a/gcc/c/ChangeLog +++ b/gcc/c/ChangeLog @@ -1,3 +1,9 @@ +2015-10-04 Jason Merrill + + * c-parser.c (c_lex_one_token): Handle @synchronized. + * c-decl.c (match_builtin_function_types): A declaration of a built-in + can change whether the function is transaction_safe. + 2015-10-02 Marek Polacek PR c/67730 diff --git a/gcc/c/c-decl.c b/gcc/c/c-decl.c index a110226..ce8406a 100644 --- a/gcc/c/c-decl.c +++ b/gcc/c/c-decl.c @@ -1659,7 +1659,19 @@ match_builtin_function_types (tree newtype, tree oldtype) } trytype = build_function_type (newrettype, tryargs); - return build_type_attribute_variant (trytype, TYPE_ATTRIBUTES (oldtype)); + + /* Allow declaration to change transaction_safe attribute. */ + tree oldattrs = TYPE_ATTRIBUTES (oldtype); + tree oldtsafe = lookup_attribute ("transaction_safe", oldattrs); + tree newattrs = TYPE_ATTRIBUTES (newtype); + tree newtsafe = lookup_attribute ("transaction_safe", newattrs); + if (oldtsafe && !newtsafe) + oldattrs = remove_attribute ("transaction_safe", oldattrs); + else if (newtsafe && !oldtsafe) + oldattrs = tree_cons (get_identifier ("transaction_safe"), + NULL_TREE, oldattrs); + + return build_type_attribute_variant (trytype, oldattrs); } /* Subroutine of diagnose_mismatched_decls. Check for function type diff --git a/gcc/c/c-parser.c b/gcc/c/c-parser.c index 00fa238..0df7d7b 100644 --- a/gcc/c/c-parser.c +++ b/gcc/c/c-parser.c @@ -390,6 +390,7 @@ c_lex_one_token (c_parser *parser, c_token *token) case RID_THROW: token->keyword = RID_AT_THROW; break; case RID_TRY: token->keyword = RID_AT_TRY; break; case RID_CATCH: token->keyword = RID_AT_CATCH; break; + case RID_SYNCHRONIZED: token->keyword = RID_AT_SYNCHRONIZED; break; default: token->keyword = C_RID_CODE (token->value); } break; diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index e6e2e0c..bc169b8 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,47 @@ +2015-10-04 Jason Merrill + + Implement N4514, C++ Extensions for Transactional Memory. + * cp-tree.h (struct cp_declarator): Add tx_qualifier field. + (BCS_NORMAL, BCS_TRANSACTION): New enumerators. + * lex.c (init_reswords): Limit TM kewords to -fgnu-tm. + * parser.c (cp_lexer_get_preprocessor_token): Fix @synchronized. + (make_call_declarator): Take tx_qualifier. + (cp_parser_tx_qualifier_opt): New. + (cp_parser_lambda_declarator_opt): Use it. + (cp_parser_direct_declarator): Likewise. + (cp_parser_statement): Handle atomic_noexcept, atomic_cancel. + (cp_parser_compound_statement): Change in_try parameter to bcs_flags. + (cp_parser_std_attribute): Map optimize_for_synchronized to + transaction_callable. + (cp_parser_transaction): Take the token. Handle atomic_noexcept. + * lambda.c (maybe_add_lambda_conv_op): Handle transaction-safety. + * call.c (enum conversion_kind): Add ck_tsafe. + (standard_conversion): Handle transaction-safety conversion. + (convert_like_real, resolve_address_of_overloaded_function): Likewise. + (check_methods): Diagnose transaction_safe_dynamic on non-virtual + function. + (look_for_tm_attr_overrides): Don't inherit transaction_safe_dynamic. + * cvt.c (tx_safe_fn_type_p, tx_unsafe_fn_variant) + (can_convert_tx_safety): New. + * typeck.c (composite_pointer_type): Handle transaction-safety. + * name-lookup.h (enum scope_kind): Add sk_transaction. + * name-lookup.c (begin_scope): Handle it. + * semantics.c (begin_compound_stmt): Pass it. + * decl.c (check_previous_goto_1): Check it. + (struct named_label_entry): Add in_transaction_scope. + (poplevel_named_label_1): Set it. + (check_goto): Check it. + (duplicate_decls): A specialization can be transaction_safe + independently of its template. + (grokdeclarator): Handle tx-qualifier. + * rtti.c (ptr_initializer): Handle transaction-safe. + * search.c (check_final_overrider): Check transaction_safe_dynamic. + Don't check transaction_safe. + * mangle.c (write_function_type): Mangle transaction_safe here. + (write_CV_qualifiers_for_type): Not here. + (write_type): Preserve transaction_safe when stripping attributes. + * error.c (dump_type_suffix): Print transaction_safe. + 2015-10-02 Marek Polacek PR c/64249 diff --git a/gcc/cp/call.c b/gcc/cp/call.c index 367d42b..050d045 100644 --- a/gcc/cp/call.c +++ b/gcc/cp/call.c @@ -51,6 +51,7 @@ along with GCC; see the file COPYING3. If not see enum conversion_kind { ck_identity, ck_lvalue, + ck_tsafe, ck_qual, ck_std, ck_ptr, @@ -1265,6 +1266,17 @@ standard_conversion (tree to, tree from, tree expr, bool c_cast_p, conv = build_conv (ck_ptr, from, conv); conv->base_p = true; } + else if (tx_safe_fn_type_p (TREE_TYPE (from))) + { + /* A prvalue of type "pointer to transaction_safe function" can be + converted to a prvalue of type "pointer to function". */ + tree unsafe = tx_unsafe_fn_variant (TREE_TYPE (from)); + if (same_type_p (unsafe, TREE_TYPE (to))) + { + from = build_pointer_type (unsafe); + conv = build_conv (ck_tsafe, from, conv); + } + } if (tcode == POINTER_TYPE) { @@ -6638,6 +6650,11 @@ convert_like_real (conversion *convs, tree expr, tree fn, int argnum, case ck_lvalue: return decay_conversion (expr, complain); + case ck_tsafe: + /* ??? Should the address of a transaction-safe pointer point to the TM + clone, and this conversion look up the primary function? */ + return build_nop (totype, expr); + case ck_qual: /* Warn about deprecated conversion if appropriate. */ string_conv_p (totype, expr, 1); diff --git a/gcc/cp/class.c b/gcc/cp/class.c index 9611dec..ef537be 100644 --- a/gcc/cp/class.c +++ b/gcc/cp/class.c @@ -4570,6 +4570,11 @@ check_methods (tree t) grok_special_member_properties. */ if (DECL_DESTRUCTOR_P (x) && user_provided_p (x)) TYPE_HAS_NONTRIVIAL_DESTRUCTOR (t) = 1; + if (!DECL_VIRTUAL_P (x) + && lookup_attribute ("transaction_safe_dynamic", DECL_ATTRIBUTES (x))) + error_at (DECL_SOURCE_LOCATION (x), + "% may only be specified for " + "a virtual function"); } } @@ -4932,8 +4937,14 @@ look_for_tm_attr_overrides (tree type, tree fndecl) o = look_for_overrides_here (basetype, fndecl); if (o) - found |= tm_attr_to_mask (find_tm_attribute - (TYPE_ATTRIBUTES (TREE_TYPE (o)))); + { + if (lookup_attribute ("transaction_safe_dynamic", + DECL_ATTRIBUTES (o))) + /* transaction_safe_dynamic is not inherited. */; + else + found |= tm_attr_to_mask (find_tm_attribute + (TYPE_ATTRIBUTES (TREE_TYPE (o)))); + } else found |= look_for_tm_attr_overrides (basetype, fndecl); } @@ -7608,7 +7619,9 @@ resolve_address_of_overloaded_function (tree target_type, continue; /* See if there's a match. */ - if (same_type_p (target_fn_type, static_fn_type (fn))) + tree fntype = static_fn_type (fn); + if (same_type_p (target_fn_type, fntype) + || can_convert_tx_safety (target_fn_type, fntype)) matches = tree_cons (fn, NULL_TREE, matches); } } @@ -7686,7 +7699,9 @@ resolve_address_of_overloaded_function (tree target_type, } /* See if there's a match. */ - if (same_type_p (target_fn_type, static_fn_type (instantiation))) + tree fntype = static_fn_type (instantiation); + if (same_type_p (target_fn_type, fntype) + || can_convert_tx_safety (target_fn_type, fntype)) matches = tree_cons (instantiation, fn, matches); } diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 80d6c4e..5acb065 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -5317,6 +5317,8 @@ struct cp_declarator { cp_virt_specifiers virt_specifiers; /* The ref-qualifier for the function. */ cp_ref_qualifier ref_qualifier; + /* The transaction-safety qualifier for the function. */ + tree tx_qualifier; /* The exception-specification for the function. */ tree exception_specification; /* The late-specified return type, if any. */ @@ -5604,6 +5606,9 @@ extern tree convert_force (tree, tree, int, extern tree build_expr_type_conversion (int, tree, bool); extern tree type_promotes_to (tree); extern tree perform_qualification_conversions (tree, tree); +extern bool tx_safe_fn_type_p (tree); +extern tree tx_unsafe_fn_variant (tree); +extern bool can_convert_tx_safety (tree, tree); /* in name-lookup.c */ extern tree pushdecl (tree); @@ -6218,9 +6223,11 @@ extern void finish_cleanup (tree, tree); extern bool is_this_parameter (tree); enum { + BCS_NORMAL = 0, BCS_NO_SCOPE = 1, BCS_TRY_BLOCK = 2, - BCS_FN_BODY = 4 + BCS_FN_BODY = 4, + BCS_TRANSACTION = 8 }; extern tree begin_compound_stmt (unsigned int); diff --git a/gcc/cp/cvt.c b/gcc/cp/cvt.c index 6d4bd9a..b615880 100644 --- a/gcc/cp/cvt.c +++ b/gcc/cp/cvt.c @@ -1790,3 +1790,36 @@ perform_qualification_conversions (tree type, tree expr) else return error_mark_node; } + +/* True iff T is a transaction-safe function type. */ + +bool +tx_safe_fn_type_p (tree t) +{ + if (TREE_CODE (t) != FUNCTION_TYPE + && TREE_CODE (t) != METHOD_TYPE) + return false; + return !!lookup_attribute ("transaction_safe", TYPE_ATTRIBUTES (t)); +} + +/* Return the transaction-unsafe variant of transaction-safe function type + T. */ + +tree +tx_unsafe_fn_variant (tree t) +{ + gcc_assert (tx_safe_fn_type_p (t)); + tree attrs = remove_attribute ("transaction_safe", + TYPE_ATTRIBUTES (t)); + return cp_build_type_attribute_variant (t, attrs); +} + +/* Return true iff FROM can convert to TO by a transaction-safety + conversion. */ + +bool +can_convert_tx_safety (tree to, tree from) +{ + return (flag_tm && tx_safe_fn_type_p (from) + && same_type_p (to, tx_unsafe_fn_variant (from))); +} diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c index 2be7d2f..eff5281 100644 --- a/gcc/cp/decl.c +++ b/gcc/cp/decl.c @@ -230,6 +230,7 @@ struct GTY((for_user)) named_label_entry { bool in_try_scope; bool in_catch_scope; bool in_omp_scope; + bool in_transaction_scope; }; #define named_labels cp_function_chain->x_named_labels @@ -498,6 +499,9 @@ poplevel_named_label_1 (named_label_entry **slot, cp_binding_level *bl) case sk_omp: ent->in_omp_scope = true; break; + case sk_transaction: + ent->in_transaction_scope = true; + break; default: break; } @@ -2049,6 +2053,17 @@ duplicate_decls (tree newdecl, tree olddecl, bool newdecl_is_friend) } } + /* An explicit specialization of a function template or of a member + function of a class template can be declared transaction_safe + independently of whether the corresponding template entity is declared + transaction_safe. */ + if (flag_tm && TREE_CODE (newdecl) == FUNCTION_DECL + && DECL_TEMPLATE_INSTANTIATION (olddecl) + && DECL_TEMPLATE_SPECIALIZATION (newdecl) + && tx_safe_fn_type_p (newtype) + && !tx_safe_fn_type_p (TREE_TYPE (newdecl))) + newtype = tx_unsafe_fn_variant (newtype); + TREE_TYPE (newdecl) = TREE_TYPE (olddecl) = newtype; if (TREE_CODE (newdecl) == FUNCTION_DECL) @@ -2975,7 +2990,7 @@ check_previous_goto_1 (tree decl, cp_binding_level* level, tree names, { cp_binding_level *b; bool identified = false, complained = false; - bool saw_eh = false, saw_omp = false; + bool saw_eh = false, saw_omp = false, saw_tm = false; if (exited_omp) { @@ -3043,6 +3058,18 @@ check_previous_goto_1 (tree decl, cp_binding_level* level, tree names, inform (input_location, " enters OpenMP structured block"); saw_omp = true; } + if (b->kind == sk_transaction && !saw_tm) + { + if (!identified) + { + complained = identify_goto (decl, locus); + identified = true; + } + if (complained) + inform (input_location, + " enters synchronized or atomic statement"); + saw_tm = true; + } } return !identified; @@ -3109,7 +3136,7 @@ check_goto (tree decl) return; } - if (ent->in_try_scope || ent->in_catch_scope + if (ent->in_try_scope || ent->in_catch_scope || ent->in_transaction_scope || ent->in_omp_scope || !vec_safe_is_empty (ent->bad_decls)) { complained = permerror (DECL_SOURCE_LOCATION (decl), @@ -3148,6 +3175,8 @@ check_goto (tree decl) inform (input_location, " enters try block"); else if (ent->in_catch_scope && !saw_catch) inform (input_location, " enters catch block"); + else if (ent->in_transaction_scope) + inform (input_location, " enters synchronized or atomic statement"); } if (ent->in_omp_scope) @@ -9976,6 +10005,8 @@ grokdeclarator (const cp_declarator *declarator, virt_specifiers = declarator->u.function.virt_specifiers; /* And ref-qualifier, too */ rqual = declarator->u.function.ref_qualifier; + /* And tx-qualifier. */ + tree tx_qual = declarator->u.function.tx_qualifier; /* Pick up the exception specifications. */ raises = declarator->u.function.exception_specification; /* If the exception-specification is ill-formed, let's pretend @@ -10153,13 +10184,24 @@ grokdeclarator (const cp_declarator *declarator, } type = build_function_type (type, arg_types); - if (declarator->std_attributes) + + tree attrs = declarator->std_attributes; + if (tx_qual) + { + tree att = build_tree_list (tx_qual, NULL_TREE); + /* transaction_safe applies to the type, but + transaction_safe_dynamic applies to the function. */ + if (is_attribute_p ("transaction_safe", tx_qual)) + attrs = chainon (attrs, att); + else + returned_attrs = chainon (returned_attrs, att); + } + if (attrs) /* [dcl.fct]/2: The optional attribute-specifier-seq appertains to the function type. */ - decl_attributes (&type, declarator->std_attributes, - 0); + decl_attributes (&type, attrs, 0); } break; diff --git a/gcc/cp/error.c b/gcc/cp/error.c index faf8744..17870b5 100644 --- a/gcc/cp/error.c +++ b/gcc/cp/error.c @@ -868,6 +868,8 @@ dump_type_suffix (cxx_pretty_printer *pp, tree t, int flags) TREE_CODE (t) == FUNCTION_TYPE && (flags & TFF_POINTER)); dump_ref_qualifier (pp, t, flags); + if (tx_safe_fn_type_p (t)) + pp_cxx_ws_string (pp, "transaction_safe"); dump_exception_spec (pp, TYPE_RAISES_EXCEPTIONS (t), flags); dump_type_suffix (pp, TREE_TYPE (t), flags); break; @@ -1570,6 +1572,12 @@ dump_function_decl (cxx_pretty_printer *pp, tree t, int flags) dump_ref_qualifier (pp, fntype, flags); } + if (tx_safe_fn_type_p (fntype)) + { + pp->padding = pp_before; + pp_cxx_ws_string (pp, "transaction_safe"); + } + if (flags & TFF_EXCEPTION_SPECIFICATION) { pp->padding = pp_before; diff --git a/gcc/cp/lambda.c b/gcc/cp/lambda.c index ea9dba0..ceab646 100644 --- a/gcc/cp/lambda.c +++ b/gcc/cp/lambda.c @@ -895,7 +895,8 @@ maybe_add_lambda_conv_op (tree type) vec *direct_argvec = 0; tree decltype_call = 0, call = 0; - tree fn_result = TREE_TYPE (TREE_TYPE (callop)); + tree optype = TREE_TYPE (callop); + tree fn_result = TREE_TYPE (optype); if (generic_lambda_p) { @@ -993,6 +994,8 @@ maybe_add_lambda_conv_op (tree type) CALL_FROM_THUNK_P (call) = 1; tree stattype = build_function_type (fn_result, FUNCTION_ARG_CHAIN (callop)); + stattype = (cp_build_type_attribute_variant + (stattype, TYPE_ATTRIBUTES (optype))); /* First build up the conversion op. */ diff --git a/gcc/cp/lex.c b/gcc/cp/lex.c index f81c17b..6acf47e 100644 --- a/gcc/cp/lex.c +++ b/gcc/cp/lex.c @@ -176,6 +176,8 @@ init_reswords (void) mask |= D_CXX11; if (!flag_concepts) mask |= D_CXX_CONCEPTS; + if (!flag_tm) + mask |= D_TRANSMEM; if (flag_no_asm) mask |= D_ASM | D_EXT; if (flag_no_gnu_keywords) diff --git a/gcc/cp/mangle.c b/gcc/cp/mangle.c index 248d280..faeea14 100644 --- a/gcc/cp/mangle.c +++ b/gcc/cp/mangle.c @@ -1911,7 +1911,13 @@ write_type (tree type) { tree t = TYPE_MAIN_VARIANT (type); if (TYPE_ATTRIBUTES (t) && !OVERLOAD_TYPE_P (t)) - t = cp_build_type_attribute_variant (t, NULL_TREE); + { + tree attrs = NULL_TREE; + if (tx_safe_fn_type_p (type)) + attrs = tree_cons (get_identifier ("transaction_safe"), + NULL_TREE, attrs); + t = cp_build_type_attribute_variant (t, attrs); + } gcc_assert (t != type); if (TREE_CODE (t) == FUNCTION_TYPE || TREE_CODE (t) == METHOD_TYPE) @@ -2209,6 +2215,7 @@ write_CV_qualifiers_for_type (const tree type) tree name = get_attribute_name (a); const attribute_spec *as = lookup_attribute_spec (name); if (as && as->affects_type_identity + && !is_attribute_p ("transaction_safe", name) && !is_attribute_p ("abi_tag", name)) vec.safe_push (a); } @@ -2470,6 +2477,9 @@ write_function_type (const tree type) write_CV_qualifiers_for_type (this_type); } + if (tx_safe_fn_type_p (type)) + write_string ("Dx"); + write_char ('F'); /* We don't track whether or not a type is `extern "C"'. Note that you can have an `extern "C"' function that does not have diff --git a/gcc/cp/name-lookup.c b/gcc/cp/name-lookup.c index bd052a4..b503012 100644 --- a/gcc/cp/name-lookup.c +++ b/gcc/cp/name-lookup.c @@ -1591,6 +1591,7 @@ begin_scope (scope_kind kind, tree entity) case sk_class: case sk_scoped_enum: case sk_function_parms: + case sk_transaction: case sk_omp: scope->keep = keep_next_level_flag; break; diff --git a/gcc/cp/name-lookup.h b/gcc/cp/name-lookup.h index 82b5e53..d430edb 100644 --- a/gcc/cp/name-lookup.h +++ b/gcc/cp/name-lookup.h @@ -121,6 +121,7 @@ enum scope_kind { specialization. Since, by definition, an explicit specialization is introduced by "template <>", this scope is always empty. */ + sk_transaction, /* A synchronized or atomic statement. */ sk_omp /* An OpenMP structured block. */ }; diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c index 46aff88..ffed595 100644 --- a/gcc/cp/parser.c +++ b/gcc/cp/parser.c @@ -829,6 +829,7 @@ cp_lexer_get_preprocessor_token (cp_lexer *lexer, cp_token *token) case RID_THROW: token->keyword = RID_AT_THROW; break; case RID_TRY: token->keyword = RID_AT_TRY; break; case RID_CATCH: token->keyword = RID_AT_CATCH; break; + case RID_SYNCHRONIZED: token->keyword = RID_AT_SYNCHRONIZED; break; default: token->keyword = C_RID_CODE (token->u.value); } } @@ -1343,7 +1344,7 @@ clear_decl_specs (cp_decl_specifier_seq *decl_specs) VAR_DECLs or FUNCTION_DECLs) should do that directly. */ static cp_declarator *make_call_declarator - (cp_declarator *, tree, cp_cv_quals, cp_virt_specifiers, cp_ref_qualifier, tree, tree, tree); + (cp_declarator *, tree, cp_cv_quals, cp_virt_specifiers, cp_ref_qualifier, tree, tree, tree, tree); static cp_declarator *make_array_declarator (cp_declarator *, tree); static cp_declarator *make_pointer_declarator @@ -1521,6 +1522,7 @@ make_call_declarator (cp_declarator *target, cp_cv_quals cv_qualifiers, cp_virt_specifiers virt_specifiers, cp_ref_qualifier ref_qualifier, + tree tx_qualifier, tree exception_specification, tree late_return_type, tree requires_clause) @@ -1533,6 +1535,7 @@ make_call_declarator (cp_declarator *target, declarator->u.function.qualifiers = cv_qualifiers; declarator->u.function.virt_specifiers = virt_specifiers; declarator->u.function.ref_qualifier = ref_qualifier; + declarator->u.function.tx_qualifier = tx_qualifier; declarator->u.function.exception_specification = exception_specification; declarator->u.function.late_return_type = late_return_type; declarator->u.function.requires_clause = requires_clause; @@ -2029,7 +2032,7 @@ static void cp_parser_label_for_labeled_statement static tree cp_parser_expression_statement (cp_parser *, tree); static tree cp_parser_compound_statement - (cp_parser *, tree, bool, bool); + (cp_parser *, tree, int, bool); static void cp_parser_statement_seq_opt (cp_parser *, tree); static tree cp_parser_selection_statement @@ -2139,6 +2142,8 @@ static cp_virt_specifiers cp_parser_virt_specifier_seq_opt (cp_parser *); static cp_ref_qualifier cp_parser_ref_qualifier_opt (cp_parser *); +static tree cp_parser_tx_qualifier_opt + (cp_parser *); static tree cp_parser_late_return_type_opt (cp_parser *, cp_declarator *, tree &, cp_cv_quals); static tree cp_parser_declarator_id @@ -2346,7 +2351,7 @@ static tree cp_parser_nested_requirement /* Transactional Memory Extensions */ static tree cp_parser_transaction - (cp_parser *, enum rid); + (cp_parser *, cp_token *); static tree cp_parser_transaction_expression (cp_parser *, enum rid); static bool cp_parser_function_transaction @@ -4262,7 +4267,7 @@ cp_parser_statement_expr (cp_parser *parser) /* Start the statement-expression. */ tree expr = begin_stmt_expr (); /* Parse the compound-statement. */ - cp_parser_compound_statement (parser, expr, false, false); + cp_parser_compound_statement (parser, expr, BCS_NORMAL, false); /* Finish up. */ expr = finish_stmt_expr (expr, false); /* Consume the ')'. */ @@ -9630,6 +9635,7 @@ cp_parser_lambda_declarator_opt (cp_parser* parser, tree lambda_expr) tree attributes = NULL_TREE; tree exception_spec = NULL_TREE; tree template_param_list = NULL_TREE; + tree tx_qual = NULL_TREE; /* The template-parameter-list is optional, but must begin with an opening angle if present. */ @@ -9680,6 +9686,8 @@ cp_parser_lambda_declarator_opt (cp_parser* parser, tree lambda_expr) LAMBDA_EXPR_MUTABLE_P (lambda_expr) = 1; } + tx_qual = cp_parser_tx_qualifier_opt (parser); + /* Parse optional exception specification. */ exception_spec = cp_parser_exception_specification_opt (parser); @@ -9727,6 +9735,7 @@ cp_parser_lambda_declarator_opt (cp_parser* parser, tree lambda_expr) declarator = make_call_declarator (declarator, param_list, quals, VIRT_SPEC_UNSPECIFIED, REF_QUAL_NONE, + tx_qual, exception_spec, /*late_return_type=*/NULL_TREE, /*requires_clause*/NULL_TREE); @@ -10043,7 +10052,10 @@ cp_parser_statement (cp_parser* parser, tree in_statement_expr, case RID_TRANSACTION_ATOMIC: case RID_TRANSACTION_RELAXED: - statement = cp_parser_transaction (parser, keyword); + case RID_SYNCHRONIZED: + case RID_ATOMIC_NOEXCEPT: + case RID_ATOMIC_CANCEL: + statement = cp_parser_transaction (parser, token); break; case RID_TRANSACTION_CANCEL: statement = cp_parser_transaction_cancel (parser); @@ -10072,7 +10084,7 @@ cp_parser_statement (cp_parser* parser, tree in_statement_expr, } /* Anything that starts with a `{' must be a compound-statement. */ else if (token->type == CPP_OPEN_BRACE) - statement = cp_parser_compound_statement (parser, NULL, false, false); + statement = cp_parser_compound_statement (parser, NULL, BCS_NORMAL, false); /* CPP_PRAGMA is a #pragma inside a function body, which constitutes a statement all its own. */ else if (token->type == CPP_PRAGMA) @@ -10327,7 +10339,7 @@ cp_parser_expression_statement (cp_parser* parser, tree in_statement_expr) static tree cp_parser_compound_statement (cp_parser *parser, tree in_statement_expr, - bool in_try, bool function_body) + int bcs_flags, bool function_body) { tree compound_stmt; @@ -10339,7 +10351,7 @@ cp_parser_compound_statement (cp_parser *parser, tree in_statement_expr, pedwarn (input_location, OPT_Wpedantic, "compound-statement in constexpr function"); /* Begin the compound-statement. */ - compound_stmt = begin_compound_stmt (in_try ? BCS_TRY_BLOCK : 0); + compound_stmt = begin_compound_stmt (bcs_flags); /* If the next keyword is `__label__' we have a label declaration. */ while (cp_lexer_next_token_is_keyword (parser->lexer, RID_LABEL)) cp_parser_label_declaration (parser); @@ -11500,7 +11512,7 @@ cp_parser_implicitly_scoped_statement (cp_parser* parser, bool *if_p, } /* if a compound is opened, we simply parse the statement directly. */ else if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE)) - statement = cp_parser_compound_statement (parser, NULL, false, false); + statement = cp_parser_compound_statement (parser, NULL, BCS_NORMAL, false); /* If the token is not a `{', then we must take special action. */ else { @@ -18451,6 +18463,8 @@ cp_parser_direct_declarator (cp_parser* parser, cv_quals = cp_parser_cv_qualifier_seq_opt (parser); /* Parse the ref-qualifier. */ ref_qual = cp_parser_ref_qualifier_opt (parser); + /* Parse the tx-qualifier. */ + tree tx_qual = cp_parser_tx_qualifier_opt (parser); /* And the exception-specification. */ exception_specification = cp_parser_exception_specification_opt (parser); @@ -18489,6 +18503,7 @@ cp_parser_direct_declarator (cp_parser* parser, cv_quals, virt_specifiers, ref_qual, + tx_qual, exception_specification, late_return, requires_clause); @@ -19101,6 +19116,41 @@ cp_parser_ref_qualifier_opt (cp_parser* parser) return ref_qual; } +/* Parse an optional tx-qualifier. + + tx-qualifier: + transaction_safe + transaction_safe_dynamic */ + +static tree +cp_parser_tx_qualifier_opt (cp_parser *parser) +{ + cp_token *token = cp_lexer_peek_token (parser->lexer); + if (token->type == CPP_NAME) + { + tree name = token->u.value; + const char *p = IDENTIFIER_POINTER (name); + const int len = strlen ("transaction_safe"); + if (!strncmp (p, "transaction_safe", len)) + { + p += len; + if (*p == '\0' + || !strcmp (p, "_dynamic")) + { + cp_lexer_consume_token (parser->lexer); + if (!flag_tm) + { + error ("%E requires %<-fgnu-tm%>", name); + return NULL_TREE; + } + else + return name; + } + } + } + return NULL_TREE; +} + /* Parse an (optional) virt-specifier-seq. virt-specifier-seq: @@ -20109,7 +20159,9 @@ cp_parser_default_argument (cp_parser *parser, bool template_parm_p) static void cp_parser_function_body (cp_parser *parser, bool in_function_try_block) { - cp_parser_compound_statement (parser, NULL, in_function_try_block, true); + cp_parser_compound_statement (parser, NULL, (in_function_try_block + ? BCS_TRY_BLOCK : BCS_NORMAL), + true); } /* Parse a ctor-initializer-opt followed by a function-body. Return @@ -22598,7 +22650,7 @@ cp_parser_try_block (cp_parser* parser) error ("% in % function"); try_block = begin_try_block (); - cp_parser_compound_statement (parser, NULL, true, false); + cp_parser_compound_statement (parser, NULL, BCS_TRY_BLOCK, false); finish_try_block (try_block); cp_parser_handler_seq (parser); finish_handler_sequence (try_block); @@ -22675,7 +22727,7 @@ cp_parser_handler (cp_parser* parser) declaration = cp_parser_exception_declaration (parser); finish_handler_parms (declaration, handler); cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN); - cp_parser_compound_statement (parser, NULL, false, false); + cp_parser_compound_statement (parser, NULL, BCS_NORMAL, false); finish_handler (handler); } @@ -23354,6 +23406,14 @@ cp_parser_std_attribute (cp_parser *parser) " use %"); TREE_PURPOSE (TREE_PURPOSE (attribute)) = get_identifier ("gnu"); } + /* Transactional Memory TS optimize_for_synchronized attribute is + equivalent to GNU transaction_callable. */ + else if (is_attribute_p ("optimize_for_synchronized", attr_id)) + TREE_PURPOSE (attribute) + = get_identifier ("transaction_callable"); + /* Transactional Memory attributes are GNU attributes. */ + else if (tm_attr_to_mask (attr_id)) + TREE_PURPOSE (attribute) = attr_id; } /* Now parse the optional argument clause of the attribute. */ @@ -28391,7 +28451,7 @@ cp_parser_objc_try_catch_finally_statement (cp_parser *parser) /* NB: The @try block needs to be wrapped in its own STATEMENT_LIST node, lest it get absorbed into the surrounding block. */ stmt = push_stmt_list (); - cp_parser_compound_statement (parser, NULL, false, false); + cp_parser_compound_statement (parser, NULL, BCS_NORMAL, false); objc_begin_try_stmt (location, pop_stmt_list (stmt)); while (cp_lexer_next_token_is_keyword (parser->lexer, RID_AT_CATCH)) @@ -28447,7 +28507,7 @@ cp_parser_objc_try_catch_finally_statement (cp_parser *parser) forget about the closing parenthesis and keep going. */ } objc_begin_catch_clause (parameter_declaration); - cp_parser_compound_statement (parser, NULL, false, false); + cp_parser_compound_statement (parser, NULL, BCS_NORMAL, false); objc_finish_catch_clause (); } if (cp_lexer_next_token_is_keyword (parser->lexer, RID_AT_FINALLY)) @@ -28457,7 +28517,7 @@ cp_parser_objc_try_catch_finally_statement (cp_parser *parser) /* NB: The @finally block needs to be wrapped in its own STATEMENT_LIST node, lest it get absorbed into the surrounding block. */ stmt = push_stmt_list (); - cp_parser_compound_statement (parser, NULL, false, false); + cp_parser_compound_statement (parser, NULL, BCS_NORMAL, false); objc_build_finally_clause (location, pop_stmt_list (stmt)); } @@ -28488,7 +28548,7 @@ cp_parser_objc_synchronized_statement (cp_parser *parser) /* NB: The @synchronized block needs to be wrapped in its own STATEMENT_LIST node, lest it get absorbed into the surrounding block. */ stmt = push_stmt_list (); - cp_parser_compound_statement (parser, NULL, false, false); + cp_parser_compound_statement (parser, NULL, BCS_NORMAL, false); return objc_build_synchronized (location, lock, pop_stmt_list (stmt)); } @@ -33964,8 +34024,8 @@ cp_parser_omp_construct (cp_parser *parser, cp_token *pragma_tok) attribute [ [ identifier ] ] - ??? Simplify this when C++0x bracket attributes are - implemented properly. */ + We use this instead of cp_parser_attributes_opt for transactions to avoid + the pedwarn in C++98 mode. */ static tree cp_parser_txn_attribute_opt (cp_parser *parser) @@ -34012,21 +34072,17 @@ cp_parser_txn_attribute_opt (cp_parser *parser) */ static tree -cp_parser_transaction (cp_parser *parser, enum rid keyword) +cp_parser_transaction (cp_parser *parser, cp_token *token) { unsigned char old_in = parser->in_transaction; unsigned char this_in = 1, new_in; - cp_token *token; + enum rid keyword = token->keyword; tree stmt, attrs, noex; - gcc_assert (keyword == RID_TRANSACTION_ATOMIC - || keyword == RID_TRANSACTION_RELAXED); - token = cp_parser_require_keyword (parser, keyword, - (keyword == RID_TRANSACTION_ATOMIC ? RT_TRANSACTION_ATOMIC - : RT_TRANSACTION_RELAXED)); - gcc_assert (token != NULL); + cp_lexer_consume_token (parser->lexer); - if (keyword == RID_TRANSACTION_RELAXED) + if (keyword == RID_TRANSACTION_RELAXED + || keyword == RID_SYNCHRONIZED) this_in |= TM_STMT_ATTR_RELAXED; else { @@ -34036,7 +34092,16 @@ cp_parser_transaction (cp_parser *parser, enum rid keyword) } /* Parse a noexcept specification. */ - noex = cp_parser_noexcept_specification_opt (parser, true, NULL, true); + if (keyword == RID_ATOMIC_NOEXCEPT) + noex = boolean_true_node; + else if (keyword == RID_ATOMIC_CANCEL) + { + /* cancel-and-throw is unimplemented. */ + sorry ("atomic_cancel"); + noex = NULL_TREE; + } + else + noex = cp_parser_noexcept_specification_opt (parser, true, NULL, true); /* Keep track if we're in the lexical scope of an outer transaction. */ new_in = this_in | (old_in & TM_STMT_ATTR_OUTER); @@ -34044,7 +34109,7 @@ cp_parser_transaction (cp_parser *parser, enum rid keyword) stmt = begin_transaction_stmt (token->location, NULL, this_in); parser->in_transaction = new_in; - cp_parser_compound_statement (parser, NULL, false, false); + cp_parser_compound_statement (parser, NULL, BCS_TRANSACTION, false); parser->in_transaction = old_in; finish_transaction_stmt (stmt, NULL, this_in, noex); diff --git a/gcc/cp/rtti.c b/gcc/cp/rtti.c index e4b6e00..85be2b2 100644 --- a/gcc/cp/rtti.c +++ b/gcc/cp/rtti.c @@ -983,6 +983,11 @@ ptr_initializer (tinfo_s *ti, tree target) if (incomplete) flags |= 8; + if (tx_safe_fn_type_p (to)) + { + flags |= 0x20; + to = tx_unsafe_fn_variant (to); + } CONSTRUCTOR_APPEND_ELT (v, NULL_TREE, init); CONSTRUCTOR_APPEND_ELT (v, NULL_TREE, build_int_cst (NULL_TREE, flags)); CONSTRUCTOR_APPEND_ELT (v, NULL_TREE, diff --git a/gcc/cp/search.c b/gcc/cp/search.c index 42db122..508e66c 100644 --- a/gcc/cp/search.c +++ b/gcc/cp/search.c @@ -2014,8 +2014,11 @@ check_final_overrider (tree overrider, tree basefn) return 0; } - /* Check for conflicting type attributes. */ - if (!comp_type_attributes (over_type, base_type)) + /* Check for conflicting type attributes. But leave transaction_safe for + set_one_vmethod_tm_attributes. */ + if (!comp_type_attributes (over_type, base_type) + && !tx_safe_fn_type_p (base_type) + && !tx_safe_fn_type_p (over_type)) { error ("conflicting type attributes specified for %q+#D", overrider); error (" overriding %q+#D", basefn); @@ -2023,6 +2026,21 @@ check_final_overrider (tree overrider, tree basefn) return 0; } + /* A function declared transaction_safe_dynamic that overrides a function + declared transaction_safe (but not transaction_safe_dynamic) is + ill-formed. */ + if (tx_safe_fn_type_p (base_type) + && lookup_attribute ("transaction_safe_dynamic", + DECL_ATTRIBUTES (overrider)) + && !lookup_attribute ("transaction_safe_dynamic", + DECL_ATTRIBUTES (basefn))) + { + error_at (DECL_SOURCE_LOCATION (overrider), + "%qD declared %", overrider); + inform (DECL_SOURCE_LOCATION (basefn), + "overriding %qD declared %", basefn); + } + if (DECL_DELETED_FN (basefn) != DECL_DELETED_FN (overrider)) { if (DECL_DELETED_FN (overrider)) diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c index f5bb0c1..ea40398 100644 --- a/gcc/cp/semantics.c +++ b/gcc/cp/semantics.c @@ -1355,7 +1355,14 @@ begin_compound_stmt (unsigned int flags) keep_next_level (false); } else - r = do_pushlevel (flags & BCS_TRY_BLOCK ? sk_try : sk_block); + { + scope_kind sk = sk_block; + if (flags & BCS_TRY_BLOCK) + sk = sk_try; + else if (flags & BCS_TRANSACTION) + sk = sk_transaction; + r = do_pushlevel (sk); + } /* When processing a template, we need to remember where the braces were, so that we can set up identical scopes when instantiating the template diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c index 482e42c..9e6f949 100644 --- a/gcc/cp/typeck.c +++ b/gcc/cp/typeck.c @@ -715,6 +715,22 @@ composite_pointer_type (tree t1, tree t2, tree arg1, tree arg2, return error_mark_node; } } + else if (TYPE_PTR_P (t1) && TYPE_PTR_P (t2) + && FUNC_OR_METHOD_TYPE_P (TREE_TYPE (t1)) + && TREE_CODE (TREE_TYPE (t2)) == TREE_CODE (TREE_TYPE (t1))) + { + /* ...if T1 is "pointer to transaction_safe function" and T2 is "pointer + to function", where the function types are otherwise the same, T2, and + vice versa.... */ + tree f1 = TREE_TYPE (t1); + tree f2 = TREE_TYPE (t2); + bool safe1 = tx_safe_fn_type_p (f1); + bool safe2 = tx_safe_fn_type_p (f2); + if (safe1 && !safe2) + t1 = build_pointer_type (tx_unsafe_fn_variant (f1)); + else if (safe2 && !safe1) + t2 = build_pointer_type (tx_unsafe_fn_variant (f2)); + } return composite_pointer_type_r (t1, t2, operation, complain); } diff --git a/gcc/testsuite/g++.dg/tm/composite1.C b/gcc/testsuite/g++.dg/tm/composite1.C new file mode 100644 index 0000000..6e82317 --- /dev/null +++ b/gcc/testsuite/g++.dg/tm/composite1.C @@ -0,0 +1,14 @@ +// Test for composite pointer type. +// { dg-options -fgnu-tm } + +void f(bool b) +{ + void (*p)() transaction_safe = 0; + void (*g)() = 0; + + g = b ? p : g; // OK + p = b ? p : g; // { dg-error "" } + + p == g; + p != g; +} diff --git a/gcc/testsuite/g++.dg/tm/dynamic1.C b/gcc/testsuite/g++.dg/tm/dynamic1.C new file mode 100644 index 0000000..a6f4956 --- /dev/null +++ b/gcc/testsuite/g++.dg/tm/dynamic1.C @@ -0,0 +1,13 @@ +// Test that transaction_safe_dynamic can only be used on virtual functions. +// { dg-options "-fgnu-tm -std=c++14" } + +void f() transaction_safe_dynamic; // { dg-error "virtual" } +auto a = []() transaction_safe_dynamic {}; // { dg-error "virtual" } +struct A { + void f() transaction_safe_dynamic; // { dg-error "virtual" } + virtual void g(); +}; + +struct B: A { + void g() transaction_safe_dynamic; +}; diff --git a/gcc/testsuite/g++.dg/tm/dynamic2.C b/gcc/testsuite/g++.dg/tm/dynamic2.C new file mode 100644 index 0000000..3003b62 --- /dev/null +++ b/gcc/testsuite/g++.dg/tm/dynamic2.C @@ -0,0 +1,17 @@ +// { dg-options "-fgnu-tm -std=c++14 -O2" } + +void unsafe(); +struct A { + virtual void f() transaction_safe_dynamic; +}; +struct B:A { + void f() { unsafe(); } +}; + +void f() transaction_safe { + B b; + A& ar = b; + // This is undefined behavior, we want to give an error with + // devirtualization. + ar.f(); // { dg-error "unsafe" } +} diff --git a/gcc/testsuite/g++.dg/tm/eh1.C b/gcc/testsuite/g++.dg/tm/eh1.C new file mode 100644 index 0000000..1561211 --- /dev/null +++ b/gcc/testsuite/g++.dg/tm/eh1.C @@ -0,0 +1,10 @@ +// A handler can involve a transaction-safety conversion. +// { dg-do run } +// { dg-options "-fgnu-tm" } + +void g() transaction_safe {} +int main() +{ + try { throw g; } + catch (void (*p)()) { } +} diff --git a/gcc/testsuite/g++.dg/tm/eh2.C b/gcc/testsuite/g++.dg/tm/eh2.C new file mode 100644 index 0000000..307a639 --- /dev/null +++ b/gcc/testsuite/g++.dg/tm/eh2.C @@ -0,0 +1,14 @@ +// A handler cannot do the reverse of a transaction-safety conversion. +// { dg-do run } +// { dg-options "-fgnu-tm" } + +extern "C" void abort(); + +void g() {} + +int main() +{ + try { throw g; } + catch (void (*p)() transaction_safe) { abort(); } + catch (...) { } +} diff --git a/gcc/testsuite/g++.dg/tm/eh4.C b/gcc/testsuite/g++.dg/tm/eh4.C new file mode 100644 index 0000000..68275e9 --- /dev/null +++ b/gcc/testsuite/g++.dg/tm/eh4.C @@ -0,0 +1,21 @@ +// Test that throwing out of an atomic_commit block commits the transaction. + +// { dg-do run } +// { dg-options "-fgnu-tm" } + +int main() +{ + static int i; + bool caught = false; + try { + atomic_commit { + i = 12; + throw 42; + i = 24; + } + } catch (int x) { + caught = (x == 42); + } + if (!caught || i != 12) + __builtin_abort(); +} diff --git a/gcc/testsuite/g++.dg/tm/inherit1.C b/gcc/testsuite/g++.dg/tm/inherit1.C new file mode 100644 index 0000000..b8480a1 --- /dev/null +++ b/gcc/testsuite/g++.dg/tm/inherit1.C @@ -0,0 +1,11 @@ +// Testcase from TM TS +// { dg-options "-std=c++14 -fgnu-tm" } + +struct B { + virtual void f() transaction_safe; +}; + +struct D3 : B +{ + void f() transaction_safe_dynamic override; // { dg-error "" "B::f() is transaction_safe" } +}; diff --git a/gcc/testsuite/g++.dg/tm/inherit2.C b/gcc/testsuite/g++.dg/tm/inherit2.C new file mode 100644 index 0000000..3b696a9 --- /dev/null +++ b/gcc/testsuite/g++.dg/tm/inherit2.C @@ -0,0 +1,33 @@ +// Testcase from TM TS +// { dg-options "-std=c++14 -fgnu-tm" } + +#include + +struct B { + virtual void f() transaction_safe; + virtual ~B() transaction_safe_dynamic; +}; +// pre-existing code +struct D1 : B +{ + void f() override { } // ok + ~D1() override { } // ok +}; +struct D2 : B +{ + void f() override { std::cout << "D2::f" << std::endl; } // { dg-error "" "transaction-safe f has transaction-unsafe definition" } + ~D2() override { std::cout << "~D2" << std::endl; } // ok +}; +int main() +{ + D2 * d2 = new D2; + B * b2 = d2; + atomic_commit { + B b; // ok + D1 d1; // ok + B& b1 = d1; + D2 x; // { dg-error "" "destructor of D2 is not transaction-safe" } + b1.f(); // ok, calls D1::f() + delete b2; // undefined behavior: calls unsafe destructor of D2 + } +} diff --git a/gcc/testsuite/g++.dg/tm/jump1.C b/gcc/testsuite/g++.dg/tm/jump1.C new file mode 100644 index 0000000..003eed0 --- /dev/null +++ b/gcc/testsuite/g++.dg/tm/jump1.C @@ -0,0 +1,23 @@ +// A goto or switch statement shall not be used to transfer control into a +// synchronized or atomic block. +// { dg-options "-fgnu-tm" } + +void f() +{ + static int i; + synchronized { + ++i; + inside: // { dg-message "" } + ++i; + } + goto inside; // { dg-message "" } + + switch (i) + { + synchronized { + ++i; + case 42: // { dg-error "" } + ++i; + } + } +} diff --git a/gcc/testsuite/g++.dg/tm/keyword1.C b/gcc/testsuite/g++.dg/tm/keyword1.C new file mode 100644 index 0000000..3537e0f --- /dev/null +++ b/gcc/testsuite/g++.dg/tm/keyword1.C @@ -0,0 +1,9 @@ +// Test that these aren't keywords without -fgnu-tm. + +int main() +{ + synchronized { } // { dg-error "not declared" } + atomic_noexcept { } // { dg-error "not declared" } + atomic_cancel { } // { dg-error "not declared" } + atomic_commit { } // { dg-error "not declared" } +} diff --git a/gcc/testsuite/g++.dg/tm/lambda1.C b/gcc/testsuite/g++.dg/tm/lambda1.C new file mode 100644 index 0000000..d0cffbf --- /dev/null +++ b/gcc/testsuite/g++.dg/tm/lambda1.C @@ -0,0 +1,10 @@ +// Test for lambda conversion. +// { dg-options "-fgnu-tm -std=c++14" } + +void f(bool b) +{ + void (*p)() transaction_safe; + + p = []() transaction_safe {}; + p = []{}; // { dg-error "transaction_safe" } +} diff --git a/gcc/testsuite/g++.dg/tm/lambda2.C b/gcc/testsuite/g++.dg/tm/lambda2.C new file mode 100644 index 0000000..82e509e --- /dev/null +++ b/gcc/testsuite/g++.dg/tm/lambda2.C @@ -0,0 +1,9 @@ +// Test for lambda call. +// { dg-options "-fgnu-tm -std=c++14" } + +void unsafe (); +void f() transaction_safe +{ + []{}(); // OK, implicitly transaction-safe. + []{unsafe();}(); // { dg-error "unsafe" } +} diff --git a/gcc/testsuite/g++.dg/tm/macro1.C b/gcc/testsuite/g++.dg/tm/macro1.C new file mode 100644 index 0000000..dcf3888 --- /dev/null +++ b/gcc/testsuite/g++.dg/tm/macro1.C @@ -0,0 +1,5 @@ +// { dg-options -fgnu-tm } + +#ifndef __cpp_transactional_memory +#error __cpp_transactional_memory not defined +#endif diff --git a/gcc/testsuite/g++.dg/tm/mangle1.C b/gcc/testsuite/g++.dg/tm/mangle1.C new file mode 100644 index 0000000..f081f8e --- /dev/null +++ b/gcc/testsuite/g++.dg/tm/mangle1.C @@ -0,0 +1,18 @@ +// Test for transaction_safe mangling. +// { dg-options -fgnu-tm } + +// { dg-final { scan-assembler "_Z1fPDxFvvE" } } +void f(void (*)() transaction_safe) {} + +// { dg-final { scan-assembler "_Z1fPDxFvvEPFvvE" } } +void f(void (*)() transaction_safe, void (*)()) {} + +// { dg-final { scan-assembler "_Z1fPDxFvvES0_" } } +void f(void (*)() transaction_safe, void (*)() transaction_safe) {} + +// { dg-final { scan-assembler "_Z1f1AIKDxFvvEE" } } +template struct A { }; +void f(A) { } + +// { dg-final { scan-assembler "_Z1fM1AIiEKDxFvvE" } } +void f(void (A::*)() const transaction_safe) { } diff --git a/gcc/testsuite/g++.dg/tm/noexcept-7.C b/gcc/testsuite/g++.dg/tm/noexcept-7.C new file mode 100644 index 0000000..bfa675c --- /dev/null +++ b/gcc/testsuite/g++.dg/tm/noexcept-7.C @@ -0,0 +1,7 @@ +// FIXME the TS says atomic_noexcept calls abort, not terminate. +// { dg-options "-fgnu-tm" } + +void f() +{ + atomic_noexcept { throw; } // { dg-warning "terminate" } +} diff --git a/gcc/testsuite/g++.dg/tm/overload1.C b/gcc/testsuite/g++.dg/tm/overload1.C new file mode 100644 index 0000000..71ecab9 --- /dev/null +++ b/gcc/testsuite/g++.dg/tm/overload1.C @@ -0,0 +1,6 @@ +// Function declarations that differ only in the presence or absence of a +// tx-qualifier cannot be overloaded. +// { dg-options "-fgnu-tm" } + +void f(); // { dg-message "" } +void f() transaction_safe; // { dg-error "" } diff --git a/gcc/testsuite/g++.dg/tm/overload2.C b/gcc/testsuite/g++.dg/tm/overload2.C new file mode 100644 index 0000000..3510779 --- /dev/null +++ b/gcc/testsuite/g++.dg/tm/overload2.C @@ -0,0 +1,9 @@ +// 13.4p1: A function with type F is selected for the function type FT of the +// target type required in the context if F (after possibly applying the +// transaction-safety conversion (4.14 [conv.tx])) is identical to FT. +// { dg-options "-fgnu-tm" } + +void f() transaction_safe; +void f(int); + +void (*p)() = f; diff --git a/gcc/testsuite/g++.dg/tm/pretty-print1.C b/gcc/testsuite/g++.dg/tm/pretty-print1.C new file mode 100644 index 0000000..d7c1de0 --- /dev/null +++ b/gcc/testsuite/g++.dg/tm/pretty-print1.C @@ -0,0 +1,6 @@ +// Test for pretty-printing in diagnostics. +// { dg-options "-fgnu-tm" } + +void f(); +void (*p)() transaction_safe = f; // { dg-error "void \\(\\*\\)\\(\\) transaction_safe" } + diff --git a/gcc/testsuite/g++.dg/tm/static_cast1.C b/gcc/testsuite/g++.dg/tm/static_cast1.C new file mode 100644 index 0000000..31606c5 --- /dev/null +++ b/gcc/testsuite/g++.dg/tm/static_cast1.C @@ -0,0 +1,9 @@ +// The inverse of a transaction-safety conversion cannot be performed with +// static_cast. +// { dg-options "-fgnu-tm" } + +typedef void (*TS)() transaction_safe; +void f() +{ + static_cast(f); // { dg-error "static_cast" } +} diff --git a/gcc/testsuite/g++.dg/tm/sync1.C b/gcc/testsuite/g++.dg/tm/sync1.C new file mode 100644 index 0000000..a567b2c --- /dev/null +++ b/gcc/testsuite/g++.dg/tm/sync1.C @@ -0,0 +1,15 @@ +// Testcase from TM TS. +// { dg-options -fgnu-tm } + +extern "C" int printf (const char *, ...); + +int f() +{ + static int i = 0; + synchronized { + printf("before %d\n", i); + ++i; + printf("after %d\n", i); + return i; + } +} diff --git a/gcc/testsuite/g++.dg/tm/sync2.C b/gcc/testsuite/g++.dg/tm/sync2.C new file mode 100644 index 0000000..f13c9c5 --- /dev/null +++ b/gcc/testsuite/g++.dg/tm/sync2.C @@ -0,0 +1,21 @@ +// { dg-do compile { target c++11 } } +// { dg-options "-fgnu-tm -fdump-tree-optimized-asmname" } + +struct Tsafe +{ + void f() transaction_safe; +}; + +void Tsafe::f() { } + +struct Tcall +{ + [[optimize_for_synchronized]] void f(); +}; + +void Tcall::f() { } + +// { dg-final { scan-tree-dump-times "_ZN5Tsafe1fEv" 1 "optimized" } } +// { dg-final { scan-tree-dump-times "_ZN5Tcall1fEv" 1 "optimized" } } +// { dg-final { scan-tree-dump-times "_ZGTtN5Tsafe1fEv" 1 "optimized" } } +// { dg-final { scan-tree-dump-times "_ZGTtN5Tcall1fEv" 1 "optimized" } } diff --git a/gcc/testsuite/g++.dg/tm/template-3.C b/gcc/testsuite/g++.dg/tm/template-3.C new file mode 100644 index 0000000..356d2a8 --- /dev/null +++ b/gcc/testsuite/g++.dg/tm/template-3.C @@ -0,0 +1,15 @@ +// { dg-options "-fgnu-tm" } + +void fn(int) transaction_safe; +void fn(double); + +template void f(T t) transaction_safe +{ + fn(t); // { dg-error "double" } +} + +void g() +{ + f(42); // OK + f(3.14); +} diff --git a/gcc/testsuite/g++.dg/tm/template-4.C b/gcc/testsuite/g++.dg/tm/template-4.C new file mode 100644 index 0000000..dd25711 --- /dev/null +++ b/gcc/testsuite/g++.dg/tm/template-4.C @@ -0,0 +1,13 @@ +// Test for transaction-safety conversion in deduction. +// { dg-options "-fgnu-tm" } + +void fn(int) transaction_safe; +void fn(); + +template void f(void(*)(T)); +template void f2(void(*)(T) transaction_safe); + +void g() +{ + f(fn); +} diff --git a/gcc/testsuite/g++.dg/tm/template-5.C b/gcc/testsuite/g++.dg/tm/template-5.C new file mode 100644 index 0000000..6501ed1 --- /dev/null +++ b/gcc/testsuite/g++.dg/tm/template-5.C @@ -0,0 +1,12 @@ +// Test for deduction based on transaction_safe. +// { dg-options "-fgnu-tm -std=c++11" } + +void f() transaction_safe; +void g(); + +template struct A; +template +struct A { }; + +A a; +A b; // { dg-error "incomplete" } diff --git a/gcc/testsuite/g++.dg/tm/unsafe1.C b/gcc/testsuite/g++.dg/tm/unsafe1.C new file mode 100644 index 0000000..91dd7b1 --- /dev/null +++ b/gcc/testsuite/g++.dg/tm/unsafe1.C @@ -0,0 +1,15 @@ +// Transaction-unsafe testcase from TM TS. +// { dg-options -fgnu-tm } + +struct S { + virtual ~S(); +}; +int f() transaction_safe { + S s; // { dg-error "unsafe" "invocation of unsafe destructor" } +} + +int g(int x) { // is transaction-safe + if (x <= 0) + return 0; + return x + g(x-1); +} diff --git a/gcc/testsuite/g++.dg/tm/unsafe2.C b/gcc/testsuite/g++.dg/tm/unsafe2.C new file mode 100644 index 0000000..1b81b31 --- /dev/null +++ b/gcc/testsuite/g++.dg/tm/unsafe2.C @@ -0,0 +1,13 @@ +// Transaction-unsafe testcase from TM TS. +// { dg-options -fgnu-tm } + +template +void f(T) transaction_safe; +template<> +void f(bool); // not transaction-safe + +int g() transaction_safe +{ + f(42); // OK + f(true); // { dg-error "unsafe" } +} diff --git a/gcc/testsuite/lib/g++.exp b/gcc/testsuite/lib/g++.exp index b440dd7..229fbc3 100644 --- a/gcc/testsuite/lib/g++.exp +++ b/gcc/testsuite/lib/g++.exp @@ -141,6 +141,10 @@ proc g++_link_flags { paths } { if [file exists "${gccpath}/librx/librx.a"] { append flags "-L${gccpath}/librx " } + if [file exists "${gccpath}/libitm/libitm.spec"] { + append flags "-B${gccpath}/libitm/ -L${gccpath}/libitm/.libs" + append ld_library_path ":${gccpath}/libitm/.libs" + } append ld_library_path [gcc-set-multilib-library-path $GXX_UNDER_TEST] } else { global tool_root_dir diff --git a/gcc/tree.c b/gcc/tree.c index 84fd34d..af31849 100644 --- a/gcc/tree.c +++ b/gcc/tree.c @@ -5017,6 +5017,8 @@ comp_type_attributes (const_tree type1, const_tree type2) if (!a) return 1; } + if (lookup_attribute ("transaction_safe", CONST_CAST_TREE (a))) + return 0; /* As some type combinations - like default calling-convention - might be compatible, we have to call the target hook to get the final result. */ return targetm.comp_type_attributes (type1, type2); diff --git a/gcc/tree.h b/gcc/tree.h index 35c72b6..4c803f4 100644 --- a/gcc/tree.h +++ b/gcc/tree.h @@ -592,6 +592,9 @@ extern void omp_clause_range_check_failed (const_tree, const char *, int, #define COMPLETE_OR_UNBOUND_ARRAY_TYPE_P(NODE) \ (COMPLETE_TYPE_P (TREE_CODE (NODE) == ARRAY_TYPE ? TREE_TYPE (NODE) : (NODE))) +#define FUNC_OR_METHOD_TYPE_P(NODE) \ + (TREE_CODE (NODE) == FUNCTION_TYPE || TREE_CODE (NODE) == METHOD_TYPE) + /* Define many boolean fields that all tree nodes have. */ /* In VAR_DECL, PARM_DECL and RESULT_DECL nodes, nonzero means address diff --git a/include/demangle.h b/include/demangle.h index e415de0..f4c4121 100644 --- a/include/demangle.h +++ b/include/demangle.h @@ -442,6 +442,8 @@ enum demangle_component_type DEMANGLE_COMPONENT_PACK_EXPANSION, /* A name with an ABI tag. */ DEMANGLE_COMPONENT_TAGGED_NAME, + /* A transaction-safe function type. */ + DEMANGLE_COMPONENT_TRANSACTION_SAFE, /* A cloned function. */ DEMANGLE_COMPONENT_CLONE }; diff --git a/libiberty/ChangeLog b/libiberty/ChangeLog index 8fdf6d2..1cd2aea 100644 --- a/libiberty/ChangeLog +++ b/libiberty/ChangeLog @@ -1,3 +1,12 @@ +2015-09-30 Jason Merrill + + * cp-demangle.c (d_cv_qualifiers): Dx means transaction_safe. + (cplus_demangle_type): Let d_cv_qualifiers handle it. + (d_dump, d_make_comp, has_return_type, d_encoding) + (d_count_templates_scopes, d_print_comp_inner) + (d_print_mod_list, d_print_mod, d_print_function_type) + (is_ctor_or_dtor): Handle DEMANGLE_COMPONENT_TRANSACTION_SAFE. + 2015-08-15 Ian Lance Taylor * cp-demangle.c (d_abi_tags): Preserve di->last_name across any diff --git a/libiberty/cp-demangle.c b/libiberty/cp-demangle.c index c5878950..ff608a3 100644 --- a/libiberty/cp-demangle.c +++ b/libiberty/cp-demangle.c @@ -686,6 +686,9 @@ d_dump (struct demangle_component *dc, int indent) case DEMANGLE_COMPONENT_RVALUE_REFERENCE_THIS: printf ("rvalue reference this\n"); break; + case DEMANGLE_COMPONENT_TRANSACTION_SAFE: + printf ("transaction_safe this\n"); + break; case DEMANGLE_COMPONENT_VENDOR_TYPE_QUAL: printf ("vendor type qualifier\n"); break; @@ -970,6 +973,7 @@ d_make_comp (struct d_info *di, enum demangle_component_type type, case DEMANGLE_COMPONENT_RESTRICT_THIS: case DEMANGLE_COMPONENT_VOLATILE_THIS: case DEMANGLE_COMPONENT_CONST_THIS: + case DEMANGLE_COMPONENT_TRANSACTION_SAFE: case DEMANGLE_COMPONENT_REFERENCE_THIS: case DEMANGLE_COMPONENT_RVALUE_REFERENCE_THIS: case DEMANGLE_COMPONENT_ARGLIST: @@ -1212,6 +1216,7 @@ has_return_type (struct demangle_component *dc) case DEMANGLE_COMPONENT_CONST_THIS: case DEMANGLE_COMPONENT_REFERENCE_THIS: case DEMANGLE_COMPONENT_RVALUE_REFERENCE_THIS: + case DEMANGLE_COMPONENT_TRANSACTION_SAFE: return has_return_type (d_left (dc)); } } @@ -1268,6 +1273,7 @@ d_encoding (struct d_info *di, int top_level) while (dc->type == DEMANGLE_COMPONENT_RESTRICT_THIS || dc->type == DEMANGLE_COMPONENT_VOLATILE_THIS || dc->type == DEMANGLE_COMPONENT_CONST_THIS + || dc->type == DEMANGLE_COMPONENT_TRANSACTION_SAFE || dc->type == DEMANGLE_COMPONENT_REFERENCE_THIS || dc->type == DEMANGLE_COMPONENT_RVALUE_REFERENCE_THIS) dc = d_left (dc); @@ -1284,6 +1290,7 @@ d_encoding (struct d_info *di, int top_level) while (dcr->type == DEMANGLE_COMPONENT_RESTRICT_THIS || dcr->type == DEMANGLE_COMPONENT_VOLATILE_THIS || dcr->type == DEMANGLE_COMPONENT_CONST_THIS + || dcr->type == DEMANGLE_COMPONENT_TRANSACTION_SAFE || dcr->type == DEMANGLE_COMPONENT_REFERENCE_THIS || dcr->type == DEMANGLE_COMPONENT_RVALUE_REFERENCE_THIS) dcr = d_left (dcr); @@ -2281,7 +2288,8 @@ cplus_demangle_type (struct d_info *di) names. */ peek = d_peek_char (di); - if (peek == 'r' || peek == 'V' || peek == 'K') + if (peek == 'r' || peek == 'V' || peek == 'K' + || (peek == 'D' && d_peek_next_char (di) == 'x')) { struct demangle_component **pret; @@ -2592,7 +2600,7 @@ cplus_demangle_type (struct d_info *di) return ret; } -/* ::= [r] [V] [K] */ +/* ::= [r] [V] [K] [Dx] */ static struct demangle_component ** d_cv_qualifiers (struct d_info *di, @@ -2603,7 +2611,8 @@ d_cv_qualifiers (struct d_info *di, pstart = pret; peek = d_peek_char (di); - while (peek == 'r' || peek == 'V' || peek == 'K') + while (peek == 'r' || peek == 'V' || peek == 'K' + || (peek == 'D' && d_peek_next_char (di) == 'x')) { enum demangle_component_type t; @@ -2622,13 +2631,19 @@ d_cv_qualifiers (struct d_info *di, : DEMANGLE_COMPONENT_VOLATILE); di->expansion += sizeof "volatile"; } - else + else if (peek == 'K') { t = (member_fn ? DEMANGLE_COMPONENT_CONST_THIS : DEMANGLE_COMPONENT_CONST); di->expansion += sizeof "const"; } + else + { + t = DEMANGLE_COMPONENT_TRANSACTION_SAFE; + di->expansion += sizeof "transaction_safe"; + d_advance (di, 1); + } *pret = d_make_comp (di, t, NULL, NULL); if (*pret == NULL) @@ -2694,7 +2709,7 @@ d_ref_qualifier (struct d_info *di, struct demangle_component *sub) return ret; } -/* ::= F [Y] [] E */ +/* ::= F [Y] [] [T] E */ static struct demangle_component * d_function_type (struct d_info *di) @@ -3899,6 +3914,7 @@ d_count_templates_scopes (int *num_templates, int *num_scopes, case DEMANGLE_COMPONENT_CONST_THIS: case DEMANGLE_COMPONENT_REFERENCE_THIS: case DEMANGLE_COMPONENT_RVALUE_REFERENCE_THIS: + case DEMANGLE_COMPONENT_TRANSACTION_SAFE: case DEMANGLE_COMPONENT_VENDOR_TYPE_QUAL: case DEMANGLE_COMPONENT_POINTER: case DEMANGLE_COMPONENT_COMPLEX: @@ -4420,6 +4436,7 @@ d_print_comp_inner (struct d_print_info *dpi, int options, && typed_name->type != DEMANGLE_COMPONENT_VOLATILE_THIS && typed_name->type != DEMANGLE_COMPONENT_CONST_THIS && typed_name->type != DEMANGLE_COMPONENT_RVALUE_REFERENCE_THIS + && typed_name->type != DEMANGLE_COMPONENT_TRANSACTION_SAFE && typed_name->type != DEMANGLE_COMPONENT_REFERENCE_THIS) break; @@ -4461,6 +4478,7 @@ d_print_comp_inner (struct d_print_info *dpi, int options, || local_name->type == DEMANGLE_COMPONENT_VOLATILE_THIS || local_name->type == DEMANGLE_COMPONENT_CONST_THIS || local_name->type == DEMANGLE_COMPONENT_REFERENCE_THIS + || local_name->type == DEMANGLE_COMPONENT_TRANSACTION_SAFE || (local_name->type == DEMANGLE_COMPONENT_RVALUE_REFERENCE_THIS)) { @@ -4796,6 +4814,7 @@ d_print_comp_inner (struct d_print_info *dpi, int options, case DEMANGLE_COMPONENT_POINTER: case DEMANGLE_COMPONENT_COMPLEX: case DEMANGLE_COMPONENT_IMAGINARY: + case DEMANGLE_COMPONENT_TRANSACTION_SAFE: modifier: { /* We keep a list of modifiers on the stack. */ @@ -5484,6 +5503,7 @@ d_print_mod_list (struct d_print_info *dpi, int options, || mods->mod->type == DEMANGLE_COMPONENT_VOLATILE_THIS || mods->mod->type == DEMANGLE_COMPONENT_CONST_THIS || mods->mod->type == DEMANGLE_COMPONENT_REFERENCE_THIS + || mods->mod->type == DEMANGLE_COMPONENT_TRANSACTION_SAFE || (mods->mod->type == DEMANGLE_COMPONENT_RVALUE_REFERENCE_THIS)))) { @@ -5542,6 +5562,7 @@ d_print_mod_list (struct d_print_info *dpi, int options, || dc->type == DEMANGLE_COMPONENT_VOLATILE_THIS || dc->type == DEMANGLE_COMPONENT_CONST_THIS || dc->type == DEMANGLE_COMPONENT_REFERENCE_THIS + || dc->type == DEMANGLE_COMPONENT_TRANSACTION_SAFE || dc->type == DEMANGLE_COMPONENT_RVALUE_REFERENCE_THIS) dc = d_left (dc); @@ -5578,6 +5599,9 @@ d_print_mod (struct d_print_info *dpi, int options, case DEMANGLE_COMPONENT_CONST_THIS: d_append_string (dpi, " const"); return; + case DEMANGLE_COMPONENT_TRANSACTION_SAFE: + d_append_string (dpi, " transaction_safe"); + return; case DEMANGLE_COMPONENT_VENDOR_TYPE_QUAL: d_append_char (dpi, ' '); d_print_comp (dpi, options, d_right (mod)); @@ -5668,6 +5692,7 @@ d_print_function_type (struct d_print_info *dpi, int options, case DEMANGLE_COMPONENT_CONST_THIS: case DEMANGLE_COMPONENT_REFERENCE_THIS: case DEMANGLE_COMPONENT_RVALUE_REFERENCE_THIS: + case DEMANGLE_COMPONENT_TRANSACTION_SAFE: break; default: break; @@ -6200,6 +6225,7 @@ is_ctor_or_dtor (const char *mangled, case DEMANGLE_COMPONENT_CONST_THIS: case DEMANGLE_COMPONENT_REFERENCE_THIS: case DEMANGLE_COMPONENT_RVALUE_REFERENCE_THIS: + case DEMANGLE_COMPONENT_TRANSACTION_SAFE: default: dc = NULL; break; diff --git a/libiberty/testsuite/demangle-expected b/libiberty/testsuite/demangle-expected index 5200cb3..041b113 100644 --- a/libiberty/testsuite/demangle-expected +++ b/libiberty/testsuite/demangle-expected @@ -4395,3 +4395,6 @@ void IndirectExternCall stdcall*)(int, int), int>(void ( regpa _ZNSt8ios_base7failureB5cxx11C1EPKcRKSt10error_code std::ios_base::failure[abi:cxx11]::failure(char const*, std::error_code const&) std::ios_base::failure[abi:cxx11]::failure +--format=gnu-v3 +_Z1fPDxFvvES0_ +f(void (*)() transaction_safe, void (*)() transaction_safe) diff --git a/libstdc++-v3/libsupc++/cxxabi.h b/libstdc++-v3/libsupc++/cxxabi.h index 571e42e..08eb7bc 100644 --- a/libstdc++-v3/libsupc++/cxxabi.h +++ b/libstdc++-v3/libsupc++/cxxabi.h @@ -281,7 +281,8 @@ namespace __cxxabiv1 __volatile_mask = 0x2, __restrict_mask = 0x4, __incomplete_mask = 0x8, - __incomplete_class_mask = 0x10 + __incomplete_class_mask = 0x10, + __transaction_safe_mask = 0x20 }; protected: diff --git a/libstdc++-v3/libsupc++/pbase_type_info.cc b/libstdc++-v3/libsupc++/pbase_type_info.cc index cbfdc17..0e96529 100644 --- a/libstdc++-v3/libsupc++/pbase_type_info.cc +++ b/libstdc++-v3/libsupc++/pbase_type_info.cc @@ -50,8 +50,19 @@ __do_catch (const type_info *thr_type, const __pbase_type_info *thrown_type = static_cast (thr_type); + + unsigned tflags = thrown_type->__flags; + + bool throw_tx = (tflags & __transaction_safe_mask); + bool catch_tx = (__flags & __transaction_safe_mask); + if (throw_tx && !catch_tx) + /* Catch can perform a transaction-safety conversion. */ + tflags &= ~__transaction_safe_mask; + if (catch_tx && !throw_tx) + /* But not the reverse. */ + return false; - if (thrown_type->__flags & ~__flags) + if (tflags & ~__flags) // We're less qualified. return false; -- 2.7.4