Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Are there extensions to let optimizers assume const-ref parameters will stay const?

Tags:

c++

Related to my former question: Are compilers not allowed to assume const-ref parameters will stay const?

My new question is: Is there compiler-specific, non-standard extensions or syntax to tell GCC/Clang/MSVC that an object is not writable? For example, here's some fake-ish code I'd like to be able to write:

void f(const int& i) {

    // At this point, compiler doesn't know if "i" can be mutated or not,
    // so it assumes it can

    // Fake-ish -- compiler now assumes "i" cannot be mutated and optimizes accordingly
    __assume(readonly i);

    // ...

}
like image 438
Jeff M Avatar asked Mar 12 '18 02:03

Jeff M


2 Answers

If i is supposed to stay const for the whole function, and f() has no side effects, you could declare it with __attribute__((pure)):

int f(const int&) __attribute__((pure));

Note that it doesn't make sense for a pure function to return void, so I changed it to int.

While this does not affect how f() is compiled, it does affect the functions calling it (check it on godbolt):

#include <iostream>

int f(const int& i) __attribute__((pure));

int main() {
    int i = 40;
    f(i);
    if (i != 40) {
        std::cout << "not 40" << std::endl;
    }
}

Here __attribute__((pure)) tells the compiler that f() will not change i, so the compiler will not generate the call to std::cout << ....

Without __attribute__((pure)), even if f() is declared to take a const int& i parameter, the compiler has to assume that the value of i may change, and generate the if and the call to std::cout << ... .

like image 102
fwyzard Avatar answered Nov 15 '22 12:11

fwyzard


I do not know of non-standard ways to permit the compiler to assume immutability, but if I read the standard correctly, I think there is a standard way to do so (adapted from your other question):

void f(const int&);

struct ImmutableInt {
    // it is UB to change val, because it is itself const
    // not just a const reference
    const int val;    
};

void g3(const ImmutableInt & i) {
    if (i.val == 42) f(i.val);
    if (i.val == 42) f(i.val); // the compiler could assume i.val has not changed
}

The problem is, currently the compilers I checked do not make use of this knowledge to actually omit reloading the value.

I do not think there are any fundamental problems with this, because clang uses similar and more complicated reasoning about the virtual table pointer to achieve devirtualization. This reasoning is somewhat more complicated because the vptr will change during construction and is therefore not entirely const. The llvm metadata used for this is called invariant.group, but I do not know if you can set it yourself in C++.

like image 23
PaulR Avatar answered Nov 15 '22 11:11

PaulR