aboutsummaryrefslogtreecommitdiff
path: root/gcc/c-family/c-omp.c
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/c-family/c-omp.c')
-rw-r--r--gcc/c-family/c-omp.c92
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)