Declaring a variable non-aliased in clang?

281 views Asked by At

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.

1

There are 1 answers

0
Hadi Brais On

The compiler will allocate a register to store calcin g unless it determines that there are other hotter variables that would be better to be stored in registers.

Now even if calc is stored in a register, this may still require a function call to complicatedCalculation and a memory access to m_val. The compiler may inline complicatedCalculation and eliminate the function call but it cannot eliminate the memory access unless it can determine that m_val is effectively a constant all the time.

What you really want is to eliminate the unnecessary memory accesses to m_val in the loop in f rather than in g. For this to happen, the compiler has to deem that g is eligible for inlining in f. Only when it's inlined can the compiler eliminate the unnecessary memory accesses. Even if g directly modifies m_val, the compiler can still allocate calc in a register and modifies it accordingly. The only caveat here is when g may throw an exception. If an exception is ever thrown, the in-memory version of m_val has 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 of m_val in 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_value is taken anywhere in the code, the compiler may not be able to eliminate any memory accesses to it. In this case, using restrict may help. m_value should 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.