6

If the C compiler knows that a pointer is not aliased, it can perform many optimizations. For example, if I compile the following function with gcc -O2:

int f_noalias(int *arr, int x)
{
    int res = 0;
    int *p = &arr[17];
    *p = x;
    res += *p;
    res += *p;
    return res;
}

the compiler knows that reading *p will always evaluate to x so the generated code is equivalent to the one generated for the following function:

int f_noalias2(int *arr, int x)
{
    int *p = &arr[17];
    *p = x;
    return 2*x;
}

However, if the compiler thinks the pointer might be aliased, it doesn't perform this optimization anymore. For example, if we modify f so that an unknown function is called between the reads to *p, the generated code will dereference p twice. The compiler assumes that the read_arr function might have modified the value that p points to.

int f_withalias(int *arr, int x)
{
    int res = 0;
    int *p = &arr[17];
    *p = x;
    res += *p;
    read_array(arr);
    res += *p;
    return res;
}

In my particular program, when the f function is running the p pointer that it holds is the only one that writes to that element of the arr array. Other functions in the code might read from arr during this time, but won't write to it. (They might write other values to arr after f finishes running though.)

So now I have three questions:

First: Is there a way I can declare my variables to give this hint to the C compiler? I tried adding a restrict annotation to p but the generated code under gcc -O2 was identical to the generated code for f_withalias

int f_restrict(int *arr, int x)
{
    int res = 0;
    int * restrict p = &arr[17];
    *p = x;
    res += *p;
    read_array(arr);
    res += *p;
    return res;
}

Second: Is my attempt to use restrict here valid? My understanding is that restrict means that no other pointers can alias p, both for reading or writing. But in my case the read_arr function clearly can also access the arr array that p is pointing to.

Third: If the answer to the previous question is "no", is there something different I can try instead of restrict?

Basically, I need to make sure that if I do *p = x in f then that write will be immediately noticed by other functions reading from arr[17]. However, I want GCC to feel free to optimize things like x = *p; y = *p to x = *p; y = x, even if there are function calls between the two reads.

hugomg
  • 68,213
  • 24
  • 160
  • 246
  • Did you try adding `restrict` to `p` and `arr` in the same code? – chux - Reinstate Monica Sep 19 '18 at 06:51
  • Changing `int *arr` to `int * restrict arr` had no effect. – hugomg Sep 19 '18 at 06:56
  • 2
    Hmm, as `restrict` _may_ result in an optimization otherwise prohibited, a lack of seen optimization does not imply a useless use of `restrict`. Just that it was useless for this compilation/options. Good luck. – chux - Reinstate Monica Sep 19 '18 at 07:01
  • Why not load `*p` into a local variable once before `read_array`? – Shafik Yaghmour Sep 19 '18 at 07:40
  • I am writing a compiler and using C as a back end. A) I would like to piggy back on GCC optimizations when possible. B) creating less local variables might reduce register pressure. If I have both `x` and `*p`, I'd like GCC to know that if x cant be stored in a register then it can be recomputed from p as needed. No need to carefully spill it to and from the stack – hugomg Sep 19 '18 at 07:52
  • I think you are lying to the compiler. You tell it no other access to the contents pointed at by `p` will happen within the scope of `f_restrict` and yet you do access it. `restrict` is a contract between the programmer and compiler, the compiler won't check for such access but rather trust the programmer. I don't believe gcc complains if you do an implicit conversion from `type* restrict` to `type*` though, which is questionable. – Lundin Sep 19 '18 at 08:19
  • @Lundin: so are you saying that the answer to my second question is NO? (If so, you could post that as an answer). Also, do you know if there is another way to get what I want? – hugomg Sep 19 '18 at 12:15
  • Ok I'll try to write an answer, but in my experience, compilers aren't all that trustworthy yet when it comes to optimizing `restrict`. – Lundin Sep 19 '18 at 12:44

1 Answers1

2

First: Is there a way I can declare my variables to give this hint to the C compiler?

int * restrict p = &arr[17]; asserts that only p and pointer expressions based on p will be used to access any object p points to for the duration of the block (except for objects that are not modified in any way). This enables the optimization of res += *p; that you propose. The fact that GCC does not so optimize is a quality issue in GCC.

Second: Is my attempt to use restrict here valid? … Basically, I need to make sure that if I do *p = x in f then that write will be immediately noticed by other functions reading from arr[17].

The latter property is not a valid use of restrict. The fact that p is declared restrict and arr[17] is modified via p means that no pointer not based on p should be used to access arr[17] during the execution of the block containing p, not even for reading. So, if something in read_array did read arr[17] (using arr, which is not based on p), it would be a violation of the restrict assertion.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
  • As per my question title, do you know if there is a different thing I could try, if restrict is too restrictive for my needs? Is saving the value in a variable like Shafik suggested the only way? – hugomg Sep 19 '18 at 12:24
  • 1
    @hugomg: I am not aware of any way to tell the compiler an object is modified only through one pointer but may be read through another. I doubt there is. A proper way to write a new compiler is to emit code in GCC’s internal representation. Thus, the new front-end parses the language and writes GCC’s internal language, and the existing back-end does optimization and generation of assembly/object code. – Eric Postpischil Sep 19 '18 at 12:39
  • @EricPostpischil: Unfortunately, I don't think gcc's internal representation can accommodate all of the semantics that would be necessary to allow generation of code which is both efficient and reliable. – supercat May 23 '22 at 21:45