3

Consider this code, compiled with gcc and -Ofast:

int f1(const char *p) {
    if (!p[0])
        return 0;

    f2(); //not inlined

    if (p[0]) { //not optimized out
        //do something
        return 0;
    } else {
        //do something else
        //not optimized out
        return 1;
    }
}

how can I get behavior where the second test and lower branch are optimized out (since p[0] is const and has already been tested)?

chqrlie
  • 131,814
  • 10
  • 121
  • 189
  • `const` does not have much to do with optimization. – M.M Feb 13 '16 at 22:07
  • @M.M. const tells the compiler to make assumptions about variables which can help optimisation choices during compilation. But I can concede that this is not its primary purpose. – cdcdcd Feb 13 '16 at 22:28
  • @cdcdcd: I'm afraid your claim only holds for objects defined as `const` qualified, not for pointers defined as pointing to `const` objects. – chqrlie Feb 13 '16 at 23:02

2 Answers2

2

There is no reason for the compiler to assume that function f2 may not modify what p is pointing to. The fact that p is defined as const char *p only tells the compiler that p cannot be used to modify the data it points to, not that the data itself is constant.

If you know the array is indeed not modified by function f2(), you can modify the code to not read it again and see if gcc will optimize accordingly:

int f1(const char *p) {
    char c = *p;
    if (!c)
        return 0;

    f2(); //not inlined

    if (c) { //should be always true
        //do something
        return 0;
    } else {
        //should be optimized out
        //do something else
        return 1;
    }
}
chqrlie
  • 131,814
  • 10
  • 121
  • 189
  • this works, but this is the solution I was trying to avoid. I want something more automatic than creating a local copy of the data every time I want this behavior – gcc-question Feb 13 '16 at 23:10
  • @gcc-question: did you try changing the definition to `int f1(const char * restrict p)` ? – chqrlie Feb 13 '16 at 23:12
  • See this question too: http://stackoverflow.com/questions/26346301/does-const-t-restrict-guarantee-the-object-pointed-to-isn-t-modified/ – chqrlie Feb 13 '16 at 23:16
2

Have you disassembled the build to make sure that it is? Try the restrict keyword along with const to ensure that the compiler knows that nothing else can change it; you're calling another function f2() the compiler cannot know whether there is another pointer to the same block of memory being used in f2. But only do so as long as nothing in this branch does modify the same block of memory.

Your if(!p[0]) seems dangerous BTW if p has been initialised to NULL - there is nothing to dereference.

chqrlie
  • 131,814
  • 10
  • 121
  • 189
cdcdcd
  • 547
  • 1
  • 5
  • 15
  • `const` is irrelevant here, but `restrict` might do the job. The precise semantics of `restrict` are so obscure I am not sure it should. – chqrlie Feb 13 '16 at 22:04
  • @chqrlie au contraire... `restrict` is one of the few things that has a precise mathematical specification, so we don't end up debating the intent of words – M.M Feb 13 '16 at 22:08
  • It can be both. It can be simultaneously precisely defined _and_ not widely known. – sh1 Feb 13 '16 at 22:12
  • @M.M: I dont pretend it does not, I'm just saying that IMHO the semantics are difficult to understand. For instance, I do not understand what it means to qualify pointer arguments with `restrict` in function declarations, as is the case in the standard header files. – chqrlie Feb 13 '16 at 22:13
  • @chqrlie as discussed [here](http://stackoverflow.com/questions/28819855/is-top-level-volatile-or-restrict-significant-in-a-function-prototype) I think it has no effect other than as documentative to the person reading the header file (or to automated tools doing static analysis for example) – M.M Feb 13 '16 at 22:18
  • @chqrlie you probably know this but for clarity, the restrict keyword simply tells the compiler that within the given scope (including branches downward), only that the restricted pointer will point to the given block of memory. So nothing else accesses the same block of memory - it's exclusive to the pointer. So in this sense access is restricted to the given pointer. – cdcdcd Feb 13 '16 at 22:26
  • @cdcdcd: I agree that this is the general idea, but the precise semantics in C11 *6.7.3.1 Formal definition of `restrict`* are so abstruse I cannot be sure that there is to it. – chqrlie Feb 13 '16 at 22:46
  • @M.M: that was my understanding, but this is both useless and deceptive. For example: `memcpy(ptr, ptr, 0);` is perfectly OK, yet passing identical values for 2 restrict qualified pointer arguments seems incorrect. The `restrict` keyword in standard function prototypes does not improve readability, au contraire: `FILE *fopen(const char * restrict filename, const char * restrict mode);`... Why would `const char *r = "r"; return fopen(r, r);` pose any problem? – chqrlie Feb 13 '16 at 22:53
  • Your `fopen` call is correct since neither string is written. (`restrict` is a promise that if the pointed-to object is written, it will only be read or written through that same pointer (or a pointer based on it). It's like impromptu strict aliasing for objects of the same type – M.M Feb 13 '16 at 22:59
  • @M.M: In the case of the declaration of `fopen`, I just cannot understand what promise `restrict` is supposed to indicate. – chqrlie Feb 13 '16 at 23:05
  • @chqrlie nothing, afaics, as far as the programmer is concerned... I guess that if you were writing the implementation of fopen, it would mean the compiler can update its `FILE` structure or whatever else it uses without risk of corrupting the arguments – M.M Feb 13 '16 at 23:09
  • @M.M: I agree with your interpretation, but it would have the unneeded side effect of making `char *r = "r"; fopen(r, r)` invoke undefined behavior. – chqrlie Feb 14 '16 at 00:58