Is this compiler transformation allowed?
Asked Answered
T

2

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 ?

Teachin answered 25/8, 2017 at 15:42 Comment(18)
Yes (but it cannot really use the name tmp of course). But why do you care?Sackey
@BaummitAugen Really? If y can be accessed from other threads, the transformation would potentially introduce a data race where there was none.Musky
@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<int>, but the Q does not say that.)Sackey
@hvd Thats is why you have to provided synchronization. The as-of rules assumes only a single threadToomer
@BaummitAugen: even if x is always false ?Duvetyn
@BaummitAugen Suppose x is 0 and never modified. Suppose two threads run the OP's code. No UB there, even without synchronisation.Musky
@Duvetyn in that case, why would the compiler apply that transformation?Lempres
Okay, good point you two. Good thing I don't write compilers it seems. XDSackey
@hvd Yes, that is what inspired this question, I'll add some contextTeachin
@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.Narrowminded
@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.Sackey
@BaummitAugen Thanks for the reference, I somehow missed that one.Narrowminded
Is this a Q about an exact version of C++, or any version >11?Thearchy
@Thearchy About C++11 or later, but it's hard to express that with tagsTeachin
Do you want an answer based on exact std quotes or a practically correct answer?Thearchy
@Thearchy You make it sound like if it's in the standard, it can't be correct :)Teachin
@Teachin 1) Writing spec is difficult. 2). The C++ committee has almost no one good at this activity.Thearchy
@Frank "The as-is rule" what do you mean by that?Thearchy
S
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.

Sackey answered 25/8, 2017 at 16:17 Comment(4)
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?Nannie
@Nannie 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".Sackey
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.Gazzo
@Gazzo "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.Thearchy
T
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.

Thearchy answered 10/7, 2019 at 16:55 Comment(2)
But it is actually rejected, so it should be fineTeachin
@Teachin 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.Thearchy

© 2022 - 2024 — McMap. All rights reserved.