On Tuesday, 7 September 2021 19:36:22 CEST Jason Merrill wrote: > > case PAREN_EXPR: > > - RETURN (finish_parenthesized_expr (RECUR (TREE_OPERAND (t, 0)))); > > + if (REF_PARENTHESIZED_P (t)) > > + RETURN (finish_parenthesized_expr (RECUR (TREE_OPERAND (t, 0)))); > > + else > > + RETURN (RECUR (TREE_OPERAND (t, 0))); > > I think you need to build a new PAREN_EXPR in the assoc barrier case as > well, for it to have any effect in templates.
My intent was to ignore __builtin_assoc_barrier in templates / constexpr evaluation since it's not affected by -fassociative-math anyway. Or do you mean something else? > Please also add a comment mentioning __builtin_assoc_barrier. I added a comment to that effect to both the cp/pt.c and cp/constexpr.c changes. New patch attached. -- ────────────────────────────────────────────────────────────────────────── Dr. Matthias Kretz https://mattkretz.github.io GSI Helmholtz Centre for Heavy Ion Research https://gsi.de stdₓ::simd ──────────────────────────────────────────────────────────────────────────
diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c index 681fcc972f4..c62a6398a47 100644 --- a/gcc/c-family/c-common.c +++ b/gcc/c-family/c-common.c @@ -384,6 +384,7 @@ const struct c_common_resword c_common_reswords[] = { "__builtin_convertvector", RID_BUILTIN_CONVERTVECTOR, 0 }, { "__builtin_has_attribute", RID_BUILTIN_HAS_ATTRIBUTE, 0 }, { "__builtin_launder", RID_BUILTIN_LAUNDER, D_CXXONLY }, + { "__builtin_assoc_barrier", RID_BUILTIN_ASSOC_BARRIER, 0 }, { "__builtin_shuffle", RID_BUILTIN_SHUFFLE, 0 }, { "__builtin_shufflevector", RID_BUILTIN_SHUFFLEVECTOR, 0 }, { "__builtin_tgmath", RID_BUILTIN_TGMATH, D_CONLY }, diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h index 50ca8fb6ebd..f34dc47c2ba 100644 --- a/gcc/c-family/c-common.h +++ b/gcc/c-family/c-common.h @@ -108,7 +108,7 @@ enum rid RID_EXTENSION, RID_IMAGPART, RID_REALPART, RID_LABEL, RID_CHOOSE_EXPR, RID_TYPES_COMPATIBLE_P, RID_BUILTIN_COMPLEX, RID_BUILTIN_SHUFFLE, RID_BUILTIN_SHUFFLEVECTOR, RID_BUILTIN_CONVERTVECTOR, RID_BUILTIN_TGMATH, - RID_BUILTIN_HAS_ATTRIBUTE, + RID_BUILTIN_HAS_ATTRIBUTE, RID_BUILTIN_ASSOC_BARRIER, RID_DFLOAT32, RID_DFLOAT64, RID_DFLOAT128, /* TS 18661-3 keywords, in the same sequence as the TI_* values. */ diff --git a/gcc/c/c-decl.c b/gcc/c/c-decl.c index 983d65e930c..dcf4a2d7c32 100644 --- a/gcc/c/c-decl.c +++ b/gcc/c/c-decl.c @@ -10557,6 +10557,7 @@ names_builtin_p (const char *name) case RID_BUILTIN_HAS_ATTRIBUTE: case RID_BUILTIN_SHUFFLE: case RID_BUILTIN_SHUFFLEVECTOR: + case RID_BUILTIN_ASSOC_BARRIER: case RID_CHOOSE_EXPR: case RID_OFFSETOF: case RID_TYPES_COMPATIBLE_P: diff --git a/gcc/c/c-parser.c b/gcc/c/c-parser.c index 9a56e0c04c6..fffd81f4e5b 100644 --- a/gcc/c/c-parser.c +++ b/gcc/c/c-parser.c @@ -8931,6 +8931,7 @@ c_parser_predefined_identifier (c_parser *parser) assignment-expression , assignment-expression, ) __builtin_convertvector ( assignment-expression , type-name ) + __builtin_assoc_barrier ( assignment-expression ) offsetof-member-designator: identifier @@ -10076,6 +10077,25 @@ c_parser_postfix_expression (c_parser *parser) } } break; + case RID_BUILTIN_ASSOC_BARRIER: + { + location_t start_loc = loc; + c_parser_consume_token (parser); + matching_parens parens; + if (!parens.require_open (parser)) + { + expr.set_error (); + break; + } + e1 = c_parser_expr_no_commas (parser, NULL); + mark_exp_read (e1.value); + location_t end_loc = c_parser_peek_token (parser)->get_finish (); + parens.skip_until_found_close (parser); + expr.value = build1_loc (loc, PAREN_EXPR, TREE_TYPE (e1.value), + e1.value); + set_c_expr_source_range (&expr, start_loc, end_loc); + } + break; case RID_AT_SELECTOR: { gcc_assert (c_dialect_objc ()); diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c index 31fa5b66865..6e964837d24 100644 --- a/gcc/cp/constexpr.c +++ b/gcc/cp/constexpr.c @@ -6730,6 +6730,14 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, non_constant_p, overflow_p); break; + case PAREN_EXPR: + gcc_assert (!REF_PARENTHESIZED_P (t)); + /* A PAREN_EXPR resulting from __builtin_assoc_barrier has no effect in + constant expressions since it's unaffected by -fassociative-math. */ + r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0), lval, + non_constant_p, overflow_p); + break; + case NOP_EXPR: if (REINTERPRET_CAST_P (t)) { diff --git a/gcc/cp/cp-objcp-common.c b/gcc/cp/cp-objcp-common.c index ee255732d5a..04522a23eda 100644 --- a/gcc/cp/cp-objcp-common.c +++ b/gcc/cp/cp-objcp-common.c @@ -395,6 +395,7 @@ names_builtin_p (const char *name) case RID_BUILTIN_SHUFFLE: case RID_BUILTIN_SHUFFLEVECTOR: case RID_BUILTIN_LAUNDER: + case RID_BUILTIN_ASSOC_BARRIER: case RID_BUILTIN_BIT_CAST: case RID_OFFSETOF: case RID_HAS_NOTHROW_ASSIGN: diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 792849c361c..354f9ca4f26 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -475,7 +475,8 @@ extern GTY(()) tree cp_global_trees[CPTI_MAX]; TARGET_EXPR_DIRECT_INIT_P (in TARGET_EXPR) FNDECL_USED_AUTO (in FUNCTION_DECL) DECLTYPE_FOR_LAMBDA_PROXY (in DECLTYPE_TYPE) - REF_PARENTHESIZED_P (in COMPONENT_REF, INDIRECT_REF, SCOPE_REF, VIEW_CONVERT_EXPR) + REF_PARENTHESIZED_P (in COMPONENT_REF, INDIRECT_REF, SCOPE_REF, + VIEW_CONVERT_EXPR, PAREN_EXPR) AGGR_INIT_ZERO_FIRST (in AGGR_INIT_EXPR) CONSTRUCTOR_MUTABLE_POISON (in CONSTRUCTOR) OVL_HIDDEN_P (in OVERLOAD) @@ -4026,12 +4027,13 @@ struct GTY(()) lang_decl { #define PAREN_STRING_LITERAL_P(NODE) \ TREE_LANG_FLAG_0 (STRING_CST_CHECK (NODE)) -/* Indicates whether a COMPONENT_REF or a SCOPE_REF has been parenthesized, or - an INDIRECT_REF comes from parenthesizing a _DECL. Currently only set some - of the time in C++14 mode. */ +/* Indicates whether a COMPONENT_REF or a SCOPE_REF has been parenthesized, an + INDIRECT_REF comes from parenthesizing a _DECL, or a PAREN_EXPR identifies a + parenthesized initializer relevant for decltype(auto). Currently only set + some of the time in C++14 mode. */ #define REF_PARENTHESIZED_P(NODE) \ - TREE_LANG_FLAG_2 (TREE_CHECK4 ((NODE), COMPONENT_REF, INDIRECT_REF, SCOPE_REF, VIEW_CONVERT_EXPR)) + TREE_LANG_FLAG_2 (TREE_CHECK5 ((NODE), COMPONENT_REF, INDIRECT_REF, SCOPE_REF, VIEW_CONVERT_EXPR, PAREN_EXPR)) /* Nonzero if this AGGR_INIT_EXPR provides for initialization via a constructor call, rather than an ordinary function call. */ diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c index 8fcd9840ea5..ec5f42e5038 100644 --- a/gcc/cp/parser.c +++ b/gcc/cp/parser.c @@ -7316,6 +7316,7 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p, case RID_BUILTIN_SHUFFLE: case RID_BUILTIN_SHUFFLEVECTOR: case RID_BUILTIN_LAUNDER: + case RID_BUILTIN_ASSOC_BARRIER: { vec<tree, va_gc> *vec; @@ -7358,6 +7359,19 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p, } break; + case RID_BUILTIN_ASSOC_BARRIER: + if (vec->length () == 1) + postfix_expression = build1_loc (loc, PAREN_EXPR, + TREE_TYPE ((*vec)[0]), + (*vec)[0]); + else + { + error_at (loc, "wrong number of arguments to " + "%<__builtin_assoc_barrier%>"); + postfix_expression = error_mark_node; + } + break; + case RID_BUILTIN_SHUFFLE: if (vec->length () == 2) postfix_expression diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index c7bf7d412ca..0e9c151aced 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -20826,7 +20826,12 @@ tsubst_copy_and_build (tree t, integral_constant_expression_p)); case PAREN_EXPR: - RETURN (finish_parenthesized_expr (RECUR (TREE_OPERAND (t, 0)))); + if (REF_PARENTHESIZED_P (t)) + RETURN (finish_parenthesized_expr (RECUR (TREE_OPERAND (t, 0)))); + else + /* A PAREN_EXPR resulting from __builtin_assoc_barrier has no effect in + tsubst since it's unaffected by -fassociative-math. */ + RETURN (RECUR (TREE_OPERAND (t, 0))); case VEC_PERM_EXPR: { diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c index b97dc1f6624..837fdaa041c 100644 --- a/gcc/cp/semantics.c +++ b/gcc/cp/semantics.c @@ -2048,24 +2048,17 @@ force_paren_expr (tree expr, bool even_uneval) if (cp_unevaluated_operand && !even_uneval) return expr; - if (!DECL_P (tree_strip_any_location_wrapper (expr)) - && TREE_CODE (expr) != COMPONENT_REF - && TREE_CODE (expr) != SCOPE_REF) - return expr; - - location_t loc = cp_expr_location (expr); - if (TREE_CODE (expr) == COMPONENT_REF || TREE_CODE (expr) == SCOPE_REF) REF_PARENTHESIZED_P (expr) = true; - else if (processing_template_decl) - expr = build1_loc (loc, PAREN_EXPR, TREE_TYPE (expr), expr); - else + else if (DECL_P (tree_strip_any_location_wrapper (expr))) { - expr = build1_loc (loc, VIEW_CONVERT_EXPR, TREE_TYPE (expr), expr); + location_t loc = cp_expr_location (expr); + const tree_code code = processing_template_decl ? PAREN_EXPR + : VIEW_CONVERT_EXPR; + expr = build1_loc (loc, code, TREE_TYPE (expr), expr); REF_PARENTHESIZED_P (expr) = true; } - return expr; } @@ -2090,10 +2083,8 @@ maybe_undo_parenthesized_ref (tree t) || TREE_CODE (t) == STATIC_CAST_EXPR); t = TREE_OPERAND (t, 0); } - else if (TREE_CODE (t) == PAREN_EXPR) - t = TREE_OPERAND (t, 0); - else if (TREE_CODE (t) == VIEW_CONVERT_EXPR - && REF_PARENTHESIZED_P (t)) + else if ((TREE_CODE (t) == PAREN_EXPR || TREE_CODE (t) == VIEW_CONVERT_EXPR) + && REF_PARENTHESIZED_P (t)) t = TREE_OPERAND (t, 0); return t; diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi index 96640ba156f..60cafe0b3e3 100644 --- a/gcc/doc/extend.texi +++ b/gcc/doc/extend.texi @@ -13951,6 +13951,24 @@ int g (int c) @end deftypefn +@deftypefn {Built-in Function} @var{type} __builtin_assoc_barrier (@var{type} @var{expr}) +This built-in inhibits re-association of the floating-point expression +@var{expr} with expressions consuming the return value of the built-in. The +expression @var{expr} itself can be reordered, and the whole expression +@var{expr} can be reordered with operands after the barrier. The barrier is +only relevant when @code{-fassociative-math} is active, since otherwise +floating-point is not treated as associative. + +@smallexample +float x0 = a + b - b; +float x1 = __builtin_assoc_barrier(a + b) - b; +@end smallexample + +@noindent +means that, with @code{-fassociative-math}, @code{x0} can be optimized to +@code{x0 = a} but @code{x1} cannot. +@end deftypefn + @deftypefn {Built-in Function} {void *} __builtin_assume_aligned (const void *@var{exp}, size_t @var{align}, ...) This function returns its first argument, and allows the compiler to assume that the returned pointer is at least @var{align} bytes diff --git a/gcc/testsuite/c-c++-common/builtin-assoc-barrier-1.c b/gcc/testsuite/c-c++-common/builtin-assoc-barrier-1.c new file mode 100644 index 00000000000..6fc90ccc33c --- /dev/null +++ b/gcc/testsuite/c-c++-common/builtin-assoc-barrier-1.c @@ -0,0 +1,48 @@ +// { dg-options "-O2 -ffast-math" } +/* { dg-do run } */ + +float a = 1.f; +float b = 1.e20f; + +float +fast() +{ + return __builtin_assoc_barrier (a + b) - b; +} + +__attribute__((optimize("-fno-associative-math"))) +float +normal() +{ + return a + b - b; +} + +#ifdef __cpp_constexpr +constexpr float +pm(float x, float y) +{ + return __builtin_assoc_barrier(x + y) - y; +} + +template <int x> + constexpr int + f() + { + return x; + } +#endif + +int main() +{ + if (fast() != normal()) + __builtin_abort(); +#ifdef __cpp_constexpr + constexpr float x = pm(1.f, 1.e20f); + constexpr int y = f<int(pm(1.f, 1.e20f))>(); + if (x != normal()) + __builtin_abort(); + if (y != 0) + __builtin_abort(); +#endif + return 0; +}