8

Consider this code, where x and y are integers:

if (x)
    y = 42;

Is the following compiler transformation allowed ?

int tmp = y;
y = 42;

if (!x)
    y = tmp;

context:

This is from Bjarne Stroustrup's FAQ:

// start with x==0 and y==0

if (x) y = 1;   // Thread 1 

if (y) x = 1;   // Thread 2

The FAQ states this is data race free; with x and y both 0, none of the vars should be written to.
But what if the transformation is allowed ?

curiousguy
  • 8,038
  • 2
  • 40
  • 58
LWimsey
  • 6,189
  • 2
  • 25
  • 53
  • 3
    Yes (but it cannot really use the name `tmp` of course). But why do you care? – Baum mit Augen Aug 25 '17 at 15:44
  • 8
    @BaummitAugen Really? If `y` can be accessed from other threads, the transformation would potentially introduce a data race where there was none. –  Aug 25 '17 at 15:48
  • 3
    @hvd No other thread can change `x` or access `y`; that would be a UB already. To be explicit: There either will be no new race or the code was broken before. (Unless `x` is `std::atomic`, but the Q does not say that.) – Baum mit Augen Aug 25 '17 at 15:49
  • 2
    @hvd Thats is why you have to provided synchronization. The as-of rules assumes only a single thread – NathanOliver Aug 25 '17 at 15:50
  • @BaummitAugen: even if `x` is always false ? – Jarod42 Aug 25 '17 at 15:52
  • 3
    @BaummitAugen Suppose `x` is `0` and never modified. Suppose two threads run the OP's code. No UB there, even without synchronisation. –  Aug 25 '17 at 15:53
  • @Jarod42 in that case, why would the compiler apply that transformation? – 463035818_is_not_an_ai Aug 25 '17 at 15:53
  • 1
    Okay, good point you two. Good thing I don't write compilers it seems. XD – Baum mit Augen Aug 25 '17 at 15:54
  • @hvd Yes, that is what inspired this question, I'll add some context – LWimsey Aug 25 '17 at 15:55
  • @hvd Even then, my reading of the standard is that the compiler is allowed to introduce such data races as long as y is not volatile or atomic. The as-is rule should still apply as long as there is no side-effect to the code. –  Aug 25 '17 at 16:01
  • @Frank No, I think they are right. *"Compiler transformations that introduce assignments to a potentially shared memory location that would not be modified by the abstract machine are generally precluded by this standard, since such an assignment might overwrite another assignment by a different thread in cases in which an abstract machine execution would not have encountered a data race."* 1.10/25 in N4141. – Baum mit Augen Aug 25 '17 at 16:12
  • @BaummitAugen Thanks for the reference, I somehow missed that one. –  Aug 25 '17 at 16:16
  • Is this a Q about an exact version of C++, or any version >11? – curiousguy Jul 10 '19 at 06:08
  • @curiousguy About C++11 or later, but it's hard to express that with tags – LWimsey Jul 10 '19 at 13:55
  • Do you want an answer based on exact std quotes or a practically correct answer? – curiousguy Jul 10 '19 at 16:09
  • @curiousguy You make it sound like if it's in the standard, it can't be correct :) – LWimsey Jul 10 '19 at 20:15
  • @LWimsey 1) Writing spec is difficult. 2). The C++ committee has almost no one good at this activity. – curiousguy Jul 10 '19 at 21:03
  • @Frank "_The as-is rule_" what do you mean by that? – curiousguy Jul 12 '19 at 18:01

2 Answers2

7

Unlike I wrote in my incorrect comment, this transformation is actually not allowed if y is potentially shared between threads and the compiler cannot prove any existing UB in the original code.

The standard explicitly says:

Compiler transformations that introduce assignments to a potentially shared memory location that would not be modified by the abstract machine are generally precluded by this standard, since such an assignment might overwrite another assignment by a different thread in cases in which an abstract machine execution would not have encountered a data race.

[intro.multithread] (1.10/22) in N3337, (1.10/25) in N4141.

So if x is always 0, the original code would be race-free, while the transformed one wouldn't. Thus the transformation is not legal.

Baum mit Augen
  • 49,044
  • 25
  • 144
  • 182
  • Quick question, the standard says "generally precluded...", does that mean there exists a reason it wouldn't/won't be considered illegal? Or is that up to the compiler to decide? – user3164339 Aug 25 '17 at 22:02
  • 1
    @user3164339 This quote is part if a *"Note:"*, which is not quite as formal as the rest, it's more like a summary or important conclusions. *"Generally precluded"* in this context means *"unless allowed by special circumstances"*. – Baum mit Augen Aug 25 '17 at 22:07
  • 3
    In particular, contrary to a certain other incorrect comment, the as-if rule itself is devoid of any limitation to a single thread. By making data races UB, the standard permits optimizers to assume that data races do not occur and optimize accordingly, which in practice enables optimizers to largely limit their analysis to a single thread. But that's nowhere a permission for implementations to introduce data races when there is none. – T.C. Aug 26 '17 at 04:03
  • @T.C. "_the as-if rule itself is devoid of any limitation to a single thread_" You made it sound like the as-if rule is unlimited in some scope and limited in some other scope. – curiousguy Jul 10 '19 at 16:57
0

If it was, then you just wouldn't be able to exclude access to any object that's reachable from global variables or other variables. The compiler could even tentatively call functions that are never called, whenever an indirect call is made, and "cancel" their effect afterward by restoring the original value.

Going down this opti-pessimisation path, it could do division by zero in advance then "ignore" the result if the divisor was zero, even if that's a trap and program is stopped.

This is patently absurd and must be rejected whether or not the standard says it should be.

curiousguy
  • 8,038
  • 2
  • 40
  • 58
  • 1
    But it is actually rejected, so it should be fine – LWimsey Jul 10 '19 at 20:26
  • @LWimsey Yes. But common sense beats the std text, always. There is no case when compilers were changed to conform when common sense says they were right. – curiousguy Jul 10 '19 at 20:45