diff options
50 files changed, 3337 insertions, 153 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog index e59e37a52cc..23cf4eca02b 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,48 @@ +2013-11-07 Andrew MacLeod <amacleod@redhat.com> + Joseph Myers <joseph@codesourcery.com> + + * tree-core.h (enum cv_qualifier): Add TYPE_QUAL_ATOMIC. + (enum tree_index): Add TI_ATOMICQI_TYPE, TI_ATOMICHI_TYPE, + TI_ATOMICSI_TYPE, TI_ATOMICDI_TYPE and TI_ATOMICTI_TYPE. + (struct tree_base): Add atomic_flag field. + * tree.h (TYPE_ATOMIC): New accessor macro. + (TYPE_QUALS, TYPE_QUALS_NO_ADDR_SPACE): Add TYPE_QUAL_ATOMIC. + (TYPE_QUALS_NO_ADDR_SPACE_NO_ATOMIC): New macro. + (atomicQI_type_node, atomicHI_type_node, atomicSI_type_node) + (atomicDI_type_node, atomicTI_type_node): New macros for type + nodes. + * tree.c (set_type_quals): Set TYPE_ATOMIC. + (find_atomic_core_type): New function. + (build_qualified_type): Adjust alignment for qualified types. + (build_atomic_base): New function + (build_common_tree_nodes): Build atomicQI_type_node, + atomicHI_type_node, atomicSI_type_node, atomicDI_type_node and + atomicTI_type_node. + * print-tree.c (print_node): Print atomic qualifier. + * tree-pretty-print.c (dump_generic_node): Print atomic type + attribute. + * target.def (atomic_assign_expand_fenv): New hook. + * doc/tm.texi.in (TARGET_ATOMIC_ASSIGN_EXPAND_FENV): New @hook. + * doc/tm.texi: Regenerate. + * targhooks.c (default_atomic_assign_expand_fenv): New function. + * targhooks.h (default_atomic_assign_expand_fenv): Declare. + * sync-builtins.def (__atomic_feraiseexcept): New built-in + function. + * config/i386/i386-builtin-types.def (VOID_FTYPE_PUSHORT): New + function type. + * config/i386/i386.c (enum ix86_builtins): Add + IX86_BUILTIN_FNSTENV, IX86_BUILTIN_FLDENV, IX86_BUILTIN_FNSTSW and + IX86_BUILTIN_FNCLEX. + (bdesc_special_args): Add __builtin_ia32_fnstenv, + __builtin_ia32_fldenv, __builtin_ia32_fnstsw and + __builtin_ia32_fnclex. + (ix86_expand_builtin): Handle the new built-in functions. + (ix86_atomic_assign_expand_fenv): New function. + (TARGET_ATOMIC_ASSIGN_EXPAND_FENV): New macro. + * config/i386/i386.md (UNSPECV_FNSTENV, UNSPECV_FLDENV) + (UNSPECV_FNSTSW, UNSPECV_FNCLEX): New unspecs. + (fnstenv, fldenv, fnstsw, fnclex): New insns. + 2013-11-07 Steve Ellcey <sellcey@mips.com> * config/mips/mti-linux.h (SYSROOT_SUFFIX_SPEC): Add fp64 directory. diff --git a/gcc/c-family/ChangeLog b/gcc/c-family/ChangeLog index 4a4e0617d5b..4ff4310e8b6 100644 --- a/gcc/c-family/ChangeLog +++ b/gcc/c-family/ChangeLog @@ -1,3 +1,15 @@ +2013-11-07 Andrew MacLeod <amacleod@redhat.com> + Joseph Myers <joseph@codesourcery.com> + + * c-common.h (enum rid): Add RID_ATOMIC. + * c-common.c (c_common_reswords): Add _Atomic. + (sync_resolve_params): Use TYPE_MAIN_VARIANT on pointer argument. + (keyword_is_type_qualifier): Accept RID_ATOMIC. + * c-format.c (check_format_types): Check for extra _Atomic + qualifiers in format argument. + * c-pretty-print.c (pp_c_cv_qualifiers): Handle atomic qualifier. + (pp_c_type_qualifier_list): Mention _Atomic in comment. + 2013-11-06 Tobias Burnus <burnus@net-b.de> * c-common.c (reason_option_codes_t): Add CPP_W_DATE_TIME. diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c index 69a068e758c..e237926c573 100644 --- a/gcc/c-family/c-common.c +++ b/gcc/c-family/c-common.c @@ -409,6 +409,7 @@ const struct c_common_resword c_common_reswords[] = { { "_Alignas", RID_ALIGNAS, D_CONLY }, { "_Alignof", RID_ALIGNOF, D_CONLY }, + { "_Atomic", RID_ATOMIC, D_CONLY }, { "_Bool", RID_BOOL, D_CONLY }, { "_Complex", RID_COMPLEX, 0 }, { "_Cilk_spawn", RID_CILK_SPAWN, 0 }, @@ -10172,6 +10173,7 @@ sync_resolve_params (location_t loc, tree orig_function, tree function, call to check_function_arguments what ever type the user used. */ function_args_iter_next (&iter); ptype = TREE_TYPE (TREE_TYPE ((*params)[0])); + ptype = TYPE_MAIN_VARIANT (ptype); /* For the rest of the values, we need to cast these to FTYPE, so that we don't get warnings for passing pointer types, etc. */ @@ -11568,6 +11570,7 @@ keyword_is_type_qualifier (enum rid keyword) case RID_CONST: case RID_VOLATILE: case RID_RESTRICT: + case RID_ATOMIC: return true; default: return false; diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h index 8dd40c83e01..74fd59fb5ee 100644 --- a/gcc/c-family/c-common.h +++ b/gcc/c-family/c-common.h @@ -66,7 +66,7 @@ enum rid RID_UNSIGNED, RID_LONG, RID_CONST, RID_EXTERN, RID_REGISTER, RID_TYPEDEF, RID_SHORT, RID_INLINE, RID_VOLATILE, RID_SIGNED, RID_AUTO, RID_RESTRICT, - RID_NORETURN, + RID_NORETURN, RID_ATOMIC, /* C extensions */ RID_COMPLEX, RID_THREAD, RID_SAT, diff --git a/gcc/c-family/c-format.c b/gcc/c-family/c-format.c index f0371d3cb6e..99cae172a5b 100644 --- a/gcc/c-family/c-format.c +++ b/gcc/c-family/c-format.c @@ -2374,6 +2374,7 @@ check_format_types (format_wanted_type *types) && pedantic && (TYPE_READONLY (cur_type) || TYPE_VOLATILE (cur_type) + || TYPE_ATOMIC (cur_type) || TYPE_RESTRICT (cur_type))) warning (OPT_Wformat_, "extra type qualifiers in format " "argument (argument %d)", diff --git a/gcc/c-family/c-pretty-print.c b/gcc/c-family/c-pretty-print.c index d0283e8af4d..652c3a89215 100644 --- a/gcc/c-family/c-pretty-print.c +++ b/gcc/c-family/c-pretty-print.c @@ -179,8 +179,16 @@ pp_c_cv_qualifiers (c_pretty_printer *pp, int qualifiers, bool func_type) if (p != NULL && (*p == '*' || *p == '&')) pp_c_whitespace (pp); + if (qualifiers & TYPE_QUAL_ATOMIC) + { + pp_c_ws_string (pp, "_Atomic"); + previous = true; + } + if (qualifiers & TYPE_QUAL_CONST) { + if (previous) + pp_c_whitespace (pp); pp_c_ws_string (pp, func_type ? "__attribute__((const))" : "const"); previous = true; } @@ -244,6 +252,7 @@ pp_c_space_for_pointer_operator (c_pretty_printer *pp, tree t) __restrict__ -- GNU C address-space-qualifier -- GNU C volatile + _Atomic -- C11 address-space-qualifier: identifier -- GNU C */ diff --git a/gcc/c/ChangeLog b/gcc/c/ChangeLog index cc335e454b5..e38bcb8cdbd 100644 --- a/gcc/c/ChangeLog +++ b/gcc/c/ChangeLog @@ -1,3 +1,72 @@ +2013-11-07 Joseph Myers <joseph@codesourcery.com> + Andrew MacLeod <amacleod@redhat.com> + + * c-aux-info.c (gen_type): Handle atomic qualifier. + * c-decl.c (validate_proto_after_old_defn): Do not remove atomic + qualifiers when compating types. + (shadow_tag_warned): Handle atomic_p in declspecs. + (quals_from_declspecs): Likewise. + (start_decl): Use c_type_promotes_to when promoting argument + types. + (grokdeclarator): Handle _Atomic. + (get_parm_info): Diagnose any qualifier on "void" as only + parameter. + (store_parm_decls_oldstyle): Do not remove atomic qualifiers when + comparing types. Use c_type_promotes_to when promoting argument + types. + (finish_function): Use c_type_promotes_to when promoting argument + types. + (build_null_declspecs): Handle atomic_p in declspecs. + (declspecs_add_qual): Handle RID_ATOMIC. + * c-parser.c (c_token_starts_typename, c_token_is_qualifier) + (c_token_starts_declspecs): Handle RID_ATOMIC. + (c_parser_declspecs): Handle atomic type specifiers and + qualifiers. + (c_parser_typeof_specifier): Remove const and _Atomic qualifiers + from types of expressions with atomic type. + (c_parser_direct_declarator_inner): Use convert_lvalue_to_rvalue. + (c_parser_attribute_any_word): Handle RID_ATOMIC. + (c_parser_initializer, c_parser_initelt, c_parser_initval) + (c_parser_statement_after_labels, c_parser_switch_statement) + (c_parser_for_statement, c_parser_expr_no_commas) + (c_parser_conditional_expression, c_parser_binary_expression) + (c_parser_cast_expression, c_parser_unary_expression) + (c_parser_postfix_expression) + (c_parser_postfix_expression_after_primary, c_parser_expression): + Use convert_lvalue_to_rvalue. + (c_parser_expression_conv, c_parser_expr_list): Document + conversion of lvalues to rvalues. Use convert_lvalue_to_rvalue. + (c_parser_objc_synchronized_statement): Use + convert_lvalue_to_rvalue. + (c_parser_objc_selector): Handle RID_ATOMIC. + (c_parser_objc_receiver, c_parser_array_notation): Use + convert_lvalue_to_rvalue. + * c-tree.h (ctsk_typeof): Adjust comment to mention use for + _Atomic (type-name). + (struct c_declspecs): Add atomic_p field. + (convert_lvalue_to_rvalue): Declare. + * c-typeck.c (c_type_promotes_to): Promote atomic types to + corresponding atomic types. + (qualify_type): Don't add _Atomic qualifiers from second argument. + (comp_target_types): Do not allow _Atomic mismatches. + (type_lists_compatible_p): Do not remove atomic qualifiers when + comparing types. + (really_atomic_lvalue, convert_lvalue_to_rvalue) + (build_atomic_assign): New functions. + (build_unary_op): Use build_atomic_assign for atomic increment and + decrement. + (build_conditional_expr): Do not treat _Atomic void as a qualified + version of void. + (build_modify_expr): Use build_atomic_assign for atomic LHS. + (find_anonymous_field_with_type, convert_to_anonymous_field) + (convert_for_assignment): Do not remove atomic qualifiers when + comparing types. + (digest_init): Do not accept initialization of arrays of atomic + elements by string constants. + (build_asm_expr): Use convert_lvalue_to_rvalue. + (build_binary_op): Do not treat _Atomic void as a qualified + version of void. + 2013-11-06 DJ Delorie <dj@redhat.com> * c-decl.c (locate_old_decl): If a previous conflicting decl is diff --git a/gcc/c/c-aux-info.c b/gcc/c/c-aux-info.c index dd9c7685bc6..823a3c49f6f 100644 --- a/gcc/c/c-aux-info.c +++ b/gcc/c/c-aux-info.c @@ -285,6 +285,8 @@ gen_type (const char *ret_val, tree t, formals_style style) switch (TREE_CODE (t)) { case POINTER_TYPE: + if (TYPE_ATOMIC (t)) + ret_val = concat ("_Atomic ", ret_val, NULL); if (TYPE_READONLY (t)) ret_val = concat ("const ", ret_val, NULL); if (TYPE_VOLATILE (t)) @@ -425,6 +427,8 @@ gen_type (const char *ret_val, tree t, formals_style style) gcc_unreachable (); } } + if (TYPE_ATOMIC (t)) + ret_val = concat ("_Atomic ", ret_val, NULL); if (TYPE_READONLY (t)) ret_val = concat ("const ", ret_val, NULL); if (TYPE_VOLATILE (t)) diff --git a/gcc/c/c-decl.c b/gcc/c/c-decl.c index 23f05163a62..9520e4d2d6e 100644 --- a/gcc/c/c-decl.c +++ b/gcc/c/c-decl.c @@ -1584,8 +1584,14 @@ validate_proto_after_old_defn (tree newdecl, tree newtype, tree oldtype) if (oldargtype == error_mark_node || newargtype == error_mark_node) return false; - oldargtype = TYPE_MAIN_VARIANT (oldargtype); - newargtype = TYPE_MAIN_VARIANT (newargtype); + oldargtype = (TYPE_ATOMIC (oldargtype) + ? c_build_qualified_type (TYPE_MAIN_VARIANT (oldargtype), + TYPE_QUAL_ATOMIC) + : TYPE_MAIN_VARIANT (oldargtype)); + newargtype = (TYPE_ATOMIC (newargtype) + ? c_build_qualified_type (TYPE_MAIN_VARIANT (newargtype), + TYPE_QUAL_ATOMIC) + : TYPE_MAIN_VARIANT (newargtype)); if (END_OF_ARGLIST (oldargtype) && END_OF_ARGLIST (newargtype)) break; @@ -3715,6 +3721,7 @@ shadow_tag_warned (const struct c_declspecs *declspecs, int warned) && declspecs->typespec_kind != ctsk_tagfirstref && (declspecs->const_p || declspecs->volatile_p + || declspecs->atomic_p || declspecs->restrict_p || declspecs->address_space)) { @@ -3804,6 +3811,7 @@ shadow_tag_warned (const struct c_declspecs *declspecs, int warned) if (!warned && !in_system_header && (declspecs->const_p || declspecs->volatile_p + || declspecs->atomic_p || declspecs->restrict_p || declspecs->address_space)) { @@ -3835,6 +3843,7 @@ quals_from_declspecs (const struct c_declspecs *specs) int quals = ((specs->const_p ? TYPE_QUAL_CONST : 0) | (specs->volatile_p ? TYPE_QUAL_VOLATILE : 0) | (specs->restrict_p ? TYPE_QUAL_RESTRICT : 0) + | (specs->atomic_p ? TYPE_QUAL_ATOMIC : 0) | (ENCODE_QUAL_ADDR_SPACE (specs->address_space))); gcc_assert (!specs->type && !specs->decl_attr @@ -4170,7 +4179,7 @@ start_decl (struct c_declarator *declarator, struct c_declspecs *declspecs, tree type = TREE_TYPE (args); if (type && INTEGRAL_TYPE_P (type) && TYPE_PRECISION (type) < TYPE_PRECISION (integer_type_node)) - DECL_ARG_TYPE (args) = integer_type_node; + DECL_ARG_TYPE (args) = c_type_promotes_to (type); } } } @@ -4943,6 +4952,7 @@ grokdeclarator (const struct c_declarator *declarator, int constp; int restrictp; int volatilep; + int atomicp; int type_quals = TYPE_UNQUALIFIED; tree name = NULL_TREE; bool funcdef_flag = false; @@ -5097,6 +5107,7 @@ grokdeclarator (const struct c_declarator *declarator, constp = declspecs->const_p + TYPE_READONLY (element_type); restrictp = declspecs->restrict_p + TYPE_RESTRICT (element_type); volatilep = declspecs->volatile_p + TYPE_VOLATILE (element_type); + atomicp = declspecs->atomic_p + TYPE_ATOMIC (element_type); as1 = declspecs->address_space; as2 = TYPE_ADDR_SPACE (element_type); address_space = ADDR_SPACE_GENERIC_P (as1)? as2 : as1; @@ -5109,6 +5120,9 @@ grokdeclarator (const struct c_declarator *declarator, pedwarn (loc, OPT_Wpedantic, "duplicate %<restrict%>"); if (volatilep > 1) pedwarn (loc, OPT_Wpedantic, "duplicate %<volatile%>"); + if (atomicp > 1) + pedwarn (loc, OPT_Wpedantic, "duplicate %<_Atomic%>"); + } if (!ADDR_SPACE_GENERIC_P (as1) && !ADDR_SPACE_GENERIC_P (as2) && as1 != as2) @@ -5122,8 +5136,16 @@ grokdeclarator (const struct c_declarator *declarator, type_quals = ((constp ? TYPE_QUAL_CONST : 0) | (restrictp ? TYPE_QUAL_RESTRICT : 0) | (volatilep ? TYPE_QUAL_VOLATILE : 0) + | (atomicp ? TYPE_QUAL_ATOMIC : 0) | ENCODE_QUAL_ADDR_SPACE (address_space)); + /* Applying the _Atomic qualifier to an array type (through the use + of typedefs or typeof) must be detected here. If the qualifier + is introduced later, any appearance of applying it to an array is + actually applying it to an element of that array. */ + if (atomicp && TREE_CODE (type) == ARRAY_TYPE) + error_at (loc, "%<_Atomic%>-qualified array type"); + /* Warn about storage classes that are invalid for certain kinds of declarations (parameters, typenames, etc.). */ @@ -5699,9 +5721,15 @@ grokdeclarator (const struct c_declarator *declarator, { /* Merge any constancy or volatility into the target type for the pointer. */ - - if (pedantic && TREE_CODE (type) == FUNCTION_TYPE - && type_quals) + if ((type_quals & TYPE_QUAL_ATOMIC) + && TREE_CODE (type) == FUNCTION_TYPE) + { + error_at (loc, + "%<_Atomic%>-qualified function type"); + type_quals &= ~TYPE_QUAL_ATOMIC; + } + else if (pedantic && TREE_CODE (type) == FUNCTION_TYPE + && type_quals) pedwarn (loc, OPT_Wpedantic, "ISO C forbids qualified function types"); if (type_quals) @@ -5815,7 +5843,20 @@ grokdeclarator (const struct c_declarator *declarator, /* Check the type and width of a bit-field. */ if (bitfield) - check_bitfield_type_and_width (&type, width, name); + { + check_bitfield_type_and_width (&type, width, name); + /* C11 makes it implementation-defined (6.7.2.1#5) whether + atomic types are permitted for bit-fields; we have no code to + make bit-field accesses atomic, so disallow them. */ + if (type_quals & TYPE_QUAL_ATOMIC) + { + if (name) + error ("bit-field %qE has atomic type", name); + else + error ("bit-field has atomic type"); + type_quals &= ~TYPE_QUAL_ATOMIC; + } + } /* Reject invalid uses of _Alignas. */ if (declspecs->alignas_p) @@ -5878,8 +5919,15 @@ grokdeclarator (const struct c_declarator *declarator, if (storage_class == csc_typedef) { tree decl; - if (pedantic && TREE_CODE (type) == FUNCTION_TYPE - && type_quals) + if ((type_quals & TYPE_QUAL_ATOMIC) + && TREE_CODE (type) == FUNCTION_TYPE) + { + error_at (loc, + "%<_Atomic%>-qualified function type"); + type_quals &= ~TYPE_QUAL_ATOMIC; + } + else if (pedantic && TREE_CODE (type) == FUNCTION_TYPE + && type_quals) pedwarn (loc, OPT_Wpedantic, "ISO C forbids qualified function types"); if (type_quals) @@ -5924,8 +5972,15 @@ grokdeclarator (const struct c_declarator *declarator, and fields. */ gcc_assert (storage_class == csc_none && !threadp && !declspecs->inline_p && !declspecs->noreturn_p); - if (pedantic && TREE_CODE (type) == FUNCTION_TYPE - && type_quals) + if ((type_quals & TYPE_QUAL_ATOMIC) + && TREE_CODE (type) == FUNCTION_TYPE) + { + error_at (loc, + "%<_Atomic%>-qualified function type"); + type_quals &= ~TYPE_QUAL_ATOMIC; + } + else if (pedantic && TREE_CODE (type) == FUNCTION_TYPE + && type_quals) pedwarn (loc, OPT_Wpedantic, "ISO C forbids const or volatile function types"); if (type_quals) @@ -5991,7 +6046,13 @@ grokdeclarator (const struct c_declarator *declarator, } else if (TREE_CODE (type) == FUNCTION_TYPE) { - if (type_quals) + if (type_quals & TYPE_QUAL_ATOMIC) + { + error_at (loc, + "%<_Atomic%>-qualified function type"); + type_quals &= ~TYPE_QUAL_ATOMIC; + } + else if (type_quals) pedwarn (loc, OPT_Wpedantic, "ISO C forbids qualified function types"); if (type_quals) @@ -6086,7 +6147,13 @@ grokdeclarator (const struct c_declarator *declarator, FUNCTION_DECL, declarator->u.id, type); decl = build_decl_attribute_variant (decl, decl_attr); - if (pedantic && type_quals && !DECL_IN_SYSTEM_HEADER (decl)) + if (type_quals & TYPE_QUAL_ATOMIC) + { + error_at (loc, + "%<_Atomic%>-qualified function type"); + type_quals &= ~TYPE_QUAL_ATOMIC; + } + else if (pedantic && type_quals && !DECL_IN_SYSTEM_HEADER (decl)) pedwarn (loc, OPT_Wpedantic, "ISO C forbids qualified function types"); @@ -6459,8 +6526,7 @@ get_parm_info (bool ellipsis, tree expr) && !DECL_NAME (b->decl) /* anonymous */ && VOID_TYPE_P (TREE_TYPE (b->decl))) /* of void type */ { - if (TREE_THIS_VOLATILE (b->decl) - || TREE_READONLY (b->decl) + if (TYPE_QUALS (TREE_TYPE (b->decl)) != TYPE_UNQUALIFIED || C_DECL_REGISTER (b->decl)) error ("%<void%> as only parameter may not be qualified"); @@ -8213,11 +8279,15 @@ store_parm_decls_oldstyle (tree fndecl, const struct c_arg_info *arg_info) type for parameters declared with qualified type. */ if (TREE_TYPE (parm) != error_mark_node && TREE_TYPE (type) != error_mark_node - && !comptypes (TYPE_MAIN_VARIANT (DECL_ARG_TYPE (parm)), - TYPE_MAIN_VARIANT (TREE_VALUE (type)))) + && ((TYPE_ATOMIC (DECL_ARG_TYPE (parm)) + != TYPE_ATOMIC (TREE_VALUE (type))) + || !comptypes (TYPE_MAIN_VARIANT (DECL_ARG_TYPE (parm)), + TYPE_MAIN_VARIANT (TREE_VALUE (type))))) { - if (TYPE_MAIN_VARIANT (TREE_TYPE (parm)) - == TYPE_MAIN_VARIANT (TREE_VALUE (type))) + if ((TYPE_ATOMIC (DECL_ARG_TYPE (parm)) + == TYPE_ATOMIC (TREE_VALUE (type))) + && (TYPE_MAIN_VARIANT (TREE_TYPE (parm)) + == TYPE_MAIN_VARIANT (TREE_VALUE (type)))) { /* Adjust argument to match prototype. E.g. a previous `int foo(float);' prototype causes @@ -8230,7 +8300,8 @@ store_parm_decls_oldstyle (tree fndecl, const struct c_arg_info *arg_info) && INTEGRAL_TYPE_P (TREE_TYPE (parm)) && TYPE_PRECISION (TREE_TYPE (parm)) < TYPE_PRECISION (integer_type_node)) - DECL_ARG_TYPE (parm) = integer_type_node; + DECL_ARG_TYPE (parm) + = c_type_promotes_to (TREE_TYPE (parm)); /* ??? Is it possible to get here with a built-in prototype or will it always have @@ -8432,7 +8503,7 @@ finish_function (void) tree type = TREE_TYPE (args); if (INTEGRAL_TYPE_P (type) && TYPE_PRECISION (type) < TYPE_PRECISION (integer_type_node)) - DECL_ARG_TYPE (args) = integer_type_node; + DECL_ARG_TYPE (args) = c_type_promotes_to (type); } } @@ -8911,6 +8982,7 @@ build_null_declspecs (void) ret->thread_p = false; ret->const_p = false; ret->volatile_p = false; + ret->atomic_p = false; ret->restrict_p = false; ret->saturating_p = false; ret->alignas_p = false; @@ -8972,6 +9044,10 @@ declspecs_add_qual (source_location loc, specs->restrict_p = true; specs->locations[cdw_restrict] = loc; break; + case RID_ATOMIC: + dupe = specs->atomic_p; + specs->atomic_p = true; + break; default: gcc_unreachable (); } diff --git a/gcc/c/c-parser.c b/gcc/c/c-parser.c index 4ae30c31916..09cce1c0924 100644 --- a/gcc/c/c-parser.c +++ b/gcc/c/c-parser.c @@ -494,6 +494,7 @@ c_token_starts_typename (c_token *token) case RID_UNION: case RID_TYPEOF: case RID_CONST: + case RID_ATOMIC: case RID_VOLATILE: case RID_RESTRICT: case RID_ATTRIBUTE: @@ -576,6 +577,7 @@ c_token_is_qualifier (c_token *token) case RID_VOLATILE: case RID_RESTRICT: case RID_ATTRIBUTE: + case RID_ATOMIC: return true; default: return false; @@ -656,6 +658,7 @@ c_token_starts_declspecs (c_token *token) case RID_ACCUM: case RID_SAT: case RID_ALIGNAS: + case RID_ATOMIC: return true; default: return false; @@ -1991,8 +1994,10 @@ c_parser_static_assert_declaration_no_semi (c_parser *parser) struct-or-union-specifier enum-specifier typedef-name + atomic-type-specifier (_Bool and _Complex are new in C99.) + (atomic-type-specifier is new in C11.) C90 6.5.3, C99 6.7.3: @@ -2001,8 +2006,10 @@ c_parser_static_assert_declaration_no_semi (c_parser *parser) restrict volatile address-space-qualifier + _Atomic (restrict is new in C99.) + (_Atomic is new in C11.) GNU extensions: @@ -2031,6 +2038,9 @@ c_parser_static_assert_declaration_no_semi (c_parser *parser) (_Fract, _Accum, and _Sat are new from ISO/IEC DTR 18037: http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1169.pdf) + atomic-type-specifier + _Atomic ( type-name ) + Objective-C: type-specifier: @@ -2224,6 +2234,64 @@ c_parser_declspecs (c_parser *parser, struct c_declspecs *specs, t = c_parser_typeof_specifier (parser); declspecs_add_type (loc, specs, t); break; + case RID_ATOMIC: + /* C parser handling of Objective-C constructs needs + checking for correct lvalue-to-rvalue conversions, and + the code in build_modify_expr handling various + Objective-C cases, and that in build_unary_op handling + Objective-C cases for increment / decrement, also needs + updating; uses of TYPE_MAIN_VARIANT in objc_compare_types + and objc_types_are_equivalent may also need updates. */ + if (c_dialect_objc ()) + sorry ("%<_Atomic%> in Objective-C"); + /* C parser handling of OpenMP constructs needs checking for + correct lvalue-to-rvalue conversions. */ + if (flag_openmp) + sorry ("%<_Atomic%> with OpenMP"); + if (!flag_isoc11) + { + if (flag_isoc99) + pedwarn (loc, OPT_Wpedantic, + "ISO C99 does not support the %<_Atomic%> qualifier"); + else + pedwarn (loc, OPT_Wpedantic, + "ISO C90 does not support the %<_Atomic%> qualifier"); + } + attrs_ok = true; + tree value; + value = c_parser_peek_token (parser)->value; + c_parser_consume_token (parser); + if (typespec_ok && c_parser_next_token_is (parser, CPP_OPEN_PAREN)) + { + /* _Atomic ( type-name ). */ + seen_type = true; + c_parser_consume_token (parser); + struct c_type_name *type = c_parser_type_name (parser); + t.kind = ctsk_typeof; + t.spec = error_mark_node; + t.expr = NULL_TREE; + t.expr_const_operands = true; + if (type != NULL) + t.spec = groktypename (type, &t.expr, + &t.expr_const_operands); + c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, + "expected %<)%>"); + if (t.spec != error_mark_node) + { + if (TREE_CODE (t.spec) == ARRAY_TYPE) + error_at (loc, "%<_Atomic%>-qualified array type"); + else if (TREE_CODE (t.spec) == FUNCTION_TYPE) + error_at (loc, "%<_Atomic%>-qualified function type"); + else if (TYPE_QUALS (t.spec) != TYPE_UNQUALIFIED) + error_at (loc, "%<_Atomic%> applied to a qualified type"); + else + t.spec = c_build_qualified_type (t.spec, TYPE_QUAL_ATOMIC); + } + declspecs_add_type (loc, specs, t); + } + else + declspecs_add_qual (loc, specs, value); + break; case RID_CONST: case RID_VOLATILE: case RID_RESTRICT: @@ -2826,6 +2894,16 @@ c_parser_typeof_specifier (c_parser *parser) if (was_vm) ret.expr = c_fully_fold (expr.value, false, &ret.expr_const_operands); pop_maybe_used (was_vm); + /* For use in macros such as those in <stdatomic.h>, remove + _Atomic and const qualifiers from atomic types. (Possibly + all qualifiers should be removed; const can be an issue for + more macros using typeof than just the <stdatomic.h> + ones.) */ + if (ret.spec != error_mark_node && TYPE_ATOMIC (ret.spec)) + ret.spec = c_build_qualified_type (ret.spec, + (TYPE_QUALS (ret.spec) + & ~(TYPE_QUAL_ATOMIC + | TYPE_QUAL_CONST))); } c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected %<)%>"); return ret; @@ -3114,7 +3192,10 @@ c_parser_direct_declarator_inner (c_parser *parser, bool id_present, struct c_declspecs *quals_attrs = build_null_declspecs (); bool static_seen; bool star_seen; - tree dimen; + struct c_expr dimen; + dimen.value = NULL_TREE; + dimen.original_code = ERROR_MARK; + dimen.original_type = NULL_TREE; c_parser_consume_token (parser); c_parser_declspecs (parser, quals_attrs, false, false, true, false, cla_prefer_id); @@ -3132,19 +3213,19 @@ c_parser_direct_declarator_inner (c_parser *parser, bool id_present, if (static_seen) { star_seen = false; - dimen = c_parser_expr_no_commas (parser, NULL).value; + dimen = c_parser_expr_no_commas (parser, NULL); } else { if (c_parser_next_token_is (parser, CPP_CLOSE_SQUARE)) { - dimen = NULL_TREE; + dimen.value = NULL_TREE; star_seen = false; } else if (flag_enable_cilkplus && c_parser_next_token_is (parser, CPP_COLON)) { - dimen = error_mark_node; + dimen.value = error_mark_node; star_seen = false; error_at (c_parser_peek_token (parser)->location, "array notations cannot be used in declaration"); @@ -3154,20 +3235,20 @@ c_parser_direct_declarator_inner (c_parser *parser, bool id_present, { if (c_parser_peek_2nd_token (parser)->type == CPP_CLOSE_SQUARE) { - dimen = NULL_TREE; + dimen.value = NULL_TREE; star_seen = true; c_parser_consume_token (parser); } else { star_seen = false; - dimen = c_parser_expr_no_commas (parser, NULL).value; + dimen = c_parser_expr_no_commas (parser, NULL); } } else { star_seen = false; - dimen = c_parser_expr_no_commas (parser, NULL).value; + dimen = c_parser_expr_no_commas (parser, NULL); } } if (c_parser_next_token_is (parser, CPP_CLOSE_SQUARE)) @@ -3186,9 +3267,9 @@ c_parser_direct_declarator_inner (c_parser *parser, bool id_present, "expected %<]%>"); return NULL; } - if (dimen) - mark_exp_read (dimen); - declarator = build_array_declarator (brace_loc, dimen, quals_attrs, + if (dimen.value) + dimen = convert_lvalue_to_rvalue (brace_loc, dimen, true, true); + declarator = build_array_declarator (brace_loc, dimen.value, quals_attrs, static_seen, star_seen); if (declarator == NULL) return NULL; @@ -3558,6 +3639,7 @@ c_parser_attribute_any_word (c_parser *parser) case RID_SAT: case RID_TRANSACTION_ATOMIC: case RID_TRANSACTION_CANCEL: + case RID_ATOMIC: ok = true; break; default: @@ -3814,7 +3896,7 @@ c_parser_initializer (c_parser *parser) ret = c_parser_expr_no_commas (parser, NULL); if (TREE_CODE (ret.value) != STRING_CST && TREE_CODE (ret.value) != COMPOUND_LITERAL_EXPR) - ret = default_function_array_read_conversion (loc, ret); + ret = convert_lvalue_to_rvalue (loc, ret, true, true); return ret; } } @@ -3993,8 +4075,8 @@ c_parser_initelt (c_parser *parser, struct obstack * braced_init_obstack) c_parser_consume_token (parser); exp_loc = c_parser_peek_token (parser)->location; next = c_parser_expr_no_commas (parser, NULL); - next = default_function_array_read_conversion (exp_loc, - next); + next = convert_lvalue_to_rvalue (exp_loc, next, + true, true); rec = build_compound_expr (comma_loc, rec, next.value); } parse_message_args: @@ -4090,7 +4172,7 @@ c_parser_initval (c_parser *parser, struct c_expr *after, if (init.value != NULL_TREE && TREE_CODE (init.value) != STRING_CST && TREE_CODE (init.value) != COMPOUND_LITERAL_EXPR) - init = default_function_array_read_conversion (loc, init); + init = convert_lvalue_to_rvalue (loc, init, true, true); } process_init_element (init, false, braced_init_obstack); } @@ -4605,12 +4687,12 @@ c_parser_statement_after_labels (c_parser *parser) } else if (c_parser_next_token_is (parser, CPP_MULT)) { - tree val; + struct c_expr val; c_parser_consume_token (parser); - val = c_parser_expression (parser).value; - mark_exp_read (val); - stmt = c_finish_goto_ptr (loc, val); + val = c_parser_expression (parser); + val = convert_lvalue_to_rvalue (loc, val, false, true); + stmt = c_finish_goto_ptr (loc, val.value); } else c_parser_error (parser, "expected identifier or %<*%>"); @@ -4659,9 +4741,10 @@ c_parser_statement_after_labels (c_parser *parser) } else { - tree expr = c_parser_expression (parser).value; - expr = c_fully_fold (expr, false, NULL); - stmt = objc_build_throw_stmt (loc, expr); + struct c_expr expr = c_parser_expression (parser); + expr = convert_lvalue_to_rvalue (loc, expr, false, false); + expr.value = c_fully_fold (expr.value, false, NULL); + stmt = objc_build_throw_stmt (loc, expr.value); goto expect_semicolon; } break; @@ -4873,6 +4956,7 @@ c_parser_if_statement (c_parser *parser) static void c_parser_switch_statement (c_parser *parser) { + struct c_expr ce; tree block, expr, body, save_break; location_t switch_loc = c_parser_peek_token (parser)->location; location_t switch_cond_loc; @@ -4882,7 +4966,9 @@ c_parser_switch_statement (c_parser *parser) if (c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>")) { switch_cond_loc = c_parser_peek_token (parser)->location; - expr = c_parser_expression (parser).value; + ce = c_parser_expression (parser); + ce = convert_lvalue_to_rvalue (switch_cond_loc, ce, true, false); + expr = ce.value; if (flag_enable_cilkplus && contains_array_notation_expr (expr)) { error_at (switch_cond_loc, @@ -5135,8 +5221,10 @@ c_parser_for_statement (c_parser *parser, bool ivdep) { init_expr: { + struct c_expr ce; tree init_expression; - init_expression = c_parser_expression (parser).value; + ce = c_parser_expression (parser); + init_expression = ce.value; parser->objc_could_be_foreach_context = false; if (c_parser_next_token_is_keyword (parser, RID_IN)) { @@ -5148,6 +5236,8 @@ c_parser_for_statement (c_parser *parser, bool ivdep) } else { + ce = convert_lvalue_to_rvalue (loc, ce, true, false); + init_expression = ce.value; c_finish_expr_stmt (loc, init_expression); c_parser_skip_until_found (parser, CPP_SEMICOLON, "expected %<;%>"); } @@ -5208,7 +5298,11 @@ c_parser_for_statement (c_parser *parser, bool ivdep) collection_expression = c_fully_fold (c_parser_expression (parser).value, false, NULL); else - incr = c_process_expr_stmt (loc, c_parser_expression (parser).value); + { + struct c_expr ce = c_parser_expression (parser); + ce = convert_lvalue_to_rvalue (loc, ce, true, false); + incr = c_process_expr_stmt (loc, ce.value); + } } c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected %<)%>"); } @@ -5565,7 +5659,7 @@ c_parser_expr_no_commas (c_parser *parser, struct c_expr *after, c_parser_consume_token (parser); exp_location = c_parser_peek_token (parser)->location; rhs = c_parser_expr_no_commas (parser, NULL); - rhs = default_function_array_read_conversion (exp_location, rhs); + rhs = convert_lvalue_to_rvalue (exp_location, rhs, true, true); ret.value = build_modify_expr (op_location, lhs.value, lhs.original_type, code, exp_location, rhs.value, @@ -5609,7 +5703,7 @@ c_parser_conditional_expression (c_parser *parser, struct c_expr *after, if (c_parser_next_token_is_not (parser, CPP_QUERY)) return cond; cond_loc = c_parser_peek_token (parser)->location; - cond = default_function_array_read_conversion (cond_loc, cond); + cond = convert_lvalue_to_rvalue (cond_loc, cond, true, true); c_parser_consume_token (parser); if (c_parser_next_token_is (parser, CPP_COLON)) { @@ -5657,7 +5751,7 @@ c_parser_conditional_expression (c_parser *parser, struct c_expr *after, { location_t exp2_loc = c_parser_peek_token (parser)->location; exp2 = c_parser_conditional_expression (parser, NULL, NULL_TREE); - exp2 = default_function_array_read_conversion (exp2_loc, exp2); + exp2 = convert_lvalue_to_rvalue (exp2_loc, exp2, true, true); } c_inhibit_evaluation_warnings -= cond.value == truthvalue_true_node; ret.value = build_conditional_expr (colon_loc, cond.value, @@ -5801,11 +5895,11 @@ c_parser_binary_expression (c_parser *parser, struct c_expr *after, break; \ } \ stack[sp - 1].expr \ - = default_function_array_read_conversion (stack[sp - 1].loc, \ - stack[sp - 1].expr); \ + = convert_lvalue_to_rvalue (stack[sp - 1].loc, \ + stack[sp - 1].expr, true, true); \ stack[sp].expr \ - = default_function_array_read_conversion (stack[sp].loc, \ - stack[sp].expr); \ + = convert_lvalue_to_rvalue (stack[sp].loc, \ + stack[sp].expr, true, true); \ if (__builtin_expect (omp_atomic_lhs != NULL_TREE, 0) && sp == 1 \ && c_parser_peek_token (parser)->type == CPP_SEMICOLON \ && ((1 << stack[sp].prec) \ @@ -5924,8 +6018,8 @@ c_parser_binary_expression (c_parser *parser, struct c_expr *after, { case TRUTH_ANDIF_EXPR: stack[sp].expr - = default_function_array_read_conversion (stack[sp].loc, - stack[sp].expr); + = convert_lvalue_to_rvalue (stack[sp].loc, + stack[sp].expr, true, true); stack[sp].expr.value = c_objc_common_truthvalue_conversion (stack[sp].loc, default_conversion (stack[sp].expr.value)); c_inhibit_evaluation_warnings += (stack[sp].expr.value @@ -5933,8 +6027,8 @@ c_parser_binary_expression (c_parser *parser, struct c_expr *after, break; case TRUTH_ORIF_EXPR: stack[sp].expr - = default_function_array_read_conversion (stack[sp].loc, - stack[sp].expr); + = convert_lvalue_to_rvalue (stack[sp].loc, + stack[sp].expr, true, true); stack[sp].expr.value = c_objc_common_truthvalue_conversion (stack[sp].loc, default_conversion (stack[sp].expr.value)); c_inhibit_evaluation_warnings += (stack[sp].expr.value @@ -6005,7 +6099,7 @@ c_parser_cast_expression (c_parser *parser, struct c_expr *after) { location_t expr_loc = c_parser_peek_token (parser)->location; expr = c_parser_cast_expression (parser, NULL); - expr = default_function_array_read_conversion (expr_loc, expr); + expr = convert_lvalue_to_rvalue (expr_loc, expr, true, true); } ret.value = c_cast_expr (cast_loc, type_name, expr.value); ret.original_code = ERROR_MARK; @@ -6096,7 +6190,7 @@ c_parser_unary_expression (c_parser *parser) c_parser_consume_token (parser); exp_loc = c_parser_peek_token (parser)->location; op = c_parser_cast_expression (parser, NULL); - op = default_function_array_read_conversion (exp_loc, op); + op = convert_lvalue_to_rvalue (exp_loc, op, true, true); ret.value = build_indirect_ref (op_loc, op.value, RO_UNARY_STAR); return ret; case CPP_PLUS: @@ -6107,25 +6201,25 @@ c_parser_unary_expression (c_parser *parser) c_parser_consume_token (parser); exp_loc = c_parser_peek_token (parser)->location; op = c_parser_cast_expression (parser, NULL); - op = default_function_array_read_conversion (exp_loc, op); + op = convert_lvalue_to_rvalue (exp_loc, op, true, true); return parser_build_unary_op (op_loc, CONVERT_EXPR, op); case CPP_MINUS: c_parser_consume_token (parser); exp_loc = c_parser_peek_token (parser)->location; op = c_parser_cast_expression (parser, NULL); - op = default_function_array_read_conversion (exp_loc, op); + op = convert_lvalue_to_rvalue (exp_loc, op, true, true); return parser_build_unary_op (op_loc, NEGATE_EXPR, op); case CPP_COMPL: c_parser_consume_token (parser); exp_loc = c_parser_peek_token (parser)->location; op = c_parser_cast_expression (parser, NULL); - op = default_function_array_read_conversion (exp_loc, op); + op = convert_lvalue_to_rvalue (exp_loc, op, true, true); return parser_build_unary_op (op_loc, BIT_NOT_EXPR, op); case CPP_NOT: c_parser_consume_token (parser); exp_loc = c_parser_peek_token (parser)->location; op = c_parser_cast_expression (parser, NULL); - op = default_function_array_read_conversion (exp_loc, op); + op = convert_lvalue_to_rvalue (exp_loc, op, true, true); return parser_build_unary_op (op_loc, TRUTH_NOT_EXPR, op); case CPP_AND_AND: /* Refer to the address of a label as a pointer. */ @@ -6918,10 +7012,13 @@ c_parser_postfix_expression (c_parser *parser) } else { + struct c_expr ce; tree idx; loc = c_parser_peek_token (parser)->location; c_parser_consume_token (parser); - idx = c_parser_expression (parser).value; + ce = c_parser_expression (parser); + ce = convert_lvalue_to_rvalue (loc, ce, false, false); + idx = ce.value; idx = c_fully_fold (idx, false, NULL); c_parser_skip_until_found (parser, CPP_CLOSE_SQUARE, "expected %<]%>"); @@ -7044,11 +7141,11 @@ c_parser_postfix_expression (c_parser *parser) e1_p = &(*cexpr_list)[0]; e2_p = &(*cexpr_list)[1]; - mark_exp_read (e1_p->value); + *e1_p = convert_lvalue_to_rvalue (loc, *e1_p, true, true); if (TREE_CODE (e1_p->value) == EXCESS_PRECISION_EXPR) e1_p->value = convert (TREE_TYPE (e1_p->value), TREE_OPERAND (e1_p->value, 0)); - mark_exp_read (e2_p->value); + *e2_p = convert_lvalue_to_rvalue (loc, *e2_p, true, true); if (TREE_CODE (e2_p->value) == EXCESS_PRECISION_EXPR) e2_p->value = convert (TREE_TYPE (e2_p->value), TREE_OPERAND (e2_p->value, 0)); @@ -7096,7 +7193,7 @@ c_parser_postfix_expression (c_parser *parser) } FOR_EACH_VEC_SAFE_ELT (cexpr_list, i, p) - mark_exp_read (p->value); + *p = convert_lvalue_to_rvalue (loc, *p, true, true); if (vec_safe_length (cexpr_list) == 2) expr.value = @@ -7440,7 +7537,7 @@ c_parser_postfix_expression_after_primary (c_parser *parser, case CPP_DEREF: /* Structure element reference. */ c_parser_consume_token (parser); - expr = default_function_array_conversion (expr_loc, expr); + expr = convert_lvalue_to_rvalue (expr_loc, expr, true, false); if (c_parser_next_token_is (parser, CPP_NAME)) ident = c_parser_peek_token (parser)->value; else @@ -7518,8 +7615,11 @@ c_parser_postfix_expression_after_primary (c_parser *parser, static struct c_expr c_parser_expression (c_parser *parser) { + location_t tloc = c_parser_peek_token (parser)->location; struct c_expr expr; expr = c_parser_expr_no_commas (parser, NULL); + if (c_parser_next_token_is (parser, CPP_COMMA)) + expr = convert_lvalue_to_rvalue (tloc, expr, true, false); while (c_parser_next_token_is (parser, CPP_COMMA)) { struct c_expr next; @@ -7534,7 +7634,7 @@ c_parser_expression (c_parser *parser) if (DECL_P (lhsval) || handled_component_p (lhsval)) mark_exp_read (lhsval); next = c_parser_expr_no_commas (parser, NULL); - next = default_function_array_conversion (expr_loc, next); + next = convert_lvalue_to_rvalue (expr_loc, next, true, false); expr.value = build_compound_expr (loc, expr.value, next.value); expr.original_code = COMPOUND_EXPR; expr.original_type = next.original_type; @@ -7542,8 +7642,8 @@ c_parser_expression (c_parser *parser) return expr; } -/* Parse an expression and convert functions or arrays to - pointers. */ +/* Parse an expression and convert functions or arrays to pointers and + lvalues to rvalues. */ static struct c_expr c_parser_expression_conv (c_parser *parser) @@ -7551,12 +7651,13 @@ c_parser_expression_conv (c_parser *parser) struct c_expr expr; location_t loc = c_parser_peek_token (parser)->location; expr = c_parser_expression (parser); - expr = default_function_array_conversion (loc, expr); + expr = convert_lvalue_to_rvalue (loc, expr, true, false); return expr; } /* Parse a non-empty list of expressions. If CONVERT_P, convert - functions and arrays to pointers. If FOLD_P, fold the expressions. + functions and arrays to pointers and lvalues to rvalues. If + FOLD_P, fold the expressions. nonempty-expr-list: assignment-expression @@ -7586,7 +7687,7 @@ c_parser_expr_list (c_parser *parser, bool convert_p, bool fold_p, cur_sizeof_arg_loc = c_parser_peek_2nd_token (parser)->location; expr = c_parser_expr_no_commas (parser, NULL); if (convert_p) - expr = default_function_array_read_conversion (loc, expr); + expr = convert_lvalue_to_rvalue (loc, expr, true, true); if (fold_p) expr.value = c_fully_fold (expr.value, false, NULL); ret->quick_push (expr.value); @@ -7610,7 +7711,7 @@ c_parser_expr_list (c_parser *parser, bool convert_p, bool fold_p, cur_sizeof_arg_loc = UNKNOWN_LOCATION; expr = c_parser_expr_no_commas (parser, NULL); if (convert_p) - expr = default_function_array_read_conversion (loc, expr); + expr = convert_lvalue_to_rvalue (loc, expr, true, true); if (fold_p) expr.value = c_fully_fold (expr.value, false, NULL); vec_safe_push (ret, expr.value); @@ -8516,7 +8617,9 @@ c_parser_objc_synchronized_statement (c_parser *parser) objc_maybe_warn_exceptions (loc); if (c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>")) { - expr = c_parser_expression (parser).value; + struct c_expr ce = c_parser_expression (parser); + ce = convert_lvalue_to_rvalue (loc, ce, false, false); + expr = ce.value; expr = c_fully_fold (expr, false, NULL); c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected %<)%>"); } @@ -8536,6 +8639,7 @@ c_parser_objc_synchronized_statement (c_parser *parser) break continue return goto asm sizeof typeof __alignof unsigned long const short volatile signed restrict _Complex in out inout bycopy byref oneway int char float double void _Bool + _Atomic ??? Why this selection of keywords but not, for example, storage class specifiers? */ @@ -8594,6 +8698,7 @@ c_parser_objc_selector (c_parser *parser) case RID_DOUBLE: case RID_VOID: case RID_BOOL: + case RID_ATOMIC: c_parser_consume_token (parser); return value; default: @@ -8646,6 +8751,8 @@ c_parser_objc_selector_arg (c_parser *parser) static tree c_parser_objc_receiver (c_parser *parser) { + location_t loc = c_parser_peek_token (parser)->location; + if (c_parser_peek_token (parser)->type == CPP_NAME && (c_parser_peek_token (parser)->id_kind == C_ID_TYPENAME || c_parser_peek_token (parser)->id_kind == C_ID_CLASSNAME)) @@ -8654,7 +8761,9 @@ c_parser_objc_receiver (c_parser *parser) c_parser_consume_token (parser); return objc_get_class_reference (id); } - return c_fully_fold (c_parser_expression (parser).value, false, NULL); + struct c_expr ce = c_parser_expression (parser); + ce = convert_lvalue_to_rvalue (loc, ce, false, false); + return c_fully_fold (ce.value, false, NULL); } /* Parse objc-message-args. @@ -13441,7 +13550,9 @@ c_parser_array_notation (location_t loc, c_parser *parser, tree initial_index, return error_mark_node; } c_parser_consume_token (parser); /* consume the ':' */ - end_index = c_parser_expression (parser).value; + struct c_expr ce = c_parser_expression (parser); + ce = convert_lvalue_to_rvalue (loc, ce, false, false); + end_index = ce.value; if (!end_index || end_index == error_mark_node) { c_parser_skip_to_end_of_block_or_statement (parser); @@ -13450,7 +13561,9 @@ c_parser_array_notation (location_t loc, c_parser *parser, tree initial_index, if (c_parser_peek_token (parser)->type == CPP_COLON) { c_parser_consume_token (parser); - stride = c_parser_expression (parser).value; + ce = c_parser_expression (parser); + ce = convert_lvalue_to_rvalue (loc, ce, false, false); + stride = ce.value; if (!stride || stride == error_mark_node) { c_parser_skip_to_end_of_block_or_statement (parser); diff --git a/gcc/c/c-tree.h b/gcc/c/c-tree.h index 2565ccb4f15..8dffa9c1674 100644 --- a/gcc/c/c-tree.h +++ b/gcc/c/c-tree.h @@ -163,7 +163,7 @@ enum c_typespec_kind { ctsk_typedef, /* An ObjC-specific kind of type specifier. */ ctsk_objc, - /* A typeof specifier. */ + /* A typeof specifier, or _Atomic ( type-name ). */ ctsk_typeof }; @@ -328,6 +328,8 @@ struct c_declspecs { BOOL_BITFIELD volatile_p : 1; /* Whether "restrict" was specified. */ BOOL_BITFIELD restrict_p : 1; + /* Whether "_Atomic" was specified. */ + BOOL_BITFIELD atomic_p : 1; /* Whether "_Sat" was specified. */ BOOL_BITFIELD saturating_p : 1; /* Whether any alignment specifier (even with zero alignment) was @@ -585,6 +587,8 @@ extern struct c_expr default_function_array_conversion (location_t, struct c_expr); extern struct c_expr default_function_array_read_conversion (location_t, struct c_expr); +extern struct c_expr convert_lvalue_to_rvalue (location_t, struct c_expr, + bool, bool); extern void mark_exp_read (tree); extern tree composite_type (tree, tree); extern tree build_component_ref (location_t, tree, tree); diff --git a/gcc/c/c-typeck.c b/gcc/c/c-typeck.c index 8f1d3a4837a..5ef1f9303f1 100644 --- a/gcc/c/c-typeck.c +++ b/gcc/c/c-typeck.c @@ -265,18 +265,25 @@ c_incomplete_type_error (const_tree value, const_tree type) tree c_type_promotes_to (tree type) { - if (TYPE_MAIN_VARIANT (type) == float_type_node) - return double_type_node; + tree ret = NULL_TREE; - if (c_promoting_integer_type_p (type)) + if (TYPE_MAIN_VARIANT (type) == float_type_node) + ret = double_type_node; + else if (c_promoting_integer_type_p (type)) { /* Preserve unsignedness if not really getting any wider. */ if (TYPE_UNSIGNED (type) && (TYPE_PRECISION (type) == TYPE_PRECISION (integer_type_node))) - return unsigned_type_node; - return integer_type_node; + ret = unsigned_type_node; + else + ret = integer_type_node; } + if (ret != NULL_TREE) + return (TYPE_ATOMIC (type) + ? c_build_qualified_type (ret, TYPE_QUAL_ATOMIC) + : ret); + return type; } @@ -327,7 +334,7 @@ qualify_type (tree type, tree like) return c_build_qualified_type (type, TYPE_QUALS_NO_ADDR_SPACE (type) - | TYPE_QUALS_NO_ADDR_SPACE (like) + | TYPE_QUALS_NO_ADDR_SPACE_NO_ATOMIC (like) | ENCODE_QUAL_ADDR_SPACE (as_common)); } @@ -1214,9 +1221,13 @@ comp_target_types (location_t location, tree ttl, tree ttr) /* Do not lose qualifiers on element types of array types that are pointer targets by taking their TYPE_MAIN_VARIANT. */ if (TREE_CODE (mvl) != ARRAY_TYPE) - mvl = TYPE_MAIN_VARIANT (mvl); + mvl = (TYPE_ATOMIC (mvl) + ? c_build_qualified_type (TYPE_MAIN_VARIANT (mvl), TYPE_QUAL_ATOMIC) + : TYPE_MAIN_VARIANT (mvl)); if (TREE_CODE (mvr) != ARRAY_TYPE) - mvr = TYPE_MAIN_VARIANT (mvr); + mvr = (TYPE_ATOMIC (mvr) + ? c_build_qualified_type (TYPE_MAIN_VARIANT (mvr), TYPE_QUAL_ATOMIC) + : TYPE_MAIN_VARIANT (mvr)); enum_and_int_p = false; val = comptypes_check_enum_int (mvl, mvr, &enum_and_int_p); @@ -1633,9 +1644,15 @@ type_lists_compatible_p (const_tree args1, const_tree args2, mv1 = a1 = TREE_VALUE (args1); mv2 = a2 = TREE_VALUE (args2); if (mv1 && mv1 != error_mark_node && TREE_CODE (mv1) != ARRAY_TYPE) - mv1 = TYPE_MAIN_VARIANT (mv1); + mv1 = (TYPE_ATOMIC (mv1) + ? c_build_qualified_type (TYPE_MAIN_VARIANT (mv1), + TYPE_QUAL_ATOMIC) + : TYPE_MAIN_VARIANT (mv1)); if (mv2 && mv2 != error_mark_node && TREE_CODE (mv2) != ARRAY_TYPE) - mv2 = TYPE_MAIN_VARIANT (mv2); + mv2 = (TYPE_ATOMIC (mv2) + ? c_build_qualified_type (TYPE_MAIN_VARIANT (mv2), + TYPE_QUAL_ATOMIC) + : TYPE_MAIN_VARIANT (mv2)); /* A null pointer instead of a type means there is supposed to be an argument but nothing is specified about what type it has. @@ -1678,7 +1695,10 @@ type_lists_compatible_p (const_tree args1, const_tree args2, tree mv3 = TREE_TYPE (memb); if (mv3 && mv3 != error_mark_node && TREE_CODE (mv3) != ARRAY_TYPE) - mv3 = TYPE_MAIN_VARIANT (mv3); + mv3 = (TYPE_ATOMIC (mv3) + ? c_build_qualified_type (TYPE_MAIN_VARIANT (mv3), + TYPE_QUAL_ATOMIC) + : TYPE_MAIN_VARIANT (mv3)); if (comptypes_internal (mv3, mv2, enum_and_int_p, different_types_p)) break; @@ -1700,7 +1720,10 @@ type_lists_compatible_p (const_tree args1, const_tree args2, tree mv3 = TREE_TYPE (memb); if (mv3 && mv3 != error_mark_node && TREE_CODE (mv3) != ARRAY_TYPE) - mv3 = TYPE_MAIN_VARIANT (mv3); + mv3 = (TYPE_ATOMIC (mv3) + ? c_build_qualified_type (TYPE_MAIN_VARIANT (mv3), + TYPE_QUAL_ATOMIC) + : TYPE_MAIN_VARIANT (mv3)); if (comptypes_internal (mv3, mv1, enum_and_int_p, different_types_p)) break; @@ -1913,6 +1936,84 @@ default_function_array_read_conversion (location_t loc, struct c_expr exp) return default_function_array_conversion (loc, exp); } +/* Return whether EXPR should be treated as an atomic lvalue for the + purposes of load and store handling. */ + +static bool +really_atomic_lvalue (tree expr) +{ + if (expr == error_mark_node || TREE_TYPE (expr) == error_mark_node) + return false; + if (!TYPE_ATOMIC (TREE_TYPE (expr))) + return false; + if (!lvalue_p (expr)) + return false; + + /* Ignore _Atomic on register variables, since their addresses can't + be taken so (a) atomicity is irrelevant and (b) the normal atomic + sequences wouldn't work. Ignore _Atomic on structures containing + bit-fields, since accessing elements of atomic structures or + unions is undefined behavior (C11 6.5.2.3#5), but it's unclear if + it's undefined at translation time or execution time, and the + normal atomic sequences again wouldn't work. */ + while (handled_component_p (expr)) + { + if (TREE_CODE (expr) == COMPONENT_REF + && DECL_C_BIT_FIELD (TREE_OPERAND (expr, 1))) + return false; + expr = TREE_OPERAND (expr, 0); + } + if (DECL_P (expr) && C_DECL_REGISTER (expr)) + return false; + return true; +} + +/* Convert expression EXP (location LOC) from lvalue to rvalue, + including converting functions and arrays to pointers if CONVERT_P. + If READ_P, also mark the expression as having been read. */ + +struct c_expr +convert_lvalue_to_rvalue (location_t loc, struct c_expr exp, + bool convert_p, bool read_p) +{ + if (read_p) + mark_exp_read (exp.value); + if (convert_p) + exp = default_function_array_conversion (loc, exp); + if (really_atomic_lvalue (exp.value)) + { + vec<tree, va_gc> *params; + tree nonatomic_type, tmp, tmp_addr, fndecl, func_call; + tree expr_type = TREE_TYPE (exp.value); + tree expr_addr = build_unary_op (loc, ADDR_EXPR, exp.value, 0); + tree seq_cst = build_int_cst (integer_type_node, MEMMODEL_SEQ_CST); + + gcc_assert (TYPE_ATOMIC (expr_type)); + + /* Expansion of a generic atomic load may require an addition + element, so allocate enough to prevent a resize. */ + vec_alloc (params, 4); + + /* Remove the qualifiers for the rest of the expressions and + create the VAL temp variable to hold the RHS. */ + nonatomic_type = build_qualified_type (expr_type, TYPE_UNQUALIFIED); + tmp = create_tmp_var (nonatomic_type, NULL); + tmp_addr = build_unary_op (loc, ADDR_EXPR, tmp, 0); + TREE_ADDRESSABLE (tmp) = 1; + + /* Issue __atomic_load (&expr, &tmp, SEQ_CST); */ + fndecl = builtin_decl_explicit (BUILT_IN_ATOMIC_LOAD); + params->quick_push (expr_addr); + params->quick_push (tmp_addr); + params->quick_push (seq_cst); + func_call = build_function_call_vec (loc, fndecl, params, NULL); + + /* Return tmp which contains the value loaded. */ + exp.value = build2 (COMPOUND_EXPR, nonatomic_type, func_call, tmp); + } + return exp; +} + /* EXP is an expression of integer type. Apply the integer promotions to it and return the promoted value. */ @@ -3435,6 +3536,215 @@ pointer_diff (location_t loc, tree op0, tree op1) return convert (restype, result); } +/* Expand atomic compound assignments into an approriate sequence as + specified by the C11 standard section 6.5.16.2. + given + _Atomic T1 E1 + T2 E2 + E1 op= E2 + + This sequence is used for all types for which these operations are + supported. + + In addition, built-in versions of the 'fe' prefixed routines may + need to be invoked for floating point (real, complex or vector) when + floating-point exceptions are supported. See 6.5.16.2 footnote 113. + + T1 newval; + T1 old; + T1 *addr + T2 val + fenv_t fenv + + addr = &E1; + val = (E2); + __atomic_load (addr, &old, SEQ_CST); + feholdexcept (&fenv); +loop: + newval = old op val; + if (__atomic_compare_exchange_strong (addr, &old, &newval, SEQ_CST, + SEQ_CST)) + goto done; + feclearexcept (FE_ALL_EXCEPT); + goto loop: +done: + feupdateenv (&fenv); + + Also note that the compiler is simply issuing the generic form of + the atomic operations. This requires temp(s) and has their address + taken. The atomic processing is smart enough to figure out when the + size of an object can utilize a lock-free version, and convert the + built-in call to the appropriate lock-free routine. The optimizers + will then dispose of any temps that are no longer required, and + lock-free implementations are utilized as long as there is target + support for the required size. + + If the operator is NOP_EXPR, then this is a simple assignment, and + an __atomic_store is issued to perform the assignment rather than + the above loop. + +*/ + +/* Build an atomic assignment at LOC, expanding into the proper + sequence to store LHS MODIFYCODE= RHS. Return a value representing + the result of the operation, unless RETURN_OLD_P in which case + return the old value of LHS (this is only for postincrement and + postdecrement). */ +static tree +build_atomic_assign (location_t loc, tree lhs, enum tree_code modifycode, + tree rhs, bool return_old_p) +{ + tree fndecl, func_call; + vec<tree, va_gc> *params; + tree val, nonatomic_lhs_type, nonatomic_rhs_type, newval, newval_addr; + tree old, old_addr; + tree compound_stmt; + tree stmt, goto_stmt; + tree loop_label, loop_decl, done_label, done_decl; + + tree lhs_type = TREE_TYPE (lhs); + tree lhs_addr = build_unary_op (loc, ADDR_EXPR, lhs, 0); + tree seq_cst = build_int_cst (integer_type_node, MEMMODEL_SEQ_CST); + tree rhs_type = TREE_TYPE (rhs); + + gcc_assert (TYPE_ATOMIC (lhs_type)); + + if (return_old_p) + gcc_assert (modifycode == PLUS_EXPR || modifycode == MINUS_EXPR); + + /* Allocate enough vector items for a compare_exchange. */ + vec_alloc (params, 6); + + /* Create a compound statement to hold the sequence of statements + with a loop. */ + compound_stmt = c_begin_compound_stmt (false); + + /* Fold the RHS if it hasn't already been folded. */ + if (modifycode != NOP_EXPR) + rhs = c_fully_fold (rhs, false, NULL); + + /* Remove the qualifiers for the rest of the expressions and create + the VAL temp variable to hold the RHS. */ + nonatomic_lhs_type = build_qualified_type (lhs_type, TYPE_UNQUALIFIED); + nonatomic_rhs_type = build_qualified_type (rhs_type, TYPE_UNQUALIFIED); + val = create_tmp_var (nonatomic_rhs_type, NULL); + TREE_ADDRESSABLE (val) = 1; + rhs = build2 (MODIFY_EXPR, nonatomic_rhs_type, val, rhs); + SET_EXPR_LOCATION (rhs, loc); + add_stmt (rhs); + + /* NOP_EXPR indicates it's a straight store of the RHS. Simply issue + an atomic_store. */ + if (modifycode == NOP_EXPR) + { + /* Build __atomic_store (&lhs, &val, SEQ_CST) */ + rhs = build_unary_op (loc, ADDR_EXPR, val, 0); + fndecl = builtin_decl_explicit (BUILT_IN_ATOMIC_STORE); + params->quick_push (lhs_addr); + params->quick_push (rhs); + params->quick_push (seq_cst); + func_call = build_function_call_vec (loc, fndecl, params, NULL); + add_stmt (func_call); + + /* Finish the compound statement. */ + compound_stmt = c_end_compound_stmt (loc, compound_stmt, false); + + /* VAL is the value which was stored, return a COMPOUND_STMT of + the statement and that value. */ + return build2 (COMPOUND_EXPR, nonatomic_lhs_type, compound_stmt, val); + } + + /* Create the variables and labels required for the op= form. */ + old = create_tmp_var (nonatomic_lhs_type, NULL); + old_addr = build_unary_op (loc, ADDR_EXPR, old, 0); + TREE_ADDRESSABLE (val) = 1; + + newval = create_tmp_var (nonatomic_lhs_type, NULL); + newval_addr = build_unary_op (loc, ADDR_EXPR, newval, 0); + TREE_ADDRESSABLE (newval) = 1; + + loop_decl = create_artificial_label (loc); + loop_label = build1 (LABEL_EXPR, void_type_node, loop_decl); + + done_decl = create_artificial_label (loc); + done_label = build1 (LABEL_EXPR, void_type_node, done_decl); + + /* __atomic_load (addr, &old, SEQ_CST). */ + fndecl = builtin_decl_explicit (BUILT_IN_ATOMIC_LOAD); + params->quick_push (lhs_addr); + params->quick_push (old_addr); + params->quick_push (seq_cst); + func_call = build_function_call_vec (loc, fndecl, params, NULL); + add_stmt (func_call); + params->truncate (0); + + /* Create the expressions for floating-point environment + manipulation, if required. */ + bool need_fenv = (flag_trapping_math + && (FLOAT_TYPE_P (lhs_type) || FLOAT_TYPE_P (rhs_type))); + tree hold_call = NULL_TREE, clear_call = NULL_TREE, update_call = NULL_TREE; + if (need_fenv) + targetm.atomic_assign_expand_fenv (&hold_call, &clear_call, &update_call); + + if (hold_call) + add_stmt (hold_call); + + /* loop: */ + add_stmt (loop_label); + + /* newval = old + val; */ + rhs = build_binary_op (loc, modifycode, old, val, 1); + rhs = convert_for_assignment (loc, nonatomic_lhs_type, rhs, NULL_TREE, + ic_assign, false, NULL_TREE, + NULL_TREE, 0); + if (rhs != error_mark_node) + { + rhs = build2 (MODIFY_EXPR, nonatomic_lhs_type, newval, rhs); + SET_EXPR_LOCATION (rhs, loc); + add_stmt (rhs); + } + + /* if (__atomic_compare_exchange (addr, &old, &new, false, SEQ_CST, SEQ_CST)) + goto done; */ + fndecl = builtin_decl_explicit (BUILT_IN_ATOMIC_COMPARE_EXCHANGE); + params->quick_push (lhs_addr); + params->quick_push (old_addr); + params->quick_push (newval_addr); + params->quick_push (integer_zero_node); + params->quick_push (seq_cst); + params->quick_push (seq_cst); + func_call = build_function_call_vec (loc, fndecl, params, NULL); + + goto_stmt = build1 (GOTO_EXPR, void_type_node, done_decl); + SET_EXPR_LOCATION (goto_stmt, loc); + + stmt = build3 (COND_EXPR, void_type_node, func_call, goto_stmt, NULL_TREE); + SET_EXPR_LOCATION (stmt, loc); + add_stmt (stmt); + + if (clear_call) + add_stmt (clear_call); + + /* goto loop; */ + goto_stmt = build1 (GOTO_EXPR, void_type_node, loop_decl); + SET_EXPR_LOCATION (goto_stmt, loc); + add_stmt (goto_stmt); + + /* done: */ + add_stmt (done_label); + + if (update_call) + add_stmt (update_call); + + /* Finish the compound statement. */ + compound_stmt = c_end_compound_stmt (loc, compound_stmt, false); + + /* NEWVAL is the value that was successfully stored, return a + COMPOUND_EXPR of the statement and the appropriate value. */ + return build2 (COMPOUND_EXPR, nonatomic_lhs_type, compound_stmt, + return_old_p ? old : newval); +} + /* Construct and perhaps optimize a tree representation for a unary operation. CODE, a tree_code, specifies the operation and XARG is the operand. @@ -3635,6 +3945,9 @@ build_unary_op (location_t location, /* Ensure the argument is fully folded inside any SAVE_EXPR. */ arg = c_fully_fold (arg, false, NULL); + bool atomic_op; + atomic_op = really_atomic_lvalue (arg); + /* Increment or decrement the real part of the value, and don't change the imaginary part. */ if (typecode == COMPLEX_TYPE) @@ -3644,21 +3957,25 @@ build_unary_op (location_t location, pedwarn (location, OPT_Wpedantic, "ISO C does not support %<++%> and %<--%> on complex types"); - arg = stabilize_reference (arg); - real = build_unary_op (EXPR_LOCATION (arg), REALPART_EXPR, arg, 1); - imag = build_unary_op (EXPR_LOCATION (arg), IMAGPART_EXPR, arg, 1); - real = build_unary_op (EXPR_LOCATION (arg), code, real, 1); - if (real == error_mark_node || imag == error_mark_node) - return error_mark_node; - ret = build2 (COMPLEX_EXPR, TREE_TYPE (arg), - real, imag); - goto return_build_unary_op; + if (!atomic_op) + { + arg = stabilize_reference (arg); + real = build_unary_op (EXPR_LOCATION (arg), REALPART_EXPR, arg, 1); + imag = build_unary_op (EXPR_LOCATION (arg), IMAGPART_EXPR, arg, 1); + real = build_unary_op (EXPR_LOCATION (arg), code, real, 1); + if (real == error_mark_node || imag == error_mark_node) + return error_mark_node; + ret = build2 (COMPLEX_EXPR, TREE_TYPE (arg), + real, imag); + goto return_build_unary_op; + } } /* Report invalid types. */ if (typecode != POINTER_TYPE && typecode != FIXED_POINT_TYPE - && typecode != INTEGER_TYPE && typecode != REAL_TYPE) + && typecode != INTEGER_TYPE && typecode != REAL_TYPE + && typecode != COMPLEX_TYPE) { if (code == PREINCREMENT_EXPR || code == POSTINCREMENT_EXPR) error_at (location, "wrong type argument to increment"); @@ -3749,6 +4066,24 @@ build_unary_op (location_t location, || code == POSTINCREMENT_EXPR) ? lv_increment : lv_decrement)); + /* If the argument is atomic, use the special code sequences for + atomic compound assignment. */ + if (atomic_op) + { + arg = stabilize_reference (arg); + ret = build_atomic_assign (location, arg, + ((code == PREINCREMENT_EXPR + || code == POSTINCREMENT_EXPR) + ? PLUS_EXPR + : MINUS_EXPR), + (FRACT_MODE_P (TYPE_MODE (argtype)) + ? inc + : integer_one_node), + (code == POSTINCREMENT_EXPR + || code == POSTDECREMENT_EXPR)); + goto return_build_unary_op; + } + if (TREE_CODE (TREE_TYPE (arg)) == BOOLEAN_TYPE) val = boolean_increment (code, arg); else @@ -4259,7 +4594,8 @@ build_conditional_expr (location_t colon_loc, tree ifexp, bool ifexp_bcp, "used in conditional expression"); return error_mark_node; } - else if (VOID_TYPE_P (TREE_TYPE (type1))) + else if (VOID_TYPE_P (TREE_TYPE (type1)) + && !TYPE_ATOMIC (TREE_TYPE (type1))) { if (TREE_CODE (TREE_TYPE (type2)) == FUNCTION_TYPE) pedwarn (colon_loc, OPT_Wpedantic, @@ -4268,7 +4604,8 @@ build_conditional_expr (location_t colon_loc, tree ifexp, bool ifexp_bcp, result_type = build_pointer_type (qualify_type (TREE_TYPE (type1), TREE_TYPE (type2))); } - else if (VOID_TYPE_P (TREE_TYPE (type2))) + else if (VOID_TYPE_P (TREE_TYPE (type2)) + && !TYPE_ATOMIC (TREE_TYPE (type2))) { if (TREE_CODE (TREE_TYPE (type1)) == FUNCTION_TYPE) pedwarn (colon_loc, OPT_Wpedantic, @@ -4850,6 +5187,7 @@ build_modify_expr (location_t location, tree lhs, tree lhs_origtype, tree lhstype = TREE_TYPE (lhs); tree olhstype = lhstype; bool npc; + bool is_atomic_op; /* Types that aren't fully specified cannot be used in assignments. */ lhs = require_complete_type (lhs); @@ -4862,6 +5200,8 @@ build_modify_expr (location_t location, tree lhs, tree lhs_origtype, if (!objc_is_property_ref (lhs) && !lvalue_or_else (location, lhs, lv_assign)) return error_mark_node; + is_atomic_op = really_atomic_lvalue (lhs); + if (TREE_CODE (rhs) == EXCESS_PRECISION_EXPR) { rhs_semantic_type = TREE_TYPE (rhs); @@ -4892,12 +5232,17 @@ build_modify_expr (location_t location, tree lhs, tree lhs_origtype, { lhs = c_fully_fold (lhs, false, NULL); lhs = stabilize_reference (lhs); - newrhs = build_binary_op (location, - modifycode, lhs, rhs, 1); - /* The original type of the right hand side is no longer - meaningful. */ - rhs_origtype = NULL_TREE; + /* Construct the RHS for any non-atomic compound assignemnt. */ + if (!is_atomic_op) + { + newrhs = build_binary_op (location, + modifycode, lhs, rhs, 1); + + /* The original type of the right hand side is no longer + meaningful. */ + rhs_origtype = NULL_TREE; + } } if (c_dialect_objc ()) @@ -4959,23 +5304,39 @@ build_modify_expr (location_t location, tree lhs, tree lhs_origtype, ? rhs_origtype : TREE_TYPE (rhs)); if (checktype != error_mark_node - && TYPE_MAIN_VARIANT (checktype) != TYPE_MAIN_VARIANT (lhs_origtype)) + && (TYPE_MAIN_VARIANT (checktype) != TYPE_MAIN_VARIANT (lhs_origtype) + || (is_atomic_op && modifycode != NOP_EXPR))) warning_at (location, OPT_Wc___compat, "enum conversion in assignment is invalid in C++"); } + /* If the lhs is atomic, remove that qualifier. */ + if (is_atomic_op) + { + lhstype = build_qualified_type (lhstype, + (TYPE_QUALS (lhstype) + & ~TYPE_QUAL_ATOMIC)); + olhstype = build_qualified_type (olhstype, + (TYPE_QUALS (lhstype) + & ~TYPE_QUAL_ATOMIC)); + } + /* Convert new value to destination type. Fold it first, then restore any excess precision information, for the sake of conversion warnings. */ - npc = null_pointer_constant_p (newrhs); - newrhs = c_fully_fold (newrhs, false, NULL); - if (rhs_semantic_type) - newrhs = build1 (EXCESS_PRECISION_EXPR, rhs_semantic_type, newrhs); - newrhs = convert_for_assignment (location, lhstype, newrhs, rhs_origtype, - ic_assign, npc, NULL_TREE, NULL_TREE, 0); - if (TREE_CODE (newrhs) == ERROR_MARK) - return error_mark_node; + if (!(is_atomic_op && modifycode != NOP_EXPR)) + { + npc = null_pointer_constant_p (newrhs); + newrhs = c_fully_fold (newrhs, false, NULL); + if (rhs_semantic_type) + newrhs = build1 (EXCESS_PRECISION_EXPR, rhs_semantic_type, newrhs); + newrhs = convert_for_assignment (location, lhstype, newrhs, rhs_origtype, + ic_assign, npc, NULL_TREE, + NULL_TREE, 0); + if (TREE_CODE (newrhs) == ERROR_MARK) + return error_mark_node; + } /* Emit ObjC write barrier, if necessary. */ if (c_dialect_objc () && flag_objc_gc) @@ -4990,9 +5351,14 @@ build_modify_expr (location_t location, tree lhs, tree lhs_origtype, /* Scan operands. */ - result = build2 (MODIFY_EXPR, lhstype, lhs, newrhs); - TREE_SIDE_EFFECTS (result) = 1; - protected_set_expr_location (result, location); + if (is_atomic_op) + result = build_atomic_assign (location, lhs, modifycode, newrhs, false); + else + { + result = build2 (MODIFY_EXPR, lhstype, lhs, newrhs); + TREE_SIDE_EFFECTS (result) = 1; + protected_set_expr_location (result, location); + } /* If we got the LHS in a different type for storing in, convert the result back to the nominal type of LHS @@ -5024,8 +5390,12 @@ find_anonymous_field_with_type (tree struct_type, tree type) field != NULL_TREE; field = TREE_CHAIN (field)) { + tree fieldtype = (TYPE_ATOMIC (TREE_TYPE (field)) + ? c_build_qualified_type (TREE_TYPE (field), + TYPE_QUAL_ATOMIC) + : TYPE_MAIN_VARIANT (TREE_TYPE (field))); if (DECL_NAME (field) == NULL - && comptypes (type, TYPE_MAIN_VARIANT (TREE_TYPE (field)))) + && comptypes (type, fieldtype)) { if (found) return false; @@ -5063,7 +5433,10 @@ convert_to_anonymous_field (location_t location, tree type, tree rhs) || TREE_CODE (rhs_struct_type) == UNION_TYPE); gcc_assert (POINTER_TYPE_P (type)); - lhs_main_type = TYPE_MAIN_VARIANT (TREE_TYPE (type)); + lhs_main_type = (TYPE_ATOMIC (TREE_TYPE (type)) + ? c_build_qualified_type (TREE_TYPE (type), + TYPE_QUAL_ATOMIC) + : TYPE_MAIN_VARIANT (TREE_TYPE (type))); found_field = NULL_TREE; found_sub_field = false; @@ -5075,7 +5448,11 @@ convert_to_anonymous_field (location_t location, tree type, tree rhs) || (TREE_CODE (TREE_TYPE (field)) != RECORD_TYPE && TREE_CODE (TREE_TYPE (field)) != UNION_TYPE)) continue; - if (comptypes (lhs_main_type, TYPE_MAIN_VARIANT (TREE_TYPE (field)))) + tree fieldtype = (TYPE_ATOMIC (TREE_TYPE (field)) + ? c_build_qualified_type (TREE_TYPE (field), + TYPE_QUAL_ATOMIC) + : TYPE_MAIN_VARIANT (TREE_TYPE (field))); + if (comptypes (lhs_main_type, fieldtype)) { if (found_field != NULL_TREE) return NULL_TREE; @@ -5365,17 +5742,18 @@ convert_for_assignment (location_t location, tree type, tree rhs, and vice versa; otherwise, targets must be the same. Meanwhile, the lhs target must have all the qualifiers of the rhs. */ - if (VOID_TYPE_P (ttl) || VOID_TYPE_P (ttr) + if ((VOID_TYPE_P (ttl) && !TYPE_ATOMIC (ttl)) + || (VOID_TYPE_P (ttr) && !TYPE_ATOMIC (ttr)) || comp_target_types (location, memb_type, rhstype)) { + int lquals = TYPE_QUALS (ttl) & ~TYPE_QUAL_ATOMIC; + int rquals = TYPE_QUALS (ttr) & ~TYPE_QUAL_ATOMIC; /* If this type won't generate any warnings, use it. */ - if (TYPE_QUALS (ttl) == TYPE_QUALS (ttr) + if (lquals == rquals || ((TREE_CODE (ttr) == FUNCTION_TYPE && TREE_CODE (ttl) == FUNCTION_TYPE) - ? ((TYPE_QUALS (ttl) | TYPE_QUALS (ttr)) - == TYPE_QUALS (ttr)) - : ((TYPE_QUALS (ttl) | TYPE_QUALS (ttr)) - == TYPE_QUALS (ttl)))) + ? ((lquals | rquals) == rquals) + : ((lquals | rquals) == lquals))) break; /* Keep looking for a better type, but remember this one. */ @@ -5466,9 +5844,15 @@ convert_for_assignment (location_t location, tree type, tree rhs, addr_space_t asr; if (TREE_CODE (mvl) != ARRAY_TYPE) - mvl = TYPE_MAIN_VARIANT (mvl); + mvl = (TYPE_ATOMIC (mvl) + ? c_build_qualified_type (TYPE_MAIN_VARIANT (mvl), + TYPE_QUAL_ATOMIC) + : TYPE_MAIN_VARIANT (mvl)); if (TREE_CODE (mvr) != ARRAY_TYPE) - mvr = TYPE_MAIN_VARIANT (mvr); + mvr = (TYPE_ATOMIC (mvr) + ? c_build_qualified_type (TYPE_MAIN_VARIANT (mvr), + TYPE_QUAL_ATOMIC) + : TYPE_MAIN_VARIANT (mvr)); /* Opaque pointers are treated like void pointers. */ is_opaque_pointer = vector_targets_convertible_p (ttl, ttr); @@ -5569,13 +5953,15 @@ convert_for_assignment (location_t location, tree type, tree rhs, /* Any non-function converts to a [const][volatile] void * and vice versa; otherwise, targets must be the same. Meanwhile, the lhs target must have all the qualifiers of the rhs. */ - if (VOID_TYPE_P (ttl) || VOID_TYPE_P (ttr) + if ((VOID_TYPE_P (ttl) && !TYPE_ATOMIC (ttl)) + || (VOID_TYPE_P (ttr) && !TYPE_ATOMIC (ttr)) || (target_cmp = comp_target_types (location, type, rhstype)) || is_opaque_pointer || ((c_common_unsigned_type (mvl) == c_common_unsigned_type (mvr)) - && c_common_signed_type (mvl) - == c_common_signed_type (mvr))) + && (c_common_signed_type (mvl) + == c_common_signed_type (mvr)) + && TYPE_ATOMIC (mvl) == TYPE_ATOMIC (mvr))) { if (pedantic && ((VOID_TYPE_P (ttl) && TREE_CODE (ttr) == FUNCTION_TYPE) @@ -5598,8 +5984,9 @@ convert_for_assignment (location_t location, tree type, tree rhs, else if (TREE_CODE (ttr) != FUNCTION_TYPE && TREE_CODE (ttl) != FUNCTION_TYPE) { - if (TYPE_QUALS_NO_ADDR_SPACE (ttr) - & ~TYPE_QUALS_NO_ADDR_SPACE (ttl)) + /* Assignments between atomic and non-atomic objects are OK. */ + if (TYPE_QUALS_NO_ADDR_SPACE_NO_ATOMIC (ttr) + & ~TYPE_QUALS_NO_ADDR_SPACE_NO_ATOMIC (ttl)) { WARN_FOR_QUALIFIERS (location, 0, G_("passing argument %d of %qE discards " @@ -6072,7 +6459,11 @@ digest_init (location_t init_loc, tree type, tree init, tree origtype, if (code == ARRAY_TYPE && inside_init && TREE_CODE (inside_init) == STRING_CST) { - tree typ1 = TYPE_MAIN_VARIANT (TREE_TYPE (type)); + tree typ1 + = (TYPE_ATOMIC (TREE_TYPE (type)) + ? c_build_qualified_type (TYPE_MAIN_VARIANT (TREE_TYPE (type)), + TYPE_QUAL_ATOMIC) + : TYPE_MAIN_VARIANT (TREE_TYPE (type))); /* Note that an array could be both an array of character type and an array of wchar_t if wchar_t is signed char or unsigned char. */ @@ -8610,7 +9001,7 @@ build_asm_expr (location_t loc, tree string, tree outputs, tree inputs, struct c_expr expr; memset (&expr, 0, sizeof (expr)); expr.value = input; - expr = default_function_array_conversion (loc, expr); + expr = convert_lvalue_to_rvalue (loc, expr, true, false); input = c_fully_fold (expr.value, false, NULL); if (input != error_mark_node && VOID_TYPE_P (TREE_TYPE (input))) @@ -10096,13 +10487,13 @@ build_binary_op (location_t location, enum tree_code code, "disjoint address spaces"); return error_mark_node; } - else if (VOID_TYPE_P (tt0)) + else if (VOID_TYPE_P (tt0) && !TYPE_ATOMIC (tt0)) { if (pedantic && TREE_CODE (tt1) == FUNCTION_TYPE) pedwarn (location, OPT_Wpedantic, "ISO C forbids " "comparison of %<void *%> with function pointer"); } - else if (VOID_TYPE_P (tt1)) + else if (VOID_TYPE_P (tt1) && !TYPE_ATOMIC (tt1)) { if (pedantic && TREE_CODE (tt0) == FUNCTION_TYPE) pedwarn (location, OPT_Wpedantic, "ISO C forbids " diff --git a/gcc/config/i386/i386-builtin-types.def b/gcc/config/i386/i386-builtin-types.def index 314f3e888d8..c866170bde8 100644 --- a/gcc/config/i386/i386-builtin-types.def +++ b/gcc/config/i386/i386-builtin-types.def @@ -227,6 +227,7 @@ DEF_FUNCTION_TYPE (VOID, PCVOID) DEF_FUNCTION_TYPE (VOID, PVOID) DEF_FUNCTION_TYPE (VOID, UINT64) DEF_FUNCTION_TYPE (VOID, UNSIGNED) +DEF_FUNCTION_TYPE (VOID, PUSHORT) DEF_FUNCTION_TYPE (INT, PUSHORT) DEF_FUNCTION_TYPE (INT, PUNSIGNED) DEF_FUNCTION_TYPE (INT, PULONGLONG) diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c index 2aa74b9da0d..430d5620234 100644 --- a/gcc/config/i386/i386.c +++ b/gcc/config/i386/i386.c @@ -26993,6 +26993,11 @@ enum ix86_builtins IX86_BUILTIN_LFENCE, IX86_BUILTIN_PAUSE, + IX86_BUILTIN_FNSTENV, + IX86_BUILTIN_FLDENV, + IX86_BUILTIN_FNSTSW, + IX86_BUILTIN_FNCLEX, + IX86_BUILTIN_BSRSI, IX86_BUILTIN_BSRDI, IX86_BUILTIN_RDPMC, @@ -27969,6 +27974,12 @@ static const struct builtin_description bdesc_special_args[] = { ~OPTION_MASK_ISA_64BIT, CODE_FOR_nothing, "__builtin_ia32_rdtscp", IX86_BUILTIN_RDTSCP, UNKNOWN, (int) UINT64_FTYPE_PUNSIGNED }, { ~OPTION_MASK_ISA_64BIT, CODE_FOR_pause, "__builtin_ia32_pause", IX86_BUILTIN_PAUSE, UNKNOWN, (int) VOID_FTYPE_VOID }, + /* 80387 (for use internally for atomic compound assignment). */ + { 0, CODE_FOR_fnstenv, "__builtin_ia32_fnstenv", IX86_BUILTIN_FNSTENV, UNKNOWN, (int) VOID_FTYPE_PVOID }, + { 0, CODE_FOR_fldenv, "__builtin_ia32_fldenv", IX86_BUILTIN_FLDENV, UNKNOWN, (int) VOID_FTYPE_PCVOID }, + { 0, CODE_FOR_fnstsw, "__builtin_ia32_fnstsw", IX86_BUILTIN_FNSTSW, UNKNOWN, (int) VOID_FTYPE_PUSHORT }, + { 0, CODE_FOR_fnclex, "__builtin_ia32_fnclex", IX86_BUILTIN_FNCLEX, UNKNOWN, (int) VOID_FTYPE_VOID }, + /* MMX */ { OPTION_MASK_ISA_MMX, CODE_FOR_mmx_emms, "__builtin_ia32_emms", IX86_BUILTIN_EMMS, UNKNOWN, (int) VOID_FTYPE_VOID }, @@ -32930,6 +32941,10 @@ ix86_expand_builtin (tree exp, rtx target, rtx subtarget, case IX86_BUILTIN_FXRSTOR: case IX86_BUILTIN_FXSAVE64: case IX86_BUILTIN_FXRSTOR64: + case IX86_BUILTIN_FNSTENV: + case IX86_BUILTIN_FLDENV: + case IX86_BUILTIN_FNSTSW: + mode0 = BLKmode; switch (fcode) { case IX86_BUILTIN_FXSAVE: @@ -32944,6 +32959,16 @@ ix86_expand_builtin (tree exp, rtx target, rtx subtarget, case IX86_BUILTIN_FXRSTOR64: icode = CODE_FOR_fxrstor64; break; + case IX86_BUILTIN_FNSTENV: + icode = CODE_FOR_fnstenv; + break; + case IX86_BUILTIN_FLDENV: + icode = CODE_FOR_fldenv; + break; + case IX86_BUILTIN_FNSTSW: + icode = CODE_FOR_fnstsw; + mode0 = HImode; + break; default: gcc_unreachable (); } @@ -32956,7 +32981,7 @@ ix86_expand_builtin (tree exp, rtx target, rtx subtarget, op0 = convert_memory_address (Pmode, op0); op0 = copy_addr_to_reg (op0); } - op0 = gen_rtx_MEM (BLKmode, op0); + op0 = gen_rtx_MEM (mode0, op0); pat = GEN_FCN (icode) (op0); if (pat) @@ -43566,6 +43591,103 @@ ix86_float_exceptions_rounding_supported_p (void) return TARGET_80387 || TARGET_SSE_MATH; } +/* Implement TARGET_ATOMIC_ASSIGN_EXPAND_FENV. */ + +static void +ix86_atomic_assign_expand_fenv (tree *hold, tree *clear, tree *update) +{ + if (!TARGET_80387 && !TARGET_SSE_MATH) + return; + tree exceptions_var = create_tmp_var (integer_type_node, NULL); + if (TARGET_80387) + { + tree fenv_index_type = build_index_type (size_int (6)); + tree fenv_type = build_array_type (unsigned_type_node, fenv_index_type); + tree fenv_var = create_tmp_var (fenv_type, NULL); + mark_addressable (fenv_var); + tree fenv_ptr = build_pointer_type (fenv_type); + tree fenv_addr = build1 (ADDR_EXPR, fenv_ptr, fenv_var); + fenv_addr = fold_convert (ptr_type_node, fenv_addr); + tree fnstenv = ix86_builtins[IX86_BUILTIN_FNSTENV]; + tree fldenv = ix86_builtins[IX86_BUILTIN_FLDENV]; + tree fnstsw = ix86_builtins[IX86_BUILTIN_FNSTSW]; + tree fnclex = ix86_builtins[IX86_BUILTIN_FNCLEX]; + tree hold_fnstenv = build_call_expr (fnstenv, 1, fenv_addr); + tree hold_fnclex = build_call_expr (fnclex, 0); + *hold = build2 (COMPOUND_EXPR, void_type_node, hold_fnstenv, + hold_fnclex); + *clear = build_call_expr (fnclex, 0); + tree sw_var = create_tmp_var (short_unsigned_type_node, NULL); + mark_addressable (sw_var); + tree su_ptr = build_pointer_type (short_unsigned_type_node); + tree sw_addr = build1 (ADDR_EXPR, su_ptr, sw_var); + tree fnstsw_call = build_call_expr (fnstsw, 1, sw_addr); + tree exceptions_x87 = fold_convert (integer_type_node, sw_var); + tree update_mod = build2 (MODIFY_EXPR, integer_type_node, + exceptions_var, exceptions_x87); + *update = build2 (COMPOUND_EXPR, integer_type_node, + fnstsw_call, update_mod); + tree update_fldenv = build_call_expr (fldenv, 1, fenv_addr); + *update = build2 (COMPOUND_EXPR, void_type_node, *update, update_fldenv); + } + if (TARGET_SSE_MATH) + { + tree mxcsr_orig_var = create_tmp_var (unsigned_type_node, NULL); + tree mxcsr_mod_var = create_tmp_var (unsigned_type_node, NULL); + tree stmxcsr = ix86_builtins[IX86_BUILTIN_STMXCSR]; + tree ldmxcsr = ix86_builtins[IX86_BUILTIN_LDMXCSR]; + tree stmxcsr_hold_call = build_call_expr (stmxcsr, 0); + tree hold_assign_orig = build2 (MODIFY_EXPR, unsigned_type_node, + mxcsr_orig_var, stmxcsr_hold_call); + tree hold_mod_val = build2 (BIT_IOR_EXPR, unsigned_type_node, + mxcsr_orig_var, + build_int_cst (unsigned_type_node, 0x1f80)); + hold_mod_val = build2 (BIT_AND_EXPR, unsigned_type_node, hold_mod_val, + build_int_cst (unsigned_type_node, 0xffffffc0)); + tree hold_assign_mod = build2 (MODIFY_EXPR, unsigned_type_node, + mxcsr_mod_var, hold_mod_val); + tree ldmxcsr_hold_call = build_call_expr (ldmxcsr, 1, mxcsr_mod_var); + tree hold_all = build2 (COMPOUND_EXPR, unsigned_type_node, + hold_assign_orig, hold_assign_mod); + hold_all = build2 (COMPOUND_EXPR, void_type_node, hold_all, + ldmxcsr_hold_call); + if (*hold) + *hold = build2 (COMPOUND_EXPR, void_type_node, *hold, hold_all); + else + *hold = hold_all; + tree ldmxcsr_clear_call = build_call_expr (ldmxcsr, 1, mxcsr_mod_var); + if (*clear) + *clear = build2 (COMPOUND_EXPR, void_type_node, *clear, + ldmxcsr_clear_call); + else + *clear = ldmxcsr_clear_call; + tree stxmcsr_update_call = build_call_expr (stmxcsr, 0); + tree exceptions_sse = fold_convert (integer_type_node, + stxmcsr_update_call); + if (*update) + { + tree exceptions_mod = build2 (BIT_IOR_EXPR, integer_type_node, + exceptions_var, exceptions_sse); + tree exceptions_assign = build2 (MODIFY_EXPR, integer_type_node, + exceptions_var, exceptions_mod); + *update = build2 (COMPOUND_EXPR, integer_type_node, *update, + exceptions_assign); + } + else + *update = build2 (MODIFY_EXPR, integer_type_node, + exceptions_var, exceptions_sse); + tree ldmxcsr_update_call = build_call_expr (ldmxcsr, 1, mxcsr_orig_var); + *update = build2 (COMPOUND_EXPR, void_type_node, *update, + ldmxcsr_update_call); + } + tree atomic_feraiseexcept + = builtin_decl_implicit (BUILT_IN_ATOMIC_FERAISEEXCEPT); + tree atomic_feraiseexcept_call = build_call_expr (atomic_feraiseexcept, + 1, exceptions_var); + *update = build2 (COMPOUND_EXPR, void_type_node, *update, + atomic_feraiseexcept_call); +} + /* Initialize the GCC target structure. */ #undef TARGET_RETURN_IN_MEMORY #define TARGET_RETURN_IN_MEMORY ix86_return_in_memory @@ -43677,6 +43799,9 @@ ix86_float_exceptions_rounding_supported_p (void) #undef TARGET_MEMMODEL_CHECK #define TARGET_MEMMODEL_CHECK ix86_memmodel_check +#undef TARGET_ATOMIC_ASSIGN_EXPAND_FENV +#define TARGET_ATOMIC_ASSIGN_EXPAND_FENV ix86_atomic_assign_expand_fenv + #ifdef HAVE_AS_TLS #undef TARGET_HAVE_TLS #define TARGET_HAVE_TLS true diff --git a/gcc/config/i386/i386.md b/gcc/config/i386/i386.md index a2c81e5e2c3..a37fa64a03e 100644 --- a/gcc/config/i386/i386.md +++ b/gcc/config/i386/i386.md @@ -222,6 +222,12 @@ UNSPECV_XSAVEOPT UNSPECV_XSAVEOPT64 + ;; For atomic compound assignments. + UNSPECV_FNSTENV + UNSPECV_FLDENV + UNSPECV_FNSTSW + UNSPECV_FNCLEX + ;; For RDRAND support UNSPECV_RDRAND @@ -18014,6 +18020,71 @@ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; +;; Floating-point instructions for atomic compound assignments +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +; Clobber all floating-point registers on environment save and restore +; to ensure that the TOS value saved at fnstenv is valid after fldenv. +(define_insn "fnstenv" + [(set (match_operand:BLK 0 "memory_operand" "=m") + (unspec_volatile:BLK [(const_int 0)] UNSPECV_FNSTENV)) + (clobber (reg:HI FPCR_REG)) + (clobber (reg:XF ST0_REG)) + (clobber (reg:XF ST1_REG)) + (clobber (reg:XF ST2_REG)) + (clobber (reg:XF ST3_REG)) + (clobber (reg:XF ST4_REG)) + (clobber (reg:XF ST5_REG)) + (clobber (reg:XF ST6_REG)) + (clobber (reg:XF ST7_REG))] + "TARGET_80387" + "fnstenv\t%0" + [(set_attr "type" "other") + (set_attr "memory" "store") + (set (attr "length") + (symbol_ref "ix86_attr_length_address_default (insn) + 2"))]) + +(define_insn "fldenv" + [(unspec_volatile [(match_operand:BLK 0 "memory_operand" "m")] + UNSPECV_FLDENV) + (clobber (reg:CCFP FPSR_REG)) + (clobber (reg:HI FPCR_REG)) + (clobber (reg:XF ST0_REG)) + (clobber (reg:XF ST1_REG)) + (clobber (reg:XF ST2_REG)) + (clobber (reg:XF ST3_REG)) + (clobber (reg:XF ST4_REG)) + (clobber (reg:XF ST5_REG)) + (clobber (reg:XF ST6_REG)) + (clobber (reg:XF ST7_REG))] + "TARGET_80387" + "fldenv\t%0" + [(set_attr "type" "other") + (set_attr "memory" "load") + (set (attr "length") + (symbol_ref "ix86_attr_length_address_default (insn) + 2"))]) + +(define_insn "fnstsw" + [(set (match_operand:HI 0 "memory_operand" "=m") + (unspec_volatile:HI [(const_int 0)] UNSPECV_FNSTSW))] + "TARGET_80387" + "fnstsw\t%0" + [(set_attr "type" "other") + (set_attr "memory" "store") + (set (attr "length") + (symbol_ref "ix86_attr_length_address_default (insn) + 2"))]) + +(define_insn "fnclex" + [(unspec_volatile [(const_int 0)] UNSPECV_FNCLEX)] + "TARGET_80387" + "fnclex" + [(set_attr "type" "other") + (set_attr "memory" "none") + (set_attr "length" "2")]) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; LWP instructions ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi index f5e8d2cdcec..5e7910bdd20 100644 --- a/gcc/doc/tm.texi +++ b/gcc/doc/tm.texi @@ -11485,3 +11485,7 @@ It returns true if the target supports GNU indirect functions. The support includes the assembler, linker and dynamic linker. The default value of this hook is based on target's libc. @end deftypefn + +@deftypefn {Target Hook} void TARGET_ATOMIC_ASSIGN_EXPAND_FENV (tree *@var{hold}, tree *@var{clear}, tree *@var{update}) +ISO C11 requires atomic compound assignments that may raise floating-point exceptions to raise exceptions corresponding to the arithmetic operation whose result was successfully stored in a compare-and-exchange sequence. This requires code equivalent to calls to @code{feholdexcept}, @code{feclearexcept} and @code{feupdateenv} to be generated at appropriate points in the compare-and-exchange sequence. This hook should set @code{*@var{hold}} to an expression equivalent to the call to @code{feholdexcept}, @code{*@var{clear}} to an expression equivalent to the call to @code{feclearexcept} and @code{*@var{update}} to an expression equivalent to the call to @code{feupdateenv}. The three expressions are @code{NULL_TREE} on entry to the hook and may be left as @code{NULL_TREE} if no code is required in a particular place. The default implementation leaves all three expressions as @code{NULL_TREE}. The @code{__atomic_feraiseexcept} function from @code{libatomic} may be of use as part of the code generated in @code{*@var{update}}. +@end deftypefn diff --git a/gcc/doc/tm.texi.in b/gcc/doc/tm.texi.in index 1624f4f715e..4e60d48a337 100644 --- a/gcc/doc/tm.texi.in +++ b/gcc/doc/tm.texi.in @@ -8404,3 +8404,5 @@ and the associated definitions of those functions. @hook TARGET_ATOMIC_TEST_AND_SET_TRUEVAL @hook TARGET_HAS_IFUNC_P + +@hook TARGET_ATOMIC_ASSIGN_EXPAND_FENV diff --git a/gcc/objc/ChangeLog b/gcc/objc/ChangeLog index f3fa0e06ded..e3be5f8d212 100644 --- a/gcc/objc/ChangeLog +++ b/gcc/objc/ChangeLog @@ -1,3 +1,7 @@ +2013-11-07 Andrew MacLeod <amacleod@redhat.com> + + * objc-act.c (objc_push_parm): Handle atomic qualifier. + 2013-09-25 Tom Tromey <tromey@redhat.com> * Make-lang.in (START_HDRS, cc1obj-checksum.o, objc/objc-lang.o) diff --git a/gcc/objc/objc-act.c b/gcc/objc/objc-act.c index 95ec4ecd40f..3125398c276 100644 --- a/gcc/objc/objc-act.c +++ b/gcc/objc/objc-act.c @@ -8244,6 +8244,7 @@ objc_push_parm (tree parm) c_apply_type_quals_to_decl ((TYPE_READONLY (TREE_TYPE (parm)) ? TYPE_QUAL_CONST : 0) | (TYPE_RESTRICT (TREE_TYPE (parm)) ? TYPE_QUAL_RESTRICT : 0) + | (TYPE_ATOMIC (TREE_TYPE (parm)) ? TYPE_QUAL_ATOMIC : 0) | (TYPE_VOLATILE (TREE_TYPE (parm)) ? TYPE_QUAL_VOLATILE : 0), parm); objc_parmlist = chainon (objc_parmlist, parm); diff --git a/gcc/print-tree.c b/gcc/print-tree.c index e5d6664c229..08af30d7f19 100644 --- a/gcc/print-tree.c +++ b/gcc/print-tree.c @@ -305,6 +305,8 @@ print_node (FILE *file, const char *prefix, tree node, int indent) if (TYPE_P (node) ? TYPE_READONLY (node) : TREE_READONLY (node)) fputs (" readonly", file); + if (TYPE_P (node) && TYPE_ATOMIC (node)) + fputs (" atomic", file); if (!TYPE_P (node) && TREE_CONSTANT (node)) fputs (" constant", file); else if (TYPE_P (node) && TYPE_SIZES_GIMPLIFIED (node)) diff --git a/gcc/sync-builtins.def b/gcc/sync-builtins.def index 3176f9b197a..26c8ba09f9b 100644 --- a/gcc/sync-builtins.def +++ b/gcc/sync-builtins.def @@ -606,3 +606,9 @@ DEF_SYNC_BUILTIN (BUILT_IN_ATOMIC_SIGNAL_FENCE, "__atomic_signal_fence", BT_FN_VOID_INT, ATTR_NOTHROW_LEAF_LIST) +/* This one is actually a function in libatomic and not expected to be + inlined, declared here for convenience of targets generating calls + to it. */ +DEF_SYNC_BUILTIN (BUILT_IN_ATOMIC_FERAISEEXCEPT, + "__atomic_feraiseexcept", + BT_FN_VOID_INT, ATTR_LEAF_LIST) diff --git a/gcc/target.def b/gcc/target.def index e665c1cb725..caf1cb6da6d 100644 --- a/gcc/target.def +++ b/gcc/target.def @@ -5279,7 +5279,27 @@ DEFHOOKPOD @code{atomic_test_and_set} is not exactly 1, i.e. the\ @code{bool} @code{true}.", unsigned char, 1) - + +DEFHOOK +(atomic_assign_expand_fenv, +"ISO C11 requires atomic compound assignments that may raise floating-point\ + exceptions to raise exceptions corresponding to the arithmetic operation\ + whose result was successfully stored in a compare-and-exchange sequence. \ + This requires code equivalent to calls to @code{feholdexcept},\ + @code{feclearexcept} and @code{feupdateenv} to be generated at\ + appropriate points in the compare-and-exchange sequence. This hook should\ + set @code{*@var{hold}} to an expression equivalent to the call to\ + @code{feholdexcept}, @code{*@var{clear}} to an expression equivalent to\ + the call to @code{feclearexcept} and @code{*@var{update}} to an expression\ + equivalent to the call to @code{feupdateenv}. The three expressions are\ + @code{NULL_TREE} on entry to the hook and may be left as @code{NULL_TREE}\ + if no code is required in a particular place. The default implementation\ + leaves all three expressions as @code{NULL_TREE}. The\ + @code{__atomic_feraiseexcept} function from @code{libatomic} may be of use\ + as part of the code generated in @code{*@var{update}}.", + void, (tree *hold, tree *clear, tree *update), + default_atomic_assign_expand_fenv) + /* Leave the boolean fields at the end. */ /* True if we can create zeroed data by switching to a BSS section diff --git a/gcc/targhooks.c b/gcc/targhooks.c index 7585c14b56f..e262fcb43ce 100644 --- a/gcc/targhooks.c +++ b/gcc/targhooks.c @@ -1600,6 +1600,13 @@ default_canonicalize_comparison (int *, rtx *, rtx *, bool) { } +/* Default implementation of TARGET_ATOMIC_ASSIGN_EXPAND_FENV. */ + +void +default_atomic_assign_expand_fenv (tree *, tree *, tree *) +{ +} + #ifndef PAD_VARARGS_DOWN #define PAD_VARARGS_DOWN BYTES_BIG_ENDIAN #endif diff --git a/gcc/targhooks.h b/gcc/targhooks.h index e3e613ac3f3..a939fdbc6e5 100644 --- a/gcc/targhooks.h +++ b/gcc/targhooks.h @@ -203,6 +203,7 @@ extern void default_asm_output_ident_directive (const char*); extern enum machine_mode default_cstore_mode (enum insn_code); extern bool default_member_type_forces_blk (const_tree, enum machine_mode); +extern void default_atomic_assign_expand_fenv (tree *, tree *, tree *); extern tree build_va_arg_indirect_ref (tree); extern tree std_gimplify_va_arg_expr (tree, tree, gimple_seq *, gimple_seq *); diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 6cb4b9822ef..099c392ad6c 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,16 @@ +2013-11-07 Joseph Myers <joseph@codesourcery.com> + + * lib/target-supports.exp + (check_effective_target_fenv_exceptions): New function. + * lib/atomic-dg.exp, gcc.dg/atomic/atomic.exp: New files. + * gcc.dg/atomic/c11-atomic-exec-1.c, + gcc.dg/atomic/c11-atomic-exec-2.c, + gcc.dg/atomic/c11-atomic-exec-3.c, + gcc.dg/atomic/c11-atomic-exec-4.c, + gcc.dg/atomic/c11-atomic-exec-5.c, gcc.dg/c11-atomic-1.c, + gcc.dg/c11-atomic-2.c, gcc.dg/c11-atomic-3.c, + gcc.dg/c90-atomic-1.c, gcc.dg/c99-atomic-1.c: New tests. + 2013-11-07 Cong Hou <congh@google.com> * gcc.dg/vect/vect-alias-check.c: New. diff --git a/gcc/testsuite/gcc.dg/atomic/atomic.exp b/gcc/testsuite/gcc.dg/atomic/atomic.exp new file mode 100644 index 00000000000..ac2ca729d1d --- /dev/null +++ b/gcc/testsuite/gcc.dg/atomic/atomic.exp @@ -0,0 +1,34 @@ +# Copyright (C) 2012-2013 Free Software Foundation, Inc. +# +# This file is part of GCC. +# +# GCC is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GCC is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GCC; see the file COPYING3. If not see +# <http://www.gnu.org/licenses/>. + +# GCC testsuite that uses the `dg.exp' driver. + +# Load support procs. +load_lib gcc-dg.exp +load_lib atomic-dg.exp + +# Initialize `dg'. +dg-init +if [atomic_init] { + # Main loop. + gcc-dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/*.c]] "" +} + +# All done. +atomic_finish +dg-finish diff --git a/gcc/testsuite/gcc.dg/atomic/c11-atomic-exec-1.c b/gcc/testsuite/gcc.dg/atomic/c11-atomic-exec-1.c new file mode 100644 index 00000000000..c0db93f07dc --- /dev/null +++ b/gcc/testsuite/gcc.dg/atomic/c11-atomic-exec-1.c @@ -0,0 +1,88 @@ +/* Test for _Atomic in C11. Basic execution tests for atomic loads + and stores. */ +/* { dg-do run } */ +/* { dg-options "-std=c11 -pedantic-errors" } */ + +extern void abort (void); +extern void exit (int); +extern int memcmp (const void *, const void *, __SIZE_TYPE__); + +#define CMPLX(X, Y) __builtin_complex ((X), (Y)) + +#define TEST_SIMPLE_ASSIGN(TYPE, VALUE) \ + do \ + { \ + static volatile _Atomic (TYPE) a, b = (TYPE) (VALUE); \ + if (a != 0) \ + abort (); \ + if (b != ((TYPE) (VALUE))) \ + abort (); \ + if ((a = b) != ((TYPE) (VALUE))) \ + abort (); \ + if (a != ((TYPE) (VALUE))) \ + abort (); \ + } \ + while (0) + +#define TEST_SIMPLE_ASSIGN_ARITH(VALUE) \ + do \ + { \ + TEST_SIMPLE_ASSIGN (_Bool, (VALUE)); \ + TEST_SIMPLE_ASSIGN (char, (VALUE)); \ + TEST_SIMPLE_ASSIGN (signed char, (VALUE)); \ + TEST_SIMPLE_ASSIGN (unsigned char, (VALUE)); \ + TEST_SIMPLE_ASSIGN (signed short, (VALUE)); \ + TEST_SIMPLE_ASSIGN (unsigned short, (VALUE)); \ + TEST_SIMPLE_ASSIGN (signed int, (VALUE)); \ + TEST_SIMPLE_ASSIGN (unsigned int, (VALUE)); \ + TEST_SIMPLE_ASSIGN (signed long, (VALUE)); \ + TEST_SIMPLE_ASSIGN (unsigned long, (VALUE)); \ + TEST_SIMPLE_ASSIGN (signed long long, (VALUE)); \ + TEST_SIMPLE_ASSIGN (unsigned long long, (VALUE)); \ + TEST_SIMPLE_ASSIGN (float, (VALUE)); \ + TEST_SIMPLE_ASSIGN (double, (VALUE)); \ + TEST_SIMPLE_ASSIGN (long double, (VALUE)); \ + TEST_SIMPLE_ASSIGN (_Complex float, (VALUE)); \ + TEST_SIMPLE_ASSIGN (_Complex double, (VALUE)); \ + TEST_SIMPLE_ASSIGN (_Complex long double, (VALUE)); \ + } \ + while (0) + +static void +test_simple_assign (void) +{ + TEST_SIMPLE_ASSIGN_ARITH (0); + TEST_SIMPLE_ASSIGN_ARITH (1); + TEST_SIMPLE_ASSIGN_ARITH (2); + TEST_SIMPLE_ASSIGN_ARITH (-1); + TEST_SIMPLE_ASSIGN_ARITH (1ULL << 63); + TEST_SIMPLE_ASSIGN_ARITH (1.5); + TEST_SIMPLE_ASSIGN_ARITH (CMPLX (2.5, 3.5)); + static int i; + TEST_SIMPLE_ASSIGN (int *, 0); + TEST_SIMPLE_ASSIGN (int *, &i); + struct s { short a[1024]; }; + struct s init, copy; + _Atomic struct s s1, s2; + for (int j = 0; j < 1024; j++) + init.a[j] = j; + copy = (s1 = init); + if (memcmp (&init, ©, sizeof init) != 0) + abort (); + copy = (s2 = s1); + if (memcmp (&init, ©, sizeof init) != 0) + abort (); + copy = s1; + if (memcmp (&init, ©, sizeof init) != 0) + abort (); + copy = s2; + if (memcmp (&init, ©, sizeof init) != 0) + abort (); +} + +int +main (void) +{ + test_simple_assign (); + exit (0); +} diff --git a/gcc/testsuite/gcc.dg/atomic/c11-atomic-exec-2.c b/gcc/testsuite/gcc.dg/atomic/c11-atomic-exec-2.c new file mode 100644 index 00000000000..9ee56b60193 --- /dev/null +++ b/gcc/testsuite/gcc.dg/atomic/c11-atomic-exec-2.c @@ -0,0 +1,171 @@ +/* Test for _Atomic in C11. Basic execution tests for atomic compound + assignment. */ +/* { dg-do run } */ +/* { dg-options "-std=c11 -pedantic-errors" } */ + +extern void abort (void); +extern void exit (int); + +#define CMPLX(X, Y) __builtin_complex ((X), (Y)) + +#define TEST_COMPOUND(TYPE, LHSVAL, RHSVAL, OP) \ + do \ + { \ + static volatile _Atomic (TYPE) a = (TYPE) (LHSVAL); \ + if ((a OP##= (RHSVAL)) != (TYPE) ((TYPE) (LHSVAL) OP (RHSVAL))) \ + abort (); \ + if (a != (TYPE) ((TYPE) (LHSVAL) OP (RHSVAL))) \ + abort (); \ + } \ + while (0) + +#define TEST_COMPOUND_ARITH(LHSVAL, RHSVAL, OP) \ + do \ + { \ + TEST_COMPOUND (_Bool, (LHSVAL), (RHSVAL), OP); \ + TEST_COMPOUND (char, (LHSVAL), (RHSVAL), OP); \ + TEST_COMPOUND (signed char, (LHSVAL), (RHSVAL), OP); \ + TEST_COMPOUND (unsigned char, (LHSVAL), (RHSVAL), OP); \ + TEST_COMPOUND (signed short, (LHSVAL), (RHSVAL), OP); \ + TEST_COMPOUND (unsigned short, (LHSVAL), (RHSVAL), OP); \ + TEST_COMPOUND (signed int, (LHSVAL), (RHSVAL), OP); \ + TEST_COMPOUND (unsigned int, (LHSVAL), (RHSVAL), OP); \ + TEST_COMPOUND (signed long, (LHSVAL), (RHSVAL), OP); \ + TEST_COMPOUND (unsigned long, (LHSVAL), (RHSVAL), OP); \ + TEST_COMPOUND (signed long long, (LHSVAL), (RHSVAL), OP); \ + TEST_COMPOUND (unsigned long long, (LHSVAL), (RHSVAL), OP); \ + TEST_COMPOUND (float, (LHSVAL), (RHSVAL), OP); \ + TEST_COMPOUND (double, (LHSVAL), (RHSVAL), OP); \ + TEST_COMPOUND (long double, (LHSVAL), (RHSVAL), OP); \ + TEST_COMPOUND (_Complex float, (LHSVAL), (RHSVAL), OP); \ + TEST_COMPOUND (_Complex double, (LHSVAL), (RHSVAL), OP); \ + TEST_COMPOUND (_Complex long double, (LHSVAL), (RHSVAL), OP); \ + } \ + while (0) + +#define TEST_COMPOUND_INT(LHSVAL, RHSVAL, OP) \ + do \ + { \ + TEST_COMPOUND (_Bool, (LHSVAL), (RHSVAL), OP); \ + TEST_COMPOUND (char, (LHSVAL), (RHSVAL), OP); \ + TEST_COMPOUND (signed char, (LHSVAL), (RHSVAL), OP); \ + TEST_COMPOUND (unsigned char, (LHSVAL), (RHSVAL), OP); \ + TEST_COMPOUND (signed short, (LHSVAL), (RHSVAL), OP); \ + TEST_COMPOUND (unsigned short, (LHSVAL), (RHSVAL), OP); \ + TEST_COMPOUND (signed int, (LHSVAL), (RHSVAL), OP); \ + TEST_COMPOUND (unsigned int, (LHSVAL), (RHSVAL), OP); \ + TEST_COMPOUND (signed long, (LHSVAL), (RHSVAL), OP); \ + TEST_COMPOUND (unsigned long, (LHSVAL), (RHSVAL), OP); \ + TEST_COMPOUND (signed long long, (LHSVAL), (RHSVAL), OP); \ + TEST_COMPOUND (unsigned long long, (LHSVAL), (RHSVAL), OP); \ + } \ + while (0) + +static void +test_mult (void) +{ + TEST_COMPOUND_ARITH (1, 2, *); + TEST_COMPOUND_ARITH (-3, 5, *); + TEST_COMPOUND_ARITH (-7, -20, *); + TEST_COMPOUND_ARITH (1.25, 3.5, *); + TEST_COMPOUND_ARITH (CMPLX (1.5, 2.5), CMPLX (3.5, 4.5), *); + TEST_COMPOUND_ARITH (CMPLX (1.5, 2.5), 2, *); +} + +static void +test_div (void) +{ + TEST_COMPOUND_ARITH (1, 2, /); + TEST_COMPOUND_ARITH (-6, 3, /); + TEST_COMPOUND_ARITH (-70, -10, /); + TEST_COMPOUND_ARITH (1.25, 2.5, /); + TEST_COMPOUND_ARITH (CMPLX (1.0, 1.0), CMPLX (0.5, 0.5), /); + TEST_COMPOUND_ARITH (CMPLX (1.5, 2.5), 2, /); +} + +static void +test_mod (void) +{ + TEST_COMPOUND_INT (1, 2, %); + TEST_COMPOUND_INT (-3, 5, %); + TEST_COMPOUND_INT (-7, -2, %); +} + +static void +test_plus (void) +{ + TEST_COMPOUND_ARITH (1, 2, +); + TEST_COMPOUND_ARITH (-3, 5, +); + TEST_COMPOUND_ARITH (-7, -20, +); + TEST_COMPOUND_ARITH (1.25, 3.5, +); + TEST_COMPOUND_ARITH (CMPLX (1.5, 2.5), CMPLX (3.5, 4.5), +); + TEST_COMPOUND_ARITH (CMPLX (1.5, 2.5), 2, +); + static int ia[2]; + TEST_COMPOUND (int *, &ia[1], 1, +); + TEST_COMPOUND (int *, &ia[1], -1, +); +} + +static void +test_minus (void) +{ + TEST_COMPOUND_ARITH (1, 2, -); + TEST_COMPOUND_ARITH (-3, 5, -); + TEST_COMPOUND_ARITH (-7, -20, -); + TEST_COMPOUND_ARITH (3.5, 1.25, -); + TEST_COMPOUND_ARITH (CMPLX (3.5, 4.5), CMPLX (1.5, 2.5), -); + TEST_COMPOUND_ARITH (CMPLX (3.5, 2.5), 2, -); + static int ia[2]; + TEST_COMPOUND (int *, &ia[1], 1, -); + TEST_COMPOUND (int *, &ia[1], -1, -); +} + +static void +test_lshift (void) +{ + TEST_COMPOUND_INT (1, 7, <<); + TEST_COMPOUND_INT (15, 3, <<); +} + +static void +test_rshift (void) +{ + TEST_COMPOUND_INT (1, 1, >>); + TEST_COMPOUND_INT (127, 4, >>); +} + +static void +test_and (void) +{ + TEST_COMPOUND_INT (0x1234, 0x7856, &); + TEST_COMPOUND_INT (-1, 0x12345678, &); +} + +static void +test_xor (void) +{ + TEST_COMPOUND_INT (0x1234, 0x7856, ^); + TEST_COMPOUND_INT (-1, 0x12345678, ^); +} + +static void +test_or (void) +{ + TEST_COMPOUND_INT (0x1234, 0x7856, |); + TEST_COMPOUND_INT (-12345, 0x12345678, |); +} + +int +main (void) +{ + test_mult (); + test_div (); + test_mod (); + test_plus (); + test_minus (); + test_lshift (); + test_rshift (); + test_and (); + test_xor (); + test_or (); + exit (0); +} diff --git a/gcc/testsuite/gcc.dg/atomic/c11-atomic-exec-3.c b/gcc/testsuite/gcc.dg/atomic/c11-atomic-exec-3.c new file mode 100644 index 00000000000..7bfa8c05f96 --- /dev/null +++ b/gcc/testsuite/gcc.dg/atomic/c11-atomic-exec-3.c @@ -0,0 +1,85 @@ +/* Test for _Atomic in C11. Basic execution tests for atomic + increment and decrement. */ +/* { dg-do run } */ +/* { dg-options "-std=c11 -pedantic-errors" } */ + +extern void abort (void); +extern void exit (int); + +#define TEST_INCDEC(TYPE, VALUE, PREOP, POSTOP, PRE_P, CHANGE) \ + do \ + { \ + static volatile _Atomic (TYPE) a = (TYPE) (VALUE); \ + if (PREOP a POSTOP != (PRE_P \ + ? (TYPE) ((TYPE) (VALUE) + (CHANGE)) \ + : (TYPE) (VALUE))) \ + abort (); \ + if (a != (TYPE) ((TYPE) (VALUE) + (CHANGE))) \ + abort (); \ + } \ + while (0) + +#define TEST_INCDEC_ARITH(VALUE, PREOP, POSTOP, PRE_P, CHANGE) \ + do \ + { \ + TEST_INCDEC (_Bool, (VALUE), PREOP, POSTOP, (PRE_P), (CHANGE)); \ + TEST_INCDEC (char, (VALUE), PREOP, POSTOP, (PRE_P), (CHANGE)); \ + TEST_INCDEC (signed char, (VALUE), PREOP, POSTOP, (PRE_P), \ + (CHANGE)); \ + TEST_INCDEC (unsigned char, (VALUE), PREOP, POSTOP, (PRE_P), \ + (CHANGE)); \ + TEST_INCDEC (signed short, (VALUE), PREOP, POSTOP, (PRE_P), \ + (CHANGE)); \ + TEST_INCDEC (unsigned short, (VALUE), PREOP, POSTOP, (PRE_P), \ + (CHANGE)); \ + TEST_INCDEC (signed int, (VALUE), PREOP, POSTOP, (PRE_P), \ + (CHANGE)); \ + TEST_INCDEC (unsigned int, (VALUE), PREOP, POSTOP, (PRE_P), \ + (CHANGE)); \ + TEST_INCDEC (signed long, (VALUE), PREOP, POSTOP, (PRE_P), \ + (CHANGE)); \ + TEST_INCDEC (unsigned long, (VALUE), PREOP, POSTOP, (PRE_P), \ + (CHANGE)); \ + TEST_INCDEC (signed long long, (VALUE), PREOP, POSTOP, (PRE_P), \ + (CHANGE)); \ + TEST_INCDEC (unsigned long long, (VALUE), PREOP, POSTOP, (PRE_P), \ + (CHANGE)); \ + TEST_INCDEC (float, (VALUE), PREOP, POSTOP, (PRE_P), (CHANGE)); \ + TEST_INCDEC (double, (VALUE), PREOP, POSTOP, (PRE_P), (CHANGE)); \ + TEST_INCDEC (long double, (VALUE), PREOP, POSTOP, (PRE_P), \ + (CHANGE)); \ + } \ + while (0) + +#define TEST_ALL_INCDEC_ARITH(VALUE) \ + do \ + { \ + TEST_INCDEC_ARITH ((VALUE), ++, , 1, 1); \ + TEST_INCDEC_ARITH ((VALUE), --, , 1, -1); \ + TEST_INCDEC_ARITH ((VALUE), , ++, 0, 1); \ + TEST_INCDEC_ARITH ((VALUE), , --, 0, -1); \ + } \ + while (0) + +static void +test_incdec (void) +{ + TEST_ALL_INCDEC_ARITH (0); + TEST_ALL_INCDEC_ARITH (1); + TEST_ALL_INCDEC_ARITH (2); + TEST_ALL_INCDEC_ARITH (-1); + TEST_ALL_INCDEC_ARITH (1ULL << 60); + TEST_ALL_INCDEC_ARITH (1.5); + static int ia[2]; + TEST_INCDEC (int *, &ia[1], ++, , 1, 1); + TEST_INCDEC (int *, &ia[1], --, , 1, -1); + TEST_INCDEC (int *, &ia[1], , ++, 0, 1); + TEST_INCDEC (int *, &ia[1], , --, 0, -1); +} + +int +main (void) +{ + test_incdec (); + exit (0); +} diff --git a/gcc/testsuite/gcc.dg/atomic/c11-atomic-exec-4.c b/gcc/testsuite/gcc.dg/atomic/c11-atomic-exec-4.c new file mode 100644 index 00000000000..02bb3cb7960 --- /dev/null +++ b/gcc/testsuite/gcc.dg/atomic/c11-atomic-exec-4.c @@ -0,0 +1,208 @@ +/* Test for _Atomic in C11. Test that compare-and-exchange is + operating properly when operations on the same variable are carried + out in two threads. */ +/* { dg-do run } */ +/* { dg-options "-std=c11 -pedantic-errors -pthread -D_POSIX_C_SOURCE=200809L" } */ +/* { dg-require-effective-target pthread } */ + +#include <stdint.h> +#include <pthread.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> + +#define ITER_COUNT 10000 + +static volatile _Atomic bool thread_ready; + +/* Generate test code (with NAME used to name functions and variables) + for atomic compound assignments to a variable of type LHSTYPE. The + variable is initialized to INIT, then PRE var POST is executed + ITER_COUNT times in each of two threads, and the final result + should be FINAL. A function test_main_##NAME is generated that + returns nonzero on failure, zero on success. */ + +#define TEST_FUNCS(NAME, LHSTYPE, PRE, POST, INIT, FINAL) \ + \ +static volatile _Atomic LHSTYPE var_##NAME = (INIT); \ + \ +static void * \ +test_thread_##NAME (void *arg) \ +{ \ + thread_ready = true; \ + for (int i = 0; i < ITER_COUNT; i++) \ + PRE var_##NAME POST; \ + return NULL; \ +} \ + \ +static int \ +test_main_##NAME (void) \ +{ \ + thread_ready = false; \ + pthread_t thread_id; \ + int pret = pthread_create (&thread_id, NULL, test_thread_##NAME, \ + NULL); \ + if (pret != 0) \ + { \ + printf ("pthread_create failed: %d\n", pret); \ + return 1; \ + } \ + while (!thread_ready) \ + ; \ + for (int i = 0; i < ITER_COUNT; i++) \ + PRE var_##NAME POST; \ + pthread_join (thread_id, NULL); \ + if (var_##NAME != (FINAL)) \ + { \ + printf (#NAME " failed\n"); \ + return 1; \ + } \ + else \ + { \ + printf (#NAME " passed\n"); \ + return 0; \ + } \ +} + +TEST_FUNCS (uint8_add, uint8_t, , += 1, 0, (uint8_t) 20000) +TEST_FUNCS (uint8_add_3, uint8_t, , += 3, 0, (uint8_t) 60000) +TEST_FUNCS (uint16_add, uint16_t, , += 1, 0, (uint16_t) 20000) +TEST_FUNCS (uint16_add_3, uint16_t, , += 3, 0, (uint16_t) 60000) +TEST_FUNCS (uint32_add, uint32_t, , += 1, 0, (uint32_t) 20000) +TEST_FUNCS (uint32_add_3, uint32_t, , += 3, 0, (uint32_t) 60000) +TEST_FUNCS (uint64_add, uint64_t, , += 1, 0, (uint64_t) 20000) +TEST_FUNCS (uint64_add_3, uint64_t, , += 3, 0, (uint64_t) 60000) +TEST_FUNCS (uint64_add_neg, uint64_t, , += 1, -10000, (uint64_t) 10000) +TEST_FUNCS (float_add, float, , += 1, 0, 20000) +TEST_FUNCS (double_add, double, , += 1, 0, 20000) +TEST_FUNCS (long_double_add, long double, , += 1, 0, 20000) +TEST_FUNCS (complex_float_add, _Complex float, , += 1, 0, 20000) +TEST_FUNCS (complex_double_add, _Complex double, , += 1, 0, 20000) +TEST_FUNCS (complex_long_double_add, _Complex long double, , += 1, 0, 20000) +TEST_FUNCS (uint8_postinc, uint8_t, , ++, 0, (uint8_t) 20000) +TEST_FUNCS (uint16_postinc, uint16_t, , ++, 0, (uint16_t) 20000) +TEST_FUNCS (uint32_postinc, uint32_t, , ++, 0, (uint32_t) 20000) +TEST_FUNCS (uint64_postinc, uint64_t, , ++, 0, (uint64_t) 20000) +TEST_FUNCS (uint64_postinc_neg, uint64_t, , ++, -10000, (uint64_t) 10000) +TEST_FUNCS (float_postinc, float, , ++, 0, 20000) +TEST_FUNCS (double_postinc, double, , ++, 0, 20000) +TEST_FUNCS (long_double_postinc, long double, , ++, 0, 20000) +TEST_FUNCS (uint8_preinc, uint8_t, ++, , 0, (uint8_t) 20000) +TEST_FUNCS (uint16_preinc, uint16_t, ++, , 0, (uint16_t) 20000) +TEST_FUNCS (uint32_preinc, uint32_t, ++, , 0, (uint32_t) 20000) +TEST_FUNCS (uint64_preinc, uint64_t, ++, , 0, (uint64_t) 20000) +TEST_FUNCS (uint64_preinc_neg, uint64_t, ++, , -10000, (uint64_t) 10000) +TEST_FUNCS (float_preinc, float, ++, , 0, 20000) +TEST_FUNCS (double_preinc, double, ++, , 0, 20000) +TEST_FUNCS (long_double_preinc, long double, ++, , 0, 20000) +TEST_FUNCS (uint8_sub, uint8_t, , -= 1, 0, (uint8_t) -20000) +TEST_FUNCS (uint8_sub_3, uint8_t, , -= 3, 0, (uint8_t) -60000) +TEST_FUNCS (uint16_sub, uint16_t, , -= 1, 0, (uint16_t) -20000) +TEST_FUNCS (uint16_sub_3, uint16_t, , -= 3, 0, (uint16_t) -60000) +TEST_FUNCS (uint32_sub, uint32_t, , -= 1, 0, (uint32_t) -20000) +TEST_FUNCS (uint32_sub_3, uint32_t, , -= 3, 0, (uint32_t) -60000) +TEST_FUNCS (uint64_sub, uint64_t, , -= 1, 0, (uint64_t) -20000) +TEST_FUNCS (uint64_sub_3, uint64_t, , -= 3, 0, (uint64_t) -60000) +TEST_FUNCS (uint64_sub_neg, uint64_t, , -= 1, 10000, (uint64_t) -10000) +TEST_FUNCS (float_sub, float, , -= 1, 0, -20000) +TEST_FUNCS (double_sub, double, , -= 1, 0, -20000) +TEST_FUNCS (long_double_sub, long double, , -= 1, 0, -20000) +TEST_FUNCS (complex_float_sub, _Complex float, , -= 1, 0, -20000) +TEST_FUNCS (complex_double_sub, _Complex double, , -= 1, 0, -20000) +TEST_FUNCS (complex_long_double_sub, _Complex long double, , -= 1, 0, -20000) +TEST_FUNCS (uint8_postdec, uint8_t, , --, 0, (uint8_t) -20000) +TEST_FUNCS (uint16_postdec, uint16_t, , --, 0, (uint16_t) -20000) +TEST_FUNCS (uint32_postdec, uint32_t, , --, 0, (uint32_t) -20000) +TEST_FUNCS (uint64_postdec, uint64_t, , --, 0, (uint64_t) -20000) +TEST_FUNCS (uint64_postdec_neg, uint64_t, , --, 10000, (uint64_t) -10000) +TEST_FUNCS (float_postdec, float, , --, 0, -20000) +TEST_FUNCS (double_postdec, double, , --, 0, -20000) +TEST_FUNCS (long_double_postdec, long double, , --, 0, -20000) +TEST_FUNCS (uint8_predec, uint8_t, --, , 0, (uint8_t) -20000) +TEST_FUNCS (uint16_predec, uint16_t, --, , 0, (uint16_t) -20000) +TEST_FUNCS (uint32_predec, uint32_t, --, , 0, (uint32_t) -20000) +TEST_FUNCS (uint64_predec, uint64_t, --, , 0, (uint64_t) -20000) +TEST_FUNCS (uint64_predec_neg, uint64_t, --, , 10000, (uint64_t) -10000) +TEST_FUNCS (float_predec, float, --, , 0, -20000) +TEST_FUNCS (double_predec, double, --, , 0, -20000) +TEST_FUNCS (long_double_predec, long double, --, , 0, -20000) +TEST_FUNCS (uint8_mul, uint8_t, , *= 3, 1, (uint8_t) 0x81) +TEST_FUNCS (uint16_mul, uint16_t, , *= 3, 1, (uint16_t) 0x9681) +TEST_FUNCS (uint32_mul, uint32_t, , *= 3, 1, (uint32_t) 0x62b49681U) +TEST_FUNCS (uint64_mul, uint64_t, , *= 3, 1, (uint64_t) 0xcd926beb62b49681ULL) + +int +main (void) +{ + int ret = 0; + ret |= test_main_uint8_add (); + ret |= test_main_uint8_add_3 (); + ret |= test_main_uint16_add (); + ret |= test_main_uint16_add_3 (); + ret |= test_main_uint32_add (); + ret |= test_main_uint32_add_3 (); + ret |= test_main_uint64_add (); + ret |= test_main_uint64_add_3 (); + ret |= test_main_uint64_add_neg (); + ret |= test_main_float_add (); + ret |= test_main_double_add (); + ret |= test_main_long_double_add (); + ret |= test_main_complex_float_add (); + ret |= test_main_complex_double_add (); + ret |= test_main_complex_long_double_add (); + ret |= test_main_uint8_postinc (); + ret |= test_main_uint16_postinc (); + ret |= test_main_uint32_postinc (); + ret |= test_main_uint64_postinc (); + ret |= test_main_uint64_postinc_neg (); + ret |= test_main_float_postinc (); + ret |= test_main_double_postinc (); + ret |= test_main_long_double_postinc (); + ret |= test_main_uint8_preinc (); + ret |= test_main_uint16_preinc (); + ret |= test_main_uint32_preinc (); + ret |= test_main_uint64_preinc (); + ret |= test_main_uint64_preinc_neg (); + ret |= test_main_float_preinc (); + ret |= test_main_double_preinc (); + ret |= test_main_long_double_preinc (); + ret |= test_main_uint8_sub (); + ret |= test_main_uint8_sub_3 (); + ret |= test_main_uint16_sub (); + ret |= test_main_uint16_sub_3 (); + ret |= test_main_uint32_sub (); + ret |= test_main_uint32_sub_3 (); + ret |= test_main_uint64_sub (); + ret |= test_main_uint64_sub_3 (); + ret |= test_main_uint64_sub_neg (); + ret |= test_main_float_sub (); + ret |= test_main_double_sub (); + ret |= test_main_long_double_sub (); + ret |= test_main_complex_float_sub (); + ret |= test_main_complex_double_sub (); + ret |= test_main_complex_long_double_sub (); + ret |= test_main_uint8_postdec (); + ret |= test_main_uint16_postdec (); + ret |= test_main_uint32_postdec (); + ret |= test_main_uint64_postdec (); + ret |= test_main_uint64_postdec_neg (); + ret |= test_main_float_postdec (); + ret |= test_main_double_postdec (); + ret |= test_main_long_double_postdec (); + ret |= test_main_uint8_predec (); + ret |= test_main_uint16_predec (); + ret |= test_main_uint32_predec (); + ret |= test_main_uint64_predec (); + ret |= test_main_uint64_predec_neg (); + ret |= test_main_float_predec (); + ret |= test_main_double_predec (); + ret |= test_main_long_double_predec (); + ret |= test_main_uint8_mul (); + ret |= test_main_uint16_mul (); + ret |= test_main_uint32_mul (); + ret |= test_main_uint64_mul (); + if (ret) + abort (); + else + exit (0); +} diff --git a/gcc/testsuite/gcc.dg/atomic/c11-atomic-exec-5.c b/gcc/testsuite/gcc.dg/atomic/c11-atomic-exec-5.c new file mode 100644 index 00000000000..9e6977b4286 --- /dev/null +++ b/gcc/testsuite/gcc.dg/atomic/c11-atomic-exec-5.c @@ -0,0 +1,541 @@ +/* Test for _Atomic in C11. Test floating-point exceptions for + compound assignment are consistent with result (so that if multiple + iterations of the compare-and-exchange loop are needed, exceptions + get properly cleared). */ +/* { dg-do run } */ +/* { dg-options "-std=c11 -pedantic-errors -pthread -D_POSIX_C_SOURCE=200809L" } */ +/* { dg-require-effective-target fenv_exceptions } */ +/* { dg-require-effective-target pthread } */ + +#include <fenv.h> +#include <float.h> +#include <pthread.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> + +#define TEST_ALL_EXCEPT (FE_DIVBYZERO \ + | FE_INEXACT \ + | FE_INVALID \ + | FE_OVERFLOW \ + | FE_UNDERFLOW) + +#define ITER_COUNT 10000 + +static volatile _Atomic bool thread_ready, thread_stop; + +/* Generate test code (with NAME used to name functions and variables) + for atomic compound assignments to a variable of type LHSTYPE. One + thread repeatedly stores the values INIT1 and INIT2 in a variable, + while the other repeatedly executes PRE var POST having set + floating-point exceptions to BEXC. If the value of the assignment + operation satisfies VALTEST1 (var), the floating-point exceptions + should be BEXC | EXC1; otherwise, they should be BEXC | EXC2. A + function test_main_##NAME is generated that returns nonzero on + failure, zero on success. */ + +#define TEST_FUNCS(NAME, LHSTYPE, PRE, POST, BEXC, \ + INIT1, VALTEST1, EXC1, INIT2, EXC2) \ + \ +static volatile _Atomic LHSTYPE var_##NAME; \ + \ +static void * \ +test_thread_##NAME (void *arg) \ +{ \ + thread_ready = true; \ + while (!thread_stop) \ + { \ + var_##NAME = (INIT1); \ + var_##NAME = (INIT2); \ + } \ + return NULL; \ +} \ + \ +static int \ +test_main_##NAME (void) \ +{ \ + thread_stop = false; \ + thread_ready = false; \ + var_##NAME = (INIT1); \ + pthread_t thread_id; \ + int pret = pthread_create (&thread_id, NULL, test_thread_##NAME, \ + NULL); \ + if (pret != 0) \ + { \ + printf ("pthread_create failed: %d\n", pret); \ + return 1; \ + } \ + int num_1_pass = 0, num_1_fail = 0, num_2_pass = 0, num_2_fail = 0; \ + while (!thread_ready) \ + ; \ + for (int i = 0; i < ITER_COUNT; i++) \ + { \ + feclearexcept (FE_ALL_EXCEPT); \ + feraiseexcept (BEXC); \ + LHSTYPE r = (PRE var_##NAME POST); \ + int rexc = fetestexcept (TEST_ALL_EXCEPT); \ + if (VALTEST1 (r)) \ + { \ + if (rexc == ((BEXC) | (EXC1))) \ + num_1_pass++; \ + else \ + num_1_fail++; \ + var_##NAME = (INIT2); \ + } \ + else \ + { \ + if (rexc == ((BEXC) | (EXC2))) \ + num_2_pass++; \ + else \ + num_2_fail++; \ + var_##NAME = (INIT1); \ + } \ + } \ + thread_stop = true; \ + pthread_join (thread_id, NULL); \ + printf (#NAME " (a) %d pass, %d fail; (b) %d pass, %d fail\n", \ + num_1_pass, num_1_fail, num_2_pass, num_2_fail); \ + return num_1_fail || num_2_fail; \ +} + +TEST_FUNCS (float_add_invalid, float, , += __builtin_inff (), 0, + 0, __builtin_isinf, 0, + -__builtin_inff (), FE_INVALID) +TEST_FUNCS (float_add_invalid_prev, float, , += __builtin_inff (), + FE_DIVBYZERO | FE_INEXACT | FE_OVERFLOW | FE_UNDERFLOW, + 0, __builtin_isinf, 0, + -__builtin_inff (), FE_INVALID) +TEST_FUNCS (float_add_overflow, float, , += FLT_MAX, 0, + FLT_MAX, __builtin_isinf, FE_OVERFLOW | FE_INEXACT, + 0, 0) +TEST_FUNCS (float_add_overflow_prev, float, , += FLT_MAX, FE_INVALID, + FLT_MAX, __builtin_isinf, FE_OVERFLOW | FE_INEXACT, + 0, 0) +TEST_FUNCS (float_add_overflow_double, float, , += (double) FLT_MAX, 0, + FLT_MAX, __builtin_isinf, FE_OVERFLOW | FE_INEXACT, + 0, 0) +TEST_FUNCS (float_add_overflow_long_double, float, , += (long double) FLT_MAX, 0, + FLT_MAX, __builtin_isinf, FE_OVERFLOW | FE_INEXACT, + 0, 0) +#define NOT_FLT_EPSILON_2(X) ((X) != FLT_EPSILON / 2) +TEST_FUNCS (float_add_inexact, float, , += FLT_EPSILON / 2, 0, + 1.0f, NOT_FLT_EPSILON_2, FE_INEXACT, + 0, 0) +#define NOT_0(X) ((X) != 0) +TEST_FUNCS (float_add_inexact_int, float, , += 1, 0, + FLT_EPSILON / 2, NOT_0, FE_INEXACT, + -1, 0) +TEST_FUNCS (float_preinc_inexact, float, ++, , 0, + FLT_EPSILON / 2, NOT_0, FE_INEXACT, + -1, 0) +#define NOT_MINUS_1(X) ((X) != -1) +TEST_FUNCS (float_postinc_inexact, float, , ++, 0, + FLT_EPSILON / 2, NOT_MINUS_1, FE_INEXACT, + -1, 0) +#if FLT_EVAL_METHOD == 0 +TEST_FUNCS (long_add_float_inexact, long, , += 2 / FLT_EPSILON, 0, + 1, NOT_0, FE_INEXACT, + -2 / FLT_EPSILON, 0) +#endif +#define REAL_ISINF(X) (__builtin_isinf (__real__ (X))) +TEST_FUNCS (complex_float_add_overflow, _Complex float, , += FLT_MAX, 0, + FLT_MAX, REAL_ISINF, FE_OVERFLOW | FE_INEXACT, + 0, 0) +TEST_FUNCS (float_sub_invalid, float, , -= __builtin_inff (), 0, + 0, __builtin_isinf, 0, + __builtin_inff (), FE_INVALID) +TEST_FUNCS (float_sub_overflow, float, , -= FLT_MAX, 0, + -FLT_MAX, __builtin_isinf, FE_OVERFLOW | FE_INEXACT, + 0, 0) +#define NOT_MINUS_FLT_EPSILON_2(X) ((X) != -FLT_EPSILON / 2) +TEST_FUNCS (float_sub_inexact, float, , -= FLT_EPSILON / 2, 0, + -1.0f, NOT_MINUS_FLT_EPSILON_2, FE_INEXACT, + 0, 0) +#define NOT_0(X) ((X) != 0) +TEST_FUNCS (float_sub_inexact_int, float, , -= 1, 0, + -FLT_EPSILON / 2, NOT_0, FE_INEXACT, + 1, 0) +TEST_FUNCS (float_predec_inexact, float, --, , 0, + -FLT_EPSILON / 2, NOT_0, FE_INEXACT, + 1, 0) +#define NOT_1(X) ((X) != 1) +TEST_FUNCS (float_postdec_inexact, float, , --, 0, + -FLT_EPSILON / 2, NOT_1, FE_INEXACT, + 1, 0) +#if FLT_EVAL_METHOD == 0 +TEST_FUNCS (long_sub_float_inexact, long, , -= 2 / FLT_EPSILON, 0, + -1, NOT_0, FE_INEXACT, + 2 / FLT_EPSILON, 0) +#endif +TEST_FUNCS (complex_float_sub_overflow, _Complex float, , -= FLT_MAX, 0, + -FLT_MAX, REAL_ISINF, FE_OVERFLOW | FE_INEXACT, + 0, 0) +TEST_FUNCS (float_mul_invalid, float, , *= __builtin_inff (), 0, + __builtin_inff (), __builtin_isinf, 0, + 0, FE_INVALID) +TEST_FUNCS (float_mul_overflow, float, , *= FLT_MAX, 0, + FLT_MAX, __builtin_isinf, FE_OVERFLOW | FE_INEXACT, + 0, 0) +#define IS_0(X) ((X) == 0) +TEST_FUNCS (float_mul_underflow, float, , *= FLT_MIN, 0, + FLT_MIN, IS_0, FE_UNDERFLOW | FE_INEXACT, + 1, 0) +TEST_FUNCS (float_mul_inexact, float, , *= 1 + FLT_EPSILON, 0, + 1 + FLT_EPSILON, NOT_0, FE_INEXACT, + 0, 0) +TEST_FUNCS (float_mul_inexact_int, float, , *= 3, 0, + 1 + FLT_EPSILON, NOT_0, FE_INEXACT, + 0, 0) +#if FLT_EVAL_METHOD == 0 +TEST_FUNCS(long_mul_float_inexact, long, , *= 3.0f, 0, + 1 + 1 / FLT_EPSILON, NOT_0, FE_INEXACT, + 0, 0) +#endif +TEST_FUNCS (complex_float_mul_overflow, _Complex float, , *= FLT_MAX, 0, + FLT_MAX, REAL_ISINF, FE_OVERFLOW | FE_INEXACT, + 0, 0) +TEST_FUNCS (float_div_invalid_divbyzero, float, , /= 0.0f, 0, + 1, __builtin_isinf, FE_DIVBYZERO, + 0, FE_INVALID) +TEST_FUNCS (float_div_overflow, float, , /= FLT_MIN, 0, + FLT_MAX, __builtin_isinf, FE_OVERFLOW | FE_INEXACT, + 0, 0) +TEST_FUNCS (float_div_underflow, float, , /= FLT_MAX, 0, + FLT_MIN, IS_0, FE_UNDERFLOW | FE_INEXACT, + FLT_MAX, 0) +TEST_FUNCS (float_div_inexact, float, , /= 3.0f, 0, + 1, NOT_0, FE_INEXACT, + 0, 0) +TEST_FUNCS (float_div_inexact_int, float, , /= 3, 0, + 1, NOT_0, FE_INEXACT, + 0, 0) +TEST_FUNCS (int_div_float_inexact, int, , /= 3.0f, 0, + 4, NOT_0, FE_INEXACT, + 0, 0) +TEST_FUNCS (complex_float_div_overflow, _Complex float, , /= FLT_MIN, 0, + FLT_MAX, REAL_ISINF, FE_OVERFLOW | FE_INEXACT, + 0, 0) + +TEST_FUNCS (double_add_invalid, double, , += __builtin_inf (), 0, + 0, __builtin_isinf, 0, + -__builtin_inf (), FE_INVALID) +TEST_FUNCS (double_add_overflow, double, , += DBL_MAX, 0, + DBL_MAX, __builtin_isinf, FE_OVERFLOW | FE_INEXACT, + 0, 0) +TEST_FUNCS (double_add_overflow_long_double, double, , += (long double) DBL_MAX, 0, + DBL_MAX, __builtin_isinf, FE_OVERFLOW | FE_INEXACT, + 0, 0) +#define NOT_DBL_EPSILON_2(X) ((X) != DBL_EPSILON / 2) +TEST_FUNCS (double_add_inexact, double, , += DBL_EPSILON / 2, 0, + 1.0, NOT_DBL_EPSILON_2, FE_INEXACT, + 0, 0) +TEST_FUNCS (double_add_inexact_int, double, , += 1, 0, + DBL_EPSILON / 2, NOT_0, FE_INEXACT, + -1, 0) +TEST_FUNCS (double_preinc_inexact, double, ++, , 0, + DBL_EPSILON / 2, NOT_0, FE_INEXACT, + -1, 0) +TEST_FUNCS (double_postinc_inexact, double, , ++, 0, + DBL_EPSILON / 2, NOT_MINUS_1, FE_INEXACT, + -1, 0) +#if FLT_EVAL_METHOD == 0 +TEST_FUNCS (long_long_add_double_inexact, long long, , += 2 / DBL_EPSILON, 0, + 1, NOT_0, FE_INEXACT, + -2 / DBL_EPSILON, 0) +#endif +TEST_FUNCS (complex_double_add_overflow, _Complex double, , += DBL_MAX, 0, + DBL_MAX, REAL_ISINF, FE_OVERFLOW | FE_INEXACT, + 0, 0) +TEST_FUNCS (double_sub_invalid, double, , -= __builtin_inf (), 0, + 0, __builtin_isinf, 0, + __builtin_inf (), FE_INVALID) +TEST_FUNCS (double_sub_overflow, double, , -= DBL_MAX, 0, + -DBL_MAX, __builtin_isinf, FE_OVERFLOW | FE_INEXACT, + 0, 0) +#define NOT_MINUS_DBL_EPSILON_2(X) ((X) != -DBL_EPSILON / 2) +TEST_FUNCS (double_sub_inexact, double, , -= DBL_EPSILON / 2, 0, + -1.0, NOT_MINUS_DBL_EPSILON_2, FE_INEXACT, + 0, 0) +TEST_FUNCS (double_sub_inexact_int, double, , -= 1, 0, + -DBL_EPSILON / 2, NOT_0, FE_INEXACT, + 1, 0) +TEST_FUNCS (double_predec_inexact, double, --, , 0, + -DBL_EPSILON / 2, NOT_0, FE_INEXACT, + 1, 0) +TEST_FUNCS (double_postdec_inexact, double, , --, 0, + -DBL_EPSILON / 2, NOT_1, FE_INEXACT, + 1, 0) +#if FLT_EVAL_METHOD == 0 +TEST_FUNCS (long_long_sub_double_inexact, long long, , -= 2 / DBL_EPSILON, 0, + -1, NOT_0, FE_INEXACT, + 2 / DBL_EPSILON, 0) +#endif +TEST_FUNCS (complex_double_sub_overflow, _Complex double, , -= DBL_MAX, 0, + -DBL_MAX, REAL_ISINF, FE_OVERFLOW | FE_INEXACT, + 0, 0) +TEST_FUNCS (double_mul_invalid, double, , *= __builtin_inf (), 0, + __builtin_inf (), __builtin_isinf, 0, + 0, FE_INVALID) +TEST_FUNCS (double_mul_overflow, double, , *= DBL_MAX, 0, + DBL_MAX, __builtin_isinf, FE_OVERFLOW | FE_INEXACT, + 0, 0) +TEST_FUNCS (double_mul_overflow_float, double, , *= FLT_MAX, 0, + DBL_MAX, __builtin_isinf, FE_OVERFLOW | FE_INEXACT, + 0, 0) +TEST_FUNCS (double_mul_underflow, double, , *= DBL_MIN, 0, + DBL_MIN, IS_0, FE_UNDERFLOW | FE_INEXACT, + 1, 0) +TEST_FUNCS (double_mul_inexact, double, , *= 1 + DBL_EPSILON, 0, + 1 + DBL_EPSILON, NOT_0, FE_INEXACT, + 0, 0) +TEST_FUNCS (double_mul_inexact_int, double, , *= 3, 0, + 1 + DBL_EPSILON, NOT_0, FE_INEXACT, + 0, 0) +#if FLT_EVAL_METHOD == 0 +TEST_FUNCS(long_long_mul_double_inexact, long long, , *= 3.0, 0, + 1 + 1 / DBL_EPSILON, NOT_0, FE_INEXACT, + 0, 0) +#endif +TEST_FUNCS (complex_double_mul_overflow, _Complex double, , *= DBL_MAX, 0, + DBL_MAX, REAL_ISINF, FE_OVERFLOW | FE_INEXACT, + 0, 0) +TEST_FUNCS (double_div_invalid_divbyzero, double, , /= 0.0, 0, + 1, __builtin_isinf, FE_DIVBYZERO, + 0, FE_INVALID) +TEST_FUNCS (double_div_overflow, double, , /= DBL_MIN, 0, + DBL_MAX, __builtin_isinf, FE_OVERFLOW | FE_INEXACT, + 0, 0) +TEST_FUNCS (double_div_underflow, double, , /= DBL_MAX, 0, + DBL_MIN, IS_0, FE_UNDERFLOW | FE_INEXACT, + DBL_MAX, 0) +TEST_FUNCS (double_div_inexact, double, , /= 3.0, 0, + 1, NOT_0, FE_INEXACT, + 0, 0) +TEST_FUNCS (double_div_inexact_int, double, , /= 3, 0, + 1, NOT_0, FE_INEXACT, + 0, 0) +TEST_FUNCS (int_div_double_inexact, int, , /= 3.0, 0, + 4, NOT_0, FE_INEXACT, + 0, 0) +TEST_FUNCS (complex_double_div_overflow, _Complex double, , /= DBL_MIN, 0, + DBL_MAX, REAL_ISINF, FE_OVERFLOW | FE_INEXACT, + 0, 0) + +TEST_FUNCS (long_double_add_invalid, long double, , += __builtin_infl (), 0, + 0, __builtin_isinf, 0, + -__builtin_infl (), FE_INVALID) +TEST_FUNCS (long_double_add_overflow, long double, , += LDBL_MAX, 0, + LDBL_MAX, __builtin_isinf, FE_OVERFLOW | FE_INEXACT, + 0, 0) +#define NOT_LDBL_EPSILON_2(X) ((X) != LDBL_EPSILON / 2) +#if LDBL_MANT_DIG != 106 +TEST_FUNCS (long_double_add_inexact, long double, , += LDBL_EPSILON / 2, 0, + 1.0L, NOT_LDBL_EPSILON_2, FE_INEXACT, + 0, 0) +TEST_FUNCS (long_double_add_inexact_int, long double, , += 1, 0, + LDBL_EPSILON / 2, NOT_0, FE_INEXACT, + -1, 0) +TEST_FUNCS (long_double_preinc_inexact, long double, ++, , 0, + LDBL_EPSILON / 2, NOT_0, FE_INEXACT, + -1, 0) +TEST_FUNCS (long_double_postinc_inexact, long double, , ++, 0, + LDBL_EPSILON / 2, NOT_MINUS_1, FE_INEXACT, + -1, 0) +#endif +TEST_FUNCS (complex_long_double_add_overflow, _Complex long double, , += LDBL_MAX, 0, + LDBL_MAX, REAL_ISINF, FE_OVERFLOW | FE_INEXACT, + 0, 0) +TEST_FUNCS (long_double_sub_invalid, long double, , -= __builtin_infl (), 0, + 0, __builtin_isinf, 0, + __builtin_infl (), FE_INVALID) +TEST_FUNCS (long_double_sub_overflow, long double, , -= LDBL_MAX, 0, + -LDBL_MAX, __builtin_isinf, FE_OVERFLOW | FE_INEXACT, + 0, 0) +#define NOT_MINUS_LDBL_EPSILON_2(X) ((X) != -LDBL_EPSILON / 2) +#if LDBL_MANT_DIG != 106 +TEST_FUNCS (long_double_sub_inexact, long double, , -= LDBL_EPSILON / 2, 0, + -1.0L, NOT_MINUS_LDBL_EPSILON_2, FE_INEXACT, + 0, 0) +TEST_FUNCS (long_double_sub_inexact_int, long double, , -= 1, 0, + -LDBL_EPSILON / 2, NOT_0, FE_INEXACT, + 1, 0) +TEST_FUNCS (long_double_predec_inexact, long double, --, , 0, + -LDBL_EPSILON / 2, NOT_0, FE_INEXACT, + 1, 0) +TEST_FUNCS (long_double_postdec_inexact, long double, , --, 0, + -LDBL_EPSILON / 2, NOT_1, FE_INEXACT, + 1, 0) +#endif +TEST_FUNCS (complex_long_double_sub_overflow, _Complex long double, , -= LDBL_MAX, 0, + -LDBL_MAX, REAL_ISINF, FE_OVERFLOW | FE_INEXACT, + 0, 0) +TEST_FUNCS (long_double_mul_invalid, long double, , *= __builtin_infl (), 0, + __builtin_infl (), __builtin_isinf, 0, + 0, FE_INVALID) +TEST_FUNCS (long_double_mul_overflow, long double, , *= LDBL_MAX, 0, + LDBL_MAX, __builtin_isinf, FE_OVERFLOW | FE_INEXACT, + 0, 0) +TEST_FUNCS (long_double_mul_overflow_float, long double, , *= FLT_MAX, 0, + LDBL_MAX, __builtin_isinf, FE_OVERFLOW | FE_INEXACT, + 0, 0) +TEST_FUNCS (long_double_mul_overflow_double, long double, , *= DBL_MAX, 0, + LDBL_MAX, __builtin_isinf, FE_OVERFLOW | FE_INEXACT, + 0, 0) +TEST_FUNCS (long_double_mul_underflow, long double, , *= LDBL_MIN, 0, + LDBL_MIN, IS_0, FE_UNDERFLOW | FE_INEXACT, + 1, 0) +#if LDBL_MANT_DIG != 106 +TEST_FUNCS (long_double_mul_inexact, long double, , *= 1 + LDBL_EPSILON, 0, + 1 + LDBL_EPSILON, NOT_0, FE_INEXACT, + 0, 0) +TEST_FUNCS (long_double_mul_inexact_int, long double, , *= 3, 0, + 1 + LDBL_EPSILON, NOT_0, FE_INEXACT, + 0, 0) +#endif +TEST_FUNCS (complex_long_double_mul_overflow, _Complex long double, , *= LDBL_MAX, 0, + LDBL_MAX, REAL_ISINF, FE_OVERFLOW | FE_INEXACT, + 0, 0) +TEST_FUNCS (long_double_div_invalid_divbyzero, long double, , /= 0.0L, 0, + 1, __builtin_isinf, FE_DIVBYZERO, + 0, FE_INVALID) +TEST_FUNCS (long_double_div_overflow, long double, , /= LDBL_MIN, 0, + LDBL_MAX, __builtin_isinf, FE_OVERFLOW | FE_INEXACT, + 0, 0) +TEST_FUNCS (long_double_div_underflow, long double, , /= LDBL_MAX, 0, + LDBL_MIN, IS_0, FE_UNDERFLOW | FE_INEXACT, + LDBL_MAX, 0) +TEST_FUNCS (long_double_div_inexact, long double, , /= 3.0L, 0, + 1, NOT_0, FE_INEXACT, + 0, 0) +TEST_FUNCS (long_double_div_inexact_int, long double, , /= 3, 0, + 1, NOT_0, FE_INEXACT, + 0, 0) +TEST_FUNCS (int_div_long_double_inexact, int, , /= 3.0L, 0, + 4, NOT_0, FE_INEXACT, + 0, 0) +TEST_FUNCS (complex_long_double_div_overflow, _Complex long double, , /= LDBL_MIN, 0, + LDBL_MAX, REAL_ISINF, FE_OVERFLOW | FE_INEXACT, + 0, 0) + +int +main (void) +{ + int ret = 0; + ret |= test_main_float_add_invalid (); + ret |= test_main_float_add_invalid_prev (); + ret |= test_main_float_add_overflow (); + ret |= test_main_float_add_overflow_prev (); + ret |= test_main_float_add_overflow_double (); + ret |= test_main_float_add_overflow_long_double (); + ret |= test_main_float_add_inexact (); + ret |= test_main_float_add_inexact_int (); + ret |= test_main_float_preinc_inexact (); + ret |= test_main_float_postinc_inexact (); +#if FLT_EVAL_METHOD == 0 + ret |= test_main_long_add_float_inexact (); +#endif + ret |= test_main_complex_float_add_overflow (); + ret |= test_main_float_sub_invalid (); + ret |= test_main_float_sub_overflow (); + ret |= test_main_float_sub_inexact (); + ret |= test_main_float_sub_inexact_int (); + ret |= test_main_float_predec_inexact (); + ret |= test_main_float_postdec_inexact (); +#if FLT_EVAL_METHOD == 0 + ret |= test_main_long_sub_float_inexact (); +#endif + ret |= test_main_complex_float_sub_overflow (); + ret |= test_main_float_mul_invalid (); + ret |= test_main_float_mul_overflow (); + ret |= test_main_float_mul_underflow (); + ret |= test_main_float_mul_inexact (); + ret |= test_main_float_mul_inexact_int (); +#if FLT_EVAL_METHOD == 0 + ret |= test_main_long_mul_float_inexact (); +#endif + ret |= test_main_complex_float_mul_overflow (); + ret |= test_main_float_div_invalid_divbyzero (); + ret |= test_main_float_div_overflow (); + ret |= test_main_float_div_underflow (); + ret |= test_main_float_div_inexact (); + ret |= test_main_float_div_inexact_int (); + ret |= test_main_int_div_float_inexact (); + ret |= test_main_complex_float_div_overflow (); + ret |= test_main_double_add_invalid (); + ret |= test_main_double_add_overflow (); + ret |= test_main_double_add_overflow_long_double (); + ret |= test_main_double_add_inexact (); + ret |= test_main_double_add_inexact_int (); + ret |= test_main_double_preinc_inexact (); + ret |= test_main_double_postinc_inexact (); +#if FLT_EVAL_METHOD == 0 + ret |= test_main_long_long_add_double_inexact (); +#endif + ret |= test_main_complex_double_add_overflow (); + ret |= test_main_double_sub_invalid (); + ret |= test_main_double_sub_overflow (); + ret |= test_main_double_sub_inexact (); + ret |= test_main_double_sub_inexact_int (); + ret |= test_main_double_predec_inexact (); + ret |= test_main_double_postdec_inexact (); +#if FLT_EVAL_METHOD == 0 + ret |= test_main_long_long_sub_double_inexact (); +#endif + ret |= test_main_complex_double_sub_overflow (); + ret |= test_main_double_mul_invalid (); + ret |= test_main_double_mul_overflow (); + ret |= test_main_double_mul_overflow_float (); + ret |= test_main_double_mul_underflow (); + ret |= test_main_double_mul_inexact (); + ret |= test_main_double_mul_inexact_int (); +#if FLT_EVAL_METHOD == 0 + ret |= test_main_long_long_mul_double_inexact (); +#endif + ret |= test_main_complex_double_mul_overflow (); + ret |= test_main_double_div_invalid_divbyzero (); + ret |= test_main_double_div_overflow (); + ret |= test_main_double_div_underflow (); + ret |= test_main_double_div_inexact (); + ret |= test_main_double_div_inexact_int (); + ret |= test_main_int_div_double_inexact (); + ret |= test_main_complex_double_div_overflow (); + ret |= test_main_long_double_add_invalid (); + ret |= test_main_long_double_add_overflow (); +#if LDBL_MANT_DIG != 106 + ret |= test_main_long_double_add_inexact (); + ret |= test_main_long_double_add_inexact_int (); + ret |= test_main_long_double_preinc_inexact (); + ret |= test_main_long_double_postinc_inexact (); +#endif + ret |= test_main_complex_long_double_add_overflow (); + ret |= test_main_long_double_sub_invalid (); + ret |= test_main_long_double_sub_overflow (); +#if LDBL_MANT_DIG != 106 + ret |= test_main_long_double_sub_inexact (); + ret |= test_main_long_double_sub_inexact_int (); + ret |= test_main_long_double_predec_inexact (); + ret |= test_main_long_double_postdec_inexact (); +#endif + ret |= test_main_complex_long_double_sub_overflow (); + ret |= test_main_long_double_mul_invalid (); + ret |= test_main_long_double_mul_overflow (); + ret |= test_main_long_double_mul_overflow_float (); + ret |= test_main_long_double_mul_overflow_double (); + ret |= test_main_long_double_mul_underflow (); +#if LDBL_MANT_DIG != 106 + ret |= test_main_long_double_mul_inexact (); + ret |= test_main_long_double_mul_inexact_int (); +#endif + ret |= test_main_complex_long_double_mul_overflow (); + ret |= test_main_long_double_div_invalid_divbyzero (); + ret |= test_main_long_double_div_overflow (); + ret |= test_main_long_double_div_underflow (); + ret |= test_main_long_double_div_inexact (); + ret |= test_main_long_double_div_inexact_int (); + ret |= test_main_int_div_long_double_inexact (); + ret |= test_main_complex_long_double_div_overflow (); + if (ret != 0) + abort (); + else + exit (0); +} diff --git a/gcc/testsuite/gcc.dg/c11-atomic-1.c b/gcc/testsuite/gcc.dg/c11-atomic-1.c new file mode 100644 index 00000000000..c7f9a1ef464 --- /dev/null +++ b/gcc/testsuite/gcc.dg/c11-atomic-1.c @@ -0,0 +1,267 @@ +/* Test for _Atomic in C11. Test of valid code. See c11-atomic-2.c + for more exhaustive tests of assignment cases. */ +/* { dg-do compile } */ +/* { dg-options "-std=c11 -pedantic-errors" } */ + +/* The use of _Atomic as a qualifier, and of _Atomic (type-name), give + the same type. */ +extern _Atomic int a; +extern _Atomic (int) a; +extern int *_Atomic b; +extern _Atomic (int *) b; +extern void f (int [_Atomic]); +extern void f (int *_Atomic); + +/* _Atomic may be applied to arbitrary types, with or without other + qualifiers, and assignments may be made as with non-atomic + types. Structure and union elements may be atomic. */ +_Atomic int ai1, ai2; +int i1; +volatile _Atomic long double ald1; +const _Atomic long double ald2; +long double ld1; +_Atomic _Complex double acd1, acd2; +_Complex double d1; +_Atomic volatile _Bool ab1; +int *p; +int *_Atomic restrict ap; +struct s { char c[1000]; }; +_Atomic struct s as1; +struct s s1; +struct t { _Atomic int i; }; +_Atomic struct t at1; +_Atomic struct t *atp1; +struct t t1; +union u { char c[1000]; }; +_Atomic union u au1; +union u u1; +union v { _Atomic int i; }; +_Atomic union v av1; +union v v1; + +void +func (_Atomic volatile long al1) +{ + ai1 = ai2; + ai1 = i1; + i1 = ai2; + ai1 = ald2; + ald1 = d1; + ld1 = acd2; + acd1 += ab1; + acd2 /= ai1; + p = ap; + ap = p; + ab1 = p; + as1 = s1; + s1 = as1; + at1 = t1; + t1 = at1; + /* It's unclear whether the undefined behavior (6.5.2.3#5) for + accessing elements of atomic structures and unions is at + translation or execution time; presume here that it's at + execution time. */ + t1.i = at1.i; + at1.i = t1.i; + atp1->i = t1.i; + au1 = u1; + u1 = au1; + av1 = v1; + v1 = av1; + v1.i = av1.i; + av1.i = v1.i; + /* _Atomic is valid on register variables, even if not particularly + useful. */ + register _Atomic volatile int ra1 = 1, ra2 = 2; + ra1 = ra2; + ra2 = ra1; + /* And on parameters. */ + al1 = ra1; + ra2 = al1; +} + +/* A function may return an atomic type. */ +_Atomic int +func2 (int i) +{ + return i; +} + +/* Casts may specify atomic type. */ +int +func3 (int i) +{ + return func2 ((_Atomic long) i); +} + +/* The _Atomic void type is valid. */ +_Atomic void *avp; + +/* An array of atomic elements is valid (the elements being atomic, + not the array). */ +_Atomic int aa[10]; +int +func4 (void) +{ + return aa[2]; +} + +/* Increment and decrement are valid for atomic types when they are + valid for non-atomic types. */ +void +func5 (void) +{ + ald1++; + ald1--; + ++ald1; + --ald1; + ai1++; + ai1--; + ++ai1; + --ai1; + ab1++; + ab1--; + ++ab1; + --ab1; + ap++; + ap--; + ++ap; + --ap; +} + +/* Compound literals may have atomic type. */ +_Atomic int *aiclp = &(_Atomic int) { 1 }; + +/* Test unary & and *. */ +void +func6 (void) +{ + int i = *aiclp; + _Atomic int *p = &ai2; +} + +/* Casts to atomic type are valid (although the _Atomic has little + effect because the result is an rvalue). */ +int i2 = (_Atomic int) 1.0; + +/* For pointer subtraction and comparisons, _Atomic does not count as + a qualifier. Likewise for conditional expressions. */ +_Atomic int *xaip1; +volatile _Atomic int *xaip2; +void *xvp1; + +void +func7 (void) +{ + int r; + r = xaip1 - xaip2; + r = xaip1 < xaip2; + r = xaip1 > xaip2; + r = xaip1 <= xaip2; + r = xaip1 >= xaip2; + r = xaip1 == xaip2; + r = xaip1 != xaip2; + r = xaip1 == xvp1; + r = xaip1 != xvp1; + r = xvp1 == xaip1; + r = xvp1 != xaip1; + r = xaip1 == 0; + r = ((void *) 0) == xaip2; + (void) (r ? xaip1 : xaip2); + (void) (r ? xvp1 : xaip2); + (void) (r ? xaip2 : xvp1); + (void) (r ? xaip1 : 0); + (void) (r ? 0 : xaip1); + /* The result of a conditional expression between a pointer to + qualified or unqualified (but not atomic) void, and a pointer to + an atomic type, is a pointer to appropriately qualified, not + atomic, void. As such, it is valid to use further in conditional + expressions with other pointer types. */ + (void) (r ? xaip1 : (r ? xaip1 : xvp1)); +} + +/* Pointer += and -= integer is valid. */ +void +func8 (void) +{ + b += 1; + b -= 2ULL; + ap += 3; +} + +/* Various other cases of simple assignment are valid (some already + tested above). */ +void +func9 (void) +{ + ap = 0; + ap = (void *) 0; + xvp1 = atp1; + atp1 = xvp1; +} + +/* Test compatibility of function types in cases where _Atomic matches + (see c11-atomic-3.c for corresponding cases where it doesn't + match). */ +void fc0a (int const); +void fc0a (int); +void fc0b (int _Atomic); +void fc0b (int _Atomic); +void fc1a (int); +void +fc1a (x) + volatile int x; +{ +} +void fc1b (_Atomic int); +void +fc1b (x) + volatile _Atomic int x; +{ +} +void +fc2a (x) + const int x; +{ +} +void fc2a (int); /* { dg-warning "follows non-prototype" } */ +void +fc2b (x) + _Atomic int x; +{ +} +void fc2b (_Atomic int); /* { dg-warning "follows non-prototype" } */ +void fc3a (int); +void +fc3a (x) + volatile short x; +{ +} +void fc3b (_Atomic int); +void +fc3b (x) + _Atomic short x; +{ +} +void +fc4a (x) + const short x; +{ +} +void fc4a (int); /* { dg-warning "follows non-prototype" } */ +void +fc4b (x) + _Atomic short x; +{ +} +void fc4b (_Atomic int); /* { dg-warning "follows non-prototype" } */ + +/* Test cases involving C_MAYBE_CONST_EXPR work. */ +void +func10 (_Atomic int *p) +{ + p[0 / 0] = 1; /* { dg-warning "division by zero" } */ + p[0 / 0] += 1; /* { dg-warning "division by zero" } */ + *p = 0 / 0; /* { dg-warning "division by zero" } */ + *p += 0 / 0; /* { dg-warning "division by zero" } */ +} diff --git a/gcc/testsuite/gcc.dg/c11-atomic-2.c b/gcc/testsuite/gcc.dg/c11-atomic-2.c new file mode 100644 index 00000000000..34ee081d421 --- /dev/null +++ b/gcc/testsuite/gcc.dg/c11-atomic-2.c @@ -0,0 +1,165 @@ +/* Test for _Atomic in C11. Test of valid assignment cases for + arithmetic types. */ +/* { dg-do compile } */ +/* { dg-options "-std=c11 -pedantic-errors" } */ + +#define TEST_ASSIGN(TYPE1, OP, TYPE2) \ + do \ + { \ + _Atomic TYPE1 a = 0; \ + TYPE2 b = 0; \ + _Atomic TYPE2 c = 0; \ + a OP b; \ + a OP c; \ + } \ + while (0) + +#define TEST_ASSIGN_ARITHR(TYPE1, OP) \ + do \ + { \ + TEST_ASSIGN (TYPE1, OP, _Bool); \ + TEST_ASSIGN (TYPE1, OP, char); \ + TEST_ASSIGN (TYPE1, OP, signed char); \ + TEST_ASSIGN (TYPE1, OP, unsigned char); \ + TEST_ASSIGN (TYPE1, OP, signed short); \ + TEST_ASSIGN (TYPE1, OP, unsigned short); \ + TEST_ASSIGN (TYPE1, OP, signed int); \ + TEST_ASSIGN (TYPE1, OP, unsigned int); \ + TEST_ASSIGN (TYPE1, OP, signed long); \ + TEST_ASSIGN (TYPE1, OP, unsigned long); \ + TEST_ASSIGN (TYPE1, OP, signed long long); \ + TEST_ASSIGN (TYPE1, OP, unsigned long long); \ + TEST_ASSIGN (TYPE1, OP, float); \ + TEST_ASSIGN (TYPE1, OP, double); \ + TEST_ASSIGN (TYPE1, OP, long double); \ + TEST_ASSIGN (TYPE1, OP, _Complex float); \ + TEST_ASSIGN (TYPE1, OP, _Complex double); \ + TEST_ASSIGN (TYPE1, OP, _Complex long double); \ + } \ + while (0) + +#define TEST_ASSIGN_ARITHBOTH(OP) \ + do \ + { \ + TEST_ASSIGN_ARITHR (_Bool, OP); \ + TEST_ASSIGN_ARITHR (char, OP); \ + TEST_ASSIGN_ARITHR (signed char, OP); \ + TEST_ASSIGN_ARITHR (unsigned char, OP); \ + TEST_ASSIGN_ARITHR (signed short, OP); \ + TEST_ASSIGN_ARITHR (unsigned short, OP); \ + TEST_ASSIGN_ARITHR (signed int, OP); \ + TEST_ASSIGN_ARITHR (unsigned int, OP); \ + TEST_ASSIGN_ARITHR (signed long, OP); \ + TEST_ASSIGN_ARITHR (unsigned long, OP); \ + TEST_ASSIGN_ARITHR (signed long long, OP); \ + TEST_ASSIGN_ARITHR (unsigned long long, OP); \ + TEST_ASSIGN_ARITHR (float, OP); \ + TEST_ASSIGN_ARITHR (double, OP); \ + TEST_ASSIGN_ARITHR (long double, OP); \ + TEST_ASSIGN_ARITHR (_Complex float, OP); \ + TEST_ASSIGN_ARITHR (_Complex double, OP); \ + TEST_ASSIGN_ARITHR (_Complex long double, OP); \ + } \ + while (0) + +#define TEST_ASSIGN_INTR(TYPE1, OP) \ + do \ + { \ + TEST_ASSIGN (TYPE1, OP, _Bool); \ + TEST_ASSIGN (TYPE1, OP, char); \ + TEST_ASSIGN (TYPE1, OP, signed char); \ + TEST_ASSIGN (TYPE1, OP, unsigned char); \ + TEST_ASSIGN (TYPE1, OP, signed short); \ + TEST_ASSIGN (TYPE1, OP, unsigned short); \ + TEST_ASSIGN (TYPE1, OP, signed int); \ + TEST_ASSIGN (TYPE1, OP, unsigned int); \ + TEST_ASSIGN (TYPE1, OP, signed long); \ + TEST_ASSIGN (TYPE1, OP, unsigned long); \ + TEST_ASSIGN (TYPE1, OP, signed long long); \ + TEST_ASSIGN (TYPE1, OP, unsigned long long); \ + } \ + while (0) + +#define TEST_ASSIGN_INTBOTH(OP) \ + do \ + { \ + TEST_ASSIGN_INTR (_Bool, OP); \ + TEST_ASSIGN_INTR (char, OP); \ + TEST_ASSIGN_INTR (signed char, OP); \ + TEST_ASSIGN_INTR (unsigned char, OP); \ + TEST_ASSIGN_INTR (signed short, OP); \ + TEST_ASSIGN_INTR (unsigned short, OP); \ + TEST_ASSIGN_INTR (signed int, OP); \ + TEST_ASSIGN_INTR (unsigned int, OP); \ + TEST_ASSIGN_INTR (signed long, OP); \ + TEST_ASSIGN_INTR (unsigned long, OP); \ + TEST_ASSIGN_INTR (signed long long, OP); \ + TEST_ASSIGN_INTR (unsigned long long, OP); \ + } \ + while (0) + +void +test_simple (void) +{ + TEST_ASSIGN_ARITHBOTH (=); +} + +void +test_mult (void) +{ + TEST_ASSIGN_ARITHBOTH (*=); +} + +void +test_div (void) +{ + TEST_ASSIGN_ARITHBOTH (/=); +} + +void +test_mod (void) +{ + TEST_ASSIGN_INTBOTH (%=); +} + +void +test_plus (void) +{ + TEST_ASSIGN_ARITHBOTH (+=); +} + +void +test_minus (void) +{ + TEST_ASSIGN_ARITHBOTH (-=); +} + +void +test_lshift (void) +{ + TEST_ASSIGN_INTBOTH (<<=); +} + +void +test_rshift (void) +{ + TEST_ASSIGN_INTBOTH (>>=); +} + +void +test_and (void) +{ + TEST_ASSIGN_INTBOTH (&=); +} + +void +test_xor (void) +{ + TEST_ASSIGN_INTBOTH (^=); +} + +void +test_or (void) +{ + TEST_ASSIGN_INTBOTH (|=); +} diff --git a/gcc/testsuite/gcc.dg/c11-atomic-3.c b/gcc/testsuite/gcc.dg/c11-atomic-3.c new file mode 100644 index 00000000000..4b314e88ad5 --- /dev/null +++ b/gcc/testsuite/gcc.dg/c11-atomic-3.c @@ -0,0 +1,174 @@ +/* Test for _Atomic in C11. Test of invalid code. */ +/* { dg-do compile } */ +/* { dg-options "-std=c11 -pedantic-errors" } */ + +/* Increment and decrement are invalid for atomic complex types and + atomic pointers to incomplete types, just as for the corresponding + non-atomic types. Likewise for types on which arithmetic is + invalid. */ +_Atomic _Complex float acf; +void *_Atomic apv; +struct s *_Atomic aps; +_Atomic struct t { char c; } as; + +void +func (void) +{ + acf++; /* { dg-error "complex types" } */ + acf--; /* { dg-error "complex types" } */ + ++acf; /* { dg-error "complex types" } */ + --acf; /* { dg-error "complex types" } */ + apv++; /* { dg-error "wrong type|pointer of type" } */ + apv--; /* { dg-error "wrong type|pointer of type" } */ + ++apv; /* { dg-error "wrong type|pointer of type" } */ + --apv; /* { dg-error "wrong type|pointer of type" } */ + aps++; /* { dg-error "pointer to|invalid use of undefined type" } */ + aps--; /* { dg-error "pointer to|invalid use of undefined type" } */ + ++aps; /* { dg-error "pointer to|invalid use of undefined type" } */ + --aps; /* { dg-error "pointer to|invalid use of undefined type" } */ + as++; /* { dg-error "wrong type" } */ + as--; /* { dg-error "wrong type" } */ + ++as; /* { dg-error "wrong type" } */ + --as; /* { dg-error "wrong type" } */ +} + +/* Pointer subtraction and comparisons differing in _Atomic are + invalid where such subtraction and comparisons differing in + qualifiers are valid. There is no special allowance for equality + comparisons of pointers to atomic void to pointers to object + types. Likewise for conditional expressions. */ +int *pi; +_Atomic int *pai; +_Atomic void *pav; +int r; + +void +func2 (void) +{ + r = pai - pi; /* { dg-error "invalid operands" } */ + r = pi - pai; /* { dg-error "invalid operands" } */ + r = pi < pai; /* { dg-error "distinct pointer types" } */ + r = pi > pai; /* { dg-error "distinct pointer types" } */ + r = pi <= pai; /* { dg-error "distinct pointer types" } */ + r = pi >= pai; /* { dg-error "distinct pointer types" } */ + r = pai < pi; /* { dg-error "distinct pointer types" } */ + r = pai > pi; /* { dg-error "distinct pointer types" } */ + r = pai <= pi; /* { dg-error "distinct pointer types" } */ + r = pai >= pi; /* { dg-error "distinct pointer types" } */ + r = pav == pi; /* { dg-error "distinct pointer types" } */ + r = pav != pi; /* { dg-error "distinct pointer types" } */ + r = pi == pav; /* { dg-error "distinct pointer types" } */ + r = pi != pav; /* { dg-error "distinct pointer types" } */ + (void) (r ? pai : pi); /* { dg-error "pointer type mismatch" } */ + (void) (r ? pi : pai); /* { dg-error "pointer type mismatch" } */ + (void) (r ? pai : pav); /* { dg-error "pointer type mismatch" } */ + (void) (r ? pav : pai); /* { dg-error "pointer type mismatch" } */ +} + +/* Likewise for pointer assignment. */ +void +func3 (void) +{ + pai = pi; /* { dg-error "incompatible pointer type" } */ + pi = pai; /* { dg-error "incompatible pointer type" } */ + pav = pai; /* { dg-error "incompatible pointer type" } */ + pai = pav; /* { dg-error "incompatible pointer type" } */ +} + +/* Cases that are invalid for normal assignments are just as invalid + (and should not ICE) when the LHS is atomic. */ +void +func4 (void) +{ + as = acf; /* { dg-error "incompatible types" } */ + apv = as; /* { dg-error "incompatible types" } */ + as += 1; /* { dg-error "invalid operands" } */ + apv -= 1; /* { dg-error "pointer of type" } */ + apv *= 1; /* { dg-error "invalid operands" } */ + apv /= 1; /* { dg-error "invalid operands" } */ + apv %= 1; /* { dg-error "invalid operands" } */ + apv <<= 1; /* { dg-error "invalid operands" } */ + apv >>= 1; /* { dg-error "invalid operands" } */ + apv &= 1; /* { dg-error "invalid operands" } */ + apv ^= 1; /* { dg-error "invalid operands" } */ + apv |= 1; /* { dg-error "invalid operands" } */ +} + +/* We don't allow atomic bit-fields in GCC (implementation-defined + whether they are permitted). */ +struct abf +{ + _Atomic int i : 1; /* { dg-error "atomic type" } */ + _Atomic int : 0; /* { dg-error "atomic type" } */ +}; + +/* _Atomic (type-name) may not use a name for an array, function, + qualified or atomic type. */ +_Atomic (int [2]) v0; /* { dg-error "array type" } */ +_Atomic (void (void)) v1; /* { dg-error "function type" } */ +_Atomic (_Atomic int) v2; /* { dg-error "applied to a qualified type" } */ +_Atomic (const int) v3; /* { dg-error "applied to a qualified type" } */ +_Atomic (volatile int) v4; /* { dg-error "applied to a qualified type" } */ +_Atomic (int *restrict) v5; /* { dg-error "applied to a qualified type" } */ + +/* _Atomic, used as a qualifier, may not be applied to a function or + array type. */ +typedef int arraytype[2]; +typedef void functiontype (void); +_Atomic arraytype v6; /* { dg-error "array type" } */ +_Atomic arraytype *v7; /* { dg-error "array type" } */ +typedef _Atomic arraytype v8; /* { dg-error "array type" } */ +int v9 = sizeof (_Atomic arraytype); /* { dg-error "array type" } */ +void v10 (_Atomic arraytype parm); /* { dg-error "array type" } */ +struct v11 { _Atomic arraytype f; }; /* { dg-error "array type" } */ +_Atomic functiontype v12; /* { dg-error "function type" } */ +_Atomic functiontype *v13; /* { dg-error "function type" } */ +typedef _Atomic functiontype *v14; /* { dg-error "function type" } */ +void v15 (_Atomic functiontype parm); /* { dg-error "function type" } */ + +/* Function parameters, when function types are required to be + compatible, may not differ in the presence of _Atomic. See + c11-atomic-1.c for corresponding tests where _Atomic matches. */ +void fc0 (int _Atomic); /* { dg-message "previous declaration" } */ +void fc0 (int); /* { dg-error "conflicting types" } */ +void fc1 (int); /* { dg-message "prototype declaration" } */ +void +fc1 (x) + _Atomic int x; /* { dg-error "match prototype" } */ +{ +} +void +fc2 (x) /* { dg-message "previous definition" } */ + _Atomic int x; +{ +} +void fc2 (int); /* { dg-error "incompatible type" } */ +void fc3 (int); /* { dg-message "prototype declaration" } */ +void +fc3 (x) + _Atomic short x; /* { dg-error "match prototype" } */ +{ +} +void +fc4 (x) /* { dg-message "previous definition" } */ + _Atomic short x; +{ +} +void fc4 (int); /* { dg-error "incompatible type" } */ + +/* Arrays of atomic elements cannot be initialized with string + literals. */ +_Atomic char si0[] = ""; /* { dg-error "inappropriate type" } */ +_Atomic char si1[] = u8""; /* { dg-error "inappropriate type" } */ +_Atomic signed char si2[] = ""; /* { dg-error "inappropriate type" } */ +_Atomic signed char si3[] = u8""; /* { dg-error "inappropriate type" } */ +_Atomic unsigned char si4[] = ""; /* { dg-error "inappropriate type" } */ +_Atomic unsigned char si5[] = u8""; /* { dg-error "inappropriate type" } */ +_Atomic __WCHAR_TYPE__ si6[] = L""; /* { dg-error "inappropriate type" } */ +_Atomic __CHAR16_TYPE__ si7[] = u""; /* { dg-error "inappropriate type" } */ +_Atomic __CHAR32_TYPE__ si8[] = U""; /* { dg-error "inappropriate type" } */ + +/* Anything that is syntactically a qualifier applied to the (void) + parameter list results in undefined behavior, which we + diagnose. */ +void fv (_Atomic void); /* { dg-error "may not be qualified" } */ diff --git a/gcc/testsuite/gcc.dg/c90-atomic-1.c b/gcc/testsuite/gcc.dg/c90-atomic-1.c new file mode 100644 index 00000000000..3506563940d --- /dev/null +++ b/gcc/testsuite/gcc.dg/c90-atomic-1.c @@ -0,0 +1,7 @@ +/* Test for _Atomic: not in C90. */ +/* { dg-do compile } */ +/* { dg-options "-std=c90 -pedantic-errors" } */ + +_Atomic int i; /* { dg-error "_Atomic" } */ +_Atomic (int) j; /* { dg-error "_Atomic" } */ +int *_Atomic p; /* { dg-error "_Atomic" } */ diff --git a/gcc/testsuite/gcc.dg/c99-atomic-1.c b/gcc/testsuite/gcc.dg/c99-atomic-1.c new file mode 100644 index 00000000000..3a13f777122 --- /dev/null +++ b/gcc/testsuite/gcc.dg/c99-atomic-1.c @@ -0,0 +1,8 @@ +/* Test for _Atomic: not in C99. */ +/* { dg-do compile } */ +/* { dg-options "-std=c99 -pedantic-errors" } */ + +_Atomic int i; /* { dg-error "_Atomic" } */ +_Atomic (int) j; /* { dg-error "_Atomic" } */ +int *_Atomic p; /* { dg-error "_Atomic" } */ +void f (int a[_Atomic]); /* { dg-error "_Atomic" } */ diff --git a/gcc/testsuite/lib/atomic-dg.exp b/gcc/testsuite/lib/atomic-dg.exp new file mode 100644 index 00000000000..c1317e47c2f --- /dev/null +++ b/gcc/testsuite/lib/atomic-dg.exp @@ -0,0 +1,104 @@ +# Copyright (C) 2013 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GCC; see the file COPYING3. If not see +# <http://www.gnu.org/licenses/>. + +# +# atomic_link_flags -- compute library path and flags to find libatomic. +# (originally from g++.exp) +# + +proc atomic_link_flags { paths } { + global srcdir + global ld_library_path + global shlib_ext + + set gccpath ${paths} + set flags "" + + set shlib_ext [get_shlib_extension] + + if { $gccpath != "" } { + if { [file exists "${gccpath}/libatomic/.libs/libatomic.a"] + || [file exists "${gccpath}/libatomic/.libs/libatomic.${shlib_ext}"] } { + append flags " -B${gccpath}/libatomic/ " + append flags " -L${gccpath}/libatomic/.libs" + append ld_library_path ":${gccpath}/libatomic/.libs" + } + } else { + global tool_root_dir + + set libatomic [lookfor_file ${tool_root_dir} libatomic] + if { $libatomic != "" } { + append flags "-L${libatomic} " + append ld_library_path ":${libatomic}" + } + } + + set_ld_library_path_env_vars + + append flags " -latomic " + return "$flags" +} + +# +# atomic_init -- called at the start of each subdir of tests +# + +proc atomic_init { args } { + global TEST_ALWAYS_FLAGS + global ALWAYS_CXXFLAGS + global TOOL_OPTIONS + global atomic_saved_TEST_ALWAYS_FLAGS + + set link_flags "" + if ![is_remote host] { + if [info exists TOOL_OPTIONS] { + set link_flags "[atomic_link_flags [get_multilibs ${TOOL_OPTIONS}]]" + } else { + set link_flags "[atomic_link_flags [get_multilibs]]" + } + } + + if [info exists TEST_ALWAYS_FLAGS] { + set atomic_saved_TEST_ALWAYS_FLAGS $TEST_ALWAYS_FLAGS + } + if [info exists ALWAYS_CXXFLAGS] { + set ALWAYS_CXXFLAGS [concat "{ldflags=$link_flags}" $ALWAYS_CXXFLAGS] + } else { + if [info exists TEST_ALWAYS_FLAGS] { + set TEST_ALWAYS_FLAGS "$link_flags $TEST_ALWAYS_FLAGS" + } else { + set TEST_ALWAYS_FLAGS "$link_flags" + } + } + return [check_no_compiler_messages_nocache libatomic_available executable { + int main (void) { return 0; } + }] +} + +# +# atomic_finish -- called at the end of each subdir of tests +# + +proc atomic_finish { args } { + global TEST_ALWAYS_FLAGS + global atomic_saved_TEST_ALWAYS_FLAGS + + if [info exists atomic_saved_TEST_ALWAYS_FLAGS] { + set TEST_ALWAYS_FLAGS $atomic_saved_TEST_ALWAYS_FLAGS + } else { + unset TEST_ALWAYS_FLAGS + } +} diff --git a/gcc/testsuite/lib/target-supports.exp b/gcc/testsuite/lib/target-supports.exp index 5ca0b76d5fc..c3d9712772e 100644 --- a/gcc/testsuite/lib/target-supports.exp +++ b/gcc/testsuite/lib/target-supports.exp @@ -5477,3 +5477,40 @@ proc check_effective_target_aarch64_large { } { return 0 } } + +# Return 1 if <fenv.h> is available with all the standard IEEE +# exceptions and floating-point exceptions are raised by arithmetic +# operations. (If the target requires special options for "inexact" +# exceptions, those need to be specified in the testcases.) + +proc check_effective_target_fenv_exceptions {} { + return [check_runtime fenv_exceptions { + #include <fenv.h> + #include <stdlib.h> + #ifndef FE_DIVBYZERO + # error Missing FE_DIVBYZERO + #endif + #ifndef FE_INEXACT + # error Missing FE_INEXACT + #endif + #ifndef FE_INVALID + # error Missing FE_INVALID + #endif + #ifndef FE_OVERFLOW + # error Missing FE_OVERFLOW + #endif + #ifndef FE_UNDERFLOW + # error Missing FE_UNDERFLOW + #endif + volatile float a = 0.0f, r; + int + main (void) + { + r = a / a; + if (fetestexcept (FE_INVALID)) + exit (0); + else + abort (); + } + } "-std=gnu99"] +} diff --git a/gcc/tree-core.h b/gcc/tree-core.h index 638b3ab3f68..351f906f001 100644 --- a/gcc/tree-core.h +++ b/gcc/tree-core.h @@ -368,7 +368,8 @@ enum cv_qualifier { TYPE_UNQUALIFIED = 0x0, TYPE_QUAL_CONST = 0x1, TYPE_QUAL_VOLATILE = 0x2, - TYPE_QUAL_RESTRICT = 0x4 + TYPE_QUAL_RESTRICT = 0x4, + TYPE_QUAL_ATOMIC = 0x8 }; /* Enumerate visibility settings. */ @@ -397,6 +398,12 @@ enum tree_index { TI_UINTDI_TYPE, TI_UINTTI_TYPE, + TI_ATOMICQI_TYPE, + TI_ATOMICHI_TYPE, + TI_ATOMICSI_TYPE, + TI_ATOMICDI_TYPE, + TI_ATOMICTI_TYPE, + TI_UINT16_TYPE, TI_UINT32_TYPE, TI_UINT64_TYPE, @@ -738,7 +745,8 @@ struct GTY(()) tree_base { unsigned packed_flag : 1; unsigned user_align : 1; unsigned nameless_flag : 1; - unsigned spare0 : 4; + unsigned atomic_flag : 1; + unsigned spare0 : 3; unsigned spare1 : 8; diff --git a/gcc/tree-pretty-print.c b/gcc/tree-pretty-print.c index fe756339f6d..7cd578cf996 100644 --- a/gcc/tree-pretty-print.c +++ b/gcc/tree-pretty-print.c @@ -878,6 +878,8 @@ dump_generic_node (pretty_printer *buffer, tree node, int spc, int flags, unsigned int quals = TYPE_QUALS (node); enum tree_code_class tclass; + if (quals & TYPE_QUAL_ATOMIC) + pp_string (buffer, "atomic "); if (quals & TYPE_QUAL_CONST) pp_string (buffer, "const "); else if (quals & TYPE_QUAL_VOLATILE) @@ -1179,6 +1181,8 @@ dump_generic_node (pretty_printer *buffer, tree node, int spc, int flags, { unsigned int quals = TYPE_QUALS (node); + if (quals & TYPE_QUAL_ATOMIC) + pp_string (buffer, "atomic "); if (quals & TYPE_QUAL_CONST) pp_string (buffer, "const "); if (quals & TYPE_QUAL_VOLATILE) diff --git a/gcc/tree.c b/gcc/tree.c index 98896f82e81..21b790a5de0 100644 --- a/gcc/tree.c +++ b/gcc/tree.c @@ -6202,6 +6202,7 @@ set_type_quals (tree type, int type_quals) TYPE_READONLY (type) = (type_quals & TYPE_QUAL_CONST) != 0; TYPE_VOLATILE (type) = (type_quals & TYPE_QUAL_VOLATILE) != 0; TYPE_RESTRICT (type) = (type_quals & TYPE_QUAL_RESTRICT) != 0; + TYPE_ATOMIC (type) = (type_quals & TYPE_QUAL_ATOMIC) != 0; TYPE_ADDR_SPACE (type) = DECODE_QUAL_ADDR_SPACE (type_quals); } @@ -6235,6 +6236,48 @@ check_aligned_type (const_tree cand, const_tree base, unsigned int align) TYPE_ATTRIBUTES (base))); } +/* This function checks to see if TYPE matches the size one of the built-in + atomic types, and returns that core atomic type. */ + +static tree +find_atomic_core_type (tree type) +{ + tree base_atomic_type; + + /* Only handle complete types. */ + if (TYPE_SIZE (type) == NULL_TREE) + return NULL_TREE; + + HOST_WIDE_INT type_size = tree_low_cst (TYPE_SIZE (type), 1); + switch (type_size) + { + case 8: + base_atomic_type = atomicQI_type_node; + break; + + case 16: + base_atomic_type = atomicHI_type_node; + break; + + case 32: + base_atomic_type = atomicSI_type_node; + break; + + case 64: + base_atomic_type = atomicDI_type_node; + break; + + case 128: + base_atomic_type = atomicTI_type_node; + break; + + default: + base_atomic_type = NULL_TREE; + } + + return base_atomic_type; +} + /* Return a version of the TYPE, qualified as indicated by the TYPE_QUALS, if one exists. If no qualified version exists yet, return NULL_TREE. */ @@ -6274,6 +6317,19 @@ build_qualified_type (tree type, int type_quals) t = build_variant_type_copy (type); set_type_quals (t, type_quals); + if (((type_quals & TYPE_QUAL_ATOMIC) == TYPE_QUAL_ATOMIC)) + { + /* See if this object can map to a basic atomic type. */ + tree atomic_type = find_atomic_core_type (type); + if (atomic_type) + { + /* Ensure the alignment of this type is compatible with + the required alignment of the atomic type. */ + if (TYPE_ALIGN (atomic_type) > TYPE_ALIGN (t)) + TYPE_ALIGN (t) = TYPE_ALIGN (atomic_type); + } + } + if (TYPE_STRUCTURAL_EQUALITY_P (type)) /* Propagate structural equality. */ SET_TYPE_STRUCTURAL_EQUALITY (t); @@ -9774,6 +9830,28 @@ make_or_reuse_accum_type (unsigned size, int unsignedp, int satp) return make_accum_type (size, unsignedp, satp); } + +/* Create an atomic variant node for TYPE. This routine is called + during initialization of data types to create the 5 basic atomic + types. The generic build_variant_type function requires these to + already be set up in order to function properly, so cannot be + called from there. */ + +static tree +build_atomic_base (tree type) +{ + tree t; + + /* Make sure its not already registered. */ + if ((t = get_qualified_type (type, TYPE_QUAL_ATOMIC))) + return t; + + t = build_variant_type_copy (type); + set_type_quals (t, TYPE_QUAL_ATOMIC); + + return t; +} + /* Create nodes for all integer types (and error_mark_node) using the sizes of C datatypes. SIGNED_CHAR specifies whether char is signed, SHORT_DOUBLE specifies whether double should be of the same precision @@ -9856,6 +9934,16 @@ build_common_tree_nodes (bool signed_char, bool short_double) unsigned_intDI_type_node = make_or_reuse_type (GET_MODE_BITSIZE (DImode), 1); unsigned_intTI_type_node = make_or_reuse_type (GET_MODE_BITSIZE (TImode), 1); + /* Don't call build_qualified type for atomics. That routine does + special processing for atomics, and until they are initialized + it's better not to make that call. */ + + atomicQI_type_node = build_atomic_base (unsigned_intQI_type_node); + atomicHI_type_node = build_atomic_base (unsigned_intHI_type_node); + atomicSI_type_node = build_atomic_base (unsigned_intSI_type_node); + atomicDI_type_node = build_atomic_base (unsigned_intDI_type_node); + atomicTI_type_node = build_atomic_base (unsigned_intTI_type_node); + access_public_node = get_identifier ("public"); access_protected_node = get_identifier ("protected"); access_private_node = get_identifier ("private"); diff --git a/gcc/tree.h b/gcc/tree.h index 5f9d0ea14cc..96948f152a5 100644 --- a/gcc/tree.h +++ b/gcc/tree.h @@ -1598,6 +1598,9 @@ extern enum machine_mode vector_type_mode (const_tree); /* Nonzero in a type considered volatile as a whole. */ #define TYPE_VOLATILE(NODE) (TYPE_CHECK (NODE)->base.volatile_flag) +/* Nonzero in a type considered atomic as a whole. */ +#define TYPE_ATOMIC(NODE) (TYPE_CHECK (NODE)->base.u.bits.atomic_flag) + /* Means this type is const-qualified. */ #define TYPE_READONLY(NODE) (TYPE_CHECK (NODE)->base.readonly_flag) @@ -1627,6 +1630,7 @@ extern enum machine_mode vector_type_mode (const_tree); #define TYPE_QUALS(NODE) \ ((int) ((TYPE_READONLY (NODE) * TYPE_QUAL_CONST) \ | (TYPE_VOLATILE (NODE) * TYPE_QUAL_VOLATILE) \ + | (TYPE_ATOMIC (NODE) * TYPE_QUAL_ATOMIC) \ | (TYPE_RESTRICT (NODE) * TYPE_QUAL_RESTRICT) \ | (ENCODE_QUAL_ADDR_SPACE (TYPE_ADDR_SPACE (NODE))))) @@ -1634,6 +1638,14 @@ extern enum machine_mode vector_type_mode (const_tree); #define TYPE_QUALS_NO_ADDR_SPACE(NODE) \ ((int) ((TYPE_READONLY (NODE) * TYPE_QUAL_CONST) \ | (TYPE_VOLATILE (NODE) * TYPE_QUAL_VOLATILE) \ + | (TYPE_ATOMIC (NODE) * TYPE_QUAL_ATOMIC) \ + | (TYPE_RESTRICT (NODE) * TYPE_QUAL_RESTRICT))) + +/* The same as TYPE_QUALS without the address space and atomic + qualifications. */ +#define TYPE_QUALS_NO_ADDR_SPACE_NO_ATOMIC(NODE) \ + ((int) ((TYPE_READONLY (NODE) * TYPE_QUAL_CONST) \ + | (TYPE_VOLATILE (NODE) * TYPE_QUAL_VOLATILE) \ | (TYPE_RESTRICT (NODE) * TYPE_QUAL_RESTRICT))) /* These flags are available for each language front end to use internally. */ @@ -3176,6 +3188,12 @@ tree_operand_check_code (const_tree __t, enum tree_code __code, int __i, #define unsigned_intDI_type_node global_trees[TI_UINTDI_TYPE] #define unsigned_intTI_type_node global_trees[TI_UINTTI_TYPE] +#define atomicQI_type_node global_trees[TI_ATOMICQI_TYPE] +#define atomicHI_type_node global_trees[TI_ATOMICHI_TYPE] +#define atomicSI_type_node global_trees[TI_ATOMICSI_TYPE] +#define atomicDI_type_node global_trees[TI_ATOMICDI_TYPE] +#define atomicTI_type_node global_trees[TI_ATOMICTI_TYPE] + #define uint16_type_node global_trees[TI_UINT16_TYPE] #define uint32_type_node global_trees[TI_UINT32_TYPE] #define uint64_type_node global_trees[TI_UINT64_TYPE] diff --git a/libatomic/ChangeLog b/libatomic/ChangeLog index 425ab1190a2..900a6198e97 100644 --- a/libatomic/ChangeLog +++ b/libatomic/ChangeLog @@ -1,3 +1,13 @@ +2013-11-07 Joseph Myers <joseph@codesourcery.com> + + * fenv.c: New file. + * libatomic.map (LIBATOMIC_1.1): New symbol version. Include + __atomic_feraiseexcept. + * configure.ac (libtool_VERSION): Change to 2:0:1. + (fenv.h): Test for header. + * Makefile.am (libatomic_la_SOURCES): Add fenv.c. + * Makefile.in, auto-config.h.in, configure: Regenerate. + 2013-10-17 Michael Hudson-Doyle <michael.hudson@linaro.org> * libatomic/configure.tgt (aarch64*): Remove code preventing diff --git a/libatomic/Makefile.am b/libatomic/Makefile.am index a98ee6437ce..537b24a7149 100644 --- a/libatomic/Makefile.am +++ b/libatomic/Makefile.am @@ -67,7 +67,8 @@ endif libatomic_version_info = -version-info $(libtool_VERSION) libatomic_la_LDFLAGS = $(libatomic_version_info) $(libatomic_version_script) -libatomic_la_SOURCES = gload.c gstore.c gcas.c gexch.c glfree.c lock.c init.c +libatomic_la_SOURCES = gload.c gstore.c gcas.c gexch.c glfree.c lock.c init.c \ + fenv.c SIZEOBJS = load store cas exch fadd fsub fand fior fxor fnand tas SIZES = @SIZES@ diff --git a/libatomic/Makefile.in b/libatomic/Makefile.in index 46e60c94cdb..22c384b4322 100644 --- a/libatomic/Makefile.in +++ b/libatomic/Makefile.in @@ -90,14 +90,14 @@ am__base_list = \ am__installdirs = "$(DESTDIR)$(toolexeclibdir)" LTLIBRARIES = $(noinst_LTLIBRARIES) $(toolexeclib_LTLIBRARIES) am_libatomic_la_OBJECTS = gload.lo gstore.lo gcas.lo gexch.lo \ - glfree.lo lock.lo init.lo + glfree.lo lock.lo init.lo fenv.lo libatomic_la_OBJECTS = $(am_libatomic_la_OBJECTS) libatomic_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(libatomic_la_LDFLAGS) $(LDFLAGS) -o $@ libatomic_convenience_la_DEPENDENCIES = $(libatomic_la_LIBADD) am__objects_1 = gload.lo gstore.lo gcas.lo gexch.lo glfree.lo lock.lo \ - init.lo + init.lo fenv.lo am_libatomic_convenience_la_OBJECTS = $(am__objects_1) libatomic_convenience_la_OBJECTS = \ $(am_libatomic_convenience_la_OBJECTS) @@ -286,7 +286,9 @@ noinst_LTLIBRARIES = libatomic_convenience.la @LIBAT_BUILD_VERSIONED_SHLIB_SUN_TRUE@@LIBAT_BUILD_VERSIONED_SHLIB_TRUE@libatomic_version_dep = libatomic.map-sun libatomic_version_info = -version-info $(libtool_VERSION) libatomic_la_LDFLAGS = $(libatomic_version_info) $(libatomic_version_script) -libatomic_la_SOURCES = gload.c gstore.c gcas.c gexch.c glfree.c lock.c init.c +libatomic_la_SOURCES = gload.c gstore.c gcas.c gexch.c glfree.c lock.c init.c \ + fenv.c + SIZEOBJS = load store cas exch fadd fsub fand fior fxor fnand tas EXTRA_libatomic_la_SOURCES = $(addsuffix _n.c,$(SIZEOBJS)) libatomic_la_DEPENDENCIES = $(libatomic_la_LIBADD) $(libatomic_version_dep) @@ -425,6 +427,7 @@ mostlyclean-compile: distclean-compile: -rm -f *.tab.c +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fenv.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gcas.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gexch.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/glfree.Plo@am__quote@ diff --git a/libatomic/auto-config.h.in b/libatomic/auto-config.h.in index ab080e7d6e4..83e54e2db3b 100644 --- a/libatomic/auto-config.h.in +++ b/libatomic/auto-config.h.in @@ -105,6 +105,9 @@ /* Define to 1 if you have the <dlfcn.h> header file. */ #undef HAVE_DLFCN_H +/* Define to 1 if you have the <fenv.h> header file. */ +#undef HAVE_FENV_H + /* Define to 1 if the target supports __attribute__((ifunc(...))). */ #undef HAVE_IFUNC diff --git a/libatomic/configure b/libatomic/configure index 6a27ebdad9f..d707b096c2e 100755 --- a/libatomic/configure +++ b/libatomic/configure @@ -2000,6 +2000,93 @@ rm -f conftest.val return $ac_retval } # ac_fn_c_compute_int + +# ac_fn_c_check_header_mongrel LINENO HEADER VAR INCLUDES +# ------------------------------------------------------- +# Tests whether HEADER exists, giving a warning if it cannot be compiled using +# the include files in INCLUDES and setting the cache variable VAR +# accordingly. +ac_fn_c_check_header_mongrel () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if { as_var=$3; eval "test \"\${$as_var+set}\" = set"; }; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if { as_var=$3; eval "test \"\${$as_var+set}\" = set"; }; then : + $as_echo_n "(cached) " >&6 +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +else + # Is the header compilable? +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 usability" >&5 +$as_echo_n "checking $2 usability... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +#include <$2> +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_header_compiler=yes +else + ac_header_compiler=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_compiler" >&5 +$as_echo "$ac_header_compiler" >&6; } + +# Is the header present? +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 presence" >&5 +$as_echo_n "checking $2 presence... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <$2> +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + ac_header_preproc=yes +else + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5 +$as_echo "$ac_header_preproc" >&6; } + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in #(( + yes:no: ) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&5 +$as_echo "$as_me: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 +$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} + ;; + no:yes:* ) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: present but cannot be compiled" >&5 +$as_echo "$as_me: WARNING: $2: present but cannot be compiled" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: check for missing prerequisite headers?" >&5 +$as_echo "$as_me: WARNING: $2: check for missing prerequisite headers?" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: see the Autoconf documentation" >&5 +$as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&5 +$as_echo "$as_me: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 +$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} + ;; +esac + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if { as_var=$3; eval "test \"\${$as_var+set}\" = set"; }; then : + $as_echo_n "(cached) " >&6 +else + eval "$3=\$ac_header_compiler" +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +fi + eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;} + +} # ac_fn_c_check_header_mongrel cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. @@ -11019,7 +11106,7 @@ else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF -#line 11022 "configure" +#line 11109 "configure" #include "confdefs.h" #if HAVE_DLFCN_H @@ -11125,7 +11212,7 @@ else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF -#line 11128 "configure" +#line 11215 "configure" #include "confdefs.h" #if HAVE_DLFCN_H @@ -11389,7 +11476,7 @@ fi # For libtool versioning info, format is CURRENT:REVISION:AGE -libtool_VERSION=1:0:0 +libtool_VERSION=2:0:1 # Get target configury. @@ -11953,6 +12040,18 @@ ac_config_commands="$ac_config_commands gstdint.h" +for ac_header in fenv.h +do : + ac_fn_c_check_header_mongrel "$LINENO" "fenv.h" "ac_cv_header_fenv_h" "$ac_includes_default" +if test "x$ac_cv_header_fenv_h" = x""yes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_FENV_H 1 +_ACEOF + +fi + +done + # Check for common type sizes diff --git a/libatomic/configure.ac b/libatomic/configure.ac index 0dc4a9809ec..fd2d35bf04f 100644 --- a/libatomic/configure.ac +++ b/libatomic/configure.ac @@ -148,7 +148,7 @@ AC_SUBST(enable_static) AM_MAINTAINER_MODE # For libtool versioning info, format is CURRENT:REVISION:AGE -libtool_VERSION=1:0:0 +libtool_VERSION=2:0:1 AC_SUBST(libtool_VERSION) # Get target configury. @@ -165,6 +165,7 @@ CFLAGS="$save_CFLAGS -fno-sync-libcalls $XCFLAGS" AC_STDC_HEADERS ACX_HEADER_STRING GCC_HEADER_STDINT(gstdint.h) +AC_CHECK_HEADERS([fenv.h]) # Check for common type sizes LIBAT_FORALL_MODES([LIBAT_HAVE_INT_MODE]) diff --git a/libatomic/fenv.c b/libatomic/fenv.c new file mode 100644 index 00000000000..752cf3b378d --- /dev/null +++ b/libatomic/fenv.c @@ -0,0 +1,72 @@ +/* Copyright (C) 2012-2013 Free Software Foundation, Inc. + + This file is part of the GNU Atomic Library (libatomic). + + Libatomic is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + Libatomic is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + Under Section 7 of GPL version 3, you are granted additional + permissions described in the GCC Runtime Library Exception, version + 3.1, as published by the Free Software Foundation. + + You should have received a copy of the GNU General Public License and + a copy of the GCC Runtime Library Exception along with this program; + see the files COPYING3 and COPYING.RUNTIME respectively. If not, see + <http://www.gnu.org/licenses/>. */ + +#include "libatomic_i.h" + +#ifdef HAVE_FENV_H +# include <fenv.h> +#endif + +/* Raise the supported floating-point exceptions from EXCEPTS. Other + bits in EXCEPTS are ignored. */ + +void +__atomic_feraiseexcept (int excepts __attribute__ ((unused))) +{ + volatile float r __attribute__ ((unused)); +#ifdef FE_INVALID + if (excepts & FE_INVALID) + { + volatile float zero = 0.0f; + r = zero / zero; + } +#endif +#ifdef FE_DIVBYZERO + if (excepts & FE_DIVBYZERO) + { + volatile float zero = 0.0f; + r = 1.0f / zero; + } +#endif +#ifdef FE_OVERFLOW + if (excepts & FE_OVERFLOW) + { + volatile float max = __FLT_MAX__; + r = max * max; + } +#endif +#ifdef FE_UNDERFLOW + if (excepts & FE_UNDERFLOW) + { + volatile float min = __FLT_MIN__; + r = min * min; + } +#endif +#ifdef FE_INEXACT + if (excepts & FE_INEXACT) + { + volatile float three = 3.0f; + r = 1.0f / three; + } +#endif +} diff --git a/libatomic/libatomic.map b/libatomic/libatomic.map index bcf077370a9..8fd2bafd80c 100644 --- a/libatomic/libatomic.map +++ b/libatomic/libatomic.map @@ -95,3 +95,7 @@ LIBATOMIC_1.0 { local: *; }; +LIBATOMIC_1.1 { + global: + __atomic_feraiseexcept; +} LIBATOMIC_1.0; |