Question about restrict keyword

eLiu

Diamond Member
Jun 4, 2001
6,407
1
0
Hi all,
My compiler (icc) is generating a warning that confuses me:

PXVec.c(305): warning #167: argument of type "double *restrict *" is incompatible with parameter of type "void *"
free( Value );
^
In a struct, I have:
typedef struct{
double * restrict * restrict Value; //an array of pointers
} PX_Vec;

Then in some code...
destroy(PX_Vec *Xvec){
free(Xvec->Value); //Value previously malloc'ed as malloc(size*sizeof(double *));
}

Why am I getting a warning on this? Is there any way around it?

Thanks,
-Eric

Edit: I have another question too...
When you're using double ** to mean "an array of pointers", does it even make sense to put restrict twice?

If it matters, the idea is to have a large block of memory (double *Data) divided into numChunks chunks of varying size. Then the "Value" pointer array has numChunks pointers, each one pointing to the start of a new chunk in the array Data.

Then operations on the elements of Data will either be carried out indexing from the "Data" pointer directly OR from the "Value" pointer, but never both simultaneously.

So when looking inside Data or Value, I want the compiler to know that this array is not aliased to anything else.
 
Last edited:

Cogman

Lifer
Sep 19, 2000
10,286
145
106
two restricts do not make sense. you should have double** restrict.

