Description
I've just closed rust-lang/rfcs#2360 due to @rkruppe's input that by trying to specify what black_box
and clobber
do there the RFC is basically specifying a subset of the memory model.
So I'd like to ask feedback for these here first.
The specification in the RFC is very loose. mem::black_box(x)
is specified as follows:
- writes the address of
x
to memory - reads all memory
- writes all memory
while mem::clobber()
:
- reads all memory
- writes all memory
where I have no idea how to specify memory but @rkruppe came up with a minimal example that helps:
{
let tmp = x;
clobber();
}
Here, the compiler is allowed to optimize the store of x
to tmp
away, because the only code that has the address of tmp
is in that scope, and that code does not read or write from tmp
. In the specification of clobber
, when it states "read/write from/to all memory", "memory" does not refer to tmp
. However, if I change the example to:
{
let tmp = x;
black_box(&tmp);
clobber();
}
in this case clobber()
requires the store of x
to tmp
to be live, because black_box
has written the address of tmp
to "memory", and thus the "read/write to/from memory" in clobber
can do something with tmp
.
So a big question I have is what is "memory" here, and how does tmp
in the first example differs from the rest?
I don't know how these could make sense in Rust memory model, what the wording for their specification should be, and I am barely qualified to follow the discussion at all. The RFC contains more information, and lots of godbolt links, but black_box
and clobber
proposed implementaiton is this:
#[inline(always)]
fn clobber() {
unsafe { asm!("" : : : "memory" : "volatile") };
}
#[inline(always)]
fn black_box<T>(x: T) -> T {
unsafe {
asm!("" // template
: // output operands
: "r"(&x) // input operands
// r: any general purpose register
: "memory" // clobbers all memory
: "volatile" // has side-effects
);
x
}
}
Also, @rkruppe asked:
I'd like to know the difference between this and compiler_fence(SeqCst)
To which @nagisa answered:
The difference between asm! with a memory clobber and compiler_fence exists in the fact, that memory clobber requires compiler to actually reload the memory if they want to use it again (as memory is… clobbered – considered changed), whereas compiler_fence only enforces that memory accesses are not reordered and the compiler still may use the usual rules to figure that it needn’t to reload stuff.