Hi! I've backported following 25 patches from trunk to 9.x, bootstrapped/regtested on x86_64-linux and i686-linux, committed to gcc-9-branch.
Jakub
2019-12-20 Jakub Jelinek <ja...@redhat.com> Backported from mainline 2019-11-21 Jakub Jelinek <ja...@redhat.com> Jason Merrill <ja...@redhat.com> PR c++/90842 * parser.c (cp_parser_decl_specifier_seq): For concept or typedef break early if CP_PARSER_FLAGS_ONLY_MUTABLE_OR_CONSTEXPR. For type specifiers, set CP_PARSER_FLAGS_NO_TYPE_DEFINITIONS if CP_PARSER_FLAGS_ONLY_MUTABLE_OR_CONSTEXPR is set. * g++.dg/cpp1y/lambda-generic-90842.C: New test. --- gcc/cp/parser.c (revision 278537) +++ gcc/cp/parser.c (revision 278538) @@ -13998,6 +13998,10 @@ cp_parser_decl_specifier_seq (cp_parser* case RID_CONCEPT: ds = ds_concept; cp_lexer_consume_token (parser->lexer); + + if (flags & CP_PARSER_FLAGS_ONLY_MUTABLE_OR_CONSTEXPR) + break; + /* In C++20 a concept definition is just 'concept name = expr;' Support that syntax by pretending we've seen 'bool'. */ if (cp_lexer_next_token_is (parser->lexer, CPP_NAME) @@ -14025,6 +14029,10 @@ cp_parser_decl_specifier_seq (cp_parser* ds = ds_typedef; /* Consume the token. */ cp_lexer_consume_token (parser->lexer); + + if (flags & CP_PARSER_FLAGS_ONLY_MUTABLE_OR_CONSTEXPR) + break; + /* A constructor declarator cannot appear in a typedef. */ constructor_possible_p = false; /* The "typedef" keyword can only occur in a declaration; we @@ -14120,6 +14128,9 @@ cp_parser_decl_specifier_seq (cp_parser* bool is_cv_qualifier; tree type_spec; + if (flags & CP_PARSER_FLAGS_ONLY_MUTABLE_OR_CONSTEXPR) + flags |= CP_PARSER_FLAGS_NO_TYPE_DEFINITIONS; + type_spec = cp_parser_type_specifier (parser, flags, decl_specs, --- gcc/testsuite/g++.dg/cpp1y/lambda-generic-90842.C (nonexistent) +++ gcc/testsuite/g++.dg/cpp1y/lambda-generic-90842.C (revision 278538) @@ -0,0 +1,7 @@ +// PR c++/90842 +// { dg-do compile { target c++14 } } + +auto a = [](auto x) struct C { void foo (); } {}; // { dg-error "expected" } + // { dg-error "type-specifier invalid in lambda" "" { target *-*-* } .-1 } +auto b = [](auto x) mutable typedef {}; // { dg-error "'typedef' invalid in lambda" } +auto d = [](auto x) mutable friend {}; // { dg-error "'friend' invalid in lambda" }
2019-12-20 Jakub Jelinek <ja...@redhat.com> Backported from mainline 2019-11-22 Jakub Jelinek <ja...@redhat.com> PR c/90677 * c-common.h (identifier_global_tag): Declare. * c-format.c (get_pointer_to_named_type): Renamed to ... (get_named_type): ... this. Use identifier_global_tag instead of identifier_global_value, handle the return value being a TYPE_P. (init_dynamic_diag_info): Adjust get_pointer_to_named_type callers to call get_named_type instead. Formatting fixes. * c-decl.c (identifier_global_tag): Define. * cp-objcp-common.c (identifier_global_tag): Define. * c-c++-common/pr90677.c: New test. --- gcc/c-family/c-common.h (revision 278633) +++ gcc/c-family/c-common.h (revision 278634) @@ -800,6 +800,7 @@ extern void c_register_addr_space (const extern bool in_late_binary_op; extern const char *c_addr_space_name (addr_space_t as); extern tree identifier_global_value (tree); +extern tree identifier_global_tag (tree); extern tree c_linkage_bindings (tree); extern void record_builtin_type (enum rid, const char *, tree); extern tree build_void_list_node (void); --- gcc/c-family/c-format.c (revision 278633) +++ gcc/c-family/c-format.c (revision 278634) @@ -4899,31 +4899,32 @@ init_dynamic_gfc_info (void) } } -/* Lookup the type named NAME and return a pointer-to-NAME type if found. - Otherwise, return void_type_node if NAME has not been used yet, or NULL_TREE if - NAME is not a type (issuing an error). */ +/* Lookup the type named NAME and return a NAME type if found. + Otherwise, return void_type_node if NAME has not been used yet, + or NULL_TREE if NAME is not a type (issuing an error). */ static tree -get_pointer_to_named_type (const char *name) +get_named_type (const char *name) { - tree result; - if ((result = maybe_get_identifier (name))) + if (tree result = maybe_get_identifier (name)) { - result = identifier_global_value (result); + result = identifier_global_tag (result); if (result) { - if (TREE_CODE (result) != TYPE_DECL) + if (TYPE_P (result)) + ; + else if (TREE_CODE (result) == TYPE_DECL) + result = TREE_TYPE (result); + else { error ("%qs is not defined as a type", name); result = NULL_TREE; } - else - result = TREE_TYPE (result); } + return result; } else - result = void_type_node; - return result; + return void_type_node; } /* Determine the types of "tree" and "location_t" in the code being @@ -4953,23 +4954,24 @@ init_dynamic_diag_info (void) an extra type level. */ if ((local_tree_type_node = maybe_get_identifier ("tree"))) { - local_tree_type_node = identifier_global_value (local_tree_type_node); + local_tree_type_node + = identifier_global_value (local_tree_type_node); if (local_tree_type_node) { if (TREE_CODE (local_tree_type_node) != TYPE_DECL) { error ("%<tree%> is not defined as a type"); - local_tree_type_node = 0; + local_tree_type_node = NULL_TREE; } else if (TREE_CODE (TREE_TYPE (local_tree_type_node)) != POINTER_TYPE) { error ("%<tree%> is not defined as a pointer type"); - local_tree_type_node = 0; + local_tree_type_node = NULL_TREE; } else - local_tree_type_node = - TREE_TYPE (TREE_TYPE (local_tree_type_node)); + local_tree_type_node + = TREE_TYPE (TREE_TYPE (local_tree_type_node)); } } else @@ -4979,12 +4981,12 @@ init_dynamic_diag_info (void) /* Similar to the above but for gimple*. */ if (!local_gimple_ptr_node || local_gimple_ptr_node == void_type_node) - local_gimple_ptr_node = get_pointer_to_named_type ("gimple"); + local_gimple_ptr_node = get_named_type ("gimple"); /* Similar to the above but for cgraph_node*. */ if (!local_cgraph_node_ptr_node || local_cgraph_node_ptr_node == void_type_node) - local_cgraph_node_ptr_node = get_pointer_to_named_type ("cgraph_node"); + local_cgraph_node_ptr_node = get_named_type ("cgraph_node"); static tree hwi; --- gcc/c/c-decl.c (revision 278633) +++ gcc/c/c-decl.c (revision 278634) @@ -9941,6 +9941,20 @@ identifier_global_value (tree t) return NULL_TREE; } +/* Return the global value of tag T as a symbol. */ + +tree +identifier_global_tag (tree t) +{ + struct c_binding *b; + + for (b = I_TAG_BINDING (t); b; b = b->shadowed) + if (B_IN_FILE_SCOPE (b) || B_IN_EXTERNAL_SCOPE (b)) + return b->decl; + + return NULL_TREE; +} + /* In C, the only C-linkage public declaration is at file scope. */ tree --- gcc/cp/cp-objcp-common.c (revision 278633) +++ gcc/cp/cp-objcp-common.c (revision 278634) @@ -352,6 +352,15 @@ identifier_global_value (tree name) return get_global_binding (name); } +/* Similarly, but return struct/class/union NAME instead. */ + +tree +identifier_global_tag (tree name) +{ + return lookup_qualified_name (global_namespace, name, /*prefer_type*/2, + /*complain*/false); +} + /* Register c++-specific dumps. */ void --- gcc/testsuite/c-c++-common/pr90677.c (nonexistent) +++ gcc/testsuite/c-c++-common/pr90677.c (revision 278634) @@ -0,0 +1,11 @@ +/* PR c/90677 */ +/* { dg-do compile } */ +/* { dg-options "-W -Wall" } */ + +struct cgraph_node; +union tree_node; +typedef union tree_node *tree; +union gimple_statement_d; +typedef union gimple_statement_d *gimple; +struct cgraph_node *cgraph_node (tree); +void foo (int, const char *, ...) __attribute__((__format__(__gcc_diag__, 2, 3)));
2019-12-20 Jakub Jelinek <ja...@redhat.com> Backported from mainline 2019-11-23 Jakub Jelinek <ja...@redhat.com> PR target/92615 * config/i386/i386.c (ix86_md_asm_adjust): If dest_mode is GET_MODE (dest), is not QImode, using ZERO_EXTEND and dest is not register_operand, force x into register before storing it into dest. Formatting fix. * gcc.target/i386/pr92615.c: New test. --- gcc/config/i386/i386.c (revision 278641) +++ gcc/config/i386/i386.c (revision 278642) @@ -20819,11 +20819,15 @@ ix86_md_asm_adjust (vec<rtx> &outputs, v { x = force_reg (dest_mode, const0_rtx); - emit_insn (gen_movstrictqi - (gen_lowpart (QImode, x), destqi)); + emit_insn (gen_movstrictqi (gen_lowpart (QImode, x), destqi)); } else - x = gen_rtx_ZERO_EXTEND (dest_mode, destqi); + { + x = gen_rtx_ZERO_EXTEND (dest_mode, destqi); + if (dest_mode == GET_MODE (dest) + && !register_operand (dest, GET_MODE (dest))) + x = force_reg (dest_mode, x); + } } if (dest_mode != GET_MODE (dest)) --- gcc/testsuite/gcc.target/i386/pr92615.c (nonexistent) +++ gcc/testsuite/gcc.target/i386/pr92615.c (revision 278642) @@ -0,0 +1,45 @@ +/* PR target/92615 */ +/* { dg-do compile } */ +/* { dg-options "-O2" } */ + +void *a; +long long b; +char c; + +void +foo (void) +{ + void *p; + long long q; + char r; + __asm__ ("" : : "r" (&p), "r" (&q), "r" (&r)); + __asm__ ("" : "=@cca" (p)); + a = p; + __asm__ ("" : "=@cca" (q)); + b = q; + __asm__ ("" : "=@cca" (r)); + c = r; + __asm__ ("" : : "r" (&p), "r" (&q), "r" (&r)); +} + +void +bar (void) +{ + void *p; + long long q; + char r; + __asm__ ("" : "=@cca" (p)); + a = p; + __asm__ ("" : "=@cca" (q)); + b = q; + __asm__ ("" : "=@cca" (r)); + c = r; + __asm__ ("" : : "r" (p), "A" (q), "q" (r)); +} + +void +baz (void) +{ + void *p = (void *) &p; + __asm__ __volatile__ ("" : "=@ccng" (p) : "r" (1)); +}
2019-12-20 Jakub Jelinek <ja...@redhat.com> Backported from mainline 2019-11-26 Jakub Jelinek <ja...@redhat.com> PR tree-optimization/92644 * tree-ssa-phiopt.c (minmax_replacement): Add INTEGRAL_TYPE_P check next to INTEGER_CST checks. * g++.dg/opt/pr92644.C: New test. --- gcc/tree-ssa-phiopt.c (revision 278719) +++ gcc/tree-ssa-phiopt.c (revision 278720) @@ -1381,7 +1381,8 @@ minmax_replacement (basic_block cond_bb, /* Turn EQ/NE of extreme values to order comparisons. */ if ((cmp == NE_EXPR || cmp == EQ_EXPR) - && TREE_CODE (rhs) == INTEGER_CST) + && TREE_CODE (rhs) == INTEGER_CST + && INTEGRAL_TYPE_P (TREE_TYPE (rhs))) { if (wi::eq_p (wi::to_wide (rhs), wi::min_value (TREE_TYPE (rhs)))) { @@ -1407,7 +1408,8 @@ minmax_replacement (basic_block cond_bb, larger = rhs; /* If we have smaller < CST it is equivalent to smaller <= CST-1. Likewise smaller <= CST is equivalent to smaller < CST+1. */ - if (TREE_CODE (larger) == INTEGER_CST) + if (TREE_CODE (larger) == INTEGER_CST + && INTEGRAL_TYPE_P (TREE_TYPE (larger))) { if (cmp == LT_EXPR) { @@ -1435,7 +1437,8 @@ minmax_replacement (basic_block cond_bb, larger = gimple_cond_lhs (cond); /* If we have larger > CST it is equivalent to larger >= CST+1. Likewise larger >= CST is equivalent to larger > CST-1. */ - if (TREE_CODE (smaller) == INTEGER_CST) + if (TREE_CODE (smaller) == INTEGER_CST + && INTEGRAL_TYPE_P (TREE_TYPE (smaller))) { wi::overflow_type overflow; if (cmp == GT_EXPR) --- gcc/testsuite/g++.dg/opt/pr92644.C (nonexistent) +++ gcc/testsuite/g++.dg/opt/pr92644.C (revision 278720) @@ -0,0 +1,6 @@ +// PR tree-optimization/92644 +// { dg-do compile { target c++14 } } +// { dg-options "-O2 -fno-early-inlining" } + +inline auto foo () { return nullptr; } +int bar () { return foo () ? 1 : 0; }
2019-12-20 Jakub Jelinek <ja...@redhat.com> Backported from mainline 2019-11-26 Jakub Jelinek <ja...@redhat.com> PR sanitizer/92154 * sanitizer_common/sanitizer_platform_limits_posix.h: Cherry-pick llvm-project revision 947f9692440836dcb8d88b74b69dd379d85974ce. * sanitizer_common/sanitizer_platform_limits_posix.cc: Likewise. --- libsanitizer/sanitizer_common/sanitizer_platform_limits_posix.h (revision 278721) +++ libsanitizer/sanitizer_common/sanitizer_platform_limits_posix.h (revision 278722) @@ -211,26 +211,13 @@ namespace __sanitizer { u64 __unused1; u64 __unused2; #elif defined(__sparc__) -#if defined(__arch64__) unsigned mode; - unsigned short __pad1; -#else - unsigned short __pad1; - unsigned short mode; unsigned short __pad2; -#endif unsigned short __seq; unsigned long long __unused1; unsigned long long __unused2; -#elif defined(__mips__) || defined(__aarch64__) || defined(__s390x__) - unsigned int mode; - unsigned short __seq; - unsigned short __pad1; - unsigned long __unused1; - unsigned long __unused2; #else - unsigned short mode; - unsigned short __pad1; + unsigned int mode; unsigned short __seq; unsigned short __pad2; #if defined(__x86_64__) && !defined(_LP64) --- libsanitizer/sanitizer_common/sanitizer_platform_limits_posix.cc (revision 278721) +++ libsanitizer/sanitizer_common/sanitizer_platform_limits_posix.cc (revision 278722) @@ -1156,12 +1156,9 @@ CHECK_SIZE_AND_OFFSET(ipc_perm, uid); CHECK_SIZE_AND_OFFSET(ipc_perm, gid); CHECK_SIZE_AND_OFFSET(ipc_perm, cuid); CHECK_SIZE_AND_OFFSET(ipc_perm, cgid); -#if (!defined(__aarch64__) || !SANITIZER_LINUX || __GLIBC_PREREQ (2, 21)) && \ - !defined(__arm__) -/* On aarch64 glibc 2.20 and earlier provided incorrect mode field. */ -/* On Arm glibc 2.31 and later provide a different mode field, this field is - never used by libsanitizer so we can simply ignore this assert for all glibc - versions. */ +#if !SANITIZER_LINUX || __GLIBC_PREREQ (2, 31) +/* glibc 2.30 and earlier provided 16-bit mode field instead of 32-bit + on many architectures. */ CHECK_SIZE_AND_OFFSET(ipc_perm, mode); #endif
2019-12-20 Jakub Jelinek <ja...@redhat.com> Backported from mainline 2019-11-26 Jakub Jelinek <ja...@redhat.com> PR c++/61414 * c-attribs.c (handle_mode_attribute): Add mode attribute to ENUMERAL_TYPEs. * class.c (enum_to_min_precision): New hash_map. (enum_min_precision): New function. (check_bitfield_decl): Use it. * g++.dg/cpp0x/enum23.C: Remove xfail. * g++.dg/cpp0x/enum28.C: New test. --- gcc/c-family/c-attribs.c (revision 278735) +++ gcc/c-family/c-attribs.c (revision 278736) @@ -1866,6 +1866,7 @@ handle_mode_attribute (tree *node, tree typefm = make_signed_type (TYPE_PRECISION (typefm)); TREE_TYPE (typefm) = type; } + *no_add_attrs = false; } else if (VECTOR_MODE_P (mode) ? TREE_CODE (type) != TREE_CODE (TREE_TYPE (typefm)) --- gcc/cp/class.c (revision 278735) +++ gcc/cp/class.c (revision 278736) @@ -3265,6 +3265,60 @@ add_implicitly_declared_members (tree t, } } +/* Cache of enum_min_precision values. */ +static GTY((deletable)) hash_map<tree, int> *enum_to_min_precision; + +/* Return the minimum precision of a bit-field needed to store all + enumerators of ENUMERAL_TYPE TYPE. */ + +static int +enum_min_precision (tree type) +{ + type = TYPE_MAIN_VARIANT (type); + /* For unscoped enums without fixed underlying type and without mode + attribute we can just use precision of the underlying type. */ + if (UNSCOPED_ENUM_P (type) + && !ENUM_FIXED_UNDERLYING_TYPE_P (type) + && !lookup_attribute ("mode", TYPE_ATTRIBUTES (type))) + return TYPE_PRECISION (ENUM_UNDERLYING_TYPE (type)); + + if (enum_to_min_precision == NULL) + enum_to_min_precision = hash_map<tree, int>::create_ggc (37); + + bool existed; + int prec = enum_to_min_precision->get_or_insert (type, &existed); + if (existed) + return prec; + + tree minnode, maxnode; + if (TYPE_VALUES (type)) + { + minnode = maxnode = NULL_TREE; + for (tree values = TYPE_VALUES (type); + values; values = TREE_CHAIN (values)) + { + tree decl = TREE_VALUE (values); + tree value = DECL_INITIAL (decl); + if (value == error_mark_node) + value = integer_zero_node; + if (!minnode) + minnode = maxnode = value; + else if (tree_int_cst_lt (maxnode, value)) + maxnode = value; + else if (tree_int_cst_lt (value, minnode)) + minnode = value; + } + } + else + minnode = maxnode = integer_zero_node; + + signop sgn = tree_int_cst_sgn (minnode) >= 0 ? UNSIGNED : SIGNED; + int lowprec = tree_int_cst_min_precision (minnode, sgn); + int highprec = tree_int_cst_min_precision (maxnode, sgn); + prec = MAX (lowprec, highprec); + return prec; +} + /* FIELD is a bit-field. We are finishing the processing for its enclosing type. Issue any appropriate messages and set appropriate flags. Returns false if an error has been diagnosed. */ @@ -3326,7 +3380,7 @@ check_bitfield_decl (tree field) "width of %qD exceeds its type", field); else if (TREE_CODE (type) == ENUMERAL_TYPE) { - int prec = TYPE_PRECISION (ENUM_UNDERLYING_TYPE (type)); + int prec = enum_min_precision (type); if (compare_tree_int (w, prec) < 0) warning_at (DECL_SOURCE_LOCATION (field), 0, "%qD is too small to hold all values of %q#T", --- gcc/testsuite/g++.dg/cpp0x/enum23.C (revision 278735) +++ gcc/testsuite/g++.dg/cpp0x/enum23.C (revision 278736) @@ -5,5 +5,5 @@ enum class MyEnum { A = 1 }; struct MyClass { - MyEnum Field1 : 3; // { dg-bogus "warning: 'MyClass::Field1' is too small" "" { xfail *-*-* } } + MyEnum Field1 : 3; // { dg-bogus "warning: 'MyClass::Field1' is too small" } }; --- gcc/testsuite/g++.dg/cpp0x/enum38.C (nonexistent) +++ gcc/testsuite/g++.dg/cpp0x/enum38.C (revision 278736) @@ -0,0 +1,25 @@ +// PR c++/61414 +// { dg-do compile { target c++11 } } + +enum C { C0 = -4, C1 = 3 }; +enum D { D0 = 0, D1 = 15 }; +enum class E { E0 = -4, E1 = 3 }; +enum F : unsigned { F0 = 0, F1 = 15 }; +enum __attribute__((__mode__ (__QI__))) G { G0 = -4, G1 = 3 }; +enum __attribute__((__mode__ (__HI__))) H { H0 = 0, H1 = 15 }; + +struct S +{ + C a : 2; // { dg-warning "'S::a' is too small to hold all values of 'enum C'" } + C b : 3; // { dg-bogus "'S::b' is too small to hold all values of 'enum C'" } + D c : 3; // { dg-warning "'S::c' is too small to hold all values of 'enum D'" } + D d : 4; // { dg-bogus "'S::d' is too small to hold all values of 'enum D'" } + E e : 2; // { dg-warning "'S::e' is too small to hold all values of 'enum class E'" } + E f : 3; // { dg-bogus "'S::f' is too small to hold all values of 'enum class E'" } + F g : 3; // { dg-warning "'S::g' is too small to hold all values of 'enum F'" } + F h : 4; // { dg-bogus "'S::h' is too small to hold all values of 'enum F'" } + G i : 2; // { dg-warning "'S::i' is too small to hold all values of 'enum G'" } + G j : 3; // { dg-bogus "'S::j' is too small to hold all values of 'enum G'" } + H k : 3; // { dg-warning "'S::k' is too small to hold all values of 'enum H'" } + H l : 4; // { dg-bogus "'S::l' is too small to hold all values of 'enum H'" } +};
2019-12-20 Jakub Jelinek <ja...@redhat.com> Backported from mainline 2019-11-26 Jakub Jelinek <ja...@redhat.com> PR c++/92648 * parser.c (cp_parser_std_attribute): For unknown attributes, skip balanced token seq instead of trying to parse attribute-argument-clause as expression list. * g++.dg/cpp0x/gen-attrs-71.C: New test. --- gcc/cp/parser.c (revision 278736) +++ gcc/cp/parser.c (revision 278737) @@ -2650,6 +2650,7 @@ static bool cp_parser_init_statement_p (cp_parser *); static bool cp_parser_skip_to_closing_square_bracket (cp_parser *); +static size_t cp_parser_skip_balanced_tokens (cp_parser *, size_t); /* Concept-related syntactic transformations */ @@ -26189,6 +26190,17 @@ cp_parser_std_attribute (cp_parser *pars /* A GNU attribute that takes an identifier in parameter. */ attr_flag = id_attr; + const attribute_spec *as + = lookup_attribute_spec (TREE_PURPOSE (attribute)); + if (as == NULL) + { + /* For unknown attributes, just skip balanced tokens instead of + trying to parse the arguments. */ + for (size_t n = cp_parser_skip_balanced_tokens (parser, 1) - 1; n; --n) + cp_lexer_consume_token (parser->lexer); + return attribute; + } + vec = cp_parser_parenthesized_expression_list (parser, attr_flag, /*cast_p=*/false, /*allow_expansion_p=*/true, --- gcc/testsuite/g++.dg/cpp0x/gen-attrs-71.C (nonexistent) +++ gcc/testsuite/g++.dg/cpp0x/gen-attrs-71.C (revision 278737) @@ -0,0 +1,7 @@ +// PR c++/92648 +// { dg-do compile { target c++11 } } + +int a [[gnu::no_such_attribute(![!(!)!]!,;;)]]; // { dg-warning "ignored" } +int b [[no_such_namespace::nonexisting_attribute(linear(c, d : 2), reduction(*:e), linear(uval (f)))]]; // { dg-warning "ignored" } +int c [[gnu::nonexisting_attribute()]]; // { dg-warning "ignored" } +int d [[gnu::another_nonexistent_attr(1,"abcd",g+6)]]; // { dg-warning "ignored" }
2019-12-20 Jakub Jelinek <ja...@redhat.com> Backported from mainline 2019-11-27 Jakub Jelinek <ja...@redhat.com> PR debug/92664 * dwarf2out.c (lookup_filename): Use "<stdin>" instead of "". --- gcc/dwarf2out.c (revision 278751) +++ gcc/dwarf2out.c (revision 278752) @@ -27118,6 +27118,9 @@ lookup_filename (const char *file_name) if (!file_name) return NULL; + if (!file_name[0]) + file_name = "<stdin>"; + dwarf_file_data **slot = file_table->find_slot_with_hash (file_name, htab_hash_string (file_name), INSERT);
2019-12-20 Jakub Jelinek <ja...@redhat.com> Backported from mainline 2019-11-27 Jakub Jelinek <ja...@redhat.com> PR c++/92524 * tree.c (replace_placeholders_r): Don't walk constructor elts with RANGE_EXPR indexes. * g++.dg/cpp0x/pr92524.C: New test. --- gcc/cp/tree.c (revision 278758) +++ gcc/cp/tree.c (revision 278759) @@ -3144,6 +3144,11 @@ replace_placeholders_r (tree* t, int* wa tree type = TREE_TYPE (*valp); tree subob = obj; + /* Elements with RANGE_EXPR index shouldn't have any + placeholders in them. */ + if (ce->index && TREE_CODE (ce->index) == RANGE_EXPR) + continue; + if (TREE_CODE (*valp) == CONSTRUCTOR && AGGREGATE_TYPE_P (type)) { --- gcc/testsuite/g++.dg/cpp0x/pr92524.C (nonexistent) +++ gcc/testsuite/g++.dg/cpp0x/pr92524.C (revision 278759) @@ -0,0 +1,12 @@ +// PR c++/92524 +// { dg-do compile { target c++11 } } + +struct A { char a = '*'; }; +struct B { A b[64]; }; + +void +foo () +{ + A a; + B{a}; +}
2019-12-20 Jakub Jelinek <ja...@redhat.com> Backported from mainline 2019-11-27 Jakub Jelinek <ja...@redhat.com> PR fortran/91944 * simplify.c (gfc_simplify_spread): Check gfc_init_expr_flag instead of gfc_current_ns->sym_root->n.sym->attr.flavor == FL_PARAMETER. * gfortran.dg/spread_size_limit_2.f90: New test. --- gcc/fortran/simplify.c (revision 278761) +++ gcc/fortran/simplify.c (revision 278762) @@ -7656,7 +7656,7 @@ gfc_simplify_spread (gfc_expr *source, g nelem = mpz_get_si (size) * ncopies; if (nelem > flag_max_array_constructor) { - if (gfc_current_ns->sym_root->n.sym->attr.flavor == FL_PARAMETER) + if (gfc_init_expr_flag) { gfc_error ("The number of elements (%d) in the array constructor " "at %L requires an increase of the allowed %d upper " --- gcc/testsuite/gfortran.dg/spread_size_limit_2.f90 (nonexistent) +++ gcc/testsuite/gfortran.dg/spread_size_limit_2.f90 (revision 278762) @@ -0,0 +1,11 @@ +! PR fortran/91944 +! { dg-do compile } +! { dg-options "-fmax-array-constructor=65535" } + +program pr91944 + integer, parameter :: n = 10 + integer, parameter :: m = 65536 + integer :: i + integer :: x(n,m) = spread([(i,i=1,n)], dim=2, ncopies=m) ! { dg-error "requires an increase of the allowed 65535 upper limit" } + print *, x(n,m) +end
2019-12-20 Jakub Jelinek <ja...@redhat.com> Backported from mainline 2019-11-28 Jakub Jelinek <ja...@redhat.com> PR c++/92695 * decl2.c (mark_used): Don't call note_vague_linkage_fn for pure virtual functions, even if they are declared inline. * g++.dg/warn/inline3.C: New test. --- gcc/cp/decl2.c (revision 278801) +++ gcc/cp/decl2.c (revision 278802) @@ -5596,8 +5596,11 @@ mark_used (tree decl, tsubst_flags_t com vec_safe_push (no_linkage_decls, decl); } - if (TREE_CODE (decl) == FUNCTION_DECL && DECL_DECLARED_INLINE_P (decl) - && !DECL_INITIAL (decl) && !DECL_ARTIFICIAL (decl)) + if (TREE_CODE (decl) == FUNCTION_DECL + && DECL_DECLARED_INLINE_P (decl) + && !DECL_INITIAL (decl) + && !DECL_ARTIFICIAL (decl) + && !DECL_PURE_VIRTUAL_P (decl)) /* Remember it, so we can check it was defined. */ note_vague_linkage_fn (decl); --- gcc/testsuite/g++.dg/warn/inline3.C (nonexistent) +++ gcc/testsuite/g++.dg/warn/inline3.C (revision 278802) @@ -0,0 +1,20 @@ +struct S { + inline virtual void foo () = 0; // { dg-bogus "used but never defined" } +#if __cplusplus > 201703L + constexpr virtual void bar () = 0; // { dg-bogus "used but never defined" "" { target c++2a } } +#else + inline virtual void bar () = 0; // { dg-bogus "used but never defined" "" { target c++17_down } } +#endif + S () {} +}; +struct T : public S { + inline virtual void foo () {} +#if __cplusplus > 201703L + constexpr virtual void bar () {} +#else + inline virtual void bar () {} +#endif + T () {} +}; +T t; +void foo (S *s) { s->foo (); s->bar (); }
2019-12-20 Jakub Jelinek <ja...@redhat.com> Backported from mainline 2019-11-29 Jakub Jelinek <ja...@redhat.com> PR c++/60228 * parser.c (cp_parser_omp_declare_reduction_exprs): If processing_template_decl, wrap the combiner or initializer into EXPR_STMT. * decl.c (start_preparsed_function): Don't start a lambda scope for DECL_OMP_DECLARE_REDUCTION_P functions. (finish_function): Don't finish a lambda scope for DECL_OMP_DECLARE_REDUCTION_P functions, nor cp_fold_function them nor cp_genericize them. * mangle.c (decl_mangling_context): Look through DECL_OMP_DECLARE_REDUCTION_P functions. * semantics.c (expand_or_defer_fn_1): For DECL_OMP_DECLARE_REDUCTION_P functions, use tentative linkage, don't keep their bodies with -fkeep-inline-functions and return false at the end. * g++.dg/gomp/openmp-simd-2.C: Don't expect bodies for DECL_OMP_DECLARE_REDUCTION_P functions. * testsuite/libgomp.c++/udr-20.C: New test. * testsuite/libgomp.c++/udr-21.C: New test. --- gcc/cp/decl.c (revision 278830) +++ gcc/cp/decl.c (revision 278831) @@ -15632,7 +15632,8 @@ start_preparsed_function (tree decl1, tr && !implicit_default_ctor_p (decl1)) cp_ubsan_maybe_initialize_vtbl_ptrs (current_class_ptr); - start_lambda_scope (decl1); + if (!DECL_OMP_DECLARE_REDUCTION_P (decl1)) + start_lambda_scope (decl1); return true; } @@ -16040,7 +16041,8 @@ finish_function (bool inline_p) if (fndecl == NULL_TREE) return error_mark_node; - finish_lambda_scope (); + if (!DECL_OMP_DECLARE_REDUCTION_P (fndecl)) + finish_lambda_scope (); if (c_dialect_objc ()) objc_finish_function (); @@ -16157,7 +16159,7 @@ finish_function (bool inline_p) invoke_plugin_callbacks (PLUGIN_PRE_GENERICIZE, fndecl); /* Perform delayed folding before NRV transformation. */ - if (!processing_template_decl) + if (!processing_template_decl && !DECL_OMP_DECLARE_REDUCTION_P (fndecl)) cp_fold_function (fndecl); /* Set up the named return value optimization, if we can. Candidate @@ -16280,7 +16282,8 @@ finish_function (bool inline_p) if (!processing_template_decl) { struct language_function *f = DECL_SAVED_FUNCTION_DATA (fndecl); - cp_genericize (fndecl); + if (!DECL_OMP_DECLARE_REDUCTION_P (fndecl)) + cp_genericize (fndecl); /* Clear out the bits we don't need. */ f->x_current_class_ptr = NULL; f->x_current_class_ref = NULL; --- gcc/cp/semantics.c (revision 278830) +++ gcc/cp/semantics.c (revision 278831) @@ -4417,7 +4417,7 @@ expand_or_defer_fn_1 (tree fn) if (DECL_INTERFACE_KNOWN (fn)) /* We've already made a decision as to how this function will be handled. */; - else if (!at_eof) + else if (!at_eof || DECL_OMP_DECLARE_REDUCTION_P (fn)) tentative_decl_linkage (fn); else import_export_decl (fn); @@ -4429,5 +4431,6 @@ expand_or_defer_fn_1 (tree fn) if (DECL_DECLARED_INLINE_P (fn) && !DECL_REALLY_EXTERN (fn) + && !DECL_OMP_DECLARE_REDUCTION_P (fn) && (flag_keep_inline_functions || (flag_keep_inline_dllexport && lookup_attribute ("dllexport", DECL_ATTRIBUTES (fn))))) @@ -4461,6 +4464,9 @@ expand_or_defer_fn_1 (tree fn) return false; } + if (DECL_OMP_DECLARE_REDUCTION_P (fn)) + return false; + return true; } --- gcc/cp/parser.c (revision 278830) +++ gcc/cp/parser.c (revision 278831) @@ -41244,6 +41244,8 @@ cp_parser_omp_declare_reduction_exprs (t combiner = cp_parser_expression (parser); finish_expr_stmt (combiner); block = finish_omp_structured_block (block); + if (processing_template_decl) + block = build_stmt (input_location, EXPR_STMT, block); add_stmt (block); if (!cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN)) @@ -41348,6 +41350,8 @@ cp_parser_omp_declare_reduction_exprs (t block = finish_omp_structured_block (block); cp_walk_tree (&block, cp_remove_omp_priv_cleanup_stmt, omp_priv, NULL); + if (processing_template_decl) + block = build_stmt (input_location, EXPR_STMT, block); add_stmt (block); if (ctor) --- gcc/cp/mangle.c (revision 278830) +++ gcc/cp/mangle.c (revision 278831) @@ -873,7 +873,16 @@ decl_mangling_context (tree decl) else if (template_type_parameter_p (decl)) /* template type parms have no mangling context. */ return NULL_TREE; - return CP_DECL_CONTEXT (decl); + + tcontext = CP_DECL_CONTEXT (decl); + + /* Ignore the artificial declare reduction functions. */ + if (tcontext + && TREE_CODE (tcontext) == FUNCTION_DECL + && DECL_OMP_DECLARE_REDUCTION_P (tcontext)) + return decl_mangling_context (tcontext); + + return tcontext; } /* <name> ::= <unscoped-name> --- gcc/testsuite/g++.dg/gomp/openmp-simd-2.C (revision 278830) +++ gcc/testsuite/g++.dg/gomp/openmp-simd-2.C (revision 278831) @@ -36,8 +36,6 @@ void bar(int n, float *a, float *b) a[i] = b[i]; } -/* { dg-final { scan-tree-dump-times "Function void omp declare reduction operator\\+" 1 "original" } } */ -/* { dg-final { scan-tree-dump-times "Function void omp declare reduction foo" 2 "original" } } */ /* { dg-final { scan-tree-dump-times "pragma omp simd reduction\\(u\\) reduction\\(t\\) reduction\\(\\+:s\\) aligned\\(a:32\\)" 1 "original" } } */ /* { dg-final { scan-tree-dump-times "pragma omp simd safelen\\(64\\)" 1 "original" } } */ /* { dg-final { scan-tree-dump-not "omp parallel" "original" } } */ --- libgomp/testsuite/libgomp.c++/udr-20.C (nonexistent) +++ libgomp/testsuite/libgomp.c++/udr-20.C (revision 278832) @@ -0,0 +1,54 @@ +// PR c++/60228 +// { dg-additional-options "-std=c++11" } + +extern "C" void abort (); + +struct A +{ + typedef int T; + #pragma omp declare reduction (x : T : omp_out += omp_in + [](){ return 0; }()) initializer (omp_priv = [](){ return 0; }()) + static void foo (); +}; + +template <typename T> +struct B +{ + #pragma omp declare reduction (x : T : omp_out += omp_in + [](){ return T (0); }()) initializer (omp_priv = [](){ return T (0); }()) + static void foo (); +}; + +void +A::foo () +{ + int r = 0, s = 0; + #pragma omp parallel for reduction (x : r, s) + for (int i = 0; i < 64; i++) + { + r++; + s += i; + } + if (r != 64 || s != (64 * 63) / 2) + abort (); +} + +template <typename T> +void +B<T>::foo () +{ + T r = 0, s = 0; + #pragma omp parallel for reduction (x : r, s) + for (int i = 0; i < 64; i++) + { + r++; + s += i; + } + if (r != 64 || s != (64 * 63) / 2) + abort (); +} + +int +main () +{ + A::foo (); + B<long>::foo (); +} --- libgomp/testsuite/libgomp.c++/udr-21.C (nonexistent) +++ libgomp/testsuite/libgomp.c++/udr-21.C (revision 278832) @@ -0,0 +1,54 @@ +// PR c++/60228 +// { dg-additional-options "-std=c++11" } + +extern "C" void abort (); + +struct A +{ + typedef int T; + #pragma omp declare reduction (y : T : [&omp_out, &omp_in]() { omp_out += omp_in; return 0; }()) initializer (omp_priv = [omp_orig]() { return omp_orig; }()) + static void foo (); +}; + +template <typename T> +struct B +{ + #pragma omp declare reduction (y : T : [&omp_out, &omp_in]() { omp_out += omp_in; return 0; }()) initializer (omp_priv = [omp_orig]() { return omp_orig; }()) + static void foo (); +}; + +void +A::foo () +{ + int r = 0, s = 0; + #pragma omp parallel for reduction (y : r, s) + for (int i = 0; i < 64; i++) + { + r++; + s += i; + } + if (r != 64 || s != (64 * 63) / 2) + abort (); +} + +template <typename T> +void +B<T>::foo () +{ + T r = 0, s = 0; + #pragma omp parallel for reduction (y : r, s) + for (int i = 0; i < 64; i++) + { + r++; + s += i; + } + if (r != 64 || s != (64 * 63) / 2) + abort (); +} + +int +main () +{ + A::foo (); + B<short>::foo (); +}
2019-12-20 Jakub Jelinek <ja...@redhat.com> Backported from mainline 2019-12-02 Jakub Jelinek <ja...@redhat.com> PR c++/92695 * constexpr.c (cxx_eval_constant_expression) <case OBJ_TYPE_REF>: Use STRIP_NOPS before checking for ADDR_EXPR. * g++.dg/cpp2a/constexpr-virtual15.C: New test. --- gcc/cp/constexpr.c (revision 278911) +++ gcc/cp/constexpr.c (revision 278912) @@ -5548,6 +5548,7 @@ cxx_eval_constant_expression (const cons tree obj = OBJ_TYPE_REF_OBJECT (t); obj = cxx_eval_constant_expression (ctx, obj, lval, non_constant_p, overflow_p); + STRIP_NOPS (obj); /* We expect something in the form of &x.D.2103.D.2094; get x. */ if (TREE_CODE (obj) != ADDR_EXPR || !DECL_P (get_base_address (TREE_OPERAND (obj, 0)))) --- gcc/testsuite/g++.dg/cpp2a/constexpr-virtual15.C (nonexistent) +++ gcc/testsuite/g++.dg/cpp2a/constexpr-virtual15.C (revision 278912) @@ -0,0 +1,7 @@ +// PR c++/92695 +// { dg-do compile { target c++2a } } + +struct A { virtual int get() = 0; }; +struct B : A { constexpr int get() override { return 10; } }; +struct D { B b[2]; A* c{&(b[0])}; }; +static_assert(D{}.c->get() == 10);
2019-12-20 Jakub Jelinek <ja...@redhat.com> Backported from mainline 2019-12-03 Jakub Jelinek <ja...@redhat.com> PR c++/92695 * constexpr.c (cxx_bind_parameters_in_call): For virtual calls, adjust the first argument to point to the derived object rather than its base. * g++.dg/cpp2a/constexpr-virtual14.C: New test. --- gcc/cp/constexpr.c (revision 278920) +++ gcc/cp/constexpr.c (revision 278921) @@ -1419,6 +1419,28 @@ cxx_bind_parameters_in_call (const const arg = adjust_temp_type (type, arg); if (!TREE_CONSTANT (arg)) *non_constant_args = true; + + /* For virtual calls, adjust the this argument, so that it is + the object on which the method is called, rather than + one of its bases. */ + if (i == 0 && DECL_VIRTUAL_P (fun)) + { + tree addr = arg; + STRIP_NOPS (addr); + if (TREE_CODE (addr) == ADDR_EXPR) + { + tree obj = TREE_OPERAND (addr, 0); + while (TREE_CODE (obj) == COMPONENT_REF + && DECL_FIELD_IS_BASE (TREE_OPERAND (obj, 1)) + && !same_type_ignoring_top_level_qualifiers_p + (TREE_TYPE (obj), DECL_CONTEXT (fun))) + obj = TREE_OPERAND (obj, 0); + if (obj != TREE_OPERAND (addr, 0)) + arg = build_fold_addr_expr_with_type (obj, + TREE_TYPE (arg)); + } + } + *p = build_tree_list (parms, arg); p = &TREE_CHAIN (*p); } --- gcc/testsuite/g++.dg/cpp2a/constexpr-virtual14.C (nonexistent) +++ gcc/testsuite/g++.dg/cpp2a/constexpr-virtual14.C (revision 278921) @@ -0,0 +1,27 @@ +// PR c++/92695 +// { dg-do compile { target c++2a } } + +struct A { + virtual int get () = 0; + virtual int set (A *o) = 0; +}; +struct B : A { + constexpr int get () override { return 10; } + constexpr int set (A *o) override { a = o; return 20; } + A *a {}; +}; +constexpr auto addressof = [] (A &n) { return &n; }; +struct C { + B b; + A *c { addressof (b) }; + constexpr int add () { return c->set (addressof (b)); } +}; +struct D { + B b[2]; + A *c { addressof (b[0]) }; + constexpr int add () { return c->set (addressof (b[0])); } +}; +template <typename T> +constexpr int get () { T f; return f.add (); } +static_assert (get<C> () == 20); +static_assert (get<D> () == 20);
2019-12-20 Jakub Jelinek <ja...@redhat.com> Backported from mainline 2019-12-03 Jakub Jelinek <ja...@redhat.com> PR c++/92732 * typeck2.c (digest_nsdmi_init): For bitfields, use DECL_BIT_FIELD_TYPE instead of TREE_TYPE. * g++.dg/cpp2a/bitfield3.C: Don't expect narrowing conversion warnings. * g++.dg/cpp2a/bitfield4.C: New test. --- gcc/cp/typeck2.c (revision 278922) +++ gcc/cp/typeck2.c (revision 278923) @@ -1335,6 +1335,8 @@ digest_nsdmi_init (tree decl, tree init, gcc_assert (TREE_CODE (decl) == FIELD_DECL); tree type = TREE_TYPE (decl); + if (DECL_BIT_FIELD_TYPE (decl)) + type = DECL_BIT_FIELD_TYPE (decl); int flags = LOOKUP_IMPLICIT; if (DIRECT_LIST_INIT_P (init)) { --- gcc/testsuite/g++.dg/cpp2a/bitfield3.C (revision 278922) +++ gcc/testsuite/g++.dg/cpp2a/bitfield3.C (revision 278923) @@ -15,11 +15,9 @@ const int b = 0; struct S { int c : 5 = 2 * a; // { dg-warning "default member initializers for bit-fields only available with" "" { target c++17_down } } int d : 6 { c + a }; // { dg-warning "default member initializers for bit-fields only available with" "" { target c++17_down } } - // { dg-warning "narrowing conversion of" "" { target *-*-* } .-1 } int e : true ? 7 : a = 3; int f : (true ? 8 : b) = d + a; // { dg-warning "default member initializers for bit-fields only available with" "" { target c++17_down } } int g : (true ? 9 : b) { f + a }; // { dg-warning "default member initializers for bit-fields only available with" "" { target c++17_down } } - // { dg-warning "narrowing conversion of" "" { target *-*-* } .-1 } int h : 1 || new int { 0 }; int i = g + a; }; @@ -28,11 +26,9 @@ template <bool V, int W> struct U { int j : W = 3 * a; // { dg-warning "default member initializers for bit-fields only available with" "" { target c++17_down } } int k : W { j + a }; // { dg-warning "default member initializers for bit-fields only available with" "" { target c++17_down } } - // { dg-warning "narrowing conversion of" "" { target *-*-* } .-1 } int l : V ? 7 : a = 3; int m : (V ? W : b) = k + a; // { dg-warning "default member initializers for bit-fields only available with" "" { target c++17_down } } int n : (V ? W : b) { m + a }; // { dg-warning "default member initializers for bit-fields only available with" "" { target c++17_down } } - // { dg-warning "narrowing conversion of" "" { target *-*-* } .-1 } int o : 1 || new int { 0 }; int p = n + a; }; --- gcc/testsuite/g++.dg/cpp2a/bitfield4.C (nonexistent) +++ gcc/testsuite/g++.dg/cpp2a/bitfield4.C (revision 278923) @@ -0,0 +1,12 @@ +// PR c++/92732 +// { dg-do compile { target c++17 } } +// { dg-options "" } + +enum class byte : unsigned char { }; +using uint8_t = unsigned char; + +struct T +{ + byte a : 2 = byte{0}; // { dg-warning "default member initializers for bit-fields only available with" "" { target c++17_down } } + uint8_t b : 2 = 0; // { dg-warning "default member initializers for bit-fields only available with" "" { target c++17_down } } +} t;
2019-12-20 Jakub Jelinek <ja...@redhat.com> PR fortran/92756 * omp-low.c (check_omp_nesting_restrictions): If lang_GNU_Fortran, diagnose teams not closely nested inside of target. Backported from mainline 2019-12-04 Jakub Jelinek <ja...@redhat.com> PR fortran/92756 * gfortran.dg/gomp/teams1.f90: New test. --- gcc/omp-low.c.jj 2019-08-01 08:29:30.880772337 +0200 +++ gcc/omp-low.c 2019-12-20 15:29:59.052918458 +0100 @@ -3041,6 +3041,16 @@ check_omp_nesting_restrictions (gimple * } break; case GIMPLE_OMP_TEAMS: + if ((ctx == NULL + || gimple_code (ctx->stmt) != GIMPLE_OMP_TARGET + || gimple_omp_target_kind (ctx->stmt) != GF_OMP_TARGET_KIND_REGION) + && lang_GNU_Fortran ()) + { + error_at (gimple_location (stmt), + "%<teams%> construct not closely nested inside of " + "%<target%> construct"); + return false; + } if (ctx == NULL) break; else if (gimple_code (ctx->stmt) != GIMPLE_OMP_TARGET --- gcc/testsuite/gfortran.dg/gomp/teams1.f90 (nonexistent) +++ gcc/testsuite/gfortran.dg/gomp/teams1.f90 (revision 278956) @@ -0,0 +1,8 @@ +! PR fortran/92756 + +program pr92756 + integer :: i + !$omp teams distribute parallel do ! { dg-error "'teams' construct not closely nested inside of 'target' construct" } + do i = 1, 64 + end do +end
2019-12-20 Jakub Jelinek <ja...@redhat.com> Backported from mainline 2019-12-05 Jakub Jelinek <ja...@redhat.com> PR fortran/92781 * trans-decl.c (gfc_get_symbol_decl): If sym->backend_decl is current_function_decl, add length to current rather than parent function and expect DECL_CONTEXT (length) to be current_function_decl. * gfortran.dg/pr92781.f90: New test. --- gcc/fortran/trans-decl.c (revision 278987) +++ gcc/fortran/trans-decl.c (revision 278988) @@ -1631,15 +1631,18 @@ gfc_get_symbol_decl (gfc_symbol * sym) /* Add the string length to the same context as the symbol. */ if (DECL_CONTEXT (length) == NULL_TREE) { - if (DECL_CONTEXT (sym->backend_decl) - == current_function_decl) + if (sym->backend_decl == current_function_decl + || (DECL_CONTEXT (sym->backend_decl) + == current_function_decl)) gfc_add_decl_to_function (length); else gfc_add_decl_to_parent_function (length); } - gcc_assert (DECL_CONTEXT (sym->backend_decl) - == DECL_CONTEXT (length)); + gcc_assert (sym->backend_decl == current_function_decl + ? DECL_CONTEXT (length) == current_function_decl + : (DECL_CONTEXT (sym->backend_decl) + == DECL_CONTEXT (length))); gfc_defer_symbol_init (sym); } --- gcc/testsuite/gfortran.dg/pr92781.f90 (nonexistent) +++ gcc/testsuite/gfortran.dg/pr92781.f90 (revision 278988) @@ -0,0 +1,11 @@ +! PR fortran/92781 +! { dg-do compile } + +function foo () + character(:), allocatable :: foo + call bar () + foo = 'abc' +contains + subroutine bar + end +end
2019-12-20 Jakub Jelinek <ja...@redhat.com> Backported from mainline 2019-12-06 Jakub Jelinek <ja...@redhat.com> PR fortran/92775 * trans.h (struct lang_type, struct lang_decl): Remove span member. (GFC_DECL_SPAN, GFC_TYPE_ARRAY_SPAN): Remove macros. * trans-array.h (gfc_get_descriptor_offsets_for_info): Add another argument. * trans-array.c (gfc_get_descriptor_offsets_for_info): Add SPAN_OFF argument and initialize *SPAN_OFF to the offset of span field. * trans-types.c (gfc_get_array_descr_info): Adjust gfc_get_descriptor_offsets_for_info caller. Compute elem_size as base->span instead of TYPE_SIZE_UNIT (etype) constant. --- gcc/fortran/trans-array.h (revision 279044) +++ gcc/fortran/trans-array.h (revision 279045) @@ -163,7 +163,7 @@ void gfc_trans_array_cobounds (tree, stm /* Build expressions for accessing components of an array descriptor. */ void gfc_get_descriptor_offsets_for_info (const_tree, tree *, tree *, tree *, tree *, - tree *, tree *, tree *); + tree *, tree *, tree *, tree *); tree gfc_conv_descriptor_data_get (tree); tree gfc_conv_descriptor_data_addr (tree); --- gcc/fortran/trans-array.c (revision 279044) +++ gcc/fortran/trans-array.c (revision 279045) @@ -540,9 +540,10 @@ gfc_conv_shift_descriptor_lbound (stmtbl void gfc_get_descriptor_offsets_for_info (const_tree desc_type, tree *data_off, - tree *dtype_off, tree *dim_off, - tree *dim_size, tree *stride_suboff, - tree *lower_suboff, tree *upper_suboff) + tree *dtype_off, tree *span_off, + tree *dim_off, tree *dim_size, + tree *stride_suboff, tree *lower_suboff, + tree *upper_suboff) { tree field; tree type; @@ -552,6 +553,8 @@ gfc_get_descriptor_offsets_for_info (con *data_off = byte_position (field); field = gfc_advance_chain (TYPE_FIELDS (type), DTYPE_FIELD); *dtype_off = byte_position (field); + field = gfc_advance_chain (TYPE_FIELDS (type), SPAN_FIELD); + *span_off = byte_position (field); field = gfc_advance_chain (TYPE_FIELDS (type), DIMENSION_FIELD); *dim_off = byte_position (field); type = TREE_TYPE (TREE_TYPE (field)); --- gcc/fortran/trans-types.c (revision 279044) +++ gcc/fortran/trans-types.c (revision 279045) @@ -3266,7 +3266,7 @@ gfc_get_array_descr_info (const_tree typ int rank, dim; bool indirect = false; tree etype, ptype, t, base_decl; - tree data_off, dim_off, dtype_off, dim_size, elem_size; + tree data_off, span_off, dim_off, dtype_off, dim_size, elem_size; tree lower_suboff, upper_suboff, stride_suboff; tree dtype, field, rank_off; @@ -3323,12 +3323,13 @@ gfc_get_array_descr_info (const_tree typ if (indirect) base_decl = build1 (INDIRECT_REF, ptype, base_decl); - elem_size = fold_convert (gfc_array_index_type, TYPE_SIZE_UNIT (etype)); - - gfc_get_descriptor_offsets_for_info (type, &data_off, &dtype_off, &dim_off, - &dim_size, &stride_suboff, + gfc_get_descriptor_offsets_for_info (type, &data_off, &dtype_off, &span_off, + &dim_off, &dim_size, &stride_suboff, &lower_suboff, &upper_suboff); + t = fold_build_pointer_plus (base_decl, span_off); + elem_size = build1 (INDIRECT_REF, gfc_array_index_type, t); + t = base_decl; if (!integer_zerop (data_off)) t = fold_build_pointer_plus (t, data_off); --- gcc/fortran/trans.h (revision 279044) +++ gcc/fortran/trans.h (revision 279045) @@ -981,7 +981,6 @@ struct GTY(()) lang_type { tree offset; tree dtype; tree dataptr_type; - tree span; tree base_decl[2]; tree nonrestricted_type; tree caf_token; @@ -997,7 +996,6 @@ struct GTY(()) lang_decl { address of target label. */ tree stringlen; tree addr; - tree span; /* For assumed-shape coarrays. */ tree token, caf_offset; unsigned int scalar_allocatable : 1; @@ -1008,7 +1006,6 @@ struct GTY(()) lang_decl { #define GFC_DECL_ASSIGN_ADDR(node) DECL_LANG_SPECIFIC(node)->addr #define GFC_DECL_STRING_LEN(node) DECL_LANG_SPECIFIC(node)->stringlen -#define GFC_DECL_SPAN(node) DECL_LANG_SPECIFIC(node)->span #define GFC_DECL_TOKEN(node) DECL_LANG_SPECIFIC(node)->token #define GFC_DECL_CAF_OFFSET(node) DECL_LANG_SPECIFIC(node)->caf_offset #define GFC_DECL_SAVED_DESCRIPTOR(node) \ @@ -1059,7 +1056,6 @@ struct GTY(()) lang_decl { #define GFC_TYPE_ARRAY_DTYPE(node) (TYPE_LANG_SPECIFIC(node)->dtype) #define GFC_TYPE_ARRAY_DATAPTR_TYPE(node) \ (TYPE_LANG_SPECIFIC(node)->dataptr_type) -#define GFC_TYPE_ARRAY_SPAN(node) (TYPE_LANG_SPECIFIC(node)->span) #define GFC_TYPE_ARRAY_BASE_DECL(node, internal) \ (TYPE_LANG_SPECIFIC(node)->base_decl[(internal)])
2019-12-20 Jakub Jelinek <ja...@redhat.com> Backported from mainline 2019-12-06 Jakub Jelinek <ja...@redhat.com> PR c++/92831 - CWG 1299, not extending temporary lifetime for ?: * cp-tree.h (extend_ref_init_temps): Add a new argument with NULL default arg. * call.c (set_up_extended_ref_temp): Add COND_GUARD argument, pass it down to extend_ref_init_temps. Before pushing cleanup, if COND_GUARD is non-NULL, create a bool temporary if needed, initialize to false and guard the cleanup with the temporary being true. (extend_ref_init_temps_1): Add COND_GUARD argument, pass it down to recursive calls and set_up_extended_ref_temp. Handle COND_EXPR. (extend_ref_init_temps): Add COND_GUARD argument, pass it down to recursive calls and to extend_ref_init_temps_1. * g++.dg/cpp0x/temp-extend2.C: New test. --- gcc/cp/cp-tree.h (revision 279063) +++ gcc/cp/cp-tree.h (revision 279064) @@ -6321,7 +6321,9 @@ extern tree convert_for_arg_passing (tr extern bool is_properly_derived_from (tree, tree); extern tree initialize_reference (tree, tree, int, tsubst_flags_t); -extern tree extend_ref_init_temps (tree, tree, vec<tree, va_gc>**); +extern tree extend_ref_init_temps (tree, tree, + vec<tree, va_gc>**, + tree * = NULL); extern tree make_temporary_var_for_ref_to_temp (tree, tree); extern bool type_has_extended_temps (tree); extern tree strip_top_quals (tree); --- gcc/cp/call.c (revision 279063) +++ gcc/cp/call.c (revision 279064) @@ -11449,7 +11449,7 @@ make_temporary_var_for_ref_to_temp (tree static tree set_up_extended_ref_temp (tree decl, tree expr, vec<tree, va_gc> **cleanups, - tree *initp) + tree *initp, tree *cond_guard) { tree init; tree type; @@ -11480,7 +11480,8 @@ set_up_extended_ref_temp (tree decl, tre /* Recursively extend temps in this initializer. */ TARGET_EXPR_INITIAL (expr) - = extend_ref_init_temps (decl, TARGET_EXPR_INITIAL (expr), cleanups); + = extend_ref_init_temps (decl, TARGET_EXPR_INITIAL (expr), cleanups, + cond_guard); /* Any reference temp has a non-trivial initializer. */ DECL_NONTRIVIALLY_INITIALIZED_P (var) = true; @@ -11521,7 +11522,29 @@ set_up_extended_ref_temp (tree decl, tre { tree cleanup = cxx_maybe_build_cleanup (var, tf_warning_or_error); if (cleanup) - vec_safe_push (*cleanups, cleanup); + { + if (cond_guard && cleanup != error_mark_node) + { + if (*cond_guard == NULL_TREE) + { + *cond_guard = build_decl (input_location, VAR_DECL, + NULL_TREE, boolean_type_node); + DECL_ARTIFICIAL (*cond_guard) = 1; + DECL_IGNORED_P (*cond_guard) = 1; + DECL_CONTEXT (*cond_guard) = current_function_decl; + layout_decl (*cond_guard, 0); + add_decl_expr (*cond_guard); + tree set = cp_build_modify_expr (UNKNOWN_LOCATION, + *cond_guard, NOP_EXPR, + boolean_false_node, + tf_warning_or_error); + finish_expr_stmt (set); + } + cleanup = build3 (COND_EXPR, void_type_node, + *cond_guard, cleanup, NULL_TREE); + } + vec_safe_push (*cleanups, cleanup); + } } /* We must be careful to destroy the temporary only @@ -11626,7 +11649,8 @@ initialize_reference (tree type, tree ex which is bound either to a reference or a std::initializer_list. */ static tree -extend_ref_init_temps_1 (tree decl, tree init, vec<tree, va_gc> **cleanups) +extend_ref_init_temps_1 (tree decl, tree init, vec<tree, va_gc> **cleanups, + tree *cond_guard) { tree sub = init; tree *p; @@ -11634,20 +11658,52 @@ extend_ref_init_temps_1 (tree decl, tree if (TREE_CODE (sub) == COMPOUND_EXPR) { TREE_OPERAND (sub, 1) - = extend_ref_init_temps_1 (decl, TREE_OPERAND (sub, 1), cleanups); + = extend_ref_init_temps_1 (decl, TREE_OPERAND (sub, 1), cleanups, + cond_guard); + return init; + } + if (TREE_CODE (sub) == COND_EXPR) + { + tree cur_cond_guard = NULL_TREE; + if (TREE_OPERAND (sub, 1)) + TREE_OPERAND (sub, 1) + = extend_ref_init_temps_1 (decl, TREE_OPERAND (sub, 1), cleanups, + &cur_cond_guard); + if (cur_cond_guard) + { + tree set = cp_build_modify_expr (UNKNOWN_LOCATION, cur_cond_guard, + NOP_EXPR, boolean_true_node, + tf_warning_or_error); + TREE_OPERAND (sub, 1) + = cp_build_compound_expr (set, TREE_OPERAND (sub, 1), + tf_warning_or_error); + } + cur_cond_guard = NULL_TREE; + if (TREE_OPERAND (sub, 2)) + TREE_OPERAND (sub, 2) + = extend_ref_init_temps_1 (decl, TREE_OPERAND (sub, 2), cleanups, + &cur_cond_guard); + if (cur_cond_guard) + { + tree set = cp_build_modify_expr (UNKNOWN_LOCATION, cur_cond_guard, + NOP_EXPR, boolean_true_node, + tf_warning_or_error); + TREE_OPERAND (sub, 2) + = cp_build_compound_expr (set, TREE_OPERAND (sub, 2), + tf_warning_or_error); + } return init; } if (TREE_CODE (sub) != ADDR_EXPR) return init; /* Deal with binding to a subobject. */ for (p = &TREE_OPERAND (sub, 0); - (TREE_CODE (*p) == COMPONENT_REF - || TREE_CODE (*p) == ARRAY_REF); ) + TREE_CODE (*p) == COMPONENT_REF || TREE_CODE (*p) == ARRAY_REF; ) p = &TREE_OPERAND (*p, 0); if (TREE_CODE (*p) == TARGET_EXPR) { tree subinit = NULL_TREE; - *p = set_up_extended_ref_temp (decl, *p, cleanups, &subinit); + *p = set_up_extended_ref_temp (decl, *p, cleanups, &subinit, cond_guard); recompute_tree_invariant_for_addr_expr (sub); if (init != sub) init = fold_convert (TREE_TYPE (init), sub); @@ -11662,13 +11718,14 @@ extend_ref_init_temps_1 (tree decl, tree lifetime to match that of DECL. */ tree -extend_ref_init_temps (tree decl, tree init, vec<tree, va_gc> **cleanups) +extend_ref_init_temps (tree decl, tree init, vec<tree, va_gc> **cleanups, + tree *cond_guard) { tree type = TREE_TYPE (init); if (processing_template_decl) return init; if (TYPE_REF_P (type)) - init = extend_ref_init_temps_1 (decl, init, cleanups); + init = extend_ref_init_temps_1 (decl, init, cleanups, cond_guard); else { tree ctor = init; @@ -11681,7 +11738,8 @@ extend_ref_init_temps (tree decl, tree i /* The temporary array underlying a std::initializer_list is handled like a reference temporary. */ tree array = CONSTRUCTOR_ELT (ctor, 0)->value; - array = extend_ref_init_temps_1 (decl, array, cleanups); + array = extend_ref_init_temps_1 (decl, array, cleanups, + cond_guard); CONSTRUCTOR_ELT (ctor, 0)->value = array; } else @@ -11690,7 +11748,8 @@ extend_ref_init_temps (tree decl, tree i constructor_elt *p; vec<constructor_elt, va_gc> *elts = CONSTRUCTOR_ELTS (ctor); FOR_EACH_VEC_SAFE_ELT (elts, i, p) - p->value = extend_ref_init_temps (decl, p->value, cleanups); + p->value = extend_ref_init_temps (decl, p->value, cleanups, + cond_guard); } recompute_constructor_flags (ctor); if (decl_maybe_constant_var_p (decl) && TREE_CONSTANT (ctor)) --- gcc/testsuite/g++.dg/cpp0x/temp-extend2.C (nonexistent) +++ gcc/testsuite/g++.dg/cpp0x/temp-extend2.C (revision 279064) @@ -0,0 +1,36 @@ +// PR c++/92831 +// { dg-do run { target c++11 } } + +template<typename T> using id = T; +struct S { S () { s++; } ~S () { s--; } S (int) { s++; } static int s; }; +int S::s = 0; + +void +bar (bool cond, bool cond2) +{ + if (S::s != (cond ? cond2 ? 7 : 5 : cond2 ? 8 : 9)) + __builtin_abort (); +} + +void +foo (bool cond, bool cond2) +{ + int i = 1; + // temporary array has same lifetime as a + S&& a = id<S[3]>{1, 2, 3}[i]; + // temporary S has same lifetime as b + const S& b = static_cast<const S&>(0); + // exactly one of the four temporaries is lifetime-extended + S&& c = cond ? cond2 ? id<S[3]>{1, 2, 3}[i] : static_cast<S&&>(0) + : cond2 ? id<S[4]>{1, 2, 3, 4}[i] : id<S[5]>{1, 2, 3, 4, 5}[i]; + bar (cond, cond2); +} + +int +main () +{ + foo (true, true); + foo (true, false); + foo (false, true); + foo (false, false); +}
2019-12-20 Jakub Jelinek <ja...@redhat.com> Backported from mainline 2019-12-11 Jakub Jelinek <ja...@redhat.com> PR target/92723 * tree-vect-patterns.c (vect_recog_rotate_pattern): If dt is not vect_internal_def, use oprnd1 as is, without trying to cast it. Formatting fix. * gcc.dg/vect/pr92723.c: New test. --- gcc/tree-vect-patterns.c (revision 279264) +++ gcc/tree-vect-patterns.c (revision 279265) @@ -2070,14 +2070,12 @@ vect_recog_rotate_pattern (stmt_vec_info *type_out = vectype; - if (dt == vect_external_def - && TREE_CODE (oprnd1) == SSA_NAME) + if (dt == vect_external_def && TREE_CODE (oprnd1) == SSA_NAME) ext_def = vect_get_external_def_edge (vinfo, oprnd1); def = NULL_TREE; scalar_int_mode mode = SCALAR_INT_TYPE_MODE (type); - if (TREE_CODE (oprnd1) == INTEGER_CST - || TYPE_MODE (TREE_TYPE (oprnd1)) == mode) + if (dt != vect_internal_def || TYPE_MODE (TREE_TYPE (oprnd1)) == mode) def = oprnd1; else if (def_stmt && gimple_assign_cast_p (def_stmt)) { @@ -2092,14 +2090,7 @@ vect_recog_rotate_pattern (stmt_vec_info { def = vect_recog_temp_ssa_var (type, NULL); def_stmt = gimple_build_assign (def, NOP_EXPR, oprnd1); - if (ext_def) - { - basic_block new_bb - = gsi_insert_on_edge_immediate (ext_def, def_stmt); - gcc_assert (!new_bb); - } - else - append_pattern_def_seq (stmt_vinfo, def_stmt); + append_pattern_def_seq (stmt_vinfo, def_stmt); } stype = TREE_TYPE (def); scalar_int_mode smode = SCALAR_INT_TYPE_MODE (stype); --- gcc/testsuite/gcc.dg/vect/pr92723.c (nonexistent) +++ gcc/testsuite/gcc.dg/vect/pr92723.c (revision 279265) @@ -0,0 +1,9 @@ +/* { dg-do compile } */ + +void +foo (unsigned long long *x, unsigned long long *y, int z) +{ + int i; + for (i = 0; i < 1024; i++) + x[i] = (y[i] >> z) | (y[i] << (-z & (__SIZEOF_LONG_LONG__ * __CHAR_BIT__ - 1))); +}
2019-12-20 Jakub Jelinek <ja...@redhat.com> Backported from mainline 2019-12-11 Jakub Jelinek <ja...@redhat.com> PR fortran/92899 * trans-openmp.c (gfc_trans_omp_atomic): For GFC_OMP_ATOMIC_SWAP, do look through conversion on expr2 if any. * testsuite/libgomp.fortran/atomic1.f90: New test. --- gcc/fortran/trans-openmp.c (revision 279265) +++ gcc/fortran/trans-openmp.c (revision 279266) @@ -3534,7 +3534,6 @@ gfc_trans_omp_atomic (gfc_code *code) expr2 = code->expr2; if (((atomic_code->ext.omp_atomic & GFC_OMP_ATOMIC_MASK) != GFC_OMP_ATOMIC_WRITE) - && (atomic_code->ext.omp_atomic & GFC_OMP_ATOMIC_SWAP) == 0 && expr2->expr_type == EXPR_FUNCTION && expr2->value.function.isym && expr2->value.function.isym->id == GFC_ISYM_CONVERSION) --- libgomp/testsuite/libgomp.fortran/atomic1.f90 (nonexistent) +++ libgomp/testsuite/libgomp.fortran/atomic1.f90 (revision 279266) @@ -0,0 +1,46 @@ +! PR fortran/92899 + +program pr92899 + real :: x = 1.0 + double precision :: y + integer(kind=4) :: z = 4 + integer(kind=8) :: w + !$omp atomic capture + y = x + x = 2.0 + !$omp end atomic + if (y /= 1.0 .or. x /= 2.0) stop 1 + !$omp atomic capture + x = y + y = 3.0 + !$omp end atomic + if (x /= 1.0 .or. y /= 3.0) stop 2 + !$omp atomic capture + w = z + z = 5 + !$omp end atomic + if (w /= 4 .or. z /= 5) stop 3 + !$omp atomic capture + z = w + w = 6 + !$omp end atomic + if (z /= 4 .or. w /= 6) stop 4 + !$omp atomic write + x = y + !$omp end atomic + if (x /= 3.0 .or. y /= 3.0) stop 5 + x = 7.0 + !$omp atomic write + y = x + !$omp end atomic + if (x /= 7.0 .or. y /= 7.0) stop 6 + !$omp atomic write + z = w + !$omp end atomic + if (z /= 6 .or. w /= 6) stop 7 + z = 8 + !$omp atomic write + w = z + !$omp end atomic + if (z /= 8 .or. w /= 8) stop 8 +end
2019-12-20 Jakub Jelinek <ja...@redhat.com> Backported from mainline 2019-12-12 Jakub Jelinek <ja...@redhat.com> PR target/92904 * config/i386/i386.c (ix86_gimplify_va_arg): If need_intregs and not need_temp, decrease alignment of the read because the GPR save area only guarantees 8-byte alignment. * gcc.c-torture/execute/pr92904.c: New test. --- gcc/config/i386/i386.c (revision 279326) +++ gcc/config/i386/i386.c (revision 279327) @@ -4277,6 +4277,7 @@ ix86_gimplify_va_arg (tree valist, tree tree ptrtype; machine_mode nat_mode; unsigned int arg_boundary; + unsigned int type_align; /* Only 64bit target needs something special. */ if (is_va_list_char_pointer (TREE_TYPE (valist))) @@ -4334,6 +4335,7 @@ ix86_gimplify_va_arg (tree valist, tree /* Pull the value out of the saved registers. */ addr = create_tmp_var (ptr_type_node, "addr"); + type_align = TYPE_ALIGN (type); if (container) { @@ -4504,6 +4506,9 @@ ix86_gimplify_va_arg (tree valist, tree t = build2 (PLUS_EXPR, TREE_TYPE (gpr), gpr, build_int_cst (TREE_TYPE (gpr), needed_intregs * 8)); gimplify_assign (gpr, t, pre_p); + /* The GPR save area guarantees only 8-byte alignment. */ + if (!need_temp) + type_align = MIN (type_align, 64); } if (needed_sseregs) @@ -4548,6 +4553,7 @@ ix86_gimplify_va_arg (tree valist, tree if (container) gimple_seq_add_stmt (pre_p, gimple_build_label (lab_over)); + type = build_aligned_type (type, type_align); ptrtype = build_pointer_type_for_mode (type, ptr_mode, true); addr = fold_convert (ptrtype, addr); --- gcc/testsuite/gcc.c-torture/execute/pr92904.c (nonexistent) +++ gcc/testsuite/gcc.c-torture/execute/pr92904.c (revision 279327) @@ -0,0 +1,395 @@ +/* PR target/92904 */ + +#include <stdarg.h> + +struct S { long long a, b; }; +struct __attribute__((aligned (16))) T { long long a, b; }; +struct U { double a, b, c, d; }; +struct __attribute__((aligned (32))) V { double a, b, c, d; }; +struct W { double a; long long b; }; +struct __attribute__((aligned (16))) X { double a; long long b; }; +#if __SIZEOF_INT128__ == 2 * __SIZEOF_LONG_LONG__ +__int128 b; +#endif +struct S c; +struct T d; +struct U e; +struct V f; +struct W g; +struct X h; + +#if __SIZEOF_INT128__ == 2 * __SIZEOF_LONG_LONG__ +__attribute__((noipa)) __int128 +f1 (int x, ...) +{ + __int128 r; + va_list ap; + va_start (ap, x); + while (x--) + va_arg (ap, int); + r = va_arg (ap, __int128); + va_end (ap); + return r; +} +#endif + +__attribute__((noipa)) struct S +f2 (int x, ...) +{ + struct S r; + va_list ap; + va_start (ap, x); + while (x--) + va_arg (ap, int); + r = va_arg (ap, struct S); + va_end (ap); + return r; +} + +__attribute__((noipa)) struct T +f3 (int x, ...) +{ + struct T r; + va_list ap; + va_start (ap, x); + while (x--) + va_arg (ap, int); + r = va_arg (ap, struct T); + va_end (ap); + return r; +} + +#if __SIZEOF_INT128__ == 2 * __SIZEOF_LONG_LONG__ +__attribute__((noipa)) void +f4 (int x, ...) +{ + va_list ap; + va_start (ap, x); + while (x--) + va_arg (ap, int); + b = va_arg (ap, __int128); + va_end (ap); +} +#endif + +__attribute__((noipa)) void +f5 (int x, ...) +{ + va_list ap; + va_start (ap, x); + while (x--) + va_arg (ap, int); + c = va_arg (ap, struct S); + va_end (ap); +} + +__attribute__((noipa)) void +f6 (int x, ...) +{ + va_list ap; + va_start (ap, x); + while (x--) + va_arg (ap, int); + d = va_arg (ap, struct T); + va_end (ap); +} + +__attribute__((noipa)) struct U +f7 (int x, ...) +{ + struct U r; + va_list ap; + va_start (ap, x); + while (x--) + va_arg (ap, double); + r = va_arg (ap, struct U); + va_end (ap); + return r; +} + +__attribute__((noipa)) struct V +f8 (int x, ...) +{ + struct V r; + va_list ap; + va_start (ap, x); + while (x--) + va_arg (ap, double); + r = va_arg (ap, struct V); + va_end (ap); + return r; +} + +__attribute__((noipa)) void +f9 (int x, ...) +{ + va_list ap; + va_start (ap, x); + while (x--) + va_arg (ap, double); + e = va_arg (ap, struct U); + va_end (ap); +} + +__attribute__((noipa)) void +f10 (int x, ...) +{ + va_list ap; + va_start (ap, x); + while (x--) + va_arg (ap, double); + f = va_arg (ap, struct V); + va_end (ap); +} + +__attribute__((noipa)) struct W +f11 (int x, ...) +{ + struct W r; + va_list ap; + va_start (ap, x); + while (x--) + { + va_arg (ap, int); + va_arg (ap, double); + } + r = va_arg (ap, struct W); + va_end (ap); + return r; +} + +__attribute__((noipa)) struct X +f12 (int x, ...) +{ + struct X r; + va_list ap; + va_start (ap, x); + while (x--) + { + va_arg (ap, int); + va_arg (ap, double); + } + r = va_arg (ap, struct X); + va_end (ap); + return r; +} + +__attribute__((noipa)) void +f13 (int x, ...) +{ + va_list ap; + va_start (ap, x); + while (x--) + { + va_arg (ap, int); + va_arg (ap, double); + } + g = va_arg (ap, struct W); + va_end (ap); +} + +__attribute__((noipa)) void +f14 (int x, ...) +{ + va_list ap; + va_start (ap, x); + while (x--) + { + va_arg (ap, int); + va_arg (ap, double); + } + h = va_arg (ap, struct X); + va_end (ap); +} + +int +main () +{ + union Y { +#if __SIZEOF_INT128__ == 2 * __SIZEOF_LONG_LONG__ + __int128 b; +#endif + struct S c; + struct T d; + struct U e; + struct V f; + struct W g; + struct X h; + } u, v; + u.c.a = 0x5555555555555555ULL; + u.c.b = 0xaaaaaaaaaaaaaaaaULL; +#define C(x) \ + do { \ + if (u.c.a != x.c.a || u.c.b != x.c.b) __builtin_abort (); \ + u.c.a++; \ + u.c.b--; \ + } while (0) +#if __SIZEOF_INT128__ == 2 * __SIZEOF_LONG_LONG__ + v.b = f1 (0, u.b); C (v); + v.b = f1 (1, 0, u.b); C (v); + v.b = f1 (2, 0, 0, u.b); C (v); + v.b = f1 (3, 0, 0, 0, u.b); C (v); + v.b = f1 (4, 0, 0, 0, 0, u.b); C (v); + v.b = f1 (5, 0, 0, 0, 0, 0, u.b); C (v); + v.b = f1 (6, 0, 0, 0, 0, 0, 0, u.b); C (v); + v.b = f1 (7, 0, 0, 0, 0, 0, 0, 0, u.b); C (v); + v.b = f1 (8, 0, 0, 0, 0, 0, 0, 0, 0, u.b); C (v); + v.b = f1 (9, 0, 0, 0, 0, 0, 0, 0, 0, 0, u.b); C (v); +#endif + v.c = f2 (0, u.c); C (v); + v.c = f2 (1, 0, u.c); C (v); + v.c = f2 (2, 0, 0, u.c); C (v); + v.c = f2 (3, 0, 0, 0, u.c); C (v); + v.c = f2 (4, 0, 0, 0, 0, u.c); C (v); + v.c = f2 (5, 0, 0, 0, 0, 0, u.c); C (v); + v.c = f2 (6, 0, 0, 0, 0, 0, 0, u.c); C (v); + v.c = f2 (7, 0, 0, 0, 0, 0, 0, 0, u.c); C (v); + v.c = f2 (8, 0, 0, 0, 0, 0, 0, 0, 0, u.c); C (v); + v.c = f2 (9, 0, 0, 0, 0, 0, 0, 0, 0, 0, u.c); C (v); + v.d = f3 (0, u.d); C (v); + v.d = f3 (1, 0, u.d); C (v); + v.d = f3 (2, 0, 0, u.d); C (v); + v.d = f3 (3, 0, 0, 0, u.d); C (v); + v.d = f3 (4, 0, 0, 0, 0, u.d); C (v); + v.d = f3 (5, 0, 0, 0, 0, 0, u.d); C (v); + v.d = f3 (6, 0, 0, 0, 0, 0, 0, u.d); C (v); + v.d = f3 (7, 0, 0, 0, 0, 0, 0, 0, u.d); C (v); + v.d = f3 (8, 0, 0, 0, 0, 0, 0, 0, 0, u.d); C (v); + v.d = f3 (9, 0, 0, 0, 0, 0, 0, 0, 0, 0, u.d); C (v); +#if __SIZEOF_INT128__ == 2 * __SIZEOF_LONG_LONG__ + f4 (0, u.b); v.b = b; C (v); + f4 (1, 0, u.b); v.b = b; C (v); + f4 (2, 0, 0, u.b); v.b = b; C (v); + f4 (3, 0, 0, 0, u.b); v.b = b; C (v); + f4 (4, 0, 0, 0, 0, u.b); v.b = b; C (v); + f4 (5, 0, 0, 0, 0, 0, u.b); v.b = b; C (v); + f4 (6, 0, 0, 0, 0, 0, 0, u.b); v.b = b; C (v); + f4 (7, 0, 0, 0, 0, 0, 0, 0, u.b); v.b = b; C (v); + f4 (8, 0, 0, 0, 0, 0, 0, 0, 0, u.b); v.b = b; C (v); + f4 (9, 0, 0, 0, 0, 0, 0, 0, 0, 0, u.b); v.b = b; C (v); +#endif + f5 (0, u.c); v.c = c; C (v); + f5 (1, 0, u.c); v.c = c; C (v); + f5 (2, 0, 0, u.c); v.c = c; C (v); + f5 (3, 0, 0, 0, u.c); v.c = c; C (v); + f5 (4, 0, 0, 0, 0, u.c); v.c = c; C (v); + f5 (5, 0, 0, 0, 0, 0, u.c); v.c = c; C (v); + f5 (6, 0, 0, 0, 0, 0, 0, u.c); v.c = c; C (v); + f5 (7, 0, 0, 0, 0, 0, 0, 0, u.c); v.c = c; C (v); + f5 (8, 0, 0, 0, 0, 0, 0, 0, 0, u.c); v.c = c; C (v); + f5 (9, 0, 0, 0, 0, 0, 0, 0, 0, 0, u.c); v.c = c; C (v); + f6 (0, u.d); v.d = d; C (v); + f6 (1, 0, u.d); v.d = d; C (v); + f6 (2, 0, 0, u.d); v.d = d; C (v); + f6 (3, 0, 0, 0, u.d); v.d = d; C (v); + f6 (4, 0, 0, 0, 0, u.d); v.d = d; C (v); + f6 (5, 0, 0, 0, 0, 0, u.d); v.d = d; C (v); + f6 (6, 0, 0, 0, 0, 0, 0, u.d); v.d = d; C (v); + f6 (7, 0, 0, 0, 0, 0, 0, 0, u.d); v.d = d; C (v); + f6 (8, 0, 0, 0, 0, 0, 0, 0, 0, u.d); v.d = d; C (v); + f6 (9, 0, 0, 0, 0, 0, 0, 0, 0, 0, u.d); v.d = d; C (v); + u.e.a = 1.25; + u.e.b = 2.75; + u.e.c = -3.5; + u.e.d = -2.0; +#undef C +#define C(x) \ + do { \ + if (u.e.a != x.e.a || u.e.b != x.e.b \ + || u.e.c != x.e.c || u.e.d != x.e.d) __builtin_abort ();\ + u.e.a++; \ + u.e.b--; \ + u.e.c++; \ + u.e.d--; \ + } while (0) + v.e = f7 (0, u.e); C (v); + v.e = f7 (1, 0.0, u.e); C (v); + v.e = f7 (2, 0.0, 0.0, u.e); C (v); + v.e = f7 (3, 0.0, 0.0, 0.0, u.e); C (v); + v.e = f7 (4, 0.0, 0.0, 0.0, 0.0, u.e); C (v); + v.e = f7 (5, 0.0, 0.0, 0.0, 0.0, 0.0, u.e); C (v); + v.e = f7 (6, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, u.e); C (v); + v.e = f7 (7, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, u.e); C (v); + v.e = f7 (8, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, u.e); C (v); + v.e = f7 (9, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, u.e); C (v); + v.f = f8 (0, u.f); C (v); + v.f = f8 (1, 0.0, u.f); C (v); + v.f = f8 (2, 0.0, 0.0, u.f); C (v); + v.f = f8 (3, 0.0, 0.0, 0.0, u.f); C (v); + v.f = f8 (4, 0.0, 0.0, 0.0, 0.0, u.f); C (v); + v.f = f8 (5, 0.0, 0.0, 0.0, 0.0, 0.0, u.f); C (v); + v.f = f8 (6, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, u.f); C (v); + v.f = f8 (7, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, u.f); C (v); + v.f = f8 (8, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, u.f); C (v); + v.f = f8 (9, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, u.f); C (v); + f9 (0, u.e); v.e = e; C (v); + f9 (1, 0.0, u.e); v.e = e; C (v); + f9 (2, 0.0, 0.0, u.e); v.e = e; C (v); + f9 (3, 0.0, 0.0, 0.0, u.e); v.e = e; C (v); + f9 (4, 0.0, 0.0, 0.0, 0.0, u.e); v.e = e; C (v); + f9 (5, 0.0, 0.0, 0.0, 0.0, 0.0, u.e); v.e = e; C (v); + f9 (6, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, u.e); v.e = e; C (v); + f9 (7, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, u.e); v.e = e; C (v); + f9 (8, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, u.e); v.e = e; C (v); + f9 (9, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, u.e); v.e = e; C (v); + f10 (0, u.f); v.f = f; C (v); + f10 (1, 0.0, u.f); v.f = f; C (v); + f10 (2, 0.0, 0.0, u.f); v.f = f; C (v); + f10 (3, 0.0, 0.0, 0.0, u.f); v.f = f; C (v); + f10 (4, 0.0, 0.0, 0.0, 0.0, u.f); v.f = f; C (v); + f10 (5, 0.0, 0.0, 0.0, 0.0, 0.0, u.f); v.f = f; C (v); + f10 (6, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, u.f); v.f = f; C (v); + f10 (7, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, u.f); v.f = f; C (v); + f10 (8, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, u.f); v.f = f; C (v); + f10 (9, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, u.f); v.f = f; C (v); + u.g.a = 9.5; + u.g.b = 0x5555555555555555ULL; +#undef C +#define C(x) \ + do { \ + if (u.e.a != x.e.a || u.e.b != x.e.b) __builtin_abort (); \ + u.e.a++; \ + u.e.b--; \ + } while (0) + v.g = f11 (0, u.g); C (v); + v.g = f11 (1, 0, 0.0, u.g); C (v); + v.g = f11 (2, 0, 0.0, 0, 0.0, u.g); C (v); + v.g = f11 (3, 0, 0.0, 0, 0.0, 0, 0.0, u.g); C (v); + v.g = f11 (4, 0, 0.0, 0, 0.0, 0, 0.0, 0, 0.0, u.g); C (v); + v.g = f11 (5, 0, 0.0, 0, 0.0, 0, 0.0, 0, 0.0, 0, 0.0, u.g); C (v); + v.g = f11 (6, 0, 0.0, 0, 0.0, 0, 0.0, 0, 0.0, 0, 0.0, 0, 0.0, u.g); C (v); + v.g = f11 (7, 0, 0.0, 0, 0.0, 0, 0.0, 0, 0.0, 0, 0.0, 0, 0.0, 0, 0.0, u.g); C (v); + v.g = f11 (8, 0, 0.0, 0, 0.0, 0, 0.0, 0, 0.0, 0, 0.0, 0, 0.0, 0, 0.0, 0, 0.0, u.g); C (v); + v.g = f11 (9, 0, 0.0, 0, 0.0, 0, 0.0, 0, 0.0, 0, 0.0, 0, 0.0, 0, 0.0, 0, 0.0, 0, 0.0, u.g); C (v); + v.h = f12 (0, u.h); C (v); + v.h = f12 (1, 0, 0.0, u.h); C (v); + v.h = f12 (2, 0, 0.0, 0, 0.0, u.h); C (v); + v.h = f12 (3, 0, 0.0, 0, 0.0, 0, 0.0, u.h); C (v); + v.h = f12 (4, 0, 0.0, 0, 0.0, 0, 0.0, 0, 0.0, u.h); C (v); + v.h = f12 (5, 0, 0.0, 0, 0.0, 0, 0.0, 0, 0.0, 0, 0.0, u.h); C (v); + v.h = f12 (6, 0, 0.0, 0, 0.0, 0, 0.0, 0, 0.0, 0, 0.0, 0, 0.0, u.h); C (v); + v.h = f12 (7, 0, 0.0, 0, 0.0, 0, 0.0, 0, 0.0, 0, 0.0, 0, 0.0, 0, 0.0, u.h); C (v); + v.h = f12 (8, 0, 0.0, 0, 0.0, 0, 0.0, 0, 0.0, 0, 0.0, 0, 0.0, 0, 0.0, 0, 0.0, u.h); C (v); + v.h = f12 (9, 0, 0.0, 0, 0.0, 0, 0.0, 0, 0.0, 0, 0.0, 0, 0.0, 0, 0.0, 0, 0.0, 0, 0.0, u.h); C (v); + f13 (0, u.g); v.g = g; C (v); + f13 (1, 0, 0.0, u.g); v.g = g; C (v); + f13 (2, 0, 0.0, 0, 0.0, u.g); v.g = g; C (v); + f13 (3, 0, 0.0, 0, 0.0, 0, 0.0, u.g); v.g = g; C (v); + f13 (4, 0, 0.0, 0, 0.0, 0, 0.0, 0, 0.0, u.g); v.g = g; C (v); + f13 (5, 0, 0.0, 0, 0.0, 0, 0.0, 0, 0.0, 0, 0.0, u.g); v.g = g; C (v); + f13 (6, 0, 0.0, 0, 0.0, 0, 0.0, 0, 0.0, 0, 0.0, 0, 0.0, u.g); v.g = g; C (v); + f13 (7, 0, 0.0, 0, 0.0, 0, 0.0, 0, 0.0, 0, 0.0, 0, 0.0, 0, 0.0, u.g); v.g = g; C (v); + f13 (8, 0, 0.0, 0, 0.0, 0, 0.0, 0, 0.0, 0, 0.0, 0, 0.0, 0, 0.0, 0, 0.0, u.g); v.g = g; C (v); + f13 (9, 0, 0.0, 0, 0.0, 0, 0.0, 0, 0.0, 0, 0.0, 0, 0.0, 0, 0.0, 0, 0.0, 0, 0.0, u.g); v.g = g; C (v); + f14 (0, u.h); v.h = h; C (v); + f14 (1, 0, 0.0, u.h); v.h = h; C (v); + f14 (2, 0, 0.0, 0, 0.0, u.h); v.h = h; C (v); + f14 (3, 0, 0.0, 0, 0.0, 0, 0.0, u.h); v.h = h; C (v); + f14 (4, 0, 0.0, 0, 0.0, 0, 0.0, 0, 0.0, u.h); v.h = h; C (v); + f14 (5, 0, 0.0, 0, 0.0, 0, 0.0, 0, 0.0, 0, 0.0, u.h); v.h = h; C (v); + f14 (6, 0, 0.0, 0, 0.0, 0, 0.0, 0, 0.0, 0, 0.0, 0, 0.0, u.h); v.h = h; C (v); + f14 (7, 0, 0.0, 0, 0.0, 0, 0.0, 0, 0.0, 0, 0.0, 0, 0.0, 0, 0.0, u.h); v.h = h; C (v); + f14 (8, 0, 0.0, 0, 0.0, 0, 0.0, 0, 0.0, 0, 0.0, 0, 0.0, 0, 0.0, 0, 0.0, u.h); v.h = h; C (v); + f14 (9, 0, 0.0, 0, 0.0, 0, 0.0, 0, 0.0, 0, 0.0, 0, 0.0, 0, 0.0, 0, 0.0, 0, 0.0, u.h); v.h = h; C (v); + return 0; +}
2019-12-20 Jakub Jelinek <ja...@redhat.com> Backported from mainline 2019-12-14 Jakub Jelinek <ja...@redhat.com> PR tree-optimization/92930 * ipa-pure-const.c (special_builtin_state): Don't handle BUILT_IN_APPLY. * gcc.dg/tree-ssa/pr92930.c: New test. --- gcc/ipa-pure-const.c (revision 279393) +++ gcc/ipa-pure-const.c (revision 279394) @@ -527,7 +527,6 @@ special_builtin_state (enum pure_const_s case BUILT_IN_CXA_END_CLEANUP: case BUILT_IN_EH_COPY_VALUES: case BUILT_IN_FRAME_ADDRESS: - case BUILT_IN_APPLY: case BUILT_IN_APPLY_ARGS: case BUILT_IN_ASAN_BEFORE_DYNAMIC_INIT: case BUILT_IN_ASAN_AFTER_DYNAMIC_INIT: --- gcc/testsuite/gcc.dg/tree-ssa/pr92930.c (nonexistent) +++ gcc/testsuite/gcc.dg/tree-ssa/pr92930.c (revision 279394) @@ -0,0 +1,19 @@ +/* PR tree-optimization/92930 */ +/* { dg-do compile { target untyped_assembly } } */ +/* { dg-options "-O2 -fdump-tree-optimized" } */ +/* { dg-final { scan-tree-dump "__builtin_apply " "optimized" } } */ +/* { dg-final { scan-tree-dump "__builtin_apply_args" "optimized" } } */ + +void foo (int a, int b, int c, int d, int e, int f, int g); + +static void bar (int a, ...) +{ + __builtin_apply (foo, __builtin_apply_args (), 20); +} + +int +main () +{ + bar (1024, 1025, 1026, 1027, 1028, 1029, 1030); + return 0; +}
2019-12-20 Jakub Jelinek <ja...@redhat.com> Backported from mainline 2019-12-14 Jakub Jelinek <ja...@redhat.com> PR ipa/92357 * ipa-fnsummary.c (ipa_fn_summary_write): Use lto_symtab_encoder_iterator with lsei_start_function_in_partition and lsei_next_function_in_partition instead of walking all cgraph nodes in encoder. --- gcc/ipa-fnsummary.c (revision 279394) +++ gcc/ipa-fnsummary.c (revision 279395) @@ -3452,24 +3452,24 @@ static void ipa_fn_summary_write (void) { struct output_block *ob = create_output_block (LTO_section_ipa_fn_summary); + lto_symtab_encoder_iterator lsei; lto_symtab_encoder_t encoder = ob->decl_state->symtab_node_encoder; unsigned int count = 0; - int i; - for (i = 0; i < lto_symtab_encoder_size (encoder); i++) + for (lsei = lsei_start_function_in_partition (encoder); !lsei_end_p (lsei); + lsei_next_function_in_partition (&lsei)) { - symtab_node *snode = lto_symtab_encoder_deref (encoder, i); - cgraph_node *cnode = dyn_cast <cgraph_node *> (snode); - if (cnode && cnode->definition && !cnode->alias) + cgraph_node *cnode = lsei_cgraph_node (lsei); + if (cnode->definition && !cnode->alias) count++; } streamer_write_uhwi (ob, count); - for (i = 0; i < lto_symtab_encoder_size (encoder); i++) + for (lsei = lsei_start_function_in_partition (encoder); !lsei_end_p (lsei); + lsei_next_function_in_partition (&lsei)) { - symtab_node *snode = lto_symtab_encoder_deref (encoder, i); - cgraph_node *cnode = dyn_cast <cgraph_node *> (snode); - if (cnode && cnode->definition && !cnode->alias) + cgraph_node *cnode = lsei_cgraph_node (lsei); + if (cnode->definition && !cnode->alias) { struct ipa_fn_summary *info = ipa_fn_summaries->get (cnode); struct bitpack_d bp;
2019-12-20 Jakub Jelinek <ja...@redhat.com> Backported from mainline 2019-12-19 Jakub Jelinek <ja...@redhat.com> PR fortran/92977 * frontend-passes.c (call_external_blas): Use || instead of |. PR fortran/92977 * frontend-passes.c (in_omp_atomic): New variable. (cfe_expr_0, matmul_to_var_expr, matmul_temp_args, inline_matmul_assign, call_external_blas): Don't optimize in EXEC_OMP_ATOMIC. (optimize_namespace): Clear in_omp_atomic. (gfc_code_walker): Set in_omp_atomic for EXEC_OMP_ATOMIC, save/restore it around. * gfortran.dg/gomp/pr92977.f90: New test. --- gcc/fortran/frontend-passes.c (revision 279553) +++ gcc/fortran/frontend-passes.c (revision 279554) @@ -92,6 +92,10 @@ static int forall_level; static bool in_omp_workshare; +/* Keep track of whether we are within an OMP atomic. */ + +static bool in_omp_atomic; + /* Keep track of whether we are within a WHERE statement. */ static bool in_where; @@ -913,9 +917,9 @@ cfe_expr_0 (gfc_expr **e, int *walk_subt gfc_expr *newvar; gfc_expr **ei, **ej; - /* Don't do this optimization within OMP workshare or ASSOC lists. */ + /* Don't do this optimization within OMP workshare/atomic or ASSOC lists. */ - if (in_omp_workshare || in_assoc_list) + if (in_omp_workshare || in_omp_atomic || in_assoc_list) { *walk_subtrees = 0; return 0; @@ -1464,6 +1468,7 @@ optimize_namespace (gfc_namespace *ns) iterator_level = 0; in_assoc_list = false; in_omp_workshare = false; + in_omp_atomic = false; if (flag_frontend_optimize) { @@ -2818,7 +2823,7 @@ matmul_to_var_expr (gfc_expr **ep, int * return 0; if (forall_level > 0 || iterator_level > 0 || in_omp_workshare - || in_where || in_assoc_list) + || in_omp_atomic || in_where || in_assoc_list) return 0; /* Check if this is already in the form c = matmul(a,b). */ @@ -2880,7 +2885,7 @@ matmul_temp_args (gfc_code **c, int *wal return 0; if (forall_level > 0 || iterator_level > 0 || in_omp_workshare - || in_where) + || in_omp_atomic || in_where) return 0; /* This has some duplication with inline_matmul_assign. This @@ -3848,7 +3853,7 @@ inline_matmul_assign (gfc_code **c, int /* For now don't do anything in OpenMP workshare, it confuses its translation, which expects only the allowed statements in there. We should figure out how to parallelize this eventually. */ - if (in_omp_workshare) + if (in_omp_workshare || in_omp_atomic) return 0; expr1 = co->expr1; @@ -4385,7 +4390,7 @@ call_external_blas (gfc_code **c, int *w /* For now don't do anything in OpenMP workshare, it confuses its translation, which expects only the allowed statements in there. */ - if (in_omp_workshare) + if (in_omp_workshare || in_omp_atomic) return 0; expr1 = co->expr1; @@ -5047,6 +5052,7 @@ gfc_code_walker (gfc_code **c, walk_code gfc_code *co; gfc_association_list *alist; bool saved_in_omp_workshare; + bool saved_in_omp_atomic; bool saved_in_where; /* There might be statement insertions before the current code, @@ -5054,6 +5060,7 @@ gfc_code_walker (gfc_code **c, walk_code co = *c; saved_in_omp_workshare = in_omp_workshare; + saved_in_omp_atomic = in_omp_atomic; saved_in_where = in_where; switch (co->op) @@ -5251,6 +5258,10 @@ gfc_code_walker (gfc_code **c, walk_code WALK_SUBEXPR (co->ext.dt->extra_comma); break; + case EXEC_OMP_ATOMIC: + in_omp_atomic = true; + break; + case EXEC_OMP_PARALLEL: case EXEC_OMP_PARALLEL_DO: case EXEC_OMP_PARALLEL_DO_SIMD: @@ -5368,6 +5379,7 @@ gfc_code_walker (gfc_code **c, walk_code select_level --; in_omp_workshare = saved_in_omp_workshare; + in_omp_atomic = saved_in_omp_atomic; in_where = saved_in_where; } } --- gcc/testsuite/gfortran.dg/gomp/pr92977.f90 (nonexistent) +++ gcc/testsuite/gfortran.dg/gomp/pr92977.f90 (revision 279554) @@ -0,0 +1,15 @@ +! PR fortran/92977 +! { dg-do compile } +! { dg-additional-options "-O2" } + +program pr92977 + integer :: n = 1 + integer :: a +!$omp atomic write + a = f(n) - f(n) +contains + integer function f(x) + integer, intent(in) :: x + f = x + end +end