aboutsummaryrefslogtreecommitdiff
path: root/gcc/c-family/c-omp.c
diff options
context:
space:
mode:
authorJakub Jelinek <jakub@redhat.com>2021-09-10 20:41:33 +0200
committerJakub Jelinek <jakub@redhat.com>2021-09-10 20:41:33 +0200
commit8122fbff770bcff183a9c3c72e8092c0ca32150b (patch)
treece01fc417dc7920da04a312ab2abcd350adec6c0 /gcc/c-family/c-omp.c
parentb7f84702b364d49824ca97d4a2fc01567301d784 (diff)
downloadgcc-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.
Diffstat (limited to 'gcc/c-family/c-omp.c')
-rw-r--r--gcc/c-family/c-omp.c173
1 files changed, 167 insertions, 6 deletions
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;
}