Is there a way to declare a variable is non-aliased in clang to allow for more optimizations where the variable is used?
I understand restrict can be used to declare pointers as non-aliasing.
However, I'm also wondering about variables which can be pointer into. I guess (perhaps wrongfully) that the compiler has to be careful about assuming things which can allow it to cache a variable's value instead of re-fetching it each time.
Example:
class Data
{
public:
void updateVal() {
// Updates m_val with some value each time it's called (value may differ across different calls)
...
}
int complicatedCalculation() const {
return 3 * m_val + 2;
}
int m_val;
};
class User
{
User(Data& data) : m_data{data} {}
void f()
{
m_data.updateVal();
for (int i=0; i<1000; ++i)
g();
}
void g()
{
// Will the optimizer be able to cache calc's value for use in all the calls to g() from f()?
int calc = m_data.complicatedCalculation();
// Do more work
...
}
Data& m_data;
};
Even if the answer to the question in the sample code is "yes", might it not change to "no" if the code were more complicated (e.g. work being under // Do more work), due to a possibility of a pointer contents being modified where the pointer might have pointed into m_data.m_val? Or is this something the compiler assumes never happens, unless it sees the address of m_val being taken somewhere in the code?
If it doesn't assume that, or even it does but the address of m_val does get taken somewhere (but we know its contents won't be modified), then it would be nice to be able to mark m_val as "safe" from aliasing concerns, so its value can be assumed to not be changed by pointer access.
The compiler will allocate a register to store
calcingunless it determines that there are other hotter variables that would be better to be stored in registers.Now even if
calcis stored in a register, this may still require a function call tocomplicatedCalculationand a memory access tom_val. The compiler may inlinecomplicatedCalculationand eliminate the function call but it cannot eliminate the memory access unless it can determine thatm_valis effectively a constant all the time.What you really want is to eliminate the unnecessary memory accesses to
m_valin the loop infrather than ing. For this to happen, the compiler has to deem thatgis eligible for inlining inf. Only when it's inlined can the compiler eliminate the unnecessary memory accesses. Even if g directly modifiesm_val, the compiler can still allocatecalcin a register and modifies it accordingly. The only caveat here is whengmay throw an exception. If an exception is ever thrown, the in-memory version ofm_valhas to be updated to the latest value before the exception is allowed to propagate. The compiler has to emit code to ensure this. Without this code, it has to update the in-memory version ofm_valin every iteration. I don't know which version of clang uses which approach. You have to examine the generated assembly code.If the address of
m_valueis taken anywhere in the code, the compiler may not be able to eliminate any memory accesses to it. In this case, usingrestrictmay help.m_valueshould not be modified through any other pointer because this violates the standard and results in undefined behavior. It's your responsibility to ensure that.I hope that you care about this either because you have experimentally determined that this is a performance bottleneck in the code or you are just curious, rather than for any other reason.