summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gcc/ChangeLog45
-rw-r--r--gcc/c-family/ChangeLog12
-rw-r--r--gcc/c-family/c-common.c3
-rw-r--r--gcc/c-family/c-common.h2
-rw-r--r--gcc/c-family/c-format.c1
-rw-r--r--gcc/c-family/c-pretty-print.c9
-rw-r--r--gcc/c/ChangeLog69
-rw-r--r--gcc/c/c-aux-info.c4
-rw-r--r--gcc/c/c-decl.c118
-rw-r--r--gcc/c/c-parser.c227
-rw-r--r--gcc/c/c-tree.h6
-rw-r--r--gcc/c/c-typeck.c513
-rw-r--r--gcc/config/i386/i386-builtin-types.def1
-rw-r--r--gcc/config/i386/i386.c127
-rw-r--r--gcc/config/i386/i386.md71
-rw-r--r--gcc/doc/tm.texi4
-rw-r--r--gcc/doc/tm.texi.in2
-rw-r--r--gcc/objc/ChangeLog4
-rw-r--r--gcc/objc/objc-act.c1
-rw-r--r--gcc/print-tree.c2
-rw-r--r--gcc/sync-builtins.def6
-rw-r--r--gcc/target.def22
-rw-r--r--gcc/targhooks.c7
-rw-r--r--gcc/targhooks.h1
-rw-r--r--gcc/testsuite/ChangeLog13
-rw-r--r--gcc/testsuite/gcc.dg/atomic/atomic.exp34
-rw-r--r--gcc/testsuite/gcc.dg/atomic/c11-atomic-exec-1.c88
-rw-r--r--gcc/testsuite/gcc.dg/atomic/c11-atomic-exec-2.c171
-rw-r--r--gcc/testsuite/gcc.dg/atomic/c11-atomic-exec-3.c85
-rw-r--r--gcc/testsuite/gcc.dg/atomic/c11-atomic-exec-4.c208
-rw-r--r--gcc/testsuite/gcc.dg/atomic/c11-atomic-exec-5.c541
-rw-r--r--gcc/testsuite/gcc.dg/c11-atomic-1.c267
-rw-r--r--gcc/testsuite/gcc.dg/c11-atomic-2.c165
-rw-r--r--gcc/testsuite/gcc.dg/c11-atomic-3.c174
-rw-r--r--gcc/testsuite/gcc.dg/c90-atomic-1.c7
-rw-r--r--gcc/testsuite/gcc.dg/c99-atomic-1.c8
-rw-r--r--gcc/testsuite/lib/atomic-dg.exp104
-rw-r--r--gcc/testsuite/lib/target-supports.exp37
-rw-r--r--gcc/tree-core.h12
-rw-r--r--gcc/tree-pretty-print.c4
-rw-r--r--gcc/tree.c88
-rw-r--r--gcc/tree.h18
-rw-r--r--libatomic/ChangeLog10
-rw-r--r--libatomic/Makefile.am3
-rw-r--r--libatomic/Makefile.in9
-rw-r--r--libatomic/auto-config.h.in3
-rwxr-xr-xlibatomic/configure105
-rw-r--r--libatomic/configure.ac3
-rw-r--r--libatomic/fenv.c72
-rw-r--r--libatomic/libatomic.map4
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, &copy, sizeof init) != 0)
+ abort ();
+ copy = (s2 = s1);
+ if (memcmp (&init, &copy, sizeof init) != 0)
+ abort ();
+ copy = s1;
+ if (memcmp (&init, &copy, sizeof init) != 0)
+ abort ();
+ copy = s2;
+ if (memcmp (&init, &copy, 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;