Re: [C11-atomic] test invalid hoist across and acquire load

2012-03-23 Thread Aldy Hernandez
After much pondering and talking with both you and Torvald, it has been 
determined that the test at hand is technically allowed to hoist the 
value of x/y because the standard guarantees that the code below is data 
race free:


if (__atomic_load_n (&flag1, __ATOMIC_ACQUIRE))
i = x + y;
if (__atomic_load_n (&flag2, __ATOMIC_ACQUIRE))
a = 10;
j = x + y;

So, since j=x+y is an unconditional load of x/y, we can assume there are 
no other threads writing to x/y.


However...

Depending on such undefined behaviors is liable to cause confusion and 
frustration with our ourselves and our users, so it is best to limit 
these optimizations across atomics altogether.  It's best to err on the 
side of caution than overly optimize across confusing data races.  As we 
become better versed in optimizing across atomics, we can relax things 
and perhaps make optimizations more aggressive.  But for now, let's mark 
this as a must fix, and make sure hoists are not allowed across acquire 
operations.


I have detailed the problem in the testcase.

> so enter it as 2 testcases, one increasing x and one increasing y, or
> better yet set it up so that this function is called twice from
> simulate_main, with other_process() increasing x the first time and
> increasing y the second time... or something like that.
>
> Andrew

Two testcases?  Now you just want to see me work more :).

Implemented as loop.

OK for branch?
Index: atomic-hoist-1.c
===
--- atomic-hoist-1.c(revision 0)
+++ atomic-hoist-1.c(revision 0)
@@ -0,0 +1,89 @@
+/* { dg-do link } */
+/* { dg-require-effective-target sync_int_long } */
+/* { dg-final { simulate-thread } } */
+
+/* Test that a hoist is not performed across an acquire barrier.  */
+
+#include 
+#include "simulate-thread.h"
+
+int iteration = 0;
+int flag1=1, flag2=1;
+unsigned int x=1, y=2, i=0x1234, j=0x5678, a;
+
+
+/* At each instruction, get a new X or Y to later verify that we have
+   not reused a value incorrectly.  */
+void simulate_thread_other_threads ()
+{
+  if (iteration == 0)
+x++;
+  else
+y++;
+}
+
+/* Return true if error, otherwise 0.  */
+int verify_result ()
+{
+  /* [i] should not equal [j], because that would mean that we hoisted
+ [x] or [y] instead of loading them again.  */
+  int fail = i == j;
+  if (fail)
+printf("FAIL: i (%u) should not equal j (%u)\n", i, j);
+  return fail;
+}
+
+int simulate_thread_step_verify ()
+{
+  return verify_result ();
+}
+
+int simulate_thread_final_verify ()
+{
+  return verify_result ();
+}
+
+__attribute__((noinline))
+void simulate_thread_main()
+{
+  for (; iteration < 2; ++iteration)
+{
+  /* The values of x or y should not be hoisted across reads of
+flag[12].
+
+For example, when the second load below synchronizes with
+another thread, the synchronization is with a release, and
+that release may cause a stored value of x/y to be flushed
+and become visible.  So, for this case, it is incorrect for
+CSE/CSA/and-others to hoist x or y above the load of
+flag2.  */
+  if (__atomic_load_n (&flag1, __ATOMIC_ACQUIRE))
+   i = x + y;
+  if (__atomic_load_n (&flag2, __ATOMIC_ACQUIRE))
+   a = 10;
+  /* NOTE: According to the standard we can assume that the
+testcase is data race free, so if there is an unconditional
+load of x+y here at j=x+y, there should not be any other
+thread writing to x or y if we are indeed data race free.
+
+This means that we are technically free to hoist x/y.
+However, since depending on these undefined behaviors is
+liable to get many confused, it is best to be conservative
+with optimizations on atomics, hence the current test.  As
+we become better versed in optimizations across atomics, we
+can relax the optimizations a bit.  */
+  j = x + y;
+
+  /* Since x or y have been changing at each instruction above, i
+and j should be different.  If they are the same, we have
+hoisted something incorrectly.  */
+}
+
+}
+
+main()
+{
+  simulate_thread_main ();
+  simulate_thread_done ();
+  return 0;
+}


Re: [C11-atomic] test invalid hoist across and acquire load

