From patchwork Tue Nov 5 23:21:56 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Joseph Myers X-Patchwork-Id: 288673 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id DF87D2C032E for ; Wed, 6 Nov 2013 10:22:44 +1100 (EST) DomainKey-Signature: a=rsa-sha1; c=nofws; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:date :from:to:cc:subject:message-id:mime-version:content-type; q=dns; s=default; b=mLgQDgof0co9soZr+3U1RJOmlmffUZzSF++T16dJkiUhloqqtA lOGCdtDS87AxfbMZCu6LsQ4AhK/xgbmKQwO29LPVvAb3nLc6CQMdtzxcpNsJckff Lbtzd8kuKykk7KJDxiImRwAFShWe3WB04RI/vSuW+yD0hyhS2WcPPsMfU= DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:date :from:to:cc:subject:message-id:mime-version:content-type; s= default; bh=B/9XVQaTqTjgkoXfztNCIjEqhY4=; b=NhNhHiTjocuCLGhQfJbw oWgrC7/EkZURQmbBbabRIMlmYA6vzd/maDFztGCaW9UFLvcMaWJrDip18eBx2/Cx Wce1fY8jLoyyuIsUVR2kosyW7PznBNx8oLK7ZIDAuH+wF7iuChPnaU4sAdLaukFf T7UGyfgDDFjhvTCgYL5H2Y4= Received: (qmail 4537 invoked by alias); 5 Nov 2013 23:22:33 -0000 Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Delivered-To: mailing list gcc-patches@gcc.gnu.org Received: (qmail 4526 invoked by uid 89); 5 Nov 2013 23:22:32 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=2.3 required=5.0 tests=AWL, BAYES_50, KAM_STOCKTIP, RDNS_NONE, URIBL_BLOCKED autolearn=no version=3.3.2 X-HELO: relay1.mentorg.com Received: from Unknown (HELO relay1.mentorg.com) (192.94.38.131) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Tue, 05 Nov 2013 23:22:22 +0000 Received: from svr-orw-exc-10.mgc.mentorg.com ([147.34.98.58]) by relay1.mentorg.com with esmtp id 1Vdpwh-0003yq-CK from joseph_myers@mentor.com ; Tue, 05 Nov 2013 15:22:03 -0800 Received: from SVR-IES-FEM-01.mgc.mentorg.com ([137.202.0.104]) by SVR-ORW-EXC-10.mgc.mentorg.com with Microsoft SMTPSVC(6.0.3790.4675); Tue, 5 Nov 2013 15:22:03 -0800 Received: from digraph.polyomino.org.uk (137.202.0.76) by SVR-IES-FEM-01.mgc.mentorg.com (137.202.0.104) with Microsoft SMTP Server id 14.2.247.3; Tue, 5 Nov 2013 23:21:57 +0000 Received: from jsm28 (helo=localhost) by digraph.polyomino.org.uk with local-esmtp (Exim 4.76) (envelope-from ) id 1Vdpwa-0000KL-F5; Tue, 05 Nov 2013 23:21:56 +0000 Date: Tue, 5 Nov 2013 23:21:56 +0000 From: "Joseph S. Myers" To: CC: , , , , , , , Subject: Implement C11 _Atomic Message-ID: MIME-Version: 1.0 This patch, relative to trunk and based on work done on the C11-atomic branch, adds support for C11 _Atomic. It is intended to include all the required language support. It does not include the header; there's a version on the branch, but it needs further review against the standard and test coverage adding to the testsuite before I can propose it for mainline. Support for atomic types having bigger alignment than the corresponding non-atomic types is limited: it includes the code to increase the alignment of types whose size is exactly 1, 2, 4, 8 or 16 to that of the corresponding integer type [*], but not anything for target-specific alignment increases. There's code for target-specific alignment on the branch (and I intend to merge trunk back to the branch once this patch is on trunk, so it's easy to tell what the changes still left on the branch are), should any target maintainers wish to have such alignment. Note however that ideally libstdc++ atomics would be ABI-compatible with C atomics, requiring them to get the same alignment; the branch has an "atomic" attribute for that purpose, but I think further work on the C++ front-end changes would be needed for them to be ready for mainline. [*] The c-common.c code to resolve size-generic atomic built-in functions to size-specific ones assumes that types are naturally aligned (and so resolves to calls to the size-specific functions that require natural alignment) without checking it. If users should be able to use the size-generic functions on types with lesser alignment, e.g. _Complex double (8-byte rather than 16-byte aligned), rather than just on their _Atomic variants, this is of course a bug in the code resolving those built-in functions. (The libatomic functions properly check for alignment at runtime.) But it seems reasonable enough anyway to make _Atomic _Complex double 16-byte aligned. Full use of _Atomic requires linking with libatomic, if you're doing any atomic operations that aren't fully expanded inline; arguably libatomic should be linked in by default with --as-needed (given that previously ordinary C code didn't require the user to link any libraries explicitly unless they directly called library functions), but that's independent of this patch. Correct handling of atomic compound assignment for floating-point types also requires built-in support for floating-point environment manipulation operations roughly equivalent to feholdexcept, feclearexcept and feupdateenv (built-ins are used to avoid introducing libm dependencies, which generic C code not actually calling libm library functions shouldn't require); this patch includes such support for x86 [*], and I expect other architectures to be simpler. If you don't have a libatomic port, the execution tests for _Atomic simply won't be run; if you have libatomic but not the floating-point support, the tests will be run but c11-atomic-exec-5.c will fail (assuming the library features are present for that test to run at all - it also requires pthreads). [*] This could be optimized if desired by passing the types in question to the target hook so it can generate code for only one of x87 and SSE in most cases, much like an optimization present in glibc when it internally does feholdexcept / fesetenv / feupdateenv sequences. _Atomic support is currently disabled for Objective-C and OpenMP. For both (but mainly OpenMP), the relevant parser code needs checking to determine where convert_lvalue_to_rvalue calls need inserting to ensure that accesses to atomic variables involve atomic loads. For Objective-C, there are also various special cases of compound assignment that need special handling for atomics just as standard C compound assignment is handled differently for atomics, as well as some TYPE_MAIN_VARIANT calls to check for correctness for atomics; see the comment on the relevant sorry () call for details. OpenMP should also have TYPE_MAIN_VARIANT uses checked as well as a use of TYPE_QUALS_NO_ADDR_SPACE for a diagnostic in c_parser_omp_declare_reduction (where the diagnostic refers to a particular list of qualifiers). Bootstrapped with no regressions on x86_64-unknown-linux-gnu. OK to commit (the various non-front-end pieces)? gcc: 2013-11-05 Andrew MacLeod Joseph Myers * 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. gcc/c-family: 2013-11-05 Andrew MacLeod Joseph Myers * 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. gcc/c: 2013-11-05 Joseph Myers Andrew MacLeod * 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. gcc/objc: 2013-11-05 Andrew MacLeod * objc-act.c (objc_push_parm): Handle atomic qualifier. gcc/testsuite: 2013-11-05 Joseph Myers * 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. libatomic: 2013-11-05 Joseph Myers * 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. Index: libatomic/fenv.c =================================================================== --- libatomic/fenv.c (revision 0) +++ libatomic/fenv.c (revision 0) @@ -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 + . */ + +#include "libatomic_i.h" + +#ifdef HAVE_FENV_H +# include +#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 +} Index: libatomic/configure.ac =================================================================== --- libatomic/configure.ac (revision 204390) +++ libatomic/configure.ac (working copy) @@ -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]) Index: libatomic/auto-config.h.in =================================================================== --- libatomic/auto-config.h.in (revision 204390) +++ libatomic/auto-config.h.in (working copy) @@ -105,6 +105,9 @@ /* Define to 1 if you have the header file. */ #undef HAVE_DLFCN_H +/* Define to 1 if you have the header file. */ +#undef HAVE_FENV_H + /* Define to 1 if the target supports __attribute__((ifunc(...))). */ #undef HAVE_IFUNC Index: libatomic/Makefile.am =================================================================== --- libatomic/Makefile.am (revision 204390) +++ libatomic/Makefile.am (working copy) @@ -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@ Index: libatomic/libatomic.map =================================================================== --- libatomic/libatomic.map (revision 204390) +++ libatomic/libatomic.map (working copy) @@ -95,3 +95,7 @@ LIBATOMIC_1.0 { local: *; }; +LIBATOMIC_1.1 { + global: + __atomic_feraiseexcept; +} LIBATOMIC_1.0; Index: libatomic/configure =================================================================== --- libatomic/configure (revision 204390) +++ libatomic/configure (working copy) @@ -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,7 +12040,19 @@ 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 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for 1 byte integer" >&5 Index: libatomic/Makefile.in =================================================================== --- libatomic/Makefile.in (revision 204390) +++ libatomic/Makefile.in (working copy) @@ -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@ Index: gcc/sync-builtins.def =================================================================== --- gcc/sync-builtins.def (revision 204390) +++ gcc/sync-builtins.def (working copy) @@ -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) Index: gcc/target.def =================================================================== --- gcc/target.def (revision 204390) +++ gcc/target.def (working copy) @@ -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 Index: gcc/doc/tm.texi =================================================================== --- gcc/doc/tm.texi (revision 204390) +++ gcc/doc/tm.texi (working copy) @@ -11485,3 +11485,7 @@ It returns true if the target supports GNU indirec 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 Index: gcc/doc/tm.texi.in =================================================================== --- gcc/doc/tm.texi.in (revision 204390) +++ gcc/doc/tm.texi.in (working copy) @@ -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 Index: gcc/c-family/c-pretty-print.c =================================================================== --- gcc/c-family/c-pretty-print.c (revision 204390) +++ gcc/c-family/c-pretty-print.c (working copy) @@ -179,8 +179,16 @@ pp_c_cv_qualifiers (c_pretty_printer *pp, int qual 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 __restrict__ -- GNU C address-space-qualifier -- GNU C volatile + _Atomic -- C11 address-space-qualifier: identifier -- GNU C */ Index: gcc/c-family/c-common.c =================================================================== --- gcc/c-family/c-common.c (revision 204390) +++ gcc/c-family/c-common.c (working copy) @@ -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 }, @@ -10171,6 +10172,7 @@ sync_resolve_params (location_t loc, tree orig_fun 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. */ @@ -11567,6 +11569,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; Index: gcc/c-family/c-format.c =================================================================== --- gcc/c-family/c-format.c (revision 204390) +++ gcc/c-family/c-format.c (working copy) @@ -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)", Index: gcc/c-family/c-common.h =================================================================== --- gcc/c-family/c-common.h (revision 204390) +++ gcc/c-family/c-common.h (working copy) @@ -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, Index: gcc/config/i386/i386.md =================================================================== --- gcc/config/i386/i386.md (revision 204390) +++ gcc/config/i386/i386.md (working copy) @@ -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 ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Index: gcc/config/i386/i386-builtin-types.def =================================================================== --- gcc/config/i386/i386-builtin-types.def (revision 204390) +++ gcc/config/i386/i386-builtin-types.def (working copy) @@ -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) Index: gcc/config/i386/i386.c =================================================================== --- gcc/config/i386/i386.c (revision 204390) +++ gcc/config/i386/i386.c (working copy) @@ -26958,6 +26958,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, @@ -27934,6 +27939,12 @@ static const struct builtin_description bdesc_spec { ~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 }, @@ -32895,6 +32906,10 @@ ix86_expand_builtin (tree exp, rtx target, rtx sub 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: @@ -32909,6 +32924,16 @@ ix86_expand_builtin (tree exp, rtx target, rtx sub 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 (); } @@ -32921,7 +32946,7 @@ ix86_expand_builtin (tree exp, rtx target, rtx sub 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) @@ -43531,6 +43556,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 @@ -43642,6 +43764,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 Index: gcc/tree-core.h =================================================================== --- gcc/tree-core.h (revision 204390) +++ gcc/tree-core.h (working copy) @@ -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; Index: gcc/c/c-aux-info.c =================================================================== --- gcc/c/c-aux-info.c (revision 204390) +++ gcc/c/c-aux-info.c (working copy) @@ -285,6 +285,8 @@ gen_type (const char *ret_val, tree t, formals_sty 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_sty 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)) Index: gcc/c/c-parser.c =================================================================== --- gcc/c/c-parser.c (revision 204390) +++ gcc/c/c-parser.c (working copy) @@ -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_pars 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_pars 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_pars (_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_dec 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 , 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 + 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 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 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 { 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 "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 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 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 ivd { 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 ivd } 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 ivd 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_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, 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, { 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, stru 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, stru { 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, stru 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 { 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_parse 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_parse 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 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 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 *pa 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 *pa 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 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 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); Index: gcc/c/c-typeck.c =================================================================== --- gcc/c/c-typeck.c (revision 204390) +++ gcc/c/c-typeck.c (working copy) @@ -265,18 +265,25 @@ c_incomplete_type_error (const_tree value, const_t tree c_type_promotes_to (tree type) { + tree ret = NULL_TREE; + if (TYPE_MAIN_VARIANT (type) == float_type_node) - return double_type_node; - - if (c_promoting_integer_type_p (type)) + 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, /* 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_t 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_t 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_t 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 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 *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 *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 "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 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 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, 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, { 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, ? 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, /* 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, 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, t || 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, t || (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 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 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 /* 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 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 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 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_co "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 % 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 " Index: gcc/c/c-tree.h =================================================================== --- gcc/c/c-tree.h (revision 204390) +++ gcc/c/c-tree.h (working copy) @@ -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_conver 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); Index: gcc/c/c-decl.c =================================================================== --- gcc/c/c-decl.c (revision 204390) +++ gcc/c/c-decl.c (working copy) @@ -1584,8 +1584,14 @@ validate_proto_after_old_defn (tree newdecl, tree 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; @@ -3714,6 +3720,7 @@ shadow_tag_warned (const struct c_declspecs *decls && declspecs->typespec_kind != ctsk_tagfirstref && (declspecs->const_p || declspecs->volatile_p + || declspecs->atomic_p || declspecs->restrict_p || declspecs->address_space)) { @@ -3803,6 +3810,7 @@ shadow_tag_warned (const struct c_declspecs *decls if (!warned && !in_system_header && (declspecs->const_p || declspecs->volatile_p + || declspecs->atomic_p || declspecs->restrict_p || declspecs->address_space)) { @@ -3834,6 +3842,7 @@ quals_from_declspecs (const struct c_declspecs *sp 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 @@ -4169,7 +4178,7 @@ start_decl (struct c_declarator *declarator, struc 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); } } } @@ -4942,6 +4951,7 @@ grokdeclarator (const struct c_declarator *declara int constp; int restrictp; int volatilep; + int atomicp; int type_quals = TYPE_UNQUALIFIED; tree name = NULL_TREE; bool funcdef_flag = false; @@ -5096,6 +5106,7 @@ grokdeclarator (const struct c_declarator *declara 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; @@ -5108,6 +5119,9 @@ grokdeclarator (const struct c_declarator *declara pedwarn (loc, OPT_Wpedantic, "duplicate %"); if (volatilep > 1) pedwarn (loc, OPT_Wpedantic, "duplicate %"); + if (atomicp > 1) + pedwarn (loc, OPT_Wpedantic, "duplicate %<_Atomic%>"); + } if (!ADDR_SPACE_GENERIC_P (as1) && !ADDR_SPACE_GENERIC_P (as2) && as1 != as2) @@ -5121,8 +5135,16 @@ grokdeclarator (const struct c_declarator *declara 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.). */ @@ -5698,9 +5720,15 @@ grokdeclarator (const struct c_declarator *declara { /* 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) @@ -5814,7 +5842,20 @@ grokdeclarator (const struct c_declarator *declara /* 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) @@ -5877,8 +5918,15 @@ grokdeclarator (const struct c_declarator *declara 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) @@ -5923,8 +5971,15 @@ grokdeclarator (const struct c_declarator *declara 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) @@ -5990,7 +6045,13 @@ grokdeclarator (const struct c_declarator *declara } 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) @@ -6085,7 +6146,13 @@ grokdeclarator (const struct c_declarator *declara 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"); @@ -6458,8 +6525,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 ("% as only parameter may not be qualified"); @@ -8212,11 +8278,15 @@ store_parm_decls_oldstyle (tree fndecl, const stru 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 @@ -8229,7 +8299,8 @@ store_parm_decls_oldstyle (tree fndecl, const stru && 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 @@ -8431,7 +8502,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); } } @@ -8910,6 +8981,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; @@ -8971,6 +9043,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 (); } Index: gcc/targhooks.c =================================================================== --- gcc/targhooks.c (revision 204390) +++ gcc/targhooks.c (working copy) @@ -1600,6 +1600,13 @@ default_canonicalize_comparison (int *, rtx *, rtx { } +/* 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 Index: gcc/targhooks.h =================================================================== --- gcc/targhooks.h (revision 204390) +++ gcc/targhooks.h (working copy) @@ -203,6 +203,7 @@ extern void default_asm_output_ident_directive (co 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 *); Index: gcc/print-tree.c =================================================================== --- gcc/print-tree.c (revision 204390) +++ gcc/print-tree.c (working copy) @@ -305,6 +305,8 @@ print_node (FILE *file, const char *prefix, tree n 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)) Index: gcc/tree.c =================================================================== --- gcc/tree.c (revision 204390) +++ gcc/tree.c (working copy) @@ -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 ba 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 unsig 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 sh 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"); Index: gcc/tree.h =================================================================== --- gcc/tree.h (revision 204390) +++ gcc/tree.h (working copy) @@ -1598,6 +1598,9 @@ extern enum machine_mode vector_type_mode (const_t /* 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_t #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,8 +1638,16 @@ extern enum machine_mode vector_type_mode (const_t #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. */ #define TYPE_LANG_FLAG_0(NODE) (TYPE_CHECK (NODE)->type_common.lang_flag_0) #define TYPE_LANG_FLAG_1(NODE) (TYPE_CHECK (NODE)->type_common.lang_flag_1) @@ -3176,6 +3188,12 @@ tree_operand_check_code (const_tree __t, enum tree #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] Index: gcc/tree-pretty-print.c =================================================================== --- gcc/tree-pretty-print.c (revision 204390) +++ gcc/tree-pretty-print.c (working copy) @@ -878,6 +878,8 @@ dump_generic_node (pretty_printer *buffer, tree no 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 no { 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) Index: gcc/objc/objc-act.c =================================================================== --- gcc/objc/objc-act.c (revision 204390) +++ gcc/objc/objc-act.c (working copy) @@ -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); Index: gcc/testsuite/gcc.dg/c11-atomic-3.c =================================================================== --- gcc/testsuite/gcc.dg/c11-atomic-3.c (revision 0) +++ gcc/testsuite/gcc.dg/c11-atomic-3.c (revision 0) @@ -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" } */ Index: gcc/testsuite/gcc.dg/c90-atomic-1.c =================================================================== --- gcc/testsuite/gcc.dg/c90-atomic-1.c (revision 0) +++ gcc/testsuite/gcc.dg/c90-atomic-1.c (revision 0) @@ -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" } */ Index: gcc/testsuite/gcc.dg/c11-atomic-1.c =================================================================== --- gcc/testsuite/gcc.dg/c11-atomic-1.c (revision 0) +++ gcc/testsuite/gcc.dg/c11-atomic-1.c (revision 0) @@ -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" } */ +} Index: gcc/testsuite/gcc.dg/atomic/atomic.exp =================================================================== --- gcc/testsuite/gcc.dg/atomic/atomic.exp (revision 0) +++ gcc/testsuite/gcc.dg/atomic/atomic.exp (revision 0) @@ -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 +# . + +# 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 Index: gcc/testsuite/gcc.dg/atomic/c11-atomic-exec-1.c =================================================================== --- gcc/testsuite/gcc.dg/atomic/c11-atomic-exec-1.c (revision 0) +++ gcc/testsuite/gcc.dg/atomic/c11-atomic-exec-1.c (revision 0) @@ -0,0 +1,88 @@ +/* Test for _Atomic in C11. Basic execution tests for atomic loads + and stores. */ +/* { dg-do run } */ +/* { dg-options "-std=c11 -pedantic-errors" } */ + +extern void abort (void); +extern void exit (int); +extern int memcmp (const void *, const void *, __SIZE_TYPE__); + +#define CMPLX(X, Y) __builtin_complex ((X), (Y)) + +#define TEST_SIMPLE_ASSIGN(TYPE, VALUE) \ + do \ + { \ + static volatile _Atomic (TYPE) a, b = (TYPE) (VALUE); \ + if (a != 0) \ + abort (); \ + if (b != ((TYPE) (VALUE))) \ + abort (); \ + if ((a = b) != ((TYPE) (VALUE))) \ + abort (); \ + if (a != ((TYPE) (VALUE))) \ + abort (); \ + } \ + while (0) + +#define TEST_SIMPLE_ASSIGN_ARITH(VALUE) \ + do \ + { \ + TEST_SIMPLE_ASSIGN (_Bool, (VALUE)); \ + TEST_SIMPLE_ASSIGN (char, (VALUE)); \ + TEST_SIMPLE_ASSIGN (signed char, (VALUE)); \ + TEST_SIMPLE_ASSIGN (unsigned char, (VALUE)); \ + TEST_SIMPLE_ASSIGN (signed short, (VALUE)); \ + TEST_SIMPLE_ASSIGN (unsigned short, (VALUE)); \ + TEST_SIMPLE_ASSIGN (signed int, (VALUE)); \ + TEST_SIMPLE_ASSIGN (unsigned int, (VALUE)); \ + TEST_SIMPLE_ASSIGN (signed long, (VALUE)); \ + TEST_SIMPLE_ASSIGN (unsigned long, (VALUE)); \ + TEST_SIMPLE_ASSIGN (signed long long, (VALUE)); \ + TEST_SIMPLE_ASSIGN (unsigned long long, (VALUE)); \ + TEST_SIMPLE_ASSIGN (float, (VALUE)); \ + TEST_SIMPLE_ASSIGN (double, (VALUE)); \ + TEST_SIMPLE_ASSIGN (long double, (VALUE)); \ + TEST_SIMPLE_ASSIGN (_Complex float, (VALUE)); \ + TEST_SIMPLE_ASSIGN (_Complex double, (VALUE)); \ + TEST_SIMPLE_ASSIGN (_Complex long double, (VALUE)); \ + } \ + while (0) + +static void +test_simple_assign (void) +{ + TEST_SIMPLE_ASSIGN_ARITH (0); + TEST_SIMPLE_ASSIGN_ARITH (1); + TEST_SIMPLE_ASSIGN_ARITH (2); + TEST_SIMPLE_ASSIGN_ARITH (-1); + TEST_SIMPLE_ASSIGN_ARITH (1ULL << 63); + TEST_SIMPLE_ASSIGN_ARITH (1.5); + TEST_SIMPLE_ASSIGN_ARITH (CMPLX (2.5, 3.5)); + static int i; + TEST_SIMPLE_ASSIGN (int *, 0); + TEST_SIMPLE_ASSIGN (int *, &i); + struct s { short a[1024]; }; + struct s init, copy; + _Atomic struct s s1, s2; + for (int j = 0; j < 1024; j++) + init.a[j] = j; + copy = (s1 = init); + if (memcmp (&init, ©, sizeof init) != 0) + abort (); + copy = (s2 = s1); + if (memcmp (&init, ©, sizeof init) != 0) + abort (); + copy = s1; + if (memcmp (&init, ©, sizeof init) != 0) + abort (); + copy = s2; + if (memcmp (&init, ©, sizeof init) != 0) + abort (); +} + +int +main (void) +{ + test_simple_assign (); + exit (0); +} Index: gcc/testsuite/gcc.dg/atomic/c11-atomic-exec-2.c =================================================================== --- gcc/testsuite/gcc.dg/atomic/c11-atomic-exec-2.c (revision 0) +++ gcc/testsuite/gcc.dg/atomic/c11-atomic-exec-2.c (revision 0) @@ -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); +} Index: gcc/testsuite/gcc.dg/atomic/c11-atomic-exec-3.c =================================================================== --- gcc/testsuite/gcc.dg/atomic/c11-atomic-exec-3.c (revision 0) +++ gcc/testsuite/gcc.dg/atomic/c11-atomic-exec-3.c (revision 0) @@ -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); +} Index: gcc/testsuite/gcc.dg/atomic/c11-atomic-exec-4.c =================================================================== --- gcc/testsuite/gcc.dg/atomic/c11-atomic-exec-4.c (revision 0) +++ gcc/testsuite/gcc.dg/atomic/c11-atomic-exec-4.c (revision 0) @@ -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 +#include +#include +#include +#include + +#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); +} Index: gcc/testsuite/gcc.dg/atomic/c11-atomic-exec-5.c =================================================================== --- gcc/testsuite/gcc.dg/atomic/c11-atomic-exec-5.c (revision 0) +++ gcc/testsuite/gcc.dg/atomic/c11-atomic-exec-5.c (revision 0) @@ -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 +#include +#include +#include +#include +#include + +#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); +} Index: gcc/testsuite/gcc.dg/c11-atomic-2.c =================================================================== --- gcc/testsuite/gcc.dg/c11-atomic-2.c (revision 0) +++ gcc/testsuite/gcc.dg/c11-atomic-2.c (revision 0) @@ -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 (|=); +} Index: gcc/testsuite/gcc.dg/c99-atomic-1.c =================================================================== --- gcc/testsuite/gcc.dg/c99-atomic-1.c (revision 0) +++ gcc/testsuite/gcc.dg/c99-atomic-1.c (revision 0) @@ -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" } */ Index: gcc/testsuite/lib/atomic-dg.exp =================================================================== --- gcc/testsuite/lib/atomic-dg.exp (revision 0) +++ gcc/testsuite/lib/atomic-dg.exp (revision 0) @@ -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 +# . + +# +# 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 + } +} Index: gcc/testsuite/lib/target-supports.exp =================================================================== --- gcc/testsuite/lib/target-supports.exp (revision 204390) +++ gcc/testsuite/lib/target-supports.exp (working copy) @@ -5477,3 +5477,40 @@ proc check_effective_target_aarch64_large { } { return 0 } } + +# Return 1 if 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 + #include + #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"] +}