Why does this code trigger gcc's "maybe uninitialized" warning? - c

In plenty of other instances, gcc seems to be able to detect that the condition at the beginning of the loop initializes the variable. It even works if I drop the increment operator. It also goes away without the -02 flag. I've learned to trust that compiler warnings usually do mean something is wrong, so I'm wondering if there's something I'm missing or if it's just a weird compiler quirk.
void act(char **);
void test(int width, int height){
char * rowA[width];
char ** A;
for (int i = 0; i < width * height; ++i){
if (!(i % width)){
if (i){
act(rowA);
}
rowA[0] = 0;
A = rowA;
}
*A++ = "hello";
}
}
Compiling on gcc-6.3.0 with -Wall -Werror -02
Edit: to avoid confusion I've altered the code to be closer to the actual use case.

The warning means that the compiler couldn't prove the variable was always initialized before use.
This particular warning cannot be perfectly accurate of course, and it errs on the side of caution in that it does raise the warning if it wasn't sure. When using this warning it is common to get this sort of situation where you have to modify your correct code to make the warning go away.
Possibly it is also evidence of an optimizer bug; the optimizer should realize that if(!(i % argc)) can be optimized to if (i == 0).

Related

gcc: warn about every variable definition without initialisiation

Is there a warning flag so that gcc will warn about every variable that is defined and not immediately initialized? I know about -Wuninitialized but it does not warn about every case.
Consider this code:
int foo(int i)
{
int a;
a= i%2 ? 0x42 : 42;
return a;
}
Even tho this is perfectly valid code and does not cause UB, i would like a warning for this. The line int a; should be replaced by int a=0;. Can i tell gcc to warn about int a;?
Edit, a more complex example:
#include <stdio.h>
int foo(void)
{
int a;
if(1==scanf("%i",&a))
{
return a;
}
return 0;
}
Here i want gcc to warn about int a;
This does not seem a language problem, so I don't think GCC will ever react; actually, it is defining a value for a that might be warned against by some linter:
int a = 0;
^-- warning: redundant initialisation (value is immediately discarded)
a= i%2 ? 0x42 : 42;
What you can, perhaps, do - but this will not catch all possible cases - is recognize the issue at the static level.
I myself do this for some specific cases (which is why I have trained myself to write code in what is sometimes a unusual way -- for example, free(p); p = NULL; on a single line, or if p is to be immediately discarded, then free(p); // p = NULL;, so no pointer can ever point to freed memory, and I track this using grep. The same with fclose(fp); fp = NULL).
In this case, at least for the common data types and simple initializations, you can recognize the unassignment with a regex too:
((unsigned\\s+)?(long\\s+)?(int|char|float|double)...
(Not a real regex, just throwing in tokens at random. You may want to look at other questions to this effect).
Once you have a means of singling out all definitions, any definition that does not include an equal sign is suspect and can be printed for further analysis. You can avoid a "unneeded initialization" by writing int a; //OK and ignore the lines containing '//OK'.
In your code sample, being warned to replace the int a; line with int a = 0; will be counter-productive. A good compiler would/should warn you about the latter, as there you are assigning a value to a that will never be used (the value is immediately overwritten in the following line of code).

How do I create arrays / average [duplicate]

Okay, so this is a stripped down variant of a bug I had. The bug was that I initialized an array using a variable that wasn't initialized. Earlier I used a function to declare the number of elements using a function, but after a cleanup I forgot about it and moved all declarations to the top of the function.
I used the flags -std=c99 -Wall -Wextra -pedantic -O, and usually gcc warns about values being used before they are uninitialized, but in this specific case it didn't. So, my question is:
Is this a bug in gcc or is it possible for f(&n) to post-initialize the array size in some weird way?
#include <stdio.h>
void f(int * x) {
*x = 8;
}
int main(void) {
int n;
float a[n]; // Compiler should warn that n may contain garbage
a[7] = 3.1415;
printf("%f\n", a[7]);
f(&n); // Removing this causes the compiler warn as expected
return 0;
}
EDIT: It may be this gcc bug?
GCC is accepting float a[n] as a variable-length array. It should, however, warn you that n contains garbage when it’s used. Perhaps VLA initialization is getting rearranged in a way that makes that fact non-obvious to the code generator? If n were initialized before use, moving the call to f() above the declaration of a would clearly be wrong, but this program produces undefined behavior.

Why doesn't gcc o warn when size of array is uninitialized in this code?

Okay, so this is a stripped down variant of a bug I had. The bug was that I initialized an array using a variable that wasn't initialized. Earlier I used a function to declare the number of elements using a function, but after a cleanup I forgot about it and moved all declarations to the top of the function.
I used the flags -std=c99 -Wall -Wextra -pedantic -O, and usually gcc warns about values being used before they are uninitialized, but in this specific case it didn't. So, my question is:
Is this a bug in gcc or is it possible for f(&n) to post-initialize the array size in some weird way?
#include <stdio.h>
void f(int * x) {
*x = 8;
}
int main(void) {
int n;
float a[n]; // Compiler should warn that n may contain garbage
a[7] = 3.1415;
printf("%f\n", a[7]);
f(&n); // Removing this causes the compiler warn as expected
return 0;
}
EDIT: It may be this gcc bug?
GCC is accepting float a[n] as a variable-length array. It should, however, warn you that n contains garbage when it’s used. Perhaps VLA initialization is getting rearranged in a way that makes that fact non-obvious to the code generator? If n were initialized before use, moving the call to f() above the declaration of a would clearly be wrong, but this program produces undefined behavior.

Is redundant assignment elimination fairly typical optimization?

Given the following:
len = strlen(str);
/* code that does not read from len */
len = new_len;
Can I depend on the compiler to remove the first line?
I'm writing a code generating script. The first line is generated in one place, while everything that follows is generated elsewhere (by a virtual function in a descendant class). Most of the time, len isn't needed. Sometimes though, it is. I wonder if I can just set it in all cases and let the compiler get rid of the line if it isn't required.
No, of course you cannot "depend" on the optimization choices done by the compiler.
They can change at the user's whim (compiler command line options), or with different compiler versions.
Since the behavior you're describing is not required by the language standard, you cannot depend on compilers to implement it.
On the other hand, you can surely test it, and try to "coax" it into being optimized out, by (again) asking your compiler to do as much optimization as possible, for instance.
Compilers are pretty clever. But to rely on the compiler removing "unused" function calls is probably not a good idea. For one thing, the compiler will NEED to understand the function you are calling (so strlen is a good example here, because most compilers understand what strlen does and how it affects other things) - if the function is not one that the compiler "understands", then it can't optimise it out.
What if you did:
x = printf("Hello, World!\n");
x = printf("World, Hello!\n");
Would you think they compiler had done the right thing by removing the first printf? Probably not... So, with any function the compiler can't determine "is side-effect free", the compiler MUST call the function, even if the result is not used. Side-effect free means under normal circumstances - e.g. there is a side-effect of calling strlen() with an invalid pointer - your code will probably crash - but that's not "normal circumstances" - you'd be pretty daft to use strlen() just to check if your pointer is a valid one or not, right?
So, in other words, you probably want to make sure your call to strlen() is really needed before you call strlen - or live with the fact that the compiler may wall generate an unnecessary strlen call.
Modern compilers do seem to do an excellent job of eliminating redundant assignments. I ran the following snippet through VS 2008 and gcc 4.6 and the call to strlen (the inlined code, rather) is in fact removed. Both compilers managed to see that the both something() and something_else() produce no side effects. Calls to these are removed as well.
This occurs at /O1 in VC and /O1 in gcc.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int something(int len) {
if(len != len) {
printf("%d\n", len);
}
return 0;
}
int something_else(int len) {
return len * len;
}
int main(void) {
char *s = malloc(1000);
size_t len;
strcpy(s, "Hello world");
len = strlen(s);
something(len);
printf("%s\n", s);
len += 8;
something_else(len);
len = 5;
printf("%d\n", len);
return 0;
}
It depends on compiler & the optimization level you specify.
Here it gets assigned initially from a function call. What if that function has a side effect?
So you cannot just assume the compiler to optimize it for you.
If the initial assignment statement is free from any side effects & you specify optimization level good enough (e.g. -O3 in case of gcc) it may optimize it for you.

Rules for using the restrict keyword in C?

I'm trying to understand when and when not to use the restrict keyword in C and in what situations it provides a tangible benefit.
After reading, "Demystifying The Restrict Keyword", ( which provides some rules of thumb on usage ), I get the impression that when a function is passed pointers, it has to account for the possibility that the data pointed to might overlap (alias) with any other arguments being passed into the function. Given a function:
foo(int *a, int *b, int *c, int n) {
for (int i = 0; i<n; ++i) {
b[i] = b[i] + c[i];
a[i] = a[i] + b[i] * c[i];
}
}
the compiler has to reload c in the second expression, because maybe b and c point to the same location. It also has to wait for b to be stored before it can load a for the same reason. It then has to wait for a to be stored and must reload b and c at the beginning of the next loop. If you call the function like this:
int a[N];
foo(a, a, a, N);
then you can see why the compiler has to do this. Using restrict effectively tells the compiler that you will never do this, so that it can drop the redundant load of c and load a before b is stored.
In a different SO post, Nils Pipenbrinck, provides a working example of this scenario demonstrating the performance benefit.
So far I've gathered that it's a good idea to use restrict on pointers you pass into functions which won't be inlined. Apparently if the code is inlined the compiler can figure out that the pointers don't overlap.
Now here's where things start getting fuzzy for me.
In Ulrich Drepper's paper, "What every programmer should know about memory" he makes the statement that, "unless restrict is used, all pointer accesses are potential sources of aliasing," and he gives a specific code example of a submatrix matrix multiply where he uses restrict.
However, when I compile his example code either with or without restrict I get identical binaries in both cases. I'm using gcc version 4.2.4 (Ubuntu 4.2.4-1ubuntu4)
The thing I can't figure out in the following code is whether it needs to be rewritten to make more extensive use of restrict, or if the alias analysis in GCC is just so good that it's able to figure out that none of the arguments alias each other. For purely educational purposes, how can I make using or not using restrict matter in this code - and why?
For restrict compiled with:
gcc -DCLS=$(getconf LEVEL1_DCACHE_LINESIZE) -DUSE_RESTRICT -Wextra -std=c99 -O3 matrixMul.c -o matrixMul
Just remove -DUSE_RESTRICT to not use restrict.
#include <stdlib.h>
#include <stdio.h>
#include <emmintrin.h>
#ifdef USE_RESTRICT
#else
#define restrict
#endif
#define N 1000
double _res[N][N] __attribute__ ((aligned (64)));
double _mul1[N][N] __attribute__ ((aligned (64)))
= { [0 ... (N-1)]
= { [0 ... (N-1)] = 1.1f }};
double _mul2[N][N] __attribute__ ((aligned (64)))
= { [0 ... (N-1)]
= { [0 ... (N-1)] = 2.2f }};
#define SM (CLS / sizeof (double))
void mm(double (* restrict res)[N], double (* restrict mul1)[N],
double (* restrict mul2)[N]) __attribute__ ((noinline));
void mm(double (* restrict res)[N], double (* restrict mul1)[N],
double (* restrict mul2)[N])
{
int i, i2, j, j2, k, k2;
double *restrict rres;
double *restrict rmul1;
double *restrict rmul2;
for (i = 0; i < N; i += SM)
for (j = 0; j < N; j += SM)
for (k = 0; k < N; k += SM)
for (i2 = 0, rres = &res[i][j],
rmul1 = &mul1[i][k]; i2 < SM;
++i2, rres += N, rmul1 += N)
for (k2 = 0, rmul2 = &mul2[k][j];
k2 < SM; ++k2, rmul2 += N)
for (j2 = 0; j2 < SM; ++j2)
rres[j2] += rmul1[k2] * rmul2[j2];
}
int main (void)
{
mm(_res, _mul1, _mul2);
return 0;
}
It is a hint to the code optimizer. Using restrict ensures it that it can store a pointer variable in a CPU register and not have to flush an update of the pointer value to memory so that an alias is updated as well.
Whether or not it takes advantage of it depends heavily on implementation details of the optimizer and the CPU. Code optimizers already are heavily invested in detecting non-aliasing since it is such an important optimization. It should have no trouble detecting that in your code.
Also, GCC 4.0.0-4.4 has a regression bug that causes the restrict keyword to be ignored. This bug was reported as fixed in 4.5 (I lost the bug number though).
(I don't know if using this keyword gives you a significant advantage, actually. It's very easy for programmer to err with this qualifier as there is no enforcement, so an optimizer cannot be certain that the programmer doesn't "lie".)
When you know that a pointer A is the only pointer to some region of memory, that is, it doesn't have aliases (that is, any other pointer B will necessarily be unequal to A, B != A), you can tell this fact to the optimizer by qualifying the type of A with the "restrict" keyword.
I have written about this here: http://mathdev.org/node/23 and tried to show that some restricted pointers are in fact "linear" (as mentioned in that post).
It's worth noting that recent versions of clang are capable of generating code with a run-time check for aliasing, and two code paths: one for cases where there is potential aliasing and the other for case where is is obvious there is no chance of it.
This clearly depends on the extents of data pointed to being conspicuous to the compiler - as they would be in the example above.
I believe the prime justification is for programs making heavy use of STL - and particularly <algorithm> , where is either difficult or impossible to introduce the __restrict qualifier.
Of course, this all comes at the expense of code-size, but removes a great deal of potential for obscure bugs that could result for pointers declared as __restrict not being quite as non-overlapping as the developer thought.
I would be surprised if GCC hadn't also got this optimisation.
May be the optimisation done here don't rely on pointers not being aliased ? Unless you preload multiple mul2 element before writing result in res2, I don't see any aliasing problem.
In the first piece of code you show, it is quite clear what kind of aliases problem can occur.
Here it is not so clear.
Rereading Dreppers article, he does not specifically says restrict might solve anything. There is even this phrase :
{In theory the restrict keyword
introduced into the C language in the
1999 revision should solve the
problem. Compilers have not caught up
yet, though. The reason is mainly that
too much incorrect code exists which
would mislead the compiler and cause
it to generate incorrect object code.}
In this code, optimisations of memory access has already been done within the algorithm. The residual optimisation seems to be done in the vectorized code presented in appendice. So for the code presented here, I guess there is no difference, because no optimisation relying on restrict is done. Every pointer access is a source of aliasing, but not every optimisation relies on aliassing.
Premature optimization being the root of all evil, the use of the restrict keyword should be limited to the case your are actively studying and optimizing, not used wherever it could be used.
If there is a difference at all, moving mm to a seperate DSO (such that gcc can no longer know everything about the calling code) will be the way to demonstrate it.
Are you running on 32 or 64-bit Ubuntu? If 32-bit, then you need to add -march=core2 -mfpmath=sse (or whatever your processor architecture is), otherwise it doesn't use SSE. Secondly, in order to enable vectorization with GCC 4.2, you need to add the -ftree-vectorize option (as of 4.3 or 4.4 this is included as default in -O3). It might also be necessary to add -ffast-math (or another option providing relaxed floating point semantics) in order to allow the compiler to reorder floating point operations.
Also, add the -ftree-vectorizer-verbose=1 option to see whether it manages to vectorize the loop or not; that's an easy way to check the effect of adding the restrict keyword.
The problem with your example code is that the compiler will just inline the call and see that there is no aliasing ever possible in your example. I suggest you remove the main() function and compile it using -c.
The following C99 code can show you that the output of the program depends on restrict :
__attribute__((noinline))
int process(const int * restrict const a, int * const b) {
*b /= (*a + 1) ;
return *a + *b ;
}
int main(void) {
int data[2] = {1, 2};
return process(&data[0], &data[0]);
}
The software terminates with code 1 using restrict and 0 without restrict qualifier.
The compilation is done with gcc -std=c99 -Wall -pedantic -O3 main.c.
The flag -O1 do the job too.
It is useful to use restrict when, for example, you can tell the compiler that the loop condition remains unchanged, even if another pointer has been updated (necessarily, the loop condition couldn't change due to restrict).
And certainly so on.

Resources