diff options
Diffstat (limited to 'gcc/c-family/c-omp.c')
-rw-r--r-- | gcc/c-family/c-omp.c | 92 |
1 files changed, 82 insertions, 10 deletions
diff --git a/gcc/c-family/c-omp.c b/gcc/c-family/c-omp.c index 977cb0ea153..eef7ac0c769 100644 --- a/gcc/c-family/c-omp.c +++ b/gcc/c-family/c-omp.c @@ -181,6 +181,7 @@ c_finish_omp_atomic (location_t loc, enum tree_code code, bool test) { tree x, type, addr, pre = NULL_TREE; + HOST_WIDE_INT bitpos = 0, bitsize = 0; if (lhs == error_mark_node || rhs == error_mark_node || v == error_mark_node || lhs1 == error_mark_node @@ -209,6 +210,29 @@ c_finish_omp_atomic (location_t loc, enum tree_code code, opcode = TRUNC_DIV_EXPR; /* ??? Validate that rhs does not overlap lhs. */ + tree blhs = NULL; + if (TREE_CODE (lhs) == COMPONENT_REF + && TREE_CODE (TREE_OPERAND (lhs, 1)) == FIELD_DECL + && DECL_C_BIT_FIELD (TREE_OPERAND (lhs, 1)) + && DECL_BIT_FIELD_REPRESENTATIVE (TREE_OPERAND (lhs, 1))) + { + tree field = TREE_OPERAND (lhs, 1); + tree repr = DECL_BIT_FIELD_REPRESENTATIVE (field); + if (tree_fits_uhwi_p (DECL_FIELD_OFFSET (field)) + && tree_fits_uhwi_p (DECL_FIELD_OFFSET (repr))) + bitpos = (tree_to_uhwi (DECL_FIELD_OFFSET (field)) + - tree_to_uhwi (DECL_FIELD_OFFSET (repr))) * BITS_PER_UNIT; + else + bitpos = 0; + bitpos += (tree_to_uhwi (DECL_FIELD_BIT_OFFSET (field)) + - tree_to_uhwi (DECL_FIELD_BIT_OFFSET (repr))); + gcc_assert (tree_fits_shwi_p (DECL_SIZE (field))); + bitsize = tree_to_shwi (DECL_SIZE (field)); + blhs = lhs; + type = TREE_TYPE (repr); + lhs = build3 (COMPONENT_REF, TREE_TYPE (repr), TREE_OPERAND (lhs, 0), + repr, TREE_OPERAND (lhs, 2)); + } /* Take and save the address of the lhs. From then on we'll reference it via indirection. */ @@ -228,13 +252,18 @@ c_finish_omp_atomic (location_t loc, enum tree_code code, DECL_CONTEXT (var) = current_function_decl; addr = build4 (TARGET_EXPR, TREE_TYPE (addr), var, addr, NULL, NULL); } + tree orig_lhs = lhs; lhs = build_indirect_ref (loc, addr, RO_NULL); + tree new_lhs = lhs; if (code == OMP_ATOMIC_READ) { x = build1 (OMP_ATOMIC_READ, type, addr); SET_EXPR_LOCATION (x, loc); OMP_ATOMIC_SEQ_CST (x) = seq_cst; + if (blhs) + x = build3_loc (loc, BIT_FIELD_REF, TREE_TYPE (blhs), x, + bitsize_int (bitsize), bitsize_int (bitpos)); return build_modify_expr (loc, v, NULL_TREE, NOP_EXPR, loc, x, NULL_TREE); } @@ -242,14 +271,25 @@ c_finish_omp_atomic (location_t loc, enum tree_code code, /* There are lots of warnings, errors, and conversions that need to happen in the course of interpreting a statement. Use the normal mechanisms to do this, and then take it apart again. */ - if (swapped) + if (blhs) { - rhs = build_binary_op (loc, opcode, rhs, lhs, 1); + lhs = build3_loc (loc, BIT_FIELD_REF, TREE_TYPE (blhs), lhs, + bitsize_int (bitsize), bitsize_int (bitpos)); + 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 (swapped) + { + rhs = build_binary_op (loc, opcode, rhs, lhs, true); opcode = NOP_EXPR; } bool save = in_late_binary_op; in_late_binary_op = true; - x = build_modify_expr (loc, lhs, NULL_TREE, opcode, loc, rhs, NULL_TREE); + x = build_modify_expr (loc, blhs ? blhs : lhs, NULL_TREE, opcode, + loc, rhs, NULL_TREE); in_late_binary_op = save; if (x == error_mark_node) return error_mark_node; @@ -262,6 +302,10 @@ c_finish_omp_atomic (location_t loc, enum tree_code code, gcc_assert (TREE_CODE (x) == MODIFY_EXPR); rhs = TREE_OPERAND (x, 1); + if (blhs) + rhs = build3_loc (loc, BIT_INSERT_EXPR, type, new_lhs, + rhs, bitsize_int (bitpos)); + /* Punt the actual generation of atomic operations to common code. */ if (code == OMP_ATOMIC) type = void_type_node; @@ -273,8 +317,8 @@ c_finish_omp_atomic (location_t loc, enum tree_code code, location, just diagnose different variables. */ if (rhs1 && VAR_P (rhs1) - && VAR_P (lhs) - && rhs1 != lhs + && VAR_P (orig_lhs) + && rhs1 != orig_lhs && !test) { if (code == OMP_ATOMIC) @@ -286,29 +330,57 @@ c_finish_omp_atomic (location_t loc, enum tree_code code, return error_mark_node; } + if (lhs1 + && lhs1 != orig_lhs + && TREE_CODE (lhs1) == COMPONENT_REF + && TREE_CODE (TREE_OPERAND (lhs1, 1)) == FIELD_DECL + && DECL_C_BIT_FIELD (TREE_OPERAND (lhs1, 1)) + && DECL_BIT_FIELD_REPRESENTATIVE (TREE_OPERAND (lhs1, 1))) + { + tree field = TREE_OPERAND (lhs1, 1); + tree repr = DECL_BIT_FIELD_REPRESENTATIVE (field); + lhs1 = build3 (COMPONENT_REF, TREE_TYPE (repr), TREE_OPERAND (lhs1, 0), + repr, TREE_OPERAND (lhs1, 2)); + } + if (rhs1 + && rhs1 != orig_lhs + && TREE_CODE (rhs1) == COMPONENT_REF + && TREE_CODE (TREE_OPERAND (rhs1, 1)) == FIELD_DECL + && DECL_C_BIT_FIELD (TREE_OPERAND (rhs1, 1)) + && DECL_BIT_FIELD_REPRESENTATIVE (TREE_OPERAND (rhs1, 1))) + { + tree field = TREE_OPERAND (rhs1, 1); + tree repr = DECL_BIT_FIELD_REPRESENTATIVE (field); + rhs1 = build3 (COMPONENT_REF, TREE_TYPE (repr), TREE_OPERAND (rhs1, 0), + repr, TREE_OPERAND (rhs1, 2)); + } + if (code != OMP_ATOMIC) { /* Generally it is hard to prove lhs1 and lhs are the same memory location, just diagnose different variables. */ - if (lhs1 && VAR_P (lhs1) && VAR_P (lhs)) + if (lhs1 && VAR_P (lhs1) && VAR_P (orig_lhs)) { - if (lhs1 != lhs && !test) + if (lhs1 != orig_lhs && !test) { error_at (loc, "%<#pragma omp atomic capture%> uses two " "different variables for memory"); return error_mark_node; } } + if (blhs) + x = build3_loc (loc, BIT_FIELD_REF, TREE_TYPE (blhs), x, + bitsize_int (bitsize), bitsize_int (bitpos)); x = build_modify_expr (loc, v, NULL_TREE, NOP_EXPR, loc, x, NULL_TREE); - if (rhs1 && rhs1 != lhs) + if (rhs1 && rhs1 != orig_lhs) { tree rhs1addr = build_unary_op (loc, ADDR_EXPR, rhs1, false); if (rhs1addr == error_mark_node) return error_mark_node; x = omit_one_operand_loc (loc, type, x, rhs1addr); } - if (lhs1 && lhs1 != lhs) + if (lhs1 && lhs1 != orig_lhs) { tree lhs1addr = build_unary_op (loc, ADDR_EXPR, lhs1, false); if (lhs1addr == error_mark_node) @@ -323,7 +395,7 @@ c_finish_omp_atomic (location_t loc, enum tree_code code, } } } - else if (rhs1 && rhs1 != lhs) + else if (rhs1 && rhs1 != orig_lhs) { tree rhs1addr = build_unary_op (loc, ADDR_EXPR, rhs1, false); if (rhs1addr == error_mark_node) |