And before you ask, yes, this does have a noticeable impact on performance. Having a pointer that evaluates to a constant value at runtime makes things 20-30% faster, at least in my benchmarks. Using a raw pointer also avoids the overhead bounds checking -- all indexes into this array are masked (i.e. reg & 7) at some point.
I could be wrong, but I don't think this is possible. Constant evaluation wraps up well before the part of compilation where the address of a static will become known. You might have to use a lazily-initialized static instead of a constant.
Will that affect performance? The whole purpose of having a constant is to inline the value into the expression, eliminating the need for a lookup (the compiler knows exactly where the reg32 array is going to be). But if Rust inlines the address of the static somehow (?), then I'm happy with that too.
Does this support unions? I have three overlapping register files (8-bit, 16-bit, 32-bit), and it's a lot easier (and faster) for me to do reg8[1] instead of reg32[0] >> 8 & 255.
I think the simplest thing to do, which should have no overhead, is to just write unsafe { &mut registers as *mut _ as *mut u32 } whenever you were planning to write reg32, or assign that expression to a local variable and pass it to any functions that need it. The casting has no run-time cost.
In this case, the first load is unnecessary, since the registers array is going to remain constant (for C, Clang generates a single move, mov eax, dword ptr [rip + registers + 24]).
The one below does exactly what I need, except I don't want to hardcode all the addresses.
pub const reg32: *mut u32 = 65536 as *mut u32;
(unsafe { *reg32.offset(6 as isize) }) as u32
// mov eax, dword ptr [65560]
Is there any way to tell the compiler that the value registers is fixed and isn't going to change? That would reduce this to one read operation. Getting rid of mut makes the entire struct read-only, unfortunately.
I can't imagine that does more than one memory access.
Edt: hmm, looking at the generated assembly I see it does the same thing as you saw; I don't know why. I'd still recommend this version over the one with casts.
I'm looking for a way to prevent this, making the registers global itself immutable but the values that it points to mutable. That'd let the Rust compiler optimize out one of the two loads. Is this possible?
That doesn't change the address of the variable, though. I think the real problem is that the address isn't a compile-time constant. It can't change within a single execution of your program, but it may vary from one execution to the next.
Even though the address may be determined at launch time, the offset is a link-time constant and this should all be fixable using relative addressing and/or relocations. Nonetheless, there's no reason this would work better/faster with raw pointers vs. references.