2012-03-23 Thread Andrew MacLeod

On 03/23/2012 10:39 AM, Aldy Hernandez wrote:

On 03/21/12 12:54, Andrew MacLeod wrote:

On 03/21/2012 01:35 PM, Aldy Hernandez wrote:

In the test below, we cannot cache either [x] or [y] neither before
the load of flag1 nor the load of flag2. This is because the
corresponding store/release can flush a different value of x or y:

+ if (__atomic_load_n (&flag1, __ATOMIC_ACQUIRE))
+ i = x + y;
+
+ if (__atomic_load_n (&flag2, __ATOMIC_ACQUIRE))
+ a = 10;
+ j = x + y;



Actually, does it need to be that complicated?

can't you simply have the "other_thread" process monotonically increase
x by 1 every cycle?

then if the load is hoisted and commoned, simulate_thread_final_verify()
can simply check that if i == j, it knows that x was loaded as a common
value and reused when calculating j. with the other thread increasing x
eveyr sycle, they should never be the same value.


Hmmm, for this particular case I know CSA is commoning x + y, but what 
if another combination of passes hoists only y and leaves x alone.  It 
would be nice to test that y isn't hoisted independently of x.  Would 
it not, or do you only want to test this particular behavior?


so enter it as 2 testcases, one increasing x and one increasing y, or 
better yet  set it up so that this function is called twice from 
simulate_main, with other_process() increasing x the first time and 
increasing y the second time...  or something like that.


Andrew


Re: [C11-atomic] test invalid hoist across and acquire load

2012-03-23 Thread Aldy Hernandez

On 03/21/12 12:54, Andrew MacLeod wrote:

On 03/21/2012 01:35 PM, Aldy Hernandez wrote:

In the test below, we cannot cache either [x] or [y] neither before
the load of flag1 nor the load of flag2. This is because the
corresponding store/release can flush a different value of x or y:

+ if (__atomic_load_n (&flag1, __ATOMIC_ACQUIRE))
+ i = x + y;
+
+ if (__atomic_load_n (&flag2, __ATOMIC_ACQUIRE))
+ a = 10;
+ j = x + y;



Actually, does it need to be that complicated?

can't you simply have the "other_thread" process monotonically increase
x by 1 every cycle?

then if the load is hoisted and commoned, simulate_thread_final_verify()
can simply check that if i == j, it knows that x was loaded as a common
value and reused when calculating j. with the other thread increasing x
eveyr sycle, they should never be the same value.


Hmmm, for this particular case I know CSA is commoning x + y, but what 
if another combination of passes hoists only y and leaves x alone.  It 
would be nice to test that y isn't hoisted independently of x.  Would it 
not, or do you only want to test this particular behavior?


Aldy


Re: [C11-atomic] test invalid hoist across and acquire load

2012-03-21 Thread Andrew MacLeod

On 03/21/2012 01:35 PM, Aldy Hernandez wrote:


The pass at fault here is the combine stack adjustment RTL pass.  I 
have not looked into why this is happening, but I wanted to get this 
test into the branch lest we forget about it.


Is this OK for the branch?  Is my understanding correct?


Fine for the C11-atomic branch..  we'll accumulate all the failing tests 
there for now... and then address them when we go memory-model 
compliance hunting...


Andrew


Re: [C11-atomic] test invalid hoist across and acquire load

2012-03-21 Thread Andrew MacLeod

On 03/21/2012 01:35 PM, Aldy Hernandez wrote:
In the test below, we cannot cache either [x] or [y] neither before 
the load of flag1 nor the load of flag2.  This is because the 
corresponding store/release can flush a different value of x or y:


+  if (__atomic_load_n (&flag1, __ATOMIC_ACQUIRE))
+i = x + y;
+
+  if (__atomic_load_n (&flag2, __ATOMIC_ACQUIRE))
+a = 10;
+  j = x + y;



Actually, does it need to be that complicated?

can't you simply have the "other_thread" process monotonically increase 
x by 1 every cycle?


then if the load is hoisted and commoned,  
simulate_thread_final_verify() can simply check that if  i == j,  it 
knows that x was loaded as a common value and reused when calculating 
j.   with the other thread increasing x eveyr sycle, they should never 
be the same value.


Andrew