From: Kyrylo Tkachov <[email protected]>
The fix for PR120944 stopped value-numbering from using the value of any
store whose left-hand side is a volatile lvalue. That is stronger than
needed: what the PR needs is that a volatile *load* accesses memory again.
A store determines the contents of the object it writes whether or not the
lvalue it is performed through is volatile-qualified, so a later
non-volatile load of the same location may take the stored value: either
the object itself is not volatile-qualified, in which case nothing else can
change it, or it is and reading it through a non-volatile lvalue is
undefined (C11 6.7.3p6).
Record in the walk data whether the reference being looked up is a volatile
access, and when it is refuse every store rather than only a volatile one.
A volatile load has to reach memory, so no stored value may serve it,
whatever lvalue the store was performed through. The looked-up reference
has its volatile flag cleared by ao_ref_init_from_vn_reference, so take the
flag from the original tree in vn_reference_lookup.
The by-pieces entry point has no tree to inspect, so a second flag records
that the volatility could not be determined and keeps that path on the
narrower rule of refusing a store performed through a volatile lvalue,
which is what it did before. Both flags are needed because the two mean
different things: one says the load is volatile, the other says we do not
know. Collapsing them would make vn_reference_lookup_pieces refuse every
store, which would remove store-to-load forwarding from PRE's phi_translate
and compute_avail.
unsigned f (unsigned *p, unsigned v)
{
*(volatile unsigned *) p = v;
return p[0];
}
before: after:
str w1, [x0] str w1, [x0]
ldr w0, [x0] mov w0, w1
ret ret
sha1dc, the SHA-1 implementation with collision detection that git and
libgit2 use by default, stores its 80-word message schedule through a
volatile lvalue and reads it back with plain loads, so every round reloaded
words it had just written. On a fixed-work SHA-1 hashing loop on Grace
the patch removes 2.3% of dynamic instructions and 8.4% of cycles, which
brings GCC to within 1% of clang 22 on the same source
(Clang does not emit the loads). The number of
loads in sha1_compression_states drops by about half. The volatile stores
are all still emitted and are not merged into pairs.
Bootstrapped and tested on aarch64-none-linux-gnu and x86_64-linux.
Ok for trunk?
Thanks,
Kyrill
gcc/ChangeLog:
PR tree-optimization/120944
* tree-ssa-sccvn.cc (vn_walk_cb_data::vn_walk_cb_data): Add
ORIG_VOLATILE_P_ and UNKNOWN_VOLATILE_P_ parameters.
(vn_walk_cb_data::orig_volatile_p): New member.
(vn_walk_cb_data::unknown_volatile_p): New member.
(vn_stored_value_available_p): New function.
(vn_reference_lookup_3): Use it instead of testing the volatility
of the store left-hand side directly.
(vn_reference_lookup): Pass the volatility of OP.
gcc/testsuite/ChangeLog:
PR tree-optimization/120944
* gcc.dg/tree-ssa/ssa-fre-115.c: New test.
Signed-off-by: Kyrylo Tkachov <[email protected]>
---
gcc/testsuite/gcc.dg/tree-ssa/ssa-fre-115.c | 23 +++++++++++
gcc/tree-ssa-sccvn.cc | 46 ++++++++++++++++++---
2 files changed, 64 insertions(+), 5 deletions(-)
create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/ssa-fre-115.c
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/ssa-fre-115.c
b/gcc/testsuite/gcc.dg/tree-ssa/ssa-fre-115.c
new file mode 100644
index 00000000000..7ccbc87f816
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/ssa-fre-115.c
@@ -0,0 +1,23 @@
+/* Value-numbering may forward a value stored through a volatile lvalue
+ to a later non-volatile load of the same location. The store itself
+ has to stay. */
+
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-fre1" } */
+
+unsigned int
+f (unsigned int *p, unsigned int v)
+{
+ *(volatile unsigned int *) p = v;
+ return p[0];
+}
+
+unsigned int
+g (unsigned int *p)
+{
+ *(volatile unsigned int *) p = 42;
+ return p[0];
+}
+
+/* { dg-final { scan-tree-dump-not "= \\*p_" "fre1" } } */
+/* { dg-final { scan-tree-dump-times "={v}" 2 "fre1" } } */
diff --git a/gcc/tree-ssa-sccvn.cc b/gcc/tree-ssa-sccvn.cc
index 840bcf70e0a..dc4bce149dc 100644
--- a/gcc/tree-ssa-sccvn.cc
+++ b/gcc/tree-ssa-sccvn.cc
@@ -1935,11 +1935,15 @@ struct vn_walk_cb_data
{
vn_walk_cb_data (vn_reference_t vr_, tree orig_ref_, tree *last_vuse_ptr_,
vn_lookup_kind vn_walk_kind_, bool tbaa_p_, tree mask_,
- bool redundant_store_removal_p_)
+ bool redundant_store_removal_p_,
+ bool orig_volatile_p_ = false,
+ bool unknown_volatile_p_ = true)
: vr (vr_), last_vuse_ptr (last_vuse_ptr_), last_vuse (NULL_TREE),
mask (mask_), masked_result (NULL_TREE), same_val (NULL_TREE),
vn_walk_kind (vn_walk_kind_),
tbaa_p (tbaa_p_), redundant_store_removal_p (redundant_store_removal_p_),
+ orig_volatile_p (orig_volatile_p_),
+ unknown_volatile_p (unknown_volatile_p_),
saved_operands (vNULL), first_range (), first_set (-2),
first_base_set (-2)
{
@@ -2003,6 +2007,12 @@ struct vn_walk_cb_data
vn_lookup_kind vn_walk_kind;
bool tbaa_p;
bool redundant_store_removal_p;
+ /* Whether the reference we are looking up is known to be a volatile
+ access. */
+ bool orig_volatile_p;
+ /* Whether the original reference tree was unavailable, so that the
+ volatility of the access could not be determined. */
+ bool unknown_volatile_p;
vec<vn_reference_op_s> saved_operands;
/* The VDEFs of partial defs we come along. */
@@ -2771,6 +2781,31 @@ adjust_offsets_for_equal_base_address (tree base1,
poly_int64 *offset1,
return operand_equal_p (base1, base2, OEP_ADDRESS_OF);
}
+/* Return true if the value stored by DEF_STMT, a single assignment, may be
+ forwarded to the reference DATA is looking up.
+
+ A store determines the contents of the object it writes whether or not it
+ is performed through a volatile lvalue, so its value may be given to a
+ later non-volatile load of the same location: either the object itself is
+ not volatile-qualified, in which case nothing else can change it, or it is
+ and reading it through a non-volatile lvalue is undefined. A volatile
+ load has to access memory again, so no store may serve it (PR120944). */
+
+static bool
+vn_stored_value_available_p (vn_walk_cb_data *data, gimple *def_stmt)
+{
+ if (data->orig_volatile_p)
+ return false;
+
+ /* Without the original reference the volatility of the load is unknown,
+ so keep to the narrower rule of refusing a store performed through a
+ volatile lvalue. */
+ if (data->unknown_volatile_p)
+ return !TREE_THIS_VOLATILE (gimple_assign_lhs (def_stmt));
+
+ return true;
+}
+
/* Callback for walk_non_aliased_vuses. Tries to perform a lookup
from the statement defining VUSE and if not successful tries to
translate *REFP and VR_ through an aggregate copy at the definition
@@ -3175,7 +3210,7 @@ vn_reference_lookup_3 (ao_ref *ref, tree vuse, void
*data_,
&& gimple_assign_single_p (def_stmt)
&& gimple_assign_rhs_code (def_stmt) == CONSTRUCTOR
&& CONSTRUCTOR_NELTS (gimple_assign_rhs1 (def_stmt)) == 0
- && !TREE_THIS_VOLATILE (gimple_assign_lhs (def_stmt)))
+ && vn_stored_value_available_p (data, def_stmt))
{
tree base2;
poly_int64 offset2, size2, maxsize2;
@@ -3231,7 +3266,7 @@ vn_reference_lookup_3 (ao_ref *ref, tree vuse, void
*data_,
&& !reverse_storage_order_for_component_p (vr->operands)
&& !contains_storage_order_barrier_p (vr->operands)
&& gimple_assign_single_p (def_stmt)
- && !TREE_THIS_VOLATILE (gimple_assign_lhs (def_stmt))
+ && vn_stored_value_available_p (data, def_stmt)
&& CHAR_BIT == 8
&& BITS_PER_UNIT == 8
&& BYTES_BIG_ENDIAN == WORDS_BIG_ENDIAN
@@ -3396,7 +3431,7 @@ vn_reference_lookup_3 (ao_ref *ref, tree vuse, void
*data_,
&& !reverse_storage_order_for_component_p (vr->operands)
&& !contains_storage_order_barrier_p (vr->operands)
&& gimple_assign_single_p (def_stmt)
- && !TREE_THIS_VOLATILE (gimple_assign_lhs (def_stmt))
+ && vn_stored_value_available_p (data, def_stmt)
&& TREE_CODE (gimple_assign_rhs1 (def_stmt)) == SSA_NAME)
{
tree lhs = gimple_assign_lhs (def_stmt);
@@ -4312,7 +4347,8 @@ vn_reference_lookup (tree op, tree vuse, vn_lookup_kind
kind,
}
vn_walk_cb_data data (&vr1, r.ref ? NULL_TREE : op,
last_vuse_ptr, kind, tbaa_p, mask,
- redundant_store_removal_p);
+ redundant_store_removal_p,
+ TREE_THIS_VOLATILE (op), false);
wvnresult
= ((vn_reference_t)
--
2.50.1 (Apple Git-155)