Re: [RFC] LKMM: Add volatile_if()
From: Alan Stern <stern@rowland.harvard.edu>
Date: 2021-06-06 18:23:10
Also in:
linux-arch, lkml
On Sun, Jun 06, 2021 at 11:04:49AM -0700, Linus Torvalds wrote:
On Sun, Jun 6, 2021 at 4:56 AM Segher Boessenkool [off-list ref] wrote:quoted
And that is a simple fact, since the same assembler code (at the same spot in the program) will do the same thing no matter how that ended up there.The thing is, that's exactl;y what gcc violates. The example - you may not have been cc'd personally on that one - was something like if (READ_ONCE(a)) { barrier(); WRITE_ONCE(b,1); } else { barrier(); WRITE_ONCE(b, 1); } and currently because gcc thinks "same exact code", it will actually optimize this to (pseudo-asm): LD A "empty asm" ST $1,B which is very much NOT equivalent to LD A BEQ over "empty asm" ST $1,B JMP join over: "empty asm" ST $1,B join: and that's the whole point of the barriers. It's not equivalent exactly because of memory ordering. In the first case, there is no ordering on weak architectures. In the second case, there is always an ordering, because of CPU consistency guarantees. And no, gcc doesn't understand about memory ordering. But that's exactly why we use inline asms.quoted
And the compiler always is allowed to duplicate, join, delete, you name it, inline assembler code. The only thing that it cares about is semantics of the code, just like for any other code.See, but it VIOLATES the semantics of the code. You can't join those two empty asm's (and then remove the branch), because the semantics of the code really aren't the same any more if you do. Truly.
To be fair, the same argument applies even without the asm code. The
compiler will translate
if (READ_ONCE(a))
WRITE_ONCE(b, 1);
else
WRITE_ONCE(b, 1);
to
LD A
ST $1,B
intstead of
LD A
BEQ over
ST $1,B
JMP join
over:
ST $1,B
join:
And these two are different for the same memory ordering reasons as
above.
Alan