diff options
author | Jakub Jelinek <jakub@redhat.com> | 2021-09-10 20:41:33 +0200 |
---|---|---|
committer | Jakub Jelinek <jakub@redhat.com> | 2021-09-10 20:41:33 +0200 |
commit | 8122fbff770bcff183a9c3c72e8092c0ca32150b (patch) | |
tree | ce01fc417dc7920da04a312ab2abcd350adec6c0 | |
parent | b7f84702b364d49824ca97d4a2fc01567301d784 (diff) | |
download | gcc-8122fbff770bcff183a9c3c72e8092c0ca32150b.zip gcc-8122fbff770bcff183a9c3c72e8092c0ca32150b.tar.gz gcc-8122fbff770bcff183a9c3c72e8092c0ca32150b.tar.bz2 |
openmp: Implement OpenMP 5.1 atomics, so far for C only
This patch implements OpenMP 5.1 atomics (with clarifications from upcoming 5.2).
The most important changes are that it is now possible to write (for C/C++,
for Fortran it was possible before already) min/max atomics and more importantly
compare and exchange in various forms.
Also, acq_rel is now allowed on read/write and acq_rel/acquire are allowed on
update, and there are new compare, weak and fail clauses.
2021-09-10 Jakub Jelinek <jakub@redhat.com>
gcc/
* tree-core.h (enum omp_memory_order): Add OMP_MEMORY_ORDER_MASK,
OMP_FAIL_MEMORY_ORDER_UNSPECIFIED, OMP_FAIL_MEMORY_ORDER_RELAXED,
OMP_FAIL_MEMORY_ORDER_ACQUIRE, OMP_FAIL_MEMORY_ORDER_RELEASE,
OMP_FAIL_MEMORY_ORDER_ACQ_REL, OMP_FAIL_MEMORY_ORDER_SEQ_CST and
OMP_FAIL_MEMORY_ORDER_MASK enumerators.
(OMP_FAIL_MEMORY_ORDER_SHIFT): Define.
* gimple-pretty-print.c (dump_gimple_omp_atomic_load,
dump_gimple_omp_atomic_store): Print [weak] for weak atomic
load/store.
* gimple.h (enum gf_mask): Change GF_OMP_ATOMIC_MEMORY_ORDER
to 6-bit mask, adjust GF_OMP_ATOMIC_NEED_VALUE value and add
GF_OMP_ATOMIC_WEAK.
(gimple_omp_atomic_weak_p, gimple_omp_atomic_set_weak): New inline
functions.
* tree.h (OMP_ATOMIC_WEAK): Define.
* tree-pretty-print.c (dump_omp_atomic_memory_order): Adjust for
fail memory order being encoded in the same enum and also print
fail clause if present.
(dump_generic_node): Print weak clause if OMP_ATOMIC_WEAK.
* gimplify.c (goa_stabilize_expr): Add target_expr and rhs arguments,
handle pre_p == NULL case as a test mode that only returns value
but doesn't change gimplify nor change anything otherwise, adjust
recursive calls, add MODIFY_EXPR, ADDR_EXPR, COND_EXPR, TARGET_EXPR
and CALL_EXPR handling, adjust COMPOUND_EXPR handling for
__builtin_clear_padding calls, for !rhs gimplify as lvalue rather
than rvalue.
(gimplify_omp_atomic): Adjust goa_stabilize_expr caller. Handle
COND_EXPR rhs. Set weak flag on gimple load/store for
OMP_ATOMIC_WEAK.
* omp-expand.c (omp_memory_order_to_fail_memmodel): New function.
(omp_memory_order_to_memmodel): Adjust for fail clause encoded
in the same enum.
(expand_omp_atomic_cas): New function.
(expand_omp_atomic_pipeline): Use omp_memory_order_to_fail_memmodel
function.
(expand_omp_atomic): Attempt to optimize atomic compare and exchange
using expand_omp_atomic_cas.
gcc/c-family/
* c-common.h (c_finish_omp_atomic): Add r and weak arguments.
* c-omp.c: Include gimple-fold.h.
(c_finish_omp_atomic): Add r and weak arguments. Add support for
OpenMP 5.1 atomics.
gcc/c/
* c-parser.c (c_parser_conditional_expression): If omp_atomic_lhs and
cond.value is >, < or == with omp_atomic_lhs as one of the operands,
don't call build_conditional_expr, instead build a COND_EXPR directly.
(c_parser_binary_expression): Avoid calling parser_build_binary_op
if omp_atomic_lhs even in more cases for >, < or ==.
(c_parser_omp_atomic): Update function comment for OpenMP 5.1 atomics,
parse OpenMP 5.1 atomics and fail, compare and weak clauses, allow
acq_rel on atomic read/write and acq_rel/acquire clauses on update.
* c-typeck.c (build_binary_op): For flag_openmp only handle
MIN_EXPR/MAX_EXPR.
gcc/cp/
* parser.c (cp_parser_omp_atomic): Allow acq_rel on atomic read/write
and acq_rel/acquire clauses on update.
* semantics.c (finish_omp_atomic): Adjust c_finish_omp_atomic caller.
gcc/testsuite/
* c-c++-common/gomp/atomic-17.c (foo): Add tests for atomic read,
write or update with acq_rel clause and atomic update with acquire clause.
* c-c++-common/gomp/atomic-18.c (foo): Adjust expected diagnostics
wording, remove tests moved to atomic-17.c.
* c-c++-common/gomp/atomic-21.c: Expect only 2 omp atomic release and
2 omp atomic acq_rel directives instead of 4 omp atomic release.
* c-c++-common/gomp/atomic-25.c: New test.
* c-c++-common/gomp/atomic-26.c: New test.
* c-c++-common/gomp/atomic-27.c: New test.
* c-c++-common/gomp/atomic-28.c: New test.
* c-c++-common/gomp/atomic-29.c: New test.
* c-c++-common/gomp/atomic-30.c: New test.
* c-c++-common/goacc-gomp/atomic.c: Expect 1 omp atomic release and
1 omp atomic_acq_rel instead of 2 omp atomic release directives.
* gcc.dg/gomp/atomic-5.c: Adjust expected error diagnostic wording.
* g++.dg/gomp/atomic-18.C:Expect 4 omp atomic release and
1 omp atomic_acq_rel instead of 5 omp atomic release directives.
libgomp/
* testsuite/libgomp.c-c++-common/atomic-19.c: New test.
* testsuite/libgomp.c-c++-common/atomic-20.c: New test.
* testsuite/libgomp.c-c++-common/atomic-21.c: New test.
28 files changed, 2091 insertions, 127 deletions
diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h index d66bf15..849cefa 100644 --- a/gcc/c-family/c-common.h +++ b/gcc/c-family/c-common.h @@ -1223,8 +1223,8 @@ extern tree c_finish_omp_critical (location_t, tree, tree, tree); extern tree c_finish_omp_ordered (location_t, tree, tree); extern void c_finish_omp_barrier (location_t); extern tree c_finish_omp_atomic (location_t, enum tree_code, enum tree_code, - tree, tree, tree, tree, tree, bool, - enum omp_memory_order, bool = false); + tree, tree, tree, tree, tree, tree, bool, + enum omp_memory_order, bool, bool = false); extern bool c_omp_depend_t_p (tree); extern void c_finish_omp_depobj (location_t, tree, enum omp_clause_depend_kind, tree); diff --git a/gcc/c-family/c-omp.c b/gcc/c-family/c-omp.c index 4b95fc1..75184a3 100644 --- a/gcc/c-family/c-omp.c +++ b/gcc/c-family/c-omp.c @@ -36,6 +36,7 @@ along with GCC; see the file COPYING3. If not see #include "gimplify.h" #include "langhooks.h" #include "bitmap.h" +#include "gimple-fold.h" /* Complete a #pragma oacc wait construct. LOC is the location of @@ -215,15 +216,17 @@ c_finish_omp_taskyield (location_t loc) tree c_finish_omp_atomic (location_t loc, enum tree_code code, enum tree_code opcode, tree lhs, tree rhs, - tree v, tree lhs1, tree rhs1, bool swapped, - enum omp_memory_order memory_order, bool test) + tree v, tree lhs1, tree rhs1, tree r, bool swapped, + enum omp_memory_order memory_order, bool weak, + bool test) { - tree x, type, addr, pre = NULL_TREE; + tree x, type, addr, pre = NULL_TREE, rtmp = NULL_TREE, vtmp = NULL_TREE; HOST_WIDE_INT bitpos = 0, bitsize = 0; + enum tree_code orig_opcode = opcode; if (lhs == error_mark_node || rhs == error_mark_node || v == error_mark_node || lhs1 == error_mark_node - || rhs1 == error_mark_node) + || rhs1 == error_mark_node || r == error_mark_node) return error_mark_node; /* ??? According to one reading of the OpenMP spec, complex type are @@ -243,6 +246,12 @@ c_finish_omp_atomic (location_t loc, enum tree_code code, error_at (loc, "%<_Atomic%> expression in %<#pragma omp atomic%>"); return error_mark_node; } + if (r && r != void_list_node && !INTEGRAL_TYPE_P (TREE_TYPE (r))) + { + error_at (loc, "%<#pragma omp atomic compare capture%> with non-integral " + "comparison result"); + return error_mark_node; + } if (opcode == RDIV_EXPR) opcode = TRUNC_DIV_EXPR; @@ -299,6 +308,7 @@ c_finish_omp_atomic (location_t loc, enum tree_code code, x = build1 (OMP_ATOMIC_READ, type, addr); SET_EXPR_LOCATION (x, loc); OMP_ATOMIC_MEMORY_ORDER (x) = memory_order; + gcc_assert (!weak); if (blhs) x = build3_loc (loc, BIT_FIELD_REF, TREE_TYPE (blhs), x, bitsize_int (bitsize), bitsize_int (bitpos)); @@ -313,12 +323,29 @@ c_finish_omp_atomic (location_t loc, enum tree_code code, { lhs = build3_loc (loc, BIT_FIELD_REF, TREE_TYPE (blhs), lhs, bitsize_int (bitsize), bitsize_int (bitpos)); - if (swapped) + if (opcode == COND_EXPR) + { + bool save = in_late_binary_op; + in_late_binary_op = true; + std::swap (rhs, rhs1); + rhs1 = build_binary_op (loc, EQ_EXPR, lhs, rhs1, true); + in_late_binary_op = save; + } + else if (swapped) rhs = build_binary_op (loc, opcode, rhs, lhs, true); else if (opcode != NOP_EXPR) rhs = build_binary_op (loc, opcode, lhs, rhs, true); opcode = NOP_EXPR; } + else if (opcode == COND_EXPR) + { + bool save = in_late_binary_op; + in_late_binary_op = true; + std::swap (rhs, rhs1); + rhs1 = build_binary_op (loc, EQ_EXPR, lhs, rhs1, true); + in_late_binary_op = save; + opcode = NOP_EXPR; + } else if (swapped) { rhs = build_binary_op (loc, opcode, rhs, lhs, true); @@ -343,6 +370,100 @@ c_finish_omp_atomic (location_t loc, enum tree_code code, if (blhs) rhs = build3_loc (loc, BIT_INSERT_EXPR, type, new_lhs, rhs, bitsize_int (bitpos)); + if (orig_opcode == COND_EXPR) + { + if (error_operand_p (rhs1)) + return error_mark_node; + gcc_assert (TREE_CODE (rhs1) == EQ_EXPR); + tree cmptype = TREE_TYPE (TREE_OPERAND (rhs1, 0)); + if (SCALAR_FLOAT_TYPE_P (cmptype)) + { + bool clear_padding = false; + if (BITS_PER_UNIT == 8 && CHAR_BIT == 8) + { + HOST_WIDE_INT sz = int_size_in_bytes (cmptype), i; + gcc_assert (sz > 0); + unsigned char *buf = XALLOCAVEC (unsigned char, sz); + memset (buf, ~0, sz); + clear_type_padding_in_mask (cmptype, buf); + for (i = 0; i < sz; i++) + if (buf[i] != (unsigned char) ~0) + { + clear_padding = true; + break; + } + } + tree inttype = NULL_TREE; + if (!clear_padding && tree_fits_uhwi_p (TYPE_SIZE (cmptype))) + { + HOST_WIDE_INT prec = tree_to_uhwi (TYPE_SIZE (cmptype)); + inttype = c_common_type_for_size (prec, 1); + if (inttype + && (!tree_int_cst_equal (TYPE_SIZE (cmptype), + TYPE_SIZE (inttype)) + || TYPE_PRECISION (inttype) != prec)) + inttype = NULL_TREE; + } + if (inttype) + { + TREE_OPERAND (rhs1, 0) + = build1_loc (loc, VIEW_CONVERT_EXPR, inttype, + TREE_OPERAND (rhs1, 0)); + TREE_OPERAND (rhs1, 1) + = build1_loc (loc, VIEW_CONVERT_EXPR, inttype, + TREE_OPERAND (rhs1, 1)); + } + else + { + tree pcmptype = build_pointer_type (cmptype); + tree tmp1 = create_tmp_var_raw (cmptype); + TREE_ADDRESSABLE (tmp1) = 1; + DECL_CONTEXT (tmp1) = current_function_decl; + tmp1 = build4 (TARGET_EXPR, cmptype, tmp1, + TREE_OPERAND (rhs1, 0), NULL, NULL); + tmp1 = build1 (ADDR_EXPR, pcmptype, tmp1); + tree tmp2 = create_tmp_var_raw (cmptype); + TREE_ADDRESSABLE (tmp2) = 1; + DECL_CONTEXT (tmp2) = current_function_decl; + tmp2 = build4 (TARGET_EXPR, cmptype, tmp2, + TREE_OPERAND (rhs1, 1), NULL, NULL); + tmp2 = build1 (ADDR_EXPR, pcmptype, tmp2); + tree fndecl = builtin_decl_explicit (BUILT_IN_MEMCMP); + rhs1 = build_call_expr_loc (loc, fndecl, 3, tmp1, tmp2, + TYPE_SIZE_UNIT (cmptype)); + rhs1 = build2 (EQ_EXPR, boolean_type_node, rhs1, + integer_zero_node); + if (clear_padding) + { + fndecl = builtin_decl_explicit (BUILT_IN_CLEAR_PADDING); + tree cp1 = build_call_expr_loc (loc, fndecl, 1, tmp1); + tree cp2 = build_call_expr_loc (loc, fndecl, 1, tmp2); + rhs1 = omit_two_operands_loc (loc, boolean_type_node, + rhs1, cp2, cp1); + } + } + } + if (r) + { + tree var = create_tmp_var (boolean_type_node); + DECL_CONTEXT (var) = current_function_decl; + rtmp = build4 (TARGET_EXPR, boolean_type_node, var, + NULL, NULL, NULL); + save = in_late_binary_op; + in_late_binary_op = true; + x = build_modify_expr (loc, var, NULL_TREE, NOP_EXPR, + loc, rhs1, NULL_TREE); + in_late_binary_op = save; + if (x == error_mark_node) + return error_mark_node; + gcc_assert (TREE_CODE (x) == MODIFY_EXPR + && TREE_OPERAND (x, 0) == var); + TREE_OPERAND (x, 0) = rtmp; + rhs1 = omit_one_operand_loc (loc, boolean_type_node, x, rtmp); + } + rhs = build3_loc (loc, COND_EXPR, type, rhs1, rhs, new_lhs); + rhs1 = NULL_TREE; + } /* Punt the actual generation of atomic operations to common code. */ if (code == OMP_ATOMIC) @@ -350,6 +471,7 @@ c_finish_omp_atomic (location_t loc, enum tree_code code, x = build2 (code, type, addr, rhs); SET_EXPR_LOCATION (x, loc); OMP_ATOMIC_MEMORY_ORDER (x) = memory_order; + OMP_ATOMIC_WEAK (x) = weak; /* Generally it is hard to prove lhs1 and lhs are the same memory location, just diagnose different variables. */ @@ -412,8 +534,25 @@ c_finish_omp_atomic (location_t loc, enum tree_code code, bitsize_int (bitsize), bitsize_int (bitpos)); type = TREE_TYPE (blhs); } - x = build_modify_expr (loc, v, NULL_TREE, NOP_EXPR, + if (r) + { + vtmp = create_tmp_var (TREE_TYPE (x)); + DECL_CONTEXT (vtmp) = current_function_decl; + } + else + vtmp = v; + x = build_modify_expr (loc, vtmp, NULL_TREE, NOP_EXPR, loc, x, NULL_TREE); + if (x == error_mark_node) + return error_mark_node; + if (r) + { + vtmp = build4 (TARGET_EXPR, boolean_type_node, vtmp, + NULL, NULL, NULL); + gcc_assert (TREE_CODE (x) == MODIFY_EXPR + && TREE_OPERAND (x, 0) == TARGET_EXPR_SLOT (vtmp)); + TREE_OPERAND (x, 0) = vtmp; + } if (rhs1 && rhs1 != orig_lhs) { tree rhs1addr = build_unary_op (loc, ADDR_EXPR, rhs1, false); @@ -446,6 +585,28 @@ c_finish_omp_atomic (location_t loc, enum tree_code code, if (pre) x = omit_one_operand_loc (loc, type, x, pre); + if (r && r != void_list_node) + { + in_late_binary_op = true; + tree x2 = build_modify_expr (loc, r, NULL_TREE, NOP_EXPR, + loc, rtmp, NULL_TREE); + in_late_binary_op = save; + if (x2 == error_mark_node) + return error_mark_node; + x = omit_one_operand_loc (loc, TREE_TYPE (x2), x2, x); + } + if (v && vtmp != v) + { + in_late_binary_op = true; + tree x2 = build_modify_expr (loc, v, NULL_TREE, NOP_EXPR, + loc, vtmp, NULL_TREE); + in_late_binary_op = save; + if (x2 == error_mark_node) + return error_mark_node; + x2 = build3_loc (loc, COND_EXPR, void_type_node, rtmp, + void_node, x2); + x = omit_one_operand_loc (loc, TREE_TYPE (x2), x2, x); + } return x; } diff --git a/gcc/c/c-parser.c b/gcc/c/c-parser.c index 4d074ec..d82c042 100644 --- a/gcc/c/c-parser.c +++ b/gcc/c/c-parser.c @@ -7663,10 +7663,21 @@ c_parser_conditional_expression (c_parser *parser, struct c_expr *after, c_inhibit_evaluation_warnings -= cond.value == truthvalue_true_node; location_t loc1 = make_location (exp1.get_start (), exp1.src_range); location_t loc2 = make_location (exp2.get_start (), exp2.src_range); - ret.value = build_conditional_expr (colon_loc, cond.value, - cond.original_code == C_MAYBE_CONST_EXPR, - exp1.value, exp1.original_type, loc1, - exp2.value, exp2.original_type, loc2); + if (__builtin_expect (omp_atomic_lhs != NULL, 0) + && (TREE_CODE (cond.value) == GT_EXPR + || TREE_CODE (cond.value) == LT_EXPR + || TREE_CODE (cond.value) == EQ_EXPR) + && c_tree_equal (exp2.value, omp_atomic_lhs) + && (c_tree_equal (TREE_OPERAND (cond.value, 0), omp_atomic_lhs) + || c_tree_equal (TREE_OPERAND (cond.value, 1), omp_atomic_lhs))) + ret.value = build3_loc (colon_loc, COND_EXPR, TREE_TYPE (omp_atomic_lhs), + cond.value, exp1.value, exp2.value); + else + ret.value + = build_conditional_expr (colon_loc, cond.value, + cond.original_code == C_MAYBE_CONST_EXPR, + exp1.value, exp1.original_type, loc1, + exp2.value, exp2.original_type, loc2); ret.original_code = ERROR_MARK; if (exp1.value == error_mark_node || exp2.value == error_mark_node) ret.original_type = NULL; @@ -7849,15 +7860,27 @@ c_parser_binary_expression (c_parser *parser, struct c_expr *after, = 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) \ - & ((1 << PREC_BITOR) | (1 << PREC_BITXOR) | (1 << PREC_BITAND) \ - | (1 << PREC_SHIFT) | (1 << PREC_ADD) | (1 << PREC_MULT))) \ + && ((c_parser_next_token_is (parser, CPP_SEMICOLON) \ + && ((1 << stack[sp].prec) \ + & ((1 << PREC_BITOR) | (1 << PREC_BITXOR) \ + | (1 << PREC_BITAND) | (1 << PREC_SHIFT) \ + | (1 << PREC_ADD) | (1 << PREC_MULT) \ + | (1 << PREC_EQ)))) \ + || ((c_parser_next_token_is (parser, CPP_QUERY) \ + || (omp_atomic_lhs == void_list_node \ + && c_parser_next_token_is (parser, CPP_CLOSE_PAREN))) \ + && (stack[sp].prec == PREC_REL || stack[sp].prec == PREC_EQ)))\ && stack[sp].op != TRUNC_MOD_EXPR \ + && stack[sp].op != GE_EXPR \ + && stack[sp].op != LE_EXPR \ + && stack[sp].op != NE_EXPR \ && stack[0].expr.value != error_mark_node \ && stack[1].expr.value != error_mark_node \ - && (c_tree_equal (stack[0].expr.value, omp_atomic_lhs) \ - || c_tree_equal (stack[1].expr.value, omp_atomic_lhs))) \ + && (omp_atomic_lhs == void_list_node \ + || c_tree_equal (stack[0].expr.value, omp_atomic_lhs) \ + || c_tree_equal (stack[1].expr.value, omp_atomic_lhs) \ + || (stack[sp].op == EQ_EXPR \ + && c_parser_peek_2nd_token (parser)->keyword == RID_IF))) \ { \ tree t = make_node (stack[1].op); \ TREE_TYPE (t) = TREE_TYPE (stack[0].expr.value); \ @@ -17655,14 +17678,45 @@ c_parser_omp_allocate (location_t loc, c_parser *parser) capture-block: { v = x; update-stmt; } | { update-stmt; v = x; } | { v = x; x = expr; } - where x and v are lvalue expressions with scalar type. + OpenMP 5.1: + # pragma omp atomic compare new-line + conditional-update-atomic + + # pragma omp atomic compare capture new-line + conditional-update-capture-atomic + + conditional-update-atomic: + cond-expr-stmt | cond-update-stmt + cond-expr-stmt: + x = expr ordop x ? expr : x; + x = x ordop expr ? expr : x; + x = x == e ? d : x; + cond-update-stmt: + if (expr ordop x) { x = expr; } + if (x ordop expr) { x = expr; } + if (x == e) { x = d; } + ordop: + <, > + conditional-update-capture-atomic: + v = cond-expr-stmt + { v = x; cond-expr-stmt } + { cond-expr-stmt v = x; } + { v = x; cond-update-stmt } + { cond-update-stmt v = x; } + if (x == e) { x = d; } else { v = x; } + { r = x == e; if (r) { x = d; } } + { r = x == e; if (r) { x = d; } else { v = x; } } + + where x, r and v are lvalue expressions with scalar type, + expr, e and d are expressions with scalar type and e might be + the same as v. LOC is the location of the #pragma token. */ static void c_parser_omp_atomic (location_t loc, c_parser *parser, bool openacc) { - tree lhs = NULL_TREE, rhs = NULL_TREE, v = NULL_TREE; + tree lhs = NULL_TREE, rhs = NULL_TREE, v = NULL_TREE, r = NULL_TREE; tree lhs1 = NULL_TREE, rhs1 = NULL_TREE; tree stmt, orig_lhs, unfolded_lhs = NULL_TREE, unfolded_lhs1 = NULL_TREE; enum tree_code code = ERROR_MARK, opcode = NOP_EXPR; @@ -17674,6 +17728,12 @@ c_parser_omp_atomic (location_t loc, c_parser *parser, bool openacc) bool non_lvalue_p; bool first = true; tree clauses = NULL_TREE; + bool capture = false; + bool compare = false; + bool weak = false; + enum omp_memory_order fail = OMP_MEMORY_ORDER_UNSPECIFIED; + bool no_semicolon = false; + bool extra_scope = false; while (c_parser_next_token_is_not (parser, CPP_PRAGMA_EOL)) { @@ -17692,6 +17752,10 @@ c_parser_omp_atomic (location_t loc, c_parser *parser, bool openacc) enum tree_code new_code = ERROR_MARK; enum omp_memory_order new_memory_order = OMP_MEMORY_ORDER_UNSPECIFIED; + bool new_capture = false; + bool new_compare = false; + bool new_weak = false; + enum omp_memory_order new_fail = OMP_MEMORY_ORDER_UNSPECIFIED; if (!strcmp (p, "read")) new_code = OMP_ATOMIC_READ; @@ -17699,7 +17763,7 @@ c_parser_omp_atomic (location_t loc, c_parser *parser, bool openacc) new_code = NOP_EXPR; else if (!strcmp (p, "update")) new_code = OMP_ATOMIC; - else if (!strcmp (p, "capture")) + else if (openacc && !strcmp (p, "capture")) new_code = OMP_ATOMIC_CAPTURE_NEW; else if (openacc) { @@ -17707,6 +17771,47 @@ c_parser_omp_atomic (location_t loc, c_parser *parser, bool openacc) error_at (cloc, "expected %<read%>, %<write%>, %<update%>, " "or %<capture%> clause"); } + else if (!strcmp (p, "capture")) + new_capture = true; + else if (!strcmp (p, "compare")) + new_compare = true; + else if (!strcmp (p, "weak")) + new_weak = true; + else if (!strcmp (p, "fail")) + { + matching_parens parens; + + c_parser_consume_token (parser); + if (!parens.require_open (parser)) + continue; + + if (c_parser_next_token_is (parser, CPP_NAME)) + { + const char *q + = IDENTIFIER_POINTER (c_parser_peek_token (parser)->value); + + if (!strcmp (q, "seq_cst")) + new_fail = OMP_MEMORY_ORDER_SEQ_CST; + else if (!strcmp (q, "acquire")) + new_fail = OMP_MEMORY_ORDER_ACQUIRE; + else if (!strcmp (q, "relaxed")) + new_fail = OMP_MEMORY_ORDER_RELAXED; + } + + if (new_fail != OMP_MEMORY_ORDER_UNSPECIFIED) + { + c_parser_consume_token (parser); + if (fail != OMP_MEMORY_ORDER_UNSPECIFIED) + error_at (cloc, "too many %qs clauses", "fail"); + else + fail = new_fail; + } + else + c_parser_error (parser, "expected %<seq_cst%>, %<acquire%> " + "or %<relaxed%>"); + parens.skip_until_found_close (parser); + continue; + } else if (!strcmp (p, "seq_cst")) new_memory_order = OMP_MEMORY_ORDER_SEQ_CST; else if (!strcmp (p, "acq_rel")) @@ -17727,8 +17832,9 @@ c_parser_omp_atomic (location_t loc, c_parser *parser, bool openacc) { p = NULL; error_at (cloc, "expected %<read%>, %<write%>, %<update%>, " - "%<capture%>, %<seq_cst%>, %<acq_rel%>, " - "%<release%>, %<relaxed%> or %<hint%> clause"); + "%<capture%>, %<compare%>, %<weak%>, %<fail%>, " + "%<seq_cst%>, %<acq_rel%>, %<release%>, " + "%<relaxed%> or %<hint%> clause"); } if (p) { @@ -17751,6 +17857,27 @@ c_parser_omp_atomic (location_t loc, c_parser *parser, bool openacc) else memory_order = new_memory_order; } + else if (new_capture) + { + if (capture) + error_at (cloc, "too many %qs clauses", "capture"); + else + capture = true; + } + else if (new_compare) + { + if (compare) + error_at (cloc, "too many %qs clauses", "compare"); + else + compare = true; + } + else if (new_weak) + { + if (weak) + error_at (cloc, "too many %qs clauses", "weak"); + else + weak = true; + } c_parser_consume_token (parser); continue; } @@ -17761,6 +17888,30 @@ c_parser_omp_atomic (location_t loc, c_parser *parser, bool openacc) if (code == ERROR_MARK) code = OMP_ATOMIC; + if (capture) + { + if (code != OMP_ATOMIC) + error_at (loc, "%qs clause is incompatible with %<read%> or %<write%> " + "clauses", "capture"); + else + code = OMP_ATOMIC_CAPTURE_NEW; + } + if (compare && code != OMP_ATOMIC && code != OMP_ATOMIC_CAPTURE_NEW) + { + error_at (loc, "%qs clause is incompatible with %<read%> or %<write%> " + "clauses", "compare"); + compare = false; + } + if (fail != OMP_MEMORY_ORDER_UNSPECIFIED && !compare) + { + error_at (loc, "%qs clause requires %qs clause", "fail", "compare"); + fail = OMP_MEMORY_ORDER_UNSPECIFIED; + } + if (weak && !compare) + { + error_at (loc, "%qs clause requires %qs clause", "weak", "compare"); + weak = false; + } if (openacc) memory_order = OMP_MEMORY_ORDER_RELAXED; else if (memory_order == OMP_MEMORY_ORDER_UNSPECIFIED) @@ -17785,7 +17936,6 @@ c_parser_omp_atomic (location_t loc, c_parser *parser, bool openacc) memory_order = OMP_MEMORY_ORDER_ACQUIRE; break; case NOP_EXPR: /* atomic write */ - case OMP_ATOMIC: memory_order = OMP_MEMORY_ORDER_RELEASE; break; default: @@ -17801,36 +17951,32 @@ c_parser_omp_atomic (location_t loc, c_parser *parser, bool openacc) switch (code) { case OMP_ATOMIC_READ: - if (memory_order == OMP_MEMORY_ORDER_ACQ_REL - || memory_order == OMP_MEMORY_ORDER_RELEASE) + if (memory_order == OMP_MEMORY_ORDER_RELEASE) { error_at (loc, "%<#pragma omp atomic read%> incompatible with " - "%<acq_rel%> or %<release%> clauses"); + "%<release%> clause"); memory_order = OMP_MEMORY_ORDER_SEQ_CST; } + else if (memory_order == OMP_MEMORY_ORDER_ACQ_REL) + memory_order = OMP_MEMORY_ORDER_ACQUIRE; break; case NOP_EXPR: /* atomic write */ - if (memory_order == OMP_MEMORY_ORDER_ACQ_REL - || memory_order == OMP_MEMORY_ORDER_ACQUIRE) + if (memory_order == OMP_MEMORY_ORDER_ACQUIRE) { error_at (loc, "%<#pragma omp atomic write%> incompatible with " - "%<acq_rel%> or %<acquire%> clauses"); - memory_order = OMP_MEMORY_ORDER_SEQ_CST; - } - break; - case OMP_ATOMIC: - /* case OMP_ATOMIC_CAPTURE_NEW: - or update to OpenMP 5.1 */ - if (memory_order == OMP_MEMORY_ORDER_ACQ_REL - || memory_order == OMP_MEMORY_ORDER_ACQUIRE) - { - error_at (loc, "%<#pragma omp atomic update%> incompatible with " - "%<acq_rel%> or %<acquire%> clauses"); + "%<acquire%> clause"); memory_order = OMP_MEMORY_ORDER_SEQ_CST; } + else if (memory_order == OMP_MEMORY_ORDER_ACQ_REL) + memory_order = OMP_MEMORY_ORDER_RELEASE; break; default: break; } + if (fail != OMP_MEMORY_ORDER_UNSPECIFIED) + memory_order + = (enum omp_memory_order) (memory_order + | (fail << OMP_FAIL_MEMORY_ORDER_SHIFT)); switch (code) { @@ -17879,6 +18025,9 @@ c_parser_omp_atomic (location_t loc, c_parser *parser, bool openacc) c_parser_consume_token (parser); structured_block = true; } + else if (compare + && c_parser_next_token_is_keyword (parser, RID_IF)) + break; else { v = c_parser_cast_expression (parser, NULL).value; @@ -17890,6 +18039,12 @@ c_parser_omp_atomic (location_t loc, c_parser *parser, bool openacc) v = non_lvalue (v); if (!c_parser_require (parser, CPP_EQ, "expected %<=%>")) goto saw_error; + if (compare && c_parser_next_token_is_keyword (parser, RID_IF)) + { + eloc = c_parser_peek_token (parser)->location; + error_at (eloc, "expected expression"); + goto saw_error; + } } break; default: @@ -17899,6 +18054,179 @@ c_parser_omp_atomic (location_t loc, c_parser *parser, bool openacc) /* For structured_block case we don't know yet whether old or new x should be captured. */ restart: + if (compare && c_parser_next_token_is_keyword (parser, RID_IF)) + { + c_parser_consume_token (parser); + + matching_parens parens; + if (!parens.require_open (parser)) + goto saw_error; + eloc = c_parser_peek_token (parser)->location; + c_expr cmp_expr; + if (r) + { + cmp_expr = c_parser_cast_expression (parser, NULL); + cmp_expr = default_function_array_conversion (eloc, cmp_expr); + } + else + cmp_expr = c_parser_binary_expression (parser, NULL, void_list_node); + parens.skip_until_found_close (parser); + if (cmp_expr.value == error_mark_node) + goto saw_error; + if (r) + { + if (!c_tree_equal (cmp_expr.value, unfolded_lhs)) + goto bad_if; + cmp_expr.value = rhs1; + rhs1 = NULL_TREE; + gcc_assert (TREE_CODE (cmp_expr.value) == EQ_EXPR); + } + if (TREE_CODE (cmp_expr.value) == EQ_EXPR) + ; + else if (!structured_block && code == OMP_ATOMIC_CAPTURE_NEW) + { + error_at (EXPR_LOC_OR_LOC (cmp_expr.value, eloc), + "expected %<==%> comparison in %<if%> condition"); + goto saw_error; + } + else if (TREE_CODE (cmp_expr.value) != GT_EXPR + && TREE_CODE (cmp_expr.value) != LT_EXPR) + { + error_at (EXPR_LOC_OR_LOC (cmp_expr.value, eloc), + "expected %<==%>, %<<%> or %<>%> comparison in %<if%> " + "condition"); + goto saw_error; + } + if (!c_parser_require (parser, CPP_OPEN_BRACE, "expected %<{%>")) + goto saw_error; + + extra_scope = true; + eloc = c_parser_peek_token (parser)->location; + expr = c_parser_cast_expression (parser, NULL); + lhs = expr.value; + expr = default_function_array_conversion (eloc, expr); + unfolded_lhs = expr.value; + lhs = c_fully_fold (lhs, false, NULL, true); + orig_lhs = lhs; + if (lhs == error_mark_node) + goto saw_error; + if (!lvalue_p (unfolded_lhs)) + lhs = non_lvalue (lhs); + if (!c_parser_next_token_is (parser, CPP_EQ)) + { + c_parser_error (parser, "expected %<=%>"); + goto saw_error; + } + c_parser_consume_token (parser); + eloc = c_parser_peek_token (parser)->location; + expr = c_parser_expr_no_commas (parser, NULL); + rhs1 = expr.value; + + if (!c_parser_require (parser, CPP_SEMICOLON, "expected %<;%>")) + goto saw_error; + + if (!c_parser_require (parser, CPP_CLOSE_BRACE, "expected %<}%>")) + goto saw_error; + + extra_scope = false; + no_semicolon = true; + + if (c_tree_equal (TREE_OPERAND (cmp_expr.value, 0), unfolded_lhs)) + { + if (TREE_CODE (cmp_expr.value) == EQ_EXPR) + { + opcode = COND_EXPR; + rhs = c_fully_fold (TREE_OPERAND (cmp_expr.value, 1), + false, NULL, true); + rhs1 = c_fully_fold (rhs1, false, NULL, true); + } + else if (c_tree_equal (TREE_OPERAND (cmp_expr.value, 1), rhs1)) + { + opcode = (TREE_CODE (cmp_expr.value) == GT_EXPR + ? MIN_EXPR : MAX_EXPR); + rhs = c_fully_fold (rhs1, false, NULL, true); + rhs1 = c_fully_fold (TREE_OPERAND (cmp_expr.value, 0), + false, NULL, true); + } + else + goto bad_if; + } + else if (TREE_CODE (cmp_expr.value) == EQ_EXPR) + goto bad_if; + else if (c_tree_equal (TREE_OPERAND (cmp_expr.value, 1), unfolded_lhs) + && c_tree_equal (TREE_OPERAND (cmp_expr.value, 0), rhs1)) + { + opcode = (TREE_CODE (cmp_expr.value) == GT_EXPR + ? MAX_EXPR : MIN_EXPR); + rhs = c_fully_fold (rhs1, false, NULL, true); + rhs1 = c_fully_fold (TREE_OPERAND (cmp_expr.value, 1), + false, NULL, true); + } + else + { + bad_if: + c_parser_error (parser, + "invalid form of %<#pragma omp atomic compare%>"); + goto saw_error; + } + + if (c_parser_next_token_is_keyword (parser, RID_ELSE)) + { + if (code != OMP_ATOMIC_CAPTURE_NEW + || (structured_block && r == NULL_TREE) + || TREE_CODE (cmp_expr.value) != EQ_EXPR) + { + eloc = c_parser_peek_token (parser)->location; + error_at (eloc, "unexpected %<else%>"); + goto saw_error; + } + + c_parser_consume_token (parser); + + if (!c_parser_require (parser, CPP_OPEN_BRACE, "expected %<{%>")) + goto saw_error; + + extra_scope = true; + v = c_parser_cast_expression (parser, NULL).value; + non_lvalue_p = !lvalue_p (v); + v = c_fully_fold (v, false, NULL, true); + if (v == error_mark_node) + goto saw_error; + if (non_lvalue_p) + v = non_lvalue (v); + if (!c_parser_require (parser, CPP_EQ, "expected %<=%>")) + goto saw_error; + + expr = c_parser_expr_no_commas (parser, NULL); + + if (!c_tree_equal (expr.value, unfolded_lhs)) + goto bad_if; + + if (!c_parser_require (parser, CPP_SEMICOLON, "expected %<;%>")) + goto saw_error; + + if (!c_parser_require (parser, CPP_CLOSE_BRACE, "expected %<}%>")) + goto saw_error; + + extra_scope = false; + code = OMP_ATOMIC_CAPTURE_OLD; + if (r == NULL_TREE) + /* Signal to c_finish_omp_atomic that in + if (x == e) { x = d; } else { v = x; } + case the store to v should be conditional. */ + r = void_list_node; + } + else if (code == OMP_ATOMIC_CAPTURE_NEW && !structured_block) + { + c_parser_require_keyword (parser, RID_ELSE, "expected %<else%>"); + goto saw_error; + } + else if (code == OMP_ATOMIC_CAPTURE_NEW + && r != NULL_TREE + && v == NULL_TREE) + code = OMP_ATOMIC; + goto stmt_done; + } eloc = c_parser_peek_token (parser)->location; expr = c_parser_cast_expression (parser, NULL); lhs = expr.value; @@ -17908,9 +18236,14 @@ restart: orig_lhs = lhs; switch (TREE_CODE (lhs)) { + invalid_compare: + error_at (eloc, "invalid form of %<pragma omp atomic compare%>"); + /* FALLTHRU */ case ERROR_MARK: saw_error: c_parser_skip_to_end_of_block_or_statement (parser); + if (extra_scope && c_parser_next_token_is (parser, CPP_CLOSE_BRACE)) + c_parser_consume_token (parser); if (structured_block) { if (c_parser_next_token_is (parser, CPP_CLOSE_BRACE)) @@ -17933,6 +18266,8 @@ restart: unfolded_lhs = NULL_TREE; opcode = PLUS_EXPR; rhs = integer_one_node; + if (compare) + goto invalid_compare; break; case POSTDECREMENT_EXPR: @@ -17944,6 +18279,8 @@ restart: unfolded_lhs = NULL_TREE; opcode = MINUS_EXPR; rhs = integer_one_node; + if (compare) + goto invalid_compare; break; case COMPOUND_EXPR: @@ -17973,6 +18310,8 @@ restart: && !structured_block && TREE_CODE (orig_lhs) == COMPOUND_EXPR) code = OMP_ATOMIC_CAPTURE_OLD; + if (compare) + goto invalid_compare; break; } if (TREE_CODE (TREE_OPERAND (lhs, 1)) == TRUTH_NOT_EXPR @@ -17988,6 +18327,8 @@ restart: && !structured_block && TREE_CODE (orig_lhs) == COMPOUND_EXPR) code = OMP_ATOMIC_CAPTURE_OLD; + if (compare) + goto invalid_compare; break; } } @@ -17995,6 +18336,11 @@ restart: default: if (!lvalue_p (unfolded_lhs)) lhs = non_lvalue (lhs); + if (compare && !c_parser_next_token_is (parser, CPP_EQ)) + { + c_parser_error (parser, "expected %<=%>"); + goto saw_error; + } switch (c_parser_peek_token (parser)->type) { case CPP_MULT_EQ: @@ -18041,6 +18387,8 @@ restart: case BIT_AND_EXPR: case BIT_IOR_EXPR: case BIT_XOR_EXPR: + if (compare) + break; if (c_tree_equal (TREE_OPERAND (rhs1, 0), unfolded_lhs)) { opcode = TREE_CODE (rhs1); @@ -18061,6 +18409,78 @@ restart: goto stmt_done; } break; + case COND_EXPR: + if (!compare) + break; + if (TREE_CODE (TREE_OPERAND (rhs1, 0)) != GT_EXPR + && TREE_CODE (TREE_OPERAND (rhs1, 0)) != LT_EXPR + && TREE_CODE (TREE_OPERAND (rhs1, 0)) != EQ_EXPR) + break; + if (!TREE_OPERAND (rhs1, 1)) + break; + if (!c_tree_equal (TREE_OPERAND (rhs1, 2), unfolded_lhs)) + break; + if (c_tree_equal (TREE_OPERAND (TREE_OPERAND (rhs1, 0), 0), + unfolded_lhs)) + { + if (TREE_CODE (TREE_OPERAND (rhs1, 0)) == EQ_EXPR) + { + opcode = COND_EXPR; + rhs = c_fully_fold (TREE_OPERAND (TREE_OPERAND (rhs1, + 0), 1), + false, NULL, true); + rhs1 = c_fully_fold (TREE_OPERAND (rhs1, 1), false, + NULL, true); + goto stmt_done; + } + if (c_tree_equal (TREE_OPERAND (TREE_OPERAND (rhs1, 0), 1), + TREE_OPERAND (rhs1, 1))) + { + opcode = (TREE_CODE (TREE_OPERAND (rhs1, 0)) == GT_EXPR + ? MIN_EXPR : MAX_EXPR); + rhs = c_fully_fold (TREE_OPERAND (rhs1, 1), false, NULL, + true); + rhs1 = c_fully_fold (TREE_OPERAND (TREE_OPERAND (rhs1, + 0), 0), + false, NULL, true); + goto stmt_done; + } + } + else if (TREE_CODE (TREE_OPERAND (rhs1, 0)) == EQ_EXPR) + break; + else if (c_tree_equal (TREE_OPERAND (TREE_OPERAND (rhs1, 0), 1), + unfolded_lhs)) + { + if (c_tree_equal (TREE_OPERAND (TREE_OPERAND (rhs1, 0), 0), + TREE_OPERAND (rhs1, 1))) + { + opcode = (TREE_CODE (TREE_OPERAND (rhs1, 0)) == GT_EXPR + ? MAX_EXPR : MIN_EXPR); + rhs = c_fully_fold (TREE_OPERAND (rhs1, 1), false, NULL, + true); + rhs1 = c_fully_fold (TREE_OPERAND (TREE_OPERAND (rhs1, + 0), 1), + false, NULL, true); + goto stmt_done; + } + } + break; + case EQ_EXPR: + if (!compare + || code != OMP_ATOMIC_CAPTURE_NEW + || !structured_block + || v + || r) + break; + if (c_parser_next_token_is (parser, CPP_SEMICOLON) + && c_parser_peek_2nd_token (parser)->keyword == RID_IF) + { + r = lhs; + lhs = NULL_TREE; + c_parser_consume_token (parser); + goto restart; + } + break; case ERROR_MARK: goto saw_error; default: @@ -18109,10 +18529,12 @@ restart: break; } stmt_done: - if (structured_block && code == OMP_ATOMIC_CAPTURE_NEW) + if (structured_block && code == OMP_ATOMIC_CAPTURE_NEW && r == NULL_TREE) { - if (!c_parser_require (parser, CPP_SEMICOLON, "expected %<;%>")) + if (!no_semicolon + && !c_parser_require (parser, CPP_SEMICOLON, "expected %<;%>")) goto saw_error; + no_semicolon = false; v = c_parser_cast_expression (parser, NULL).value; non_lvalue_p = !lvalue_p (v); v = c_fully_fold (v, false, NULL, true); @@ -18135,10 +18557,16 @@ stmt_done: } if (structured_block) { - c_parser_skip_until_found (parser, CPP_SEMICOLON, "expected %<;%>"); + if (!no_semicolon) + c_parser_skip_until_found (parser, CPP_SEMICOLON, "expected %<;%>"); c_parser_require (parser, CPP_CLOSE_BRACE, "expected %<}%>"); } done: + if (weak && opcode != COND_EXPR) + { + error_at (loc, "%<weak%> clause requires atomic equality comparison"); + weak = false; + } if (unfolded_lhs && unfolded_lhs1 && !c_tree_equal (unfolded_lhs, unfolded_lhs1)) { @@ -18147,12 +18575,12 @@ done: stmt = error_mark_node; } else - stmt = c_finish_omp_atomic (loc, code, opcode, lhs, rhs, v, lhs1, rhs1, - swapped, memory_order); + stmt = c_finish_omp_atomic (loc, code, opcode, lhs, rhs, v, lhs1, rhs1, r, + swapped, memory_order, weak); if (stmt != error_mark_node) add_stmt (stmt); - if (!structured_block) + if (!structured_block && !no_semicolon) c_parser_skip_until_found (parser, CPP_SEMICOLON, "expected %<;%>"); } diff --git a/gcc/c/c-typeck.c b/gcc/c/c-typeck.c index 5849c5a..49d1bb0 100644 --- a/gcc/c/c-typeck.c +++ b/gcc/c/c-typeck.c @@ -12426,6 +12426,13 @@ build_binary_op (location_t location, enum tree_code code, maybe_warn_bool_compare (location, code, orig_op0, orig_op1); break; + case MIN_EXPR: + case MAX_EXPR: + /* Used for OpenMP atomics. */ + gcc_assert (flag_openmp); + common = 1; + break; + default: gcc_unreachable (); } diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c index f9c2c8a..e44c5c6 100644 --- a/gcc/cp/parser.c +++ b/gcc/cp/parser.c @@ -40193,7 +40193,6 @@ cp_parser_omp_atomic (cp_parser *parser, cp_token *pragma_tok, bool openacc) memory_order = OMP_MEMORY_ORDER_ACQUIRE; break; case NOP_EXPR: /* atomic write */ - case OMP_ATOMIC: memory_order = OMP_MEMORY_ORDER_RELEASE; break; default: @@ -40209,31 +40208,24 @@ cp_parser_omp_atomic (cp_parser *parser, cp_token *pragma_tok, bool openacc) switch (code) { case OMP_ATOMIC_READ: - if (memory_order == OMP_MEMORY_ORDER_ACQ_REL - || memory_order == OMP_MEMORY_ORDER_RELEASE) + if (memory_order == OMP_MEMORY_ORDER_RELEASE) { error_at (loc, "%<#pragma omp atomic read%> incompatible with " - "%<acq_rel%> or %<release%> clauses"); + "%<release%> clause"); memory_order = OMP_MEMORY_ORDER_SEQ_CST; } + else if (memory_order == OMP_MEMORY_ORDER_ACQ_REL) + memory_order = OMP_MEMORY_ORDER_ACQUIRE; break; case NOP_EXPR: /* atomic write */ - if (memory_order == OMP_MEMORY_ORDER_ACQ_REL - || memory_order == OMP_MEMORY_ORDER_ACQUIRE) + if (memory_order == OMP_MEMORY_ORDER_ACQUIRE) { error_at (loc, "%<#pragma omp atomic write%> incompatible with " - "%<acq_rel%> or %<acquire%> clauses"); - memory_order = OMP_MEMORY_ORDER_SEQ_CST; - } - break; - case OMP_ATOMIC: - if (memory_order == OMP_MEMORY_ORDER_ACQ_REL - || memory_order == OMP_MEMORY_ORDER_ACQUIRE) - { - error_at (loc, "%<#pragma omp atomic update%> incompatible with " - "%<acq_rel%> or %<acquire%> clauses"); + "%<acquire%> clause"); memory_order = OMP_MEMORY_ORDER_SEQ_CST; } + else if (memory_order == OMP_MEMORY_ORDER_ACQ_REL) + memory_order = OMP_MEMORY_ORDER_RELEASE; break; default: break; diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c index 4b7f4ac..94e6b18 100644 --- a/gcc/cp/semantics.c +++ b/gcc/cp/semantics.c @@ -9956,7 +9956,7 @@ finish_omp_atomic (location_t loc, enum tree_code code, enum tree_code opcode, return; } stmt = c_finish_omp_atomic (loc, code, opcode, lhs, rhs, - v, lhs1, rhs1, swapped, mo, + v, lhs1, rhs1, NULL_TREE, swapped, mo, false, processing_template_decl != 0); if (stmt == error_mark_node) return; diff --git a/gcc/gimple-pretty-print.c b/gcc/gimple-pretty-print.c index 53e7759..0ca4a94 100644 --- a/gcc/gimple-pretty-print.c +++ b/gcc/gimple-pretty-print.c @@ -2563,6 +2563,8 @@ dump_gimple_omp_atomic_load (pretty_printer *buffer, const gomp_atomic_load *gs, gimple_omp_atomic_memory_order (gs)); if (gimple_omp_atomic_need_value_p (gs)) pp_string (buffer, " [needed]"); + if (gimple_omp_atomic_weak_p (gs)) + pp_string (buffer, " [weak]"); newline_and_indent (buffer, spc + 2); dump_generic_node (buffer, gimple_omp_atomic_load_lhs (gs), spc, flags, false); @@ -2597,6 +2599,8 @@ dump_gimple_omp_atomic_store (pretty_printer *buffer, pp_space (buffer); if (gimple_omp_atomic_need_value_p (gs)) pp_string (buffer, "[needed] "); + if (gimple_omp_atomic_weak_p (gs)) + pp_string (buffer, "[weak] "); pp_left_paren (buffer); dump_generic_node (buffer, gimple_omp_atomic_store_val (gs), spc, flags, false); diff --git a/gcc/gimple.h b/gcc/gimple.h index 479a1c7..303623b 100644 --- a/gcc/gimple.h +++ b/gcc/gimple.h @@ -194,8 +194,9 @@ enum gf_mask { GF_OMP_RETURN_NOWAIT = 1 << 0, GF_OMP_SECTION_LAST = 1 << 0, - GF_OMP_ATOMIC_MEMORY_ORDER = (1 << 3) - 1, - GF_OMP_ATOMIC_NEED_VALUE = 1 << 3, + GF_OMP_ATOMIC_MEMORY_ORDER = (1 << 6) - 1, + GF_OMP_ATOMIC_NEED_VALUE = 1 << 6, + GF_OMP_ATOMIC_WEAK = 1 << 7, GF_PREDICT_TAKEN = 1 << 15 }; @@ -2446,6 +2447,29 @@ gimple_omp_atomic_set_need_value (gimple *g) } +/* Return true if OMP atomic load/store statement G has the + GF_OMP_ATOMIC_WEAK flag set. */ + +static inline bool +gimple_omp_atomic_weak_p (const gimple *g) +{ + if (gimple_code (g) != GIMPLE_OMP_ATOMIC_LOAD) + GIMPLE_CHECK (g, GIMPLE_OMP_ATOMIC_STORE); + return (gimple_omp_subcode (g) & GF_OMP_ATOMIC_WEAK) != 0; +} + + +/* Set the GF_OMP_ATOMIC_WEAK flag on G. */ + +static inline void +gimple_omp_atomic_set_weak (gimple *g) +{ + if (gimple_code (g) != GIMPLE_OMP_ATOMIC_LOAD) + GIMPLE_CHECK (g, GIMPLE_OMP_ATOMIC_STORE); + g->subcode |= GF_OMP_ATOMIC_WEAK; +} + + /* Return the memory order of the OMP atomic load/store statement G. */ static inline enum omp_memory_order diff --git a/gcc/gimplify.c b/gcc/gimplify.c index 8820f87..66dfd25 100644 --- a/gcc/gimplify.c +++ b/gcc/gimplify.c @@ -13859,14 +13859,15 @@ goa_lhs_expr_p (tree expr, tree addr) static int goa_stabilize_expr (tree *expr_p, gimple_seq *pre_p, tree lhs_addr, - tree lhs_var) + tree lhs_var, tree &target_expr, bool rhs) { tree expr = *expr_p; int saw_lhs; if (goa_lhs_expr_p (expr, lhs_addr)) { - *expr_p = lhs_var; + if (pre_p) + *expr_p = lhs_var; return 1; } if (is_gimple_val (expr)) @@ -13878,11 +13879,11 @@ goa_stabilize_expr (tree *expr_p, gimple_seq *pre_p, tree lhs_addr, case tcc_binary: case tcc_comparison: saw_lhs |= goa_stabilize_expr (&TREE_OPERAND (expr, 1), pre_p, lhs_addr, - lhs_var); + lhs_var, target_expr, true); /* FALLTHRU */ case tcc_unary: saw_lhs |= goa_stabilize_expr (&TREE_OPERAND (expr, 0), pre_p, lhs_addr, - lhs_var); + lhs_var, target_expr, true); break; case tcc_expression: switch (TREE_CODE (expr)) @@ -13894,36 +13895,131 @@ goa_stabilize_expr (tree *expr_p, gimple_seq *pre_p, tree lhs_addr, case TRUTH_XOR_EXPR: case BIT_INSERT_EXPR: saw_lhs |= goa_stabilize_expr (&TREE_OPERAND (expr, 1), pre_p, - lhs_addr, lhs_var); + lhs_addr, lhs_var, target_expr, true); /* FALLTHRU */ case TRUTH_NOT_EXPR: saw_lhs |= goa_stabilize_expr (&TREE_OPERAND (expr, 0), pre_p, - lhs_addr, lhs_var); + lhs_addr, lhs_var, target_expr, true); + break; + case MODIFY_EXPR: + saw_lhs |= goa_stabilize_expr (&TREE_OPERAND (expr, 1), pre_p, + lhs_addr, lhs_var, target_expr, true); + /* FALLTHRU */ + case ADDR_EXPR: + saw_lhs |= goa_stabilize_expr (&TREE_OPERAND (expr, 0), pre_p, + lhs_addr, lhs_var, target_expr, false); break; case COMPOUND_EXPR: + /* Special-case __builtin_clear_padding call before + __builtin_memcmp. */ + if (TREE_CODE (TREE_OPERAND (expr, 0)) == CALL_EXPR) + { + tree fndecl = get_callee_fndecl (TREE_OPERAND (expr, 0)); + if (fndecl + && fndecl_built_in_p (fndecl, BUILT_IN_CLEAR_PADDING) + && VOID_TYPE_P (TREE_TYPE (TREE_OPERAND (expr, 0)))) + { + saw_lhs = goa_stabilize_expr (&TREE_OPERAND (expr, 0), pre_p, + lhs_addr, lhs_var, + target_expr, true); + if (!saw_lhs) + { + expr = TREE_OPERAND (expr, 1); + if (!pre_p) + return goa_stabilize_expr (&expr, pre_p, lhs_addr, + lhs_var, target_expr, true); + *expr_p = expr; + return goa_stabilize_expr (expr_p, pre_p, lhs_addr, + lhs_var, target_expr, true); + } + else + { + saw_lhs |= goa_stabilize_expr (&TREE_OPERAND (expr, 1), + pre_p, lhs_addr, lhs_var, + target_expr, rhs); + break; + } + } + } /* Break out any preevaluations from cp_build_modify_expr. */ for (; TREE_CODE (expr) == COMPOUND_EXPR; expr = TREE_OPERAND (expr, 1)) - gimplify_stmt (&TREE_OPERAND (expr, 0), pre_p); + if (pre_p) + gimplify_stmt (&TREE_OPERAND (expr, 0), pre_p); + if (!pre_p) + return goa_stabilize_expr (&expr, pre_p, lhs_addr, lhs_var, + target_expr, rhs); *expr_p = expr; - return goa_stabilize_expr (expr_p, pre_p, lhs_addr, lhs_var); + return goa_stabilize_expr (expr_p, pre_p, lhs_addr, lhs_var, + target_expr, rhs); + case COND_EXPR: + if (!goa_stabilize_expr (&TREE_OPERAND (expr, 0), NULL, lhs_addr, + lhs_var, target_expr, true)) + break; + saw_lhs |= goa_stabilize_expr (&TREE_OPERAND (expr, 0), pre_p, + lhs_addr, lhs_var, target_expr, true); + saw_lhs |= goa_stabilize_expr (&TREE_OPERAND (expr, 1), pre_p, + lhs_addr, lhs_var, target_expr, true); + saw_lhs |= goa_stabilize_expr (&TREE_OPERAND (expr, 2), pre_p, + lhs_addr, lhs_var, target_expr, true); + break; + case TARGET_EXPR: + if (TARGET_EXPR_INITIAL (expr)) + { + if (expr == target_expr) + saw_lhs = 1; + else + { + saw_lhs = goa_stabilize_expr (&TARGET_EXPR_INITIAL (expr), + pre_p, lhs_addr, lhs_var, + target_expr, true); + if (saw_lhs && target_expr == NULL_TREE && pre_p) + target_expr = expr; + } + } + break; default: break; } break; case tcc_reference: - if (TREE_CODE (expr) == BIT_FIELD_REF) + if (TREE_CODE (expr) == BIT_FIELD_REF + || TREE_CODE (expr) == VIEW_CONVERT_EXPR) saw_lhs |= goa_stabilize_expr (&TREE_OPERAND (expr, 0), pre_p, - lhs_addr, lhs_var); + lhs_addr, lhs_var, target_expr, true); + break; + case tcc_vl_exp: + if (TREE_CODE (expr) == CALL_EXPR) + { + if (tree fndecl = get_callee_fndecl (expr)) + if (fndecl_built_in_p (fndecl, BUILT_IN_CLEAR_PADDING) + || fndecl_built_in_p (fndecl, BUILT_IN_MEMCMP)) + { + int nargs = call_expr_nargs (expr); + for (int i = 0; i < nargs; i++) + saw_lhs |= goa_stabilize_expr (&CALL_EXPR_ARG (expr, i), + pre_p, lhs_addr, lhs_var, + target_expr, true); + } + if (saw_lhs == 0 && VOID_TYPE_P (TREE_TYPE (expr))) + { + if (pre_p) + gimplify_stmt (&expr, pre_p); + return 0; + } + } break; default: break; } - if (saw_lhs == 0) + if (saw_lhs == 0 && pre_p) { enum gimplify_status gs; - gs = gimplify_expr (expr_p, pre_p, NULL, is_gimple_val, fb_rvalue); + if (rhs) + gs = gimplify_expr (expr_p, pre_p, NULL, is_gimple_val, fb_rvalue); + else + gs = gimplify_expr (expr_p, pre_p, NULL, is_gimple_lvalue, fb_lvalue); if (gs != GS_ALL_DONE) saw_lhs = -1; } @@ -13943,9 +14039,12 @@ gimplify_omp_atomic (tree *expr_p, gimple_seq *pre_p) tree tmp_load; gomp_atomic_load *loadstmt; gomp_atomic_store *storestmt; + tree target_expr = NULL_TREE; tmp_load = create_tmp_reg (type); - if (rhs && goa_stabilize_expr (&rhs, pre_p, addr, tmp_load) < 0) + if (rhs + && goa_stabilize_expr (&rhs, pre_p, addr, tmp_load, target_expr, + true) < 0) return GS_ERROR; if (gimplify_expr (&addr, pre_p, NULL, is_gimple_val, fb_rvalue) @@ -13959,11 +14058,14 @@ gimplify_omp_atomic (tree *expr_p, gimple_seq *pre_p) { /* BIT_INSERT_EXPR is not valid for non-integral bitfield representatives. Use BIT_FIELD_REF on the lhs instead. */ - if (TREE_CODE (rhs) == BIT_INSERT_EXPR + tree rhsarg = rhs; + if (TREE_CODE (rhs) == COND_EXPR) + rhsarg = TREE_OPERAND (rhs, 1); + if (TREE_CODE (rhsarg) == BIT_INSERT_EXPR && !INTEGRAL_TYPE_P (TREE_TYPE (tmp_load))) { - tree bitpos = TREE_OPERAND (rhs, 2); - tree op1 = TREE_OPERAND (rhs, 1); + tree bitpos = TREE_OPERAND (rhsarg, 2); + tree op1 = TREE_OPERAND (rhsarg, 1); tree bitsize; tree tmp_store = tmp_load; if (TREE_CODE (*expr_p) == OMP_ATOMIC_CAPTURE_OLD) @@ -13972,17 +14074,25 @@ gimplify_omp_atomic (tree *expr_p, gimple_seq *pre_p) bitsize = bitsize_int (TYPE_PRECISION (TREE_TYPE (op1))); else bitsize = TYPE_SIZE (TREE_TYPE (op1)); - gcc_assert (TREE_OPERAND (rhs, 0) == tmp_load); - tree t = build2_loc (EXPR_LOCATION (rhs), + gcc_assert (TREE_OPERAND (rhsarg, 0) == tmp_load); + tree t = build2_loc (EXPR_LOCATION (rhsarg), MODIFY_EXPR, void_type_node, - build3_loc (EXPR_LOCATION (rhs), BIT_FIELD_REF, - TREE_TYPE (op1), tmp_store, bitsize, - bitpos), op1); + build3_loc (EXPR_LOCATION (rhsarg), + BIT_FIELD_REF, TREE_TYPE (op1), + tmp_store, bitsize, bitpos), op1); + if (TREE_CODE (rhs) == COND_EXPR) + t = build3_loc (EXPR_LOCATION (rhs), COND_EXPR, void_type_node, + TREE_OPERAND (rhs, 0), t, void_node); gimplify_and_add (t, pre_p); rhs = tmp_store; } - if (gimplify_expr (&rhs, pre_p, NULL, is_gimple_val, fb_rvalue) - != GS_ALL_DONE) + bool save_allow_rhs_cond_expr = gimplify_ctxp->allow_rhs_cond_expr; + if (TREE_CODE (rhs) == COND_EXPR) + gimplify_ctxp->allow_rhs_cond_expr = true; + enum gimplify_status gs = gimplify_expr (&rhs, pre_p, NULL, + is_gimple_val, fb_rvalue); + gimplify_ctxp->allow_rhs_cond_expr = save_allow_rhs_cond_expr; + if (gs != GS_ALL_DONE) return GS_ERROR; } @@ -13990,6 +14100,11 @@ gimplify_omp_atomic (tree *expr_p, gimple_seq *pre_p) rhs = tmp_load; storestmt = gimple_build_omp_atomic_store (rhs, OMP_ATOMIC_MEMORY_ORDER (*expr_p)); + if (TREE_CODE (*expr_p) != OMP_ATOMIC_READ && OMP_ATOMIC_WEAK (*expr_p)) + { + gimple_omp_atomic_set_weak (loadstmt); + gimple_omp_atomic_set_weak (storestmt); + } gimplify_seq_add_stmt (pre_p, storestmt); switch (TREE_CODE (*expr_p)) { diff --git a/gcc/omp-expand.c b/gcc/omp-expand.c index f2b6f6f..159ae0e 100644 --- a/gcc/omp-expand.c +++ b/gcc/omp-expand.c @@ -8487,22 +8487,58 @@ expand_omp_synch (struct omp_region *region) } } +/* Translate enum omp_memory_order to enum memmodel for the embedded + fail clause in there. */ + +static enum memmodel +omp_memory_order_to_fail_memmodel (enum omp_memory_order mo) +{ + switch (mo & OMP_FAIL_MEMORY_ORDER_MASK) + { + case OMP_FAIL_MEMORY_ORDER_UNSPECIFIED: + switch (mo & OMP_MEMORY_ORDER_MASK) + { + case OMP_MEMORY_ORDER_RELAXED: return MEMMODEL_RELAXED; + case OMP_MEMORY_ORDER_ACQUIRE: return MEMMODEL_ACQUIRE; + case OMP_MEMORY_ORDER_RELEASE: return MEMMODEL_RELAXED; + case OMP_MEMORY_ORDER_ACQ_REL: return MEMMODEL_ACQUIRE; + case OMP_MEMORY_ORDER_SEQ_CST: return MEMMODEL_SEQ_CST; + default: break; + } + gcc_unreachable (); + case OMP_FAIL_MEMORY_ORDER_RELAXED: return MEMMODEL_RELAXED; + case OMP_FAIL_MEMORY_ORDER_ACQUIRE: return MEMMODEL_ACQUIRE; + case OMP_FAIL_MEMORY_ORDER_SEQ_CST: return MEMMODEL_SEQ_CST; + default: gcc_unreachable (); + } +} + /* Translate enum omp_memory_order to enum memmodel. The two enums are using different numbers so that OMP_MEMORY_ORDER_UNSPECIFIED - is 0. */ + is 0 and omp_memory_order has the fail mode encoded in it too. */ static enum memmodel omp_memory_order_to_memmodel (enum omp_memory_order mo) { - switch (mo) - { - case OMP_MEMORY_ORDER_RELAXED: return MEMMODEL_RELAXED; - case OMP_MEMORY_ORDER_ACQUIRE: return MEMMODEL_ACQUIRE; - case OMP_MEMORY_ORDER_RELEASE: return MEMMODEL_RELEASE; - case OMP_MEMORY_ORDER_ACQ_REL: return MEMMODEL_ACQ_REL; - case OMP_MEMORY_ORDER_SEQ_CST: return MEMMODEL_SEQ_CST; + enum memmodel ret, fail_ret; + switch (mo & OMP_MEMORY_ORDER_MASK) + { + case OMP_MEMORY_ORDER_RELAXED: ret = MEMMODEL_RELAXED; break; + case OMP_MEMORY_ORDER_ACQUIRE: ret = MEMMODEL_ACQUIRE; break; + case OMP_MEMORY_ORDER_RELEASE: ret = MEMMODEL_RELEASE; break; + case OMP_MEMORY_ORDER_ACQ_REL: ret = MEMMODEL_ACQ_REL; break; + case OMP_MEMORY_ORDER_SEQ_CST: ret = MEMMODEL_SEQ_CST; break; default: gcc_unreachable (); } + /* If we drop the -Winvalid-memory-model warning for C++17 P0418R2, + we can just return ret here unconditionally. Otherwise, work around + it here and make sure fail memmodel is not stronger. */ + if ((mo & OMP_FAIL_MEMORY_ORDER_MASK) == OMP_FAIL_MEMORY_ORDER_UNSPECIFIED) + return ret; + fail_ret = omp_memory_order_to_fail_memmodel (mo); + if (fail_ret > ret) + return fail_ret; + return ret; } /* A subroutine of expand_omp_atomic. Attempt to implement the atomic @@ -8782,6 +8818,261 @@ expand_omp_atomic_fetch_op (basic_block load_bb, return true; } +/* A subroutine of expand_omp_atomic. Attempt to implement the atomic + compare and exchange as an ATOMIC_COMPARE_EXCHANGE internal function. + Returns false if the expression is not of the proper form. */ + +static bool +expand_omp_atomic_cas (basic_block load_bb, tree addr, + tree loaded_val, tree stored_val, int index) +{ + /* We expect to find the following sequences: + + load_bb: + GIMPLE_OMP_ATOMIC_LOAD (tmp, mem) + + store_bb: + val = tmp == e ? d : tmp; + GIMPLE_OMP_ATOMIC_STORE (val) + + or in store_bb instead: + tmp2 = tmp == e; + val = tmp2 ? d : tmp; + GIMPLE_OMP_ATOMIC_STORE (val) + + or: + tmp3 = VIEW_CONVERT_EXPR<integral_type>(tmp); + val = e == tmp3 ? d : tmp; + GIMPLE_OMP_ATOMIC_STORE (val) + + etc. */ + + + basic_block store_bb = single_succ (load_bb); + gimple_stmt_iterator gsi = gsi_last_nondebug_bb (store_bb); + gimple *store_stmt = gsi_stmt (gsi); + if (!store_stmt || gimple_code (store_stmt) != GIMPLE_OMP_ATOMIC_STORE) + return false; + gsi_prev_nondebug (&gsi); + if (gsi_end_p (gsi)) + return false; + gimple *condexpr_stmt = gsi_stmt (gsi); + if (!is_gimple_assign (condexpr_stmt) + || gimple_assign_rhs_code (condexpr_stmt) != COND_EXPR) + return false; + if (!operand_equal_p (gimple_assign_lhs (condexpr_stmt), stored_val, 0)) + return false; + gimple *cond_stmt = NULL; + gimple *vce_stmt = NULL; + gsi_prev_nondebug (&gsi); + if (!gsi_end_p (gsi)) + { + cond_stmt = gsi_stmt (gsi); + if (!is_gimple_assign (cond_stmt)) + return false; + if (gimple_assign_rhs_code (cond_stmt) == EQ_EXPR) + { + gsi_prev_nondebug (&gsi); + if (!gsi_end_p (gsi)) + { + vce_stmt = gsi_stmt (gsi); + if (!is_gimple_assign (vce_stmt) + || gimple_assign_rhs_code (vce_stmt) != VIEW_CONVERT_EXPR) + return false; + } + } + else if (gimple_assign_rhs_code (cond_stmt) == VIEW_CONVERT_EXPR) + std::swap (vce_stmt, cond_stmt); + else + return false; + if (vce_stmt) + { + tree vce_rhs = gimple_assign_rhs1 (vce_stmt); + if (TREE_CODE (vce_rhs) != VIEW_CONVERT_EXPR + || !operand_equal_p (TREE_OPERAND (vce_rhs, 0), loaded_val)) + return false; + if (!INTEGRAL_TYPE_P (TREE_TYPE (vce_rhs)) + || !SCALAR_FLOAT_TYPE_P (TREE_TYPE (loaded_val)) + || !tree_int_cst_equal (TYPE_SIZE (TREE_TYPE (vce_rhs)), + TYPE_SIZE (TREE_TYPE (loaded_val)))) + return false; + gsi_prev_nondebug (&gsi); + if (!gsi_end_p (gsi)) + return false; + } + } + tree cond = gimple_assign_rhs1 (condexpr_stmt); + tree cond_op1, cond_op2; + if (cond_stmt) + { + if (!operand_equal_p (cond, gimple_assign_lhs (cond_stmt))) + return false; + cond_op1 = gimple_assign_rhs1 (cond_stmt); + cond_op2 = gimple_assign_rhs2 (cond_stmt); + } + else if (TREE_CODE (cond) != EQ_EXPR && TREE_CODE (cond) != NE_EXPR) + return false; + else + { + cond_op1 = TREE_OPERAND (cond, 0); + cond_op2 = TREE_OPERAND (cond, 1); + } + tree d; + if (TREE_CODE (cond) == NE_EXPR) + { + if (!operand_equal_p (gimple_assign_rhs2 (condexpr_stmt), loaded_val)) + return false; + d = gimple_assign_rhs3 (condexpr_stmt); + } + else if (!operand_equal_p (gimple_assign_rhs3 (condexpr_stmt), loaded_val)) + return false; + else + d = gimple_assign_rhs2 (condexpr_stmt); + tree e = vce_stmt ? gimple_assign_lhs (vce_stmt) : loaded_val; + if (operand_equal_p (e, cond_op1)) + e = cond_op2; + else if (operand_equal_p (e, cond_op2)) + e = cond_op1; + else + return false; + + location_t loc = gimple_location (store_stmt); + gimple *load_stmt = last_stmt (load_bb); + bool need_new = gimple_omp_atomic_need_value_p (store_stmt); + bool need_old = gimple_omp_atomic_need_value_p (load_stmt); + bool weak = gimple_omp_atomic_weak_p (load_stmt); + enum omp_memory_order omo = gimple_omp_atomic_memory_order (load_stmt); + tree mo = build_int_cst (NULL, omp_memory_order_to_memmodel (omo)); + tree fmo = build_int_cst (NULL, omp_memory_order_to_fail_memmodel (omo)); + gcc_checking_assert (!need_old || !need_new); + + enum built_in_function fncode + = (enum built_in_function) ((int) BUILT_IN_SYNC_VAL_COMPARE_AND_SWAP_N + + index + 1); + tree cmpxchg = builtin_decl_explicit (fncode); + if (cmpxchg == NULL_TREE) + return false; + tree itype = TREE_TYPE (TREE_TYPE (cmpxchg)); + + if (!can_compare_and_swap_p (TYPE_MODE (itype), true) + || !can_atomic_load_p (TYPE_MODE (itype))) + return false; + + tree type = TYPE_MAIN_VARIANT (TREE_TYPE (loaded_val)); + if (SCALAR_FLOAT_TYPE_P (type) && !vce_stmt) + return false; + + gsi = gsi_for_stmt (store_stmt); + if (!useless_type_conversion_p (itype, TREE_TYPE (e))) + { + tree ne = create_tmp_reg (itype); + gimple *g = gimple_build_assign (ne, NOP_EXPR, e); + gimple_set_location (g, loc); + gsi_insert_before (&gsi, g, GSI_SAME_STMT); + e = ne; + } + if (!useless_type_conversion_p (itype, TREE_TYPE (d))) + { + tree nd = create_tmp_reg (itype); + enum tree_code code; + if (SCALAR_FLOAT_TYPE_P (TREE_TYPE (d))) + { + code = VIEW_CONVERT_EXPR; + d = build1 (VIEW_CONVERT_EXPR, itype, d); + } + else + code = NOP_EXPR; + gimple *g = gimple_build_assign (nd, code, d); + gimple_set_location (g, loc); + gsi_insert_before (&gsi, g, GSI_SAME_STMT); + d = nd; + } + + tree ctype = build_complex_type (itype); + int flag = int_size_in_bytes (itype) + (weak ? 256 : 0); + gimple *g + = gimple_build_call_internal (IFN_ATOMIC_COMPARE_EXCHANGE, 6, addr, e, d, + build_int_cst (integer_type_node, flag), + mo, fmo); + tree cres = create_tmp_reg (ctype); + gimple_call_set_lhs (g, cres); + gimple_set_location (g, loc); + gsi_insert_before (&gsi, g, GSI_SAME_STMT); + + if (cond_stmt || need_old || need_new) + { + tree im = create_tmp_reg (itype); + g = gimple_build_assign (im, IMAGPART_EXPR, + build1 (IMAGPART_EXPR, itype, cres)); + gimple_set_location (g, loc); + gsi_insert_before (&gsi, g, GSI_SAME_STMT); + + tree re = NULL_TREE; + if (need_old || need_new) + { + re = create_tmp_reg (itype); + g = gimple_build_assign (re, REALPART_EXPR, + build1 (REALPART_EXPR, itype, cres)); + gimple_set_location (g, loc); + gsi_insert_before (&gsi, g, GSI_SAME_STMT); + } + + if (cond_stmt) + { + g = gimple_build_assign (gimple_assign_lhs (cond_stmt), + NOP_EXPR, im); + gimple_set_location (g, loc); + gsi_insert_before (&gsi, g, GSI_SAME_STMT); + } + else if (need_new) + { + g = gimple_build_assign (create_tmp_reg (itype), COND_EXPR, + build2 (NE_EXPR, boolean_type_node, + im, build_zero_cst (itype)), + d, re); + gimple_set_location (g, loc); + gsi_insert_before (&gsi, g, GSI_SAME_STMT); + re = gimple_assign_lhs (g); + } + + if (need_old || need_new) + { + tree v = need_old ? loaded_val : stored_val; + enum tree_code code; + if (SCALAR_FLOAT_TYPE_P (TREE_TYPE (v))) + { + code = VIEW_CONVERT_EXPR; + re = build1 (VIEW_CONVERT_EXPR, TREE_TYPE (v), re); + } + else if (!useless_type_conversion_p (TREE_TYPE (v), itype)) + code = NOP_EXPR; + else + code = TREE_CODE (re); + g = gimple_build_assign (v, code, re); + gimple_set_location (g, loc); + gsi_insert_before (&gsi, g, GSI_SAME_STMT); + } + } + + gsi_remove (&gsi, true); + gsi = gsi_for_stmt (load_stmt); + gsi_remove (&gsi, true); + gsi = gsi_for_stmt (condexpr_stmt); + gsi_remove (&gsi, true); + if (cond_stmt) + { + gsi = gsi_for_stmt (cond_stmt); + gsi_remove (&gsi, true); + } + if (vce_stmt) + { + gsi = gsi_for_stmt (vce_stmt); + gsi_remove (&gsi, true); + } + + return true; +} + /* A subroutine of expand_omp_atomic. Implement the atomic operation as: oldval = *addr; @@ -8825,13 +9116,8 @@ expand_omp_atomic_pipeline (basic_block load_bb, basic_block store_bb, gcc_assert (gimple_code (gsi_stmt (si)) == GIMPLE_OMP_ATOMIC_LOAD); location_t loc = gimple_location (gsi_stmt (si)); enum omp_memory_order omo = gimple_omp_atomic_memory_order (gsi_stmt (si)); - enum memmodel imo = omp_memory_order_to_memmodel (omo); - tree mo = build_int_cst (NULL, imo); - if (imo == MEMMODEL_RELEASE) - imo = MEMMODEL_RELAXED; - else if (imo == MEMMODEL_ACQ_REL) - imo = MEMMODEL_ACQUIRE; - tree fmo = build_int_cst (NULL, imo); + tree mo = build_int_cst (NULL, omp_memory_order_to_memmodel (omo)); + tree fmo = build_int_cst (NULL, omp_memory_order_to_fail_memmodel (omo)); /* For floating-point values, we'll need to view-convert them to integers so that we can perform the atomic compare and swap. Simplify the @@ -9114,6 +9400,13 @@ expand_omp_atomic (struct omp_region *region) loaded_val, stored_val, index)) return; + /* When possible, use ATOMIC_COMPARE_EXCHANGE ifn without a loop. */ + if (store_bb == single_succ (load_bb) + && !gimple_in_ssa_p (cfun) + && expand_omp_atomic_cas (load_bb, addr, loaded_val, stored_val, + index)) + return; + /* If we don't have specialized __sync builtins, try and implement as a compare and swap loop. */ if (expand_omp_atomic_pipeline (load_bb, store_bb, addr, diff --git a/gcc/testsuite/c-c++-common/goacc-gomp/atomic.c b/gcc/testsuite/c-c++-common/goacc-gomp/atomic.c index 4d18f23..e1ab03e 100644 --- a/gcc/testsuite/c-c++-common/goacc-gomp/atomic.c +++ b/gcc/testsuite/c-c++-common/goacc-gomp/atomic.c @@ -37,7 +37,8 @@ foo () /* { dg-final { scan-tree-dump-times "i = #pragma omp atomic read acquire" 1 "original" } } */ /* { dg-final { scan-tree-dump-times "i = #pragma omp atomic read relaxed" 1 "original" } } */ -/* { dg-final { scan-tree-dump-times "#pragma omp atomic release" 2 "original" } } */ +/* { dg-final { scan-tree-dump-times "#pragma omp atomic acq_rel" 1 "original" } } */ +/* { dg-final { scan-tree-dump-times "#pragma omp atomic release" 1 "original" } } */ /* { dg-final { scan-tree-dump-times "#pragma omp atomic relaxed" 2 "original" } } */ /* { dg-final { scan-tree-dump-times "v = #pragma omp atomic capture acq_rel" 1 "original" } } */ /* { dg-final { scan-tree-dump-times "v = #pragma omp atomic capture relaxed" 2 "original" } } */ diff --git a/gcc/testsuite/c-c++-common/gomp/atomic-17.c b/gcc/testsuite/c-c++-common/gomp/atomic-17.c index d313b29..001887e 100644 --- a/gcc/testsuite/c-c++-common/gomp/atomic-17.c +++ b/gcc/testsuite/c-c++-common/gomp/atomic-17.c @@ -22,8 +22,18 @@ foo () v = i = i + 1; #pragma omp atomic read acquire v = i; + #pragma omp atomic acq_rel read + v = i; #pragma omp atomic release,write i = v; + #pragma omp atomic write,acq_rel + i = v; #pragma omp atomic hint(1),update,release f = f + 2.0; + #pragma omp atomic update ,acquire + i = i + 1; + #pragma omp atomic acq_rel update + i = i + 1; + #pragma omp atomic acq_rel,hint(0) + i = i + 1; } diff --git a/gcc/testsuite/c-c++-common/gomp/atomic-18.c b/gcc/testsuite/c-c++-common/gomp/atomic-18.c index 95fbcd1..b389c6a 100644 --- a/gcc/testsuite/c-c++-common/gomp/atomic-18.c +++ b/gcc/testsuite/c-c++-common/gomp/atomic-18.c @@ -8,28 +8,18 @@ foo (int j) i = i + 1; #pragma omp atomic seq_cst release /* { dg-error "too many memory order clauses" } */ i = i + 1; - #pragma omp atomic read,release /* { dg-error "incompatible with 'acq_rel' or 'release' clauses" } */ + #pragma omp atomic read,release /* { dg-error "incompatible with 'release' clause" } */ v = i; - #pragma omp atomic acq_rel read /* { dg-error "incompatible with 'acq_rel' or 'release' clauses" } */ - v = i; - #pragma omp atomic write acq_rel /* { dg-error "incompatible with 'acq_rel' or 'acquire' clauses" } */ - i = v; - #pragma omp atomic acquire , write /* { dg-error "incompatible with 'acq_rel' or 'acquire' clauses" } */ + #pragma omp atomic acquire , write /* { dg-error "incompatible with 'acquire' clause" } */ i = v; - #pragma omp atomic update ,acquire /* { dg-error "incompatible with 'acq_rel' or 'acquire' clauses" } */ - i = i + 1; - #pragma omp atomic acq_rel update /* { dg-error "incompatible with 'acq_rel' or 'acquire' clauses" } */ - i = i + 1; - #pragma omp atomic acq_rel,hint(0) /* { dg-error "incompatible with 'acq_rel' or 'acquire' clauses" } */ - i = i + 1; - #pragma omp atomic acquire /* { dg-error "incompatible with 'acq_rel' or 'acquire' clauses" } */ - i = i + 1; - #pragma omp atomic capture hint (0) capture /* { dg-error "too many atomic clauses" } */ + #pragma omp atomic capture hint (0) capture /* { dg-error "too many 'capture' clauses" "" { target c } } */ + /* { dg-error "too many atomic clauses" "" { target c++ } .-1 } */ v = i = i + 1; #pragma omp atomic hint(j + 2) /* { dg-error "constant integer expression" } */ i = i + 1; #pragma omp atomic hint(f) /* { dg-error "integ" } */ i = i + 1; - #pragma omp atomic foobar /* { dg-error "expected 'read', 'write', 'update', 'capture', 'seq_cst', 'acq_rel', 'release', 'relaxed' or 'hint' clause" } */ - i = i + 1; /* { dg-error "expected end of line before" "" { target *-*-* } .-1 } */ + #pragma omp atomic foobar /* { dg-error "expected 'read', 'write', 'update', 'capture', 'compare', 'weak', 'fail', 'seq_cst', 'acq_rel', 'release', 'relaxed' or 'hint' clause" "" { target c } } */ + /* { dg-error "expected 'read', 'write', 'update', 'capture', 'seq_cst', 'acq_rel', 'release', 'relaxed' or 'hint' clause" "" { target c++ } .-1 } */ + i = i + 1; /* { dg-error "expected end of line before" "" { target *-*-* } .-2 } */ } diff --git a/gcc/testsuite/c-c++-common/gomp/atomic-21.c b/gcc/testsuite/c-c++-common/gomp/atomic-21.c index 8d052ae..12b1237 100644 --- a/gcc/testsuite/c-c++-common/gomp/atomic-21.c +++ b/gcc/testsuite/c-c++-common/gomp/atomic-21.c @@ -1,6 +1,7 @@ /* { dg-do compile } */ /* { dg-additional-options "-fdump-tree-original" } */ -/* { dg-final { scan-tree-dump-times "omp atomic release" 4 "original" } } */ +/* { dg-final { scan-tree-dump-times "omp atomic release" 2 "original" } } */ +/* { dg-final { scan-tree-dump-times "omp atomic acq_rel" 2 "original" } } */ /* { dg-final { scan-tree-dump-times "omp atomic read acquire" 1 "original" } } */ /* { dg-final { scan-tree-dump-times "omp atomic capture acq_rel" 1 "original" } } */ diff --git a/gcc/testsuite/c-c++-common/gomp/atomic-25.c b/gcc/testsuite/c-c++-common/gomp/atomic-25.c new file mode 100644 index 0000000..a5196a5 --- /dev/null +++ b/gcc/testsuite/c-c++-common/gomp/atomic-25.c @@ -0,0 +1,50 @@ +/* { dg-do compile { target c } } */ + +int x, r, z; +double d, v; +long double ld; + +void +foo (int y, double e, long double f) +{ + #pragma omp atomic compare update seq_cst + x = x > y ? y : x; + #pragma omp atomic compare relaxed + d = e > d ? e : d; + #pragma omp atomic compare + d = f < d ? f : d; + #pragma omp atomic compare seq_cst fail(relaxed) + x = 12U < x ? 12U : x; + #pragma omp atomic compare + x = x == 7 ? 24 : x; + #pragma omp atomic compare + x = x == 123UL ? 256LL : x; + #pragma omp atomic compare + ld = ld == f ? f + 5.0L : ld; + #pragma omp atomic compare + if (x == 9) { x = 5; } + #pragma omp atomic compare + if (x > 5) { x = 5; } + #pragma omp atomic compare + if (7 > x) { x = 7; } + #pragma omp atomic compare update capture seq_cst fail(acquire) + v = d = f > d ? f : d; + #pragma omp atomic update capture compare + v = x = x < 24ULL ? 24ULL : x; + #pragma omp atomic compare, capture, update + v = x = x == e ? f : x; + #pragma omp atomic capture compare + { v = d; if (d > e) { d = e; } } + #pragma omp atomic compare capture + { if (e < d) { d = e; } v = d; } + #pragma omp atomic compare capture + { y = x; if (x == 42) { x = 7; } } + #pragma omp atomic capture compare weak + { if (x == 42) { x = 7; } y = x; } + #pragma omp atomic capture compare fail(seq_cst) + if (d == 8.0) { d = 16.0; } else { v = d; } + #pragma omp atomic capture compare + { r = x == 8; if (r) { x = 24; } } + #pragma omp atomic compare capture + { r = x == y; if (r) { x = y + 6; } else { z = x; } } +} diff --git a/gcc/testsuite/c-c++-common/gomp/atomic-26.c b/gcc/testsuite/c-c++-common/gomp/atomic-26.c new file mode 100644 index 0000000..c7e65db --- /dev/null +++ b/gcc/testsuite/c-c++-common/gomp/atomic-26.c @@ -0,0 +1,63 @@ +/* { dg-do compile { target c } } */ + +int x; +double d; + +double +foo (int y, double e, long double f) +{ + double v; + #pragma omp atomic compare compare /* { dg-error "too many 'compare' clauses" } */ + x = x > y ? y : x; + #pragma omp atomic compare fail(seq_cst) fail(seq_cst) /* { dg-error "too many 'fail' clauses" } */ + d = e > d ? e : d; + #pragma omp atomic compare,fail(seq_cst),fail(relaxed) /* { dg-error "too many 'fail' clauses" } */ + d = e > d ? e : d; + #pragma omp atomic compare weak weak /* { dg-error "too many 'weak' clauses" } */ + d = d == e ? e + 1.0 : d; + #pragma omp atomic read capture /* { dg-error "'capture' clause is incompatible with 'read' or 'write' clauses" } */ + v = d; + #pragma omp atomic capture, write /* { dg-error "'capture' clause is incompatible with 'read' or 'write' clauses" } */ + d = v; + #pragma omp atomic read compare /* { dg-error "'compare' clause is incompatible with 'read' or 'write' clauses" } */ + v = d; + #pragma omp atomic compare, write /* { dg-error "'compare' clause is incompatible with 'read' or 'write' clauses" } */ + d = v; + #pragma omp atomic read fail(seq_cst) /* { dg-error "'fail' clause requires 'compare' clause" } */ + v = d; + #pragma omp atomic fail(relaxed), write /* { dg-error "'fail' clause requires 'compare' clause" } */ + d = v; + #pragma omp atomic fail(relaxed) update /* { dg-error "'fail' clause requires 'compare' clause" } */ + d += 3.0; + #pragma omp atomic fail(relaxed) /* { dg-error "'fail' clause requires 'compare' clause" } */ + d += 3.0; + #pragma omp atomic capture fail(relaxed) /* { dg-error "'fail' clause requires 'compare' clause" } */ + v = d += 3.0; + #pragma omp atomic read weak /* { dg-error "'weak' clause requires 'compare' clause" } */ + v = d; + #pragma omp atomic weak, write /* { dg-error "'weak' clause requires 'compare' clause" } */ + d = v; + #pragma omp atomic weak update /* { dg-error "'weak' clause requires 'compare' clause" } */ + d += 3.0; + #pragma omp atomic weak /* { dg-error "'weak' clause requires 'compare' clause" } */ + d += 3.0; + #pragma omp atomic capture weak /* { dg-error "'weak' clause requires 'compare' clause" } */ + v = d += 3.0; + #pragma omp atomic compare,weak /* { dg-error "'weak' clause requires atomic equality comparison" } */ + d = e > d ? e : d; + #pragma omp atomic compare fail /* { dg-error "expected '\\\(' before end of line" } */ + d = e > d ? e : d; + #pragma omp atomic compare fail( /* { dg-error "expected 'seq_cst', 'acquire' or 'relaxed' before end of line" } */ + d = e > d ? e : d; + #pragma omp atomic compare fail() /* { dg-error "expected 'seq_cst', 'acquire' or 'relaxed' before '\\\)' token" } */ + d = e > d ? e : d; + #pragma omp atomic compare fail(foobar) /* { dg-error "expected 'seq_cst', 'acquire' or 'relaxed' before 'foobar'" } */ + d = e > d ? e : d; + #pragma omp atomic compare fail(acq_rel) /* { dg-error "expected 'seq_cst', 'acquire' or 'relaxed' before 'acq_rel'" } */ + d = e > d ? e : d; + #pragma omp atomic compare fail(release) /* { dg-error "expected 'seq_cst', 'acquire' or 'relaxed' before 'release'" } */ + d = e > d ? e : d; + #pragma omp atomic compare fail(seq_cst /* { dg-error "expected '\\\)' before end of line" } */ + d = e > d ? e : d; + return v; +} diff --git a/gcc/testsuite/c-c++-common/gomp/atomic-27.c b/gcc/testsuite/c-c++-common/gomp/atomic-27.c new file mode 100644 index 0000000..3d61717 --- /dev/null +++ b/gcc/testsuite/c-c++-common/gomp/atomic-27.c @@ -0,0 +1,41 @@ +/* PR middle-end/88968 */ +/* { dg-do compile { target c } } */ + +struct __attribute__((packed)) S { + unsigned int a : 16; + unsigned int b : 1; +} s; + +void +foo (int y, int z) +{ + #pragma omp atomic compare + s.a = s.a == y ? z : s.a; +} + +int +bar (int y, int z) +{ + int r; + #pragma omp atomic compare capture + { r = s.a == y; if (r) { s.a = z; } } + return r; +} + +int +baz (int y, int z) +{ + int v; + #pragma omp atomic compare capture + if (s.a == y) { s.a = z; } else { v = s.a; } + return v; +} + +int +qux (int y, int z) +{ + int v; + #pragma omp atomic compare capture + v = s.a = s.a == y ? z : s.a; + return v; +} diff --git a/gcc/testsuite/c-c++-common/gomp/atomic-28.c b/gcc/testsuite/c-c++-common/gomp/atomic-28.c new file mode 100644 index 0000000..50cf223 --- /dev/null +++ b/gcc/testsuite/c-c++-common/gomp/atomic-28.c @@ -0,0 +1,43 @@ +/* { dg-do compile { target c } } */ +/* { dg-additional-options "-O2 -fdump-tree-ompexp" } */ +/* { dg-final { scan-tree-dump-times "\.ATOMIC_COMPARE_EXCHANGE \\\(\[^\n\r]*, 4, 5, 5\\\);" 1 "ompexp" { target sync_int_long } } } */ +/* { dg-final { scan-tree-dump-times "\.ATOMIC_COMPARE_EXCHANGE \\\(\[^\n\r]*, 4, 4, 2\\\);" 1 "ompexp" { target sync_int_long } } } */ +/* { dg-final { scan-tree-dump-times "\.ATOMIC_COMPARE_EXCHANGE \\\(\[^\n\r]*, 260, 5, 0\\\);" 1 "ompexp" { target sync_int_long } } } */ +/* { dg-final { scan-tree-dump-times "\.ATOMIC_COMPARE_EXCHANGE \\\(\[^\n\r]*, 4, 0, 0\\\);" 1 "ompexp" { target sync_int_long } } } */ +/* { dg-final { scan-tree-dump-not "__atomic_load_4 \\\(" "ompexp" { target sync_int_long } } } */ + +int x; + +void +foo (int y, int z) +{ + #pragma omp atomic compare seq_cst + x = x == y ? z : x; +} + +int +bar (int y, int z) +{ + int r; + #pragma omp atomic compare capture acq_rel fail (acquire) + { r = x == y; if (r) { x = z; } } + return r; +} + +int +baz (int y, int z) +{ + int v; + #pragma omp atomic compare capture seq_cst fail (relaxed) weak + if (x == y) { x = z; } else { v = x; } + return v; +} + +int +qux (int y, int z) +{ + int v; + #pragma omp atomic compare capture + v = x = x == y ? z : x; + return v; +} diff --git a/gcc/testsuite/c-c++-common/gomp/atomic-29.c b/gcc/testsuite/c-c++-common/gomp/atomic-29.c new file mode 100644 index 0000000..e574c48 --- /dev/null +++ b/gcc/testsuite/c-c++-common/gomp/atomic-29.c @@ -0,0 +1,43 @@ +/* { dg-do compile { target c } } */ +/* { dg-additional-options "-O2 -fdump-tree-ompexp" } */ +/* { dg-final { scan-tree-dump-times "\.ATOMIC_COMPARE_EXCHANGE \\\(\[^\n\r]*, 8, 5, 5\\\);" 1 "ompexp" { target sync_int_long } } } */ +/* { dg-final { scan-tree-dump-times "\.ATOMIC_COMPARE_EXCHANGE \\\(\[^\n\r]*, 8, 4, 2\\\);" 1 "ompexp" { target sync_int_long } } } */ +/* { dg-final { scan-tree-dump-times "\.ATOMIC_COMPARE_EXCHANGE \\\(\[^\n\r]*, 264, 5, 0\\\);" 1 "ompexp" { target sync_int_long } } } */ +/* { dg-final { scan-tree-dump-times "\.ATOMIC_COMPARE_EXCHANGE \\\(\[^\n\r]*, 8, 0, 0\\\);" 1 "ompexp" { target sync_int_long } } } */ +/* { dg-final { scan-tree-dump-not "__atomic_load_8 \\\(" "ompexp" { target sync_int_long } } } */ + +double x; + +void +foo (double y, double z) +{ + #pragma omp atomic compare seq_cst + x = x == y ? z : x; +} + +double +bar (double y, double z) +{ + int r; + #pragma omp atomic compare capture acq_rel fail (acquire) + { r = x == y; if (r) { x = z; } } + return r; +} + +double +baz (double y, double z) +{ + double v; + #pragma omp atomic compare capture seq_cst fail (relaxed) weak + if (x == y) { x = z; } else { v = x; } + return v; +} + +double +qux (double y, double z) +{ + double v; + #pragma omp atomic compare capture + v = x = x == y ? z : x; + return v; +} diff --git a/gcc/testsuite/c-c++-common/gomp/atomic-30.c b/gcc/testsuite/c-c++-common/gomp/atomic-30.c new file mode 100644 index 0000000..f36de70 --- /dev/null +++ b/gcc/testsuite/c-c++-common/gomp/atomic-30.c @@ -0,0 +1,137 @@ +/* { dg-do compile { target c } } */ + +int x; +double d, g; + +double +foo (int y, double e, long double f) +{ + double v; + int r, r2 = 0; + #pragma omp atomic capture compare + v = if (d == e) { d = f; }; /* { dg-error "expected expression" } */ + #pragma omp atomic compare + if; /* { dg-error "expected '\\\(' before ';' token" } */ + #pragma omp atomic compare + if (d >= e) { d = e; } /* { dg-error "expected '==', '<' or '>' comparison in 'if' condition" } */ + #pragma omp atomic compare + if (d <= e) { d = e; } /* { dg-error "expected '==', '<' or '>' comparison in 'if' condition" } */ + #pragma omp atomic compare + if (d != e) { d = e; } /* { dg-error "expected '==', '<' or '>' comparison in 'if' condition" } */ + #pragma omp atomic compare + if (d + e) { d = e; } /* { dg-error "expected '==', '<' or '>' comparison in 'if' condition" } */ + #pragma omp atomic capture compare + { r = d >= e; if (r) { d = f; } } /* { dg-error "expected '==', '<' or '>' comparison in 'if' condition" } */ + #pragma omp atomic capture compare + { r = d <= e; if (r) { d = f; } } /* { dg-error "expected '==', '<' or '>' comparison in 'if' condition" } */ + #pragma omp atomic capture compare + { r = d > e; if (r) { d = f; } } /* { dg-error "expected '==', '<' or '>' comparison in 'if' condition" } */ + #pragma omp atomic capture compare + { r = d < e; if (r) { d = f; } } /* { dg-error "expected '==', '<' or '>' comparison in 'if' condition" } */ + #pragma omp atomic capture compare + { r = d != e; if (r) { d = f; } } /* { dg-error "expected '==', '<' or '>' comparison in 'if' condition" } */ + #pragma omp atomic capture compare + { r = d + e; if (r) { d = f; } } /* { dg-error "expected '==', '<' or '>' comparison in 'if' condition" } */ + #pragma omp atomic capture compare + { r = d == e; if (r2) { d = f; } } /* { dg-error "invalid form of '#pragma omp atomic compare' before '\{' token" } */ + #pragma omp atomic capture compare + if (d > e) { d = e; } /* { dg-error "expected '==' comparison in 'if' condition" } */ + #pragma omp atomic capture compare + if (d < e) { d = e; } /* { dg-error "expected '==' comparison in 'if' condition" } */ + #pragma omp atomic compare + if (d < e) d = e; /* { dg-error "expected '\{' before 'd'" } */ + #pragma omp atomic compare + if (d == e) d = e + 1.0; /* { dg-error "expected '\{' before 'd'" } */ + #pragma omp atomic compare + if (d < e) { d += e; } /* { dg-error "expected '=' before '\\\+=' token" } */ + #pragma omp atomic compare + if (d < e) { d = e }; /* { dg-error "expected ';' before '\}' token" } */ + #pragma omp atomic compare + if (d < e) { d = e; e = 1.0; } /* { dg-error "expected '\}' before 'e'" } */ + #pragma omp atomic compare + if (e == d) { d = f; }; /* { dg-error "invalid form of '#pragma omp atomic compare' before ';' token" } */ + #pragma omp atomic compare + if (e == d) { g = f; }; /* { dg-error "invalid form of '#pragma omp atomic compare' before ';' token" } */ + #pragma omp atomic compare + if (d < e) { g = e; }; /* { dg-error "invalid form of '#pragma omp atomic compare' before ';' token" } */ + #pragma omp atomic compare + if (d > e) { g = e; }; /* { dg-error "invalid form of '#pragma omp atomic compare' before ';' token" } */ + #pragma omp atomic compare + if (d < e) { d = g; }; /* { dg-error "invalid form of '#pragma omp atomic compare' before ';' token" } */ + #pragma omp atomic compare + if (d > e) { d = g; }; /* { dg-error "invalid form of '#pragma omp atomic compare' before ';' token" } */ + #pragma omp atomic compare + if (d == e) { d = f; } else ; /* { dg-error "unexpected 'else'" } */ + #pragma omp atomic compare capture + { if (d == e) { d = f; } else { v = d; } v = d; } /* { dg-error "unexpected 'else'" } */ + #pragma omp atomic compare + if (d < e) { d = e; } else { v = d; } /* { dg-error "unexpected 'else'" } */ + #pragma omp atomic compare capture + if (d == e) { d = f; } else v = d; /* { dg-error "expected '\{' before 'v'" } */ + #pragma omp atomic compare capture + if (d == e) { d = f; } else { v += d; } /* { dg-error "expected '=' before '\\\+=' token" } */ + #pragma omp atomic compare capture + if (d == e) { d = f; } else { v = e; } /* { dg-error "invalid form of '#pragma omp atomic compare' before ';' token" } */ + #pragma omp atomic compare capture + if (d == e) { d = f; } else { v = d }; /* { dg-error "expected ';' before '\}' token" } */ + #pragma omp atomic compare capture + if (d == e) { d = f; }; /* { dg-error "expected 'else' before ';' token" } */ + #pragma omp atomic compare + x++; /* { dg-error "invalid form of 'pragma omp atomic compare'" } */ + #pragma omp atomic compare + x--; /* { dg-error "invalid form of 'pragma omp atomic compare'" } */ + #pragma omp atomic compare + ++x; /* { dg-error "invalid form of 'pragma omp atomic compare'" } */ + #pragma omp atomic compare + --x; /* { dg-error "invalid form of 'pragma omp atomic compare'" } */ + #pragma omp atomic compare + x += 3; /* { dg-error "expected '=' before '\\\+=' token" } */ + #pragma omp atomic compare + x -= 5; /* { dg-error "expected '=' before '-=' token" } */ + #pragma omp atomic compare + x *= 2; /* { dg-error "expected '=' before '\\\*=' token" } */ + #pragma omp atomic compare + x |= 5; /* { dg-error "expected '=' before '\\\|=' token" } */ + #pragma omp atomic compare + x &= ~5; /* { dg-error "expected '=' before '\\\&=' token" } */ + #pragma omp atomic compare + x ^= 5; /* { dg-error "expected '=' before '\\\^=' token" } */ + #pragma omp atomic compare + x = x + 3; /* { dg-error "invalid form of '#pragma omp atomic' before ';' token" } */ + #pragma omp atomic compare + x = x - 5; /* { dg-error "invalid form of '#pragma omp atomic' before ';' token" } */ + #pragma omp atomic compare + x = 2 * x; /* { dg-error "invalid form of '#pragma omp atomic' before ';' token" } */ + #pragma omp atomic compare + x = 5 | x; /* { dg-error "invalid form of '#pragma omp atomic' before ';' token" } */ + #pragma omp atomic compare + x = x & ~5; /* { dg-error "invalid form of '#pragma omp atomic' before ';' token" } */ + #pragma omp atomic compare + x = x | 5; /* { dg-error "invalid form of '#pragma omp atomic' before ';' token" } */ + #pragma omp atomic compare + x = x >= 5 ? 5 : x; /* { dg-error "invalid form of '#pragma omp atomic' before ';' token" } */ + #pragma omp atomic compare + x = x <= 5 ? 5 : x; /* { dg-error "invalid form of '#pragma omp atomic' before ';' token" } */ + #pragma omp atomic compare + x = x != 5 ? 7 : x; /* { dg-error "invalid form of '#pragma omp atomic' before ';' token" } */ + #pragma omp atomic compare + x = 5 == x ? 7 : x; /* { dg-error "invalid form of '#pragma omp atomic' before ';' token" } */ + #pragma omp atomic compare + x = x == 5 ? x : 7; /* { dg-error "invalid form of '#pragma omp atomic' before ';' token" } */ + #pragma omp atomic compare + x = x == 5 ? 9 : 7; /* { dg-error "invalid form of '#pragma omp atomic' before ';' token" } */ + #pragma omp atomic compare + x = x > 5 ? 6 : x; /* { dg-error "invalid form of '#pragma omp atomic' before ';' token" } */ + #pragma omp atomic compare + x = x < 5 ? 6 : x; /* { dg-error "invalid form of '#pragma omp atomic' before ';' token" } */ + #pragma omp atomic compare + x = x > 5 ? x : 6; /* { dg-error "invalid form of '#pragma omp atomic' before ';' token" } */ + #pragma omp atomic compare + x = x < 5 ? x : 6; /* { dg-error "invalid form of '#pragma omp atomic' before ';' token" } */ + #pragma omp atomic capture + r = x == 5; /* { dg-error "invalid operator for '#pragma omp atomic' before '==' token" } */ + #pragma omp atomic capture compare + r = x == 5; /* { dg-error "expected '=' before '==' token" } */ + #pragma omp atomic capture compare /* { dg-error "'#pragma omp atomic compare capture' with non-integral comparison result" } */ + { v = x == 5; if (v) { x = 6; } } +} diff --git a/gcc/testsuite/g++.dg/gomp/atomic-18.C b/gcc/testsuite/g++.dg/gomp/atomic-18.C index 896cf46..adaee84 100644 --- a/gcc/testsuite/g++.dg/gomp/atomic-18.C +++ b/gcc/testsuite/g++.dg/gomp/atomic-18.C @@ -1,6 +1,7 @@ // { dg-do compile } // { dg-additional-options "-fdump-tree-original" } -// { dg-final { scan-tree-dump-times "omp atomic release" 5 "original" } } +// { dg-final { scan-tree-dump-times "omp atomic release" 4 "original" } } +// { dg-final { scan-tree-dump-times "omp atomic acq_rel" 1 "original" } } // { dg-final { scan-tree-dump-times "omp atomic seq_cst" 1 "original" } } // { dg-final { scan-tree-dump-times "omp atomic relaxed" 2 "original" } } // { dg-final { scan-tree-dump-times "omp atomic capture acq_rel" 3 "original" } } diff --git a/gcc/testsuite/gcc.dg/gomp/atomic-5.c b/gcc/testsuite/gcc.dg/gomp/atomic-5.c index 70cfb35..36d1422 100644 --- a/gcc/testsuite/gcc.dg/gomp/atomic-5.c +++ b/gcc/testsuite/gcc.dg/gomp/atomic-5.c @@ -27,7 +27,7 @@ void f1(void) #pragma omp atomic bar() += 1; /* { dg-error "lvalue required" } */ #pragma omp atomic a /* { dg-error "expected end of line" } */ - x++; /* { dg-error "expected 'read', 'write', 'update', 'capture', 'seq_cst', 'acq_rel', 'release', 'relaxed' or 'hint' clause" "" { target *-*-* } .-1 } */ + x++; /* { dg-error "expected 'read', 'write', 'update', 'capture', 'compare', 'weak', 'fail', 'seq_cst', 'acq_rel', 'release', 'relaxed' or 'hint' clause" "" { target *-*-* } .-1 } */ #pragma omp atomic ; /* { dg-error "expected expression" } */ #pragma omp atomic diff --git a/gcc/tree-core.h b/gcc/tree-core.h index 445a09f..d3d2a8d 100644 --- a/gcc/tree-core.h +++ b/gcc/tree-core.h @@ -583,8 +583,17 @@ enum omp_memory_order { OMP_MEMORY_ORDER_ACQUIRE, OMP_MEMORY_ORDER_RELEASE, OMP_MEMORY_ORDER_ACQ_REL, - OMP_MEMORY_ORDER_SEQ_CST -}; + OMP_MEMORY_ORDER_SEQ_CST, + OMP_MEMORY_ORDER_MASK = 7, + OMP_FAIL_MEMORY_ORDER_UNSPECIFIED = OMP_MEMORY_ORDER_UNSPECIFIED * 8, + OMP_FAIL_MEMORY_ORDER_RELAXED = OMP_MEMORY_ORDER_RELAXED * 8, + OMP_FAIL_MEMORY_ORDER_ACQUIRE = OMP_MEMORY_ORDER_ACQUIRE * 8, + OMP_FAIL_MEMORY_ORDER_RELEASE = OMP_MEMORY_ORDER_RELEASE * 8, + OMP_FAIL_MEMORY_ORDER_ACQ_REL = OMP_MEMORY_ORDER_ACQ_REL * 8, + OMP_FAIL_MEMORY_ORDER_SEQ_CST = OMP_MEMORY_ORDER_SEQ_CST * 8, + OMP_FAIL_MEMORY_ORDER_MASK = OMP_MEMORY_ORDER_MASK * 8 +}; +#define OMP_FAIL_MEMORY_ORDER_SHIFT 3 /* There is a TYPE_QUAL value for each type qualifier. They can be combined by bitwise-or to form the complete set of qualifiers for a diff --git a/gcc/tree-pretty-print.c b/gcc/tree-pretty-print.c index 5495942..32225b8 100644 --- a/gcc/tree-pretty-print.c +++ b/gcc/tree-pretty-print.c @@ -1492,7 +1492,7 @@ dump_block_node (pretty_printer *pp, tree block, int spc, dump_flags_t flags) void dump_omp_atomic_memory_order (pretty_printer *pp, enum omp_memory_order mo) { - switch (mo) + switch (mo & OMP_MEMORY_ORDER_MASK) { case OMP_MEMORY_ORDER_RELAXED: pp_string (pp, " relaxed"); @@ -1514,6 +1514,22 @@ dump_omp_atomic_memory_order (pretty_printer *pp, enum omp_memory_order mo) default: gcc_unreachable (); } + switch (mo & OMP_FAIL_MEMORY_ORDER_MASK) + { + case OMP_FAIL_MEMORY_ORDER_RELAXED: + pp_string (pp, " fail(relaxed)"); + break; + case OMP_FAIL_MEMORY_ORDER_SEQ_CST: + pp_string (pp, " fail(seq_cst)"); + break; + case OMP_FAIL_MEMORY_ORDER_ACQUIRE: + pp_string (pp, " fail(acquire)"); + break; + case OMP_FAIL_MEMORY_ORDER_UNSPECIFIED: + break; + default: + gcc_unreachable (); + } } /* Helper to dump a MEM_REF node. */ @@ -3629,6 +3645,8 @@ dump_generic_node (pretty_printer *pp, tree node, int spc, dump_flags_t flags, case OMP_ATOMIC: pp_string (pp, "#pragma omp atomic"); + if (OMP_ATOMIC_WEAK (node)) + pp_string (pp, " weak"); dump_omp_atomic_memory_order (pp, OMP_ATOMIC_MEMORY_ORDER (node)); newline_and_indent (pp, spc + 2); dump_generic_node (pp, TREE_OPERAND (node, 0), spc, flags, false); @@ -3649,6 +3667,8 @@ dump_generic_node (pretty_printer *pp, tree node, int spc, dump_flags_t flags, case OMP_ATOMIC_CAPTURE_OLD: case OMP_ATOMIC_CAPTURE_NEW: pp_string (pp, "#pragma omp atomic capture"); + if (OMP_ATOMIC_WEAK (node)) + pp_string (pp, " weak"); dump_omp_atomic_memory_order (pp, OMP_ATOMIC_MEMORY_ORDER (node)); newline_and_indent (pp, spc + 2); dump_generic_node (pp, TREE_OPERAND (node, 0), spc, flags, false); @@ -1529,6 +1529,11 @@ class auto_suppress_location_wrappers (TREE_RANGE_CHECK (NODE, OMP_ATOMIC, \ OMP_ATOMIC_CAPTURE_NEW)->base.u.omp_atomic_memory_order) +/* Weak clause on OMP_ATOMIC*. */ +#define OMP_ATOMIC_WEAK(NODE) \ + (TREE_RANGE_CHECK (NODE, OMP_ATOMIC, \ + OMP_ATOMIC_CAPTURE_NEW)->base.public_flag) + /* True on a PRIVATE clause if its decl is kept around for debugging information only and its DECL_VALUE_EXPR is supposed to point to what it has been remapped to. */ diff --git a/libgomp/testsuite/libgomp.c-c++-common/atomic-19.c b/libgomp/testsuite/libgomp.c-c++-common/atomic-19.c new file mode 100644 index 0000000..3aee228 --- /dev/null +++ b/libgomp/testsuite/libgomp.c-c++-common/atomic-19.c @@ -0,0 +1,274 @@ +// { dg-do run { target c } } + +extern +#ifdef __cplusplus +"C" +#endif +void abort (void); +int x = 6; +int w, y; + +int * +foo (void) +{ + if (w) + abort (); + return &y; +} + +int +main () +{ + int v, r; + #pragma omp atomic compare + x = x > 8 ? 8 : x; + #pragma omp atomic read + v = x; + if (v != 6) + abort (); + #pragma omp atomic compare + x = x > 4 ? 4 : x; + #pragma omp atomic read + v = x; + if (v != 4) + abort (); + #pragma omp atomic compare capture + v = x = x < 8 ? 8 : x; + if (v != 8) + abort (); + #pragma omp atomic read + v = x; + if (v != 8) + abort (); + #pragma omp atomic capture compare + { v = x; x = x < 12 ? 12 : x; } + if (v != 8) + abort (); + #pragma omp atomic read + v = x; + if (v != 12) + abort (); + #pragma omp atomic capture compare + { v = x; x = x < 4 ? 4 : x; } + if (v != 12) + abort (); + #pragma omp atomic read + v = x; + if (v != 12) + abort (); + #pragma omp atomic write + x = -32; + #pragma omp atomic capture compare seq_cst fail(relaxed) + { x = 12U < x ? 12U : x; v = x; } + if (v != 12) + abort (); + #pragma omp atomic read + v = x; + if (v != 12) + abort (); + #pragma omp atomic compare + x = x == 12 ? 16 : x; + #pragma omp atomic read + v = x; + if (v != 16) + abort (); + r = 57; + #pragma omp atomic compare capture + v = x = x == 15 ? r + 7 : x; + if (v != 16) + abort (); + #pragma omp atomic read + v = x; + if (v != 16) + abort (); + #pragma omp atomic capture, update, compare seq_cst fail(acquire) + { v = x; x = x == 73ULL - r ? 12LL : x; } + if (v != 16) + abort (); + #pragma omp atomic read + v = x; + if (v != 12) + abort (); + #pragma omp atomic update, compare, capture + { x = x == 69LL - r ? (unsigned char) 6 : x; v = x; } + if (v != 6) + abort (); + #pragma omp atomic read + v = x; + if (v != 6) + abort (); + #pragma omp atomic compare + if (x > 8) { x = 8; } + #pragma omp atomic read + v = x; + if (v != 6) + abort (); + #pragma omp atomic compare + if (x > 4) { x = 4; } + #pragma omp atomic read + v = x; + if (v != 4) + abort (); + #pragma omp atomic compare capture + { if (x < 8) { x = 8; } v = x; } + if (v != 8) + abort (); + #pragma omp atomic read + v = x; + if (v != 8) + abort (); + #pragma omp atomic capture compare + { v = x; if (x < 12) { x = 12; } } + if (v != 8) + abort (); + #pragma omp atomic read + v = x; + if (v != 12) + abort (); + #pragma omp atomic capture compare + { v = x; if (x < 4) { x = 4; } } + if (v != 12) + abort (); + #pragma omp atomic read + v = x; + if (v != 12) + abort (); + #pragma omp atomic write + x = -32; + #pragma omp atomic capture compare seq_cst fail(relaxed) + { if (12U < x) { x = 12U; } v = x; } + if (v != 12) + abort (); + #pragma omp atomic read + v = x; + if (v != 12) + abort (); + #pragma omp atomic compare + if (x == 12) { x = 16; } + #pragma omp atomic read + v = x; + if (v != 16) + abort (); + r = 57; + #pragma omp atomic compare capture + { if (x == 15) { x = r + 7; } v = x; } + if (v != 16) + abort (); + #pragma omp atomic read + v = x; + if (v != 16) + abort (); + #pragma omp atomic capture, update, compare seq_cst fail(acquire) + { v = x; if (x == 73ULL - r) { x = 12LL; } } + if (v != 16) + abort (); + #pragma omp atomic read + v = x; + if (v != 12) + abort (); + #pragma omp atomic update, compare, capture + { if (x == 69LL - r) { x = (unsigned char) 6; } v = x; } + if (v != 6) + abort (); + #pragma omp atomic read + v = x; + if (v != 6) + abort (); + v = 24; + #pragma omp atomic compare capture + if (x == 12) { x = 16; } else { v = x; } + if (v != 6) + abort (); + v = 32; + #pragma omp atomic read + v = x; + if (v != 6) + abort (); + v = 147; + #pragma omp atomic capture compare + if (x == 6) { x = 57; } else { v = x; } + if (v != 147) + abort (); + #pragma omp atomic read + v = x; + if (v != 57) + abort (); + #pragma omp atomic update, capture, compare, weak, seq_cst, fail (relaxed) + { r = x == 137; if (r) { x = 174; } } + if (r) + abort (); + #pragma omp atomic read + v = x; + if (v != 57) + abort (); + #pragma omp atomic compare capture fail (relaxed) + { r = x == 57; if (r) { x = 6; } } + if (r != 1) + abort (); + #pragma omp atomic read + v = x; + if (v != 6) + abort (); + v = -5; + #pragma omp atomic capture compare + { r = x == 17; if (r) { x = 25; } else { v = x; } } + if (r || v != 6) + abort (); + #pragma omp atomic read + v = x; + if (v != 6) + abort (); + v = 15; + #pragma omp atomic capture compare + { r = x == 6; if (r) { x = 23; } else { v = x; } } + if (r != 1 || v != 15) + abort (); + #pragma omp atomic read + v = x; + if (v != 23) + abort (); + w = 1; + #pragma omp atomic compare capture + if (x == 23) { x = 57; } else { foo ()[0] = x; } + #pragma omp atomic read + v = x; + if (v != 57) + abort (); + #pragma omp atomic capture update compare + { r = x == 57; if (r) { x = 23; } else { foo ()[0] = x; } } + if (r != 1) + abort (); + #pragma omp atomic read + v = x; + if (v != 23) + abort (); + w = 0; + #pragma omp atomic compare capture + if (x == 24) { x = 57; } else { foo ()[0] = x; } + if (y != 23) + abort (); + #pragma omp atomic read + v = x; + if (v != 23) + abort (); + y = -5; + #pragma omp atomic capture update compare + { + r = x == 57; + if (r) + { + x = 27; + } + else + { + foo ()[0] = x; + } + } + if (r || y != 23) + abort (); + #pragma omp atomic read + v = x; + if (v != 23) + abort (); + return 0; +} diff --git a/libgomp/testsuite/libgomp.c-c++-common/atomic-20.c b/libgomp/testsuite/libgomp.c-c++-common/atomic-20.c new file mode 100644 index 0000000..571a714 --- /dev/null +++ b/libgomp/testsuite/libgomp.c-c++-common/atomic-20.c @@ -0,0 +1,203 @@ +// { dg-do run { target c } } + +extern +#ifdef __cplusplus +"C" +#endif +void abort (void); +float x = 6.0f; + +int +main () +{ + float v; + int r; + #pragma omp atomic compare + x = x > 8.0f ? 8.0f : x; + #pragma omp atomic read + v = x; + if (v != 6.0f) + abort (); + #pragma omp atomic compare + x = x > 4.0f ? 4.0f : x; + #pragma omp atomic read + v = x; + if (v != 4.0f) + abort (); + #pragma omp atomic compare capture + v = x = x < 8.0f ? 8.0f : x; + if (v != 8.0f) + abort (); + #pragma omp atomic read + v = x; + if (v != 8) + abort (); + #pragma omp atomic capture compare + { v = x; x = x < 12.0f ? 12.0f : x; } + if (v != 8.0f) + abort (); + #pragma omp atomic read + v = x; + if (v != 12.0f) + abort (); + #pragma omp atomic capture compare + { v = x; x = x < 4.0f ? 4.0f : x; } + if (v != 12.0f) + abort (); + #pragma omp atomic read + v = x; + if (v != 12.0f) + abort (); + #pragma omp atomic compare + x = x == 12.0 ? 16.0L : x; + #pragma omp atomic read + v = x; + if (v != 16.0) + abort (); + r = 57; + #pragma omp atomic compare capture + v = x = x == 15.0f ? r + 7.0f : x; + if (v != 16.0f) + abort (); + #pragma omp atomic read + v = x; + if (v != 16.0f) + abort (); + #pragma omp atomic capture, update, compare seq_cst fail(acquire) + { v = x; x = x == 73.0L - r ? 12.0f : x; } + if (v != 16.0f) + abort (); + #pragma omp atomic read + v = x; + if (v != 12.0f) + abort (); + #pragma omp atomic update, compare, capture + { x = x == 69.0 - r ? 6.0f : x; v = x; } + if (v != 6.0f) + abort (); + #pragma omp atomic read + v = x; + if (v != 6.0f) + abort (); + #pragma omp atomic compare + if (x > 8.0f) { x = 8.0f; } + #pragma omp atomic read + v = x; + if (v != 6.0f) + abort (); + #pragma omp atomic compare + if (x > 4.0) { x = 4.0; } + #pragma omp atomic read + v = x; + if (v != 4.0f) + abort (); + #pragma omp atomic compare capture + { if (x < 8.0f) { x = 8.0f; } v = x; } + if (v != 8.0f) + abort (); + #pragma omp atomic read + v = x; + if (v != 8.0f) + abort (); + #pragma omp atomic capture compare + { v = x; if (x < 12.0f) { x = 12.0f; } } + if (v != 8.0f) + abort (); + #pragma omp atomic read + v = x; + if (v != 12.0f) + abort (); + #pragma omp atomic capture compare + { v = x; if (x < 4.0L) { x = 4.0L; } } + if (v != 12.0f) + abort (); + #pragma omp atomic read + v = x; + if (v != 12.0f) + abort (); + #pragma omp atomic compare + if (x == 12.0f) { x = 16.0L; } + #pragma omp atomic read + v = x; + if (v != 16.0f) + abort (); + r = 57.0; + #pragma omp atomic compare capture + { if (x == 15.0f) { x = r + 7.0f; } v = x; } + if (v != 16.0f) + abort (); + #pragma omp atomic read + v = x; + if (v != 16.0f) + abort (); + #pragma omp atomic capture, update, compare seq_cst fail(acquire) + { v = x; if (x == 73.0L - r) { x = 12.0L; } } + if (v != 16.0f) + abort (); + #pragma omp atomic read + v = x; + if (v != 12.0f) + abort (); + #pragma omp atomic update, compare, capture + { if (x == 69.0L - r) { x = 6.0; } v = x; } + if (v != 6.0f) + abort (); + #pragma omp atomic read + v = x; + if (v != 6.0f) + abort (); + v = 24; + #pragma omp atomic compare capture + if (x == 12.0f) { x = 16.0f; } else { v = x; } + if (v != 6.0f) + abort (); + v = 32.0f; + #pragma omp atomic read + v = x; + if (v != 6.0f) + abort (); + v = 147.0f; + #pragma omp atomic capture compare + if (x == 6.0f) { x = 57.0f; } else { v = x; } + if (v != 147.0f) + abort (); + #pragma omp atomic read + v = x; + if (v != 57.0f) + abort (); + #pragma omp atomic update, capture, compare, weak, seq_cst, fail (relaxed) + { r = x == 137.0f; if (r) { x = 174.0f; } } + if (r) + abort (); + #pragma omp atomic read + v = x; + if (v != 57.0f) + abort (); + #pragma omp atomic compare capture fail (relaxed) + { r = x == 57.0f; if (r) { x = 6.0f; } } + if (r != 1) + abort (); + #pragma omp atomic read + v = x; + if (v != 6.0f) + abort (); + v = -5.0f; + #pragma omp atomic capture compare + { r = x == 17.0L; if (r) { x = 25.0; } else { v = x; } } + if (r || v != 6.0f) + abort (); + #pragma omp atomic read + v = x; + if (v != 6.0f) + abort (); + v = 15.0f; + #pragma omp atomic capture compare + { r = x == 6.0f; if (r) { x = 23.0f; } else { v = x; } } + if (r != 1 || v != 15.0f) + abort (); + #pragma omp atomic read + v = x; + if (v != 23.0f) + abort (); + return 0; +} diff --git a/libgomp/testsuite/libgomp.c-c++-common/atomic-21.c b/libgomp/testsuite/libgomp.c-c++-common/atomic-21.c new file mode 100644 index 0000000..87216e2 --- /dev/null +++ b/libgomp/testsuite/libgomp.c-c++-common/atomic-21.c @@ -0,0 +1,49 @@ +// { dg-do run { target c } } + +double d; +long double ld; + +int +main () +{ + double e = __builtin_copysign (0.0, -1.0), v; + long double le = __builtin_copysignl (0.0L, -1.0L), lv; + if (__builtin_memcmp (&d, &e, sizeof (d)) != 0) + { + /* Verify == comparison for atomics is done as with memcmp. */ + #pragma omp atomic compare + d = d == e ? 5.0 : d; + #pragma omp atomic read + v = d; + if (v != 0.0) + __builtin_abort (); + #pragma omp atomic compare + d = d == 0.0 ? 5.0 : d; + #pragma omp atomic read + v = d; + if (v != 5.0) + __builtin_abort (); + } + if (__builtin_memcmp (&ld, &le, sizeof (ld)) != 0) + { + __builtin_memset (&ld, 0xff, sizeof (ld)); + #pragma omp atomic write + ld = 0.0L; + __asm volatile ("" : : "g" (&ld) : "memory"); + /* Verify == comparison for atomics is done as with memcmp + with __builtin_clear_padding if needed. */ + #pragma omp atomic compare + ld = ld == le ? 5.0L : ld; + #pragma omp atomic read + lv = ld; + if (lv != 0.0L) + __builtin_abort (); + #pragma omp atomic compare + ld = ld == 0.0L ? 5.0L : ld; + #pragma omp atomic read + lv = ld; + if (lv != 5.0L) + __builtin_abort (); + } + return 0; +} |