(I'm somewhat surprised the compiler even allows it TBH.)

Just make sure you are using icc as a C99 compiler and not a c++ compiler. C++ ignores the restrict keyword.
 

degibson

Golden Member
Mar 21, 2008
1,389
0
0
Why am I getting a warning on this?
Because by implicitly casting to void*, you effectively create another pointer to the same memory region, thereby violating the 'restrict' keyword.

Is there any way around it?
Try an explicit cast. I would try it myself, but I don't have icc laying around.

When you're using double ** to mean "an array of pointers", does it even make sense to put restrict twice?
The only real value of restrict is to convince the compiler that two pointers point to different memory. Therefore, it ends up affecting code that uses those pointers.

So when looking inside Data or Value, I want the compiler to know that this array is not aliased to anything else.
If you only want the compiler to assume safety on the data array, you only need one restrict. The other restrict would be to label the index itself as unaliased.
 

eLiu

Diamond Member
Jun 4, 2001
6,407
1
0
two restricts do not make sense. you should have double** restrict.

(I'm somewhat surprised the compiler even allows it TBH.)

Just make sure you are using icc as a C99 compiler and not a c++ compiler. C++ ignores the restrict keyword.

Yeah the compiler isn't ignoring it, no worries. I don't understand why you think this is illogical or illegal. Consider:
#define nRow = 10;
#define nCol = 10;
double **myArrayA;
double **myArrayB;
myArray = malloc(nCol*sizeof(double *));
for(i=0; i<nCol; i++){
myArray = malloc(nRow*sizeof(double));
}
//myArrayB set up the same way

Then...
void dothings(double * restrict * restrict A, double * restrict * restrict B, int m, int n)
{
int i,j;
for(i=0;i<m;i++)
for(j=0;j<n;j++)
A[j] = (B[j]*100)/12;
}

called with:
dothings(myArrayA, myArrayB, nCol, nRow);

If I just wrote double ** restrict A, then the compiler knows that the pointer array does not alias another pointer array. But the individual pointers in the pointer array could all alias. (Imagine setting B=malloc(nCol*sizeof(double *)) then setting B = A+1. Let's pretend that won't segfault.) So I THINK the compiler will be forced to execute this loop in serial b/c it doesn't know that one iteration cannot affect another.

But with double * restrict * restrict A, we know that the pointer array does not alias, and also that each pointer in the array does not alias. So the pointer array of A and the data of A are uniquely accessible by **A.

This is my thinking anyway... thoughts?

And next...
Because by implicitly casting to void*, you effectively create another pointer to the same memory region, thereby violating the 'restrict' keyword.

Try an explicit cast. I would try it myself, but I don't have icc laying around.

Yeah I did try that. I got a different warning... this time about an umodifiable lvalue. I don't have the exact phrasing anymore; I recreate it if you want.

The only real value of restrict is to convince the compiler that two pointers point to different memory. Therefore, it ends up affecting code that uses those pointers.

If you only want the compiler to assume safety on the data array, you only need one restrict. The other restrict would be to label the index itself as unaliased.

Where should that restrict go?
double * restrict * A
double **restrict A
I'm thinking it's the former. Since the first one says "A is a pointer to restricted pointers to type double."

Would I want to put a restrict on the Data array as well? The array is accessed either through the pointer array or the actual Data array, but not both. So technically they alias, but there will be no writes happening so my understanding is that restricting both is reasonable.

Thanks,
-Eric
 

degibson

Golden Member
Mar 21, 2008
1,389
0
0
Where should that restrict go?
double * restrict * A
double **restrict A
double * restrict * A <-- this;

Would I want to put a restrict on the Data array as well? The array is accessed either through the pointer array or the actual Data array, but not both. So technically they alias, but there will be no writes happening so my understanding is that restricting both is reasonable.
No need to use restrict on both unless you use both in the same function. The most valuable optimization is reordering, which (usually) cannot be done if two accesses are in different function scopes.

BTW: Are you sure that icc actually uses optimizations that arise from restrict? Try compiling with -S and see if the output code is different.
 

Cogman

Lifer
Sep 19, 2000
10,286
145
106
double * restrict * A <-- this;


No need to use restrict on both unless you use both in the same function. The most valuable optimization is reordering, which (usually) cannot be done if two accesses are in different function scopes.

BTW: Are you sure that icc actually uses optimizations that arise from restrict? Try compiling with -S and see if the output code is different.

I have to say, I'm not familiar with the restrict keyword. However, the IBM documentation on it says that the ISO C++ is supposed to essentially ignore it, that it is only there from C99 compatibility.

But I could be wrong. I'm really just not too familiar with it.
 

degibson

Golden Member
Mar 21, 2008
1,389
0
0
I have to say, I'm not familiar with the restrict keyword. However, the IBM documentation on it says that the ISO C++ is supposed to essentially ignore it, that it is only there from C99 compatibility.

But I could be wrong. I'm really just not too familiar with it.

It's legal to ignore restrict. Restrict is a hint to the compiler that the data is not aliased via another pointer.

e.g.,
Code:
char * restrict p_somethingRestricted = &myData;
char x = functionOf( p_somethingRestricted[12] );
... /* Way later in the same scope */
char y = AnotherFunctionOf( p_somethingRestricted[12] );
Restrict tells the compiler that there is no way in hell that p_somethingRestricted[12] has changed since it was last read. Therefore, its ok, for instance, to register-allocate p_somethingRestricted[12] and the memory model won't break -- even if there are hard-to-analyze writes to possibly dangerous pointers between the first and second use.

In short:
Restrict, to compiler: Yo compiler! Trust me, this pointer is the only pointer to this chunk of data. I know your awesome static analysis can't be sure, but I, the mighty programmer, am entirely sure and you just need to trust me on this.

To this, most compilers reply:
Compiler, under its breath without notification to the programmer: Pfft. Whatever. You'll never know I'm not trusting you. I will just use my other incredibly awesome optimizations and things will still be blazing-fast.
 

eLiu

Diamond Member
Jun 4, 2001
6,407
1
0
double * restrict * A <-- this;
No need to use restrict on both unless you use both in the same function. The most valuable optimization is reordering, which (usually) cannot be done if two accesses are in different function scopes.

BTW: Are you sure that icc actually uses optimizations that arise from restrict? Try compiling with -S and see if the output code is different.

Haha that was a very colorful description of 'restrict' in your previous post... :p

I don't know whether icc is actually 'optimizing' anything, but the code is different. I haven't checked the assembly, but in one case where I violated that restrict contract, my code segfaulted. After removing restrict, it operates correctly. So something must've happened.

So is there nothing I can do about the warning with free()? That seems annoying...
 

degibson

Golden Member
Mar 21, 2008
1,389
0
0
So is there nothing I can do about the warning with free()? That seems annoying...

My only idea is:
When I've seen restrict used, it has been at function scope, not declaration scope. I.e., instead of declaring the pointer itself as restrict (in the struct), just pass the pointer to a function that takes a restrict*.

That way, the data is sometimes restrict'ed (e.g., when you're operating on it), and sometimes not (e.g., when you're allocating / deallocating it).
 

eLiu

Diamond Member
Jun 4, 2001
6,407
1
0
My only idea is:
When I've seen restrict used, it has been at function scope, not declaration scope. I.e., instead of declaring the pointer itself as restrict (in the struct), just pass the pointer to a function that takes a restrict*.

That way, the data is sometimes restrict'ed (e.g., when you're operating on it), and sometimes not (e.g., when you're allocating / deallocating it).

Hm the issue I've run into here is... suppose I'm passing in the array of pointers for indexing into my array:
void myfun(double * restrict * restrict indexer);

and I call it by: myfun(mystruct->indexer);
where mystruct holds "double ** indexer"

When I do this, I get a warning saying that "double * restrict *" is incompatible with "double **"

Wah.
 

degibson

Golden Member
Mar 21, 2008
1,389
0
0
Hm the issue I've run into here is... suppose I'm passing in the array of pointers for indexing into my array:
void myfun(double * restrict * restrict indexer);

and I call it by: myfun(mystruct->indexer);
where mystruct holds "double ** indexer"

When I do this, I get a warning saying that "double * restrict *" is incompatible with "double **"

Wah.

Bloody hell, icc sure is RESTRICTive. Seriously, I've got a million puns but no more ideas, sorry. :(