Can this mostly useless while loop be optimized out?

F

Francois Grieu

// This post is a valid C program

#include <stdio.h>
#include <stdlib.h>

typedef struct tFoo
{
int *fPtr;
int fIntA;
int fIntB;
} tFoo;

tFoo *gFoo;

// assuming inNbFoo>0 and inNbFoo<=SIZE_MAX/sizeof(tFoo)
// allocate inNbFoo tFoo and set all fields to NULL or 0
void setfoo(int inNbFoo)
{
gFoo = calloc(inNbFoo,sizeof(*gFoo));
// can the following be supressed?
if (gFoo!=NULL)
while(--inNbFoo)
gFoo[inNbFoo].fPtr = NULL;
}

int main(void)
{
setfoo(7);
if (gFoo!=NULL && gFoo[0].fPtr!=NULL)
printf("wrongo\n");
return 0;
}

/*
For strict portability, it appears that the while loop is mandatory,
in case NULL is not represented by the all-zero bit pattern.
But is there a portable way to determine at compile time that it
is not needed, which is the case on most machines, and/or
otherwise help the compiler to remove the while loop ?

TIA,
Francois Grieu (reposted with correction of comments)
*/
 
T

Tim Rentsch

Francois Grieu said:
// This post is a valid C program

#include <stdio.h>
#include <stdlib.h>

typedef struct tFoo
{
int *fPtr;
int fIntA;
int fIntB;
} tFoo;

tFoo *gFoo;

// assuming inNbFoo>0 and inNbFoo<=SIZE_MAX/sizeof(tFoo)
// allocate inNbFoo tFoo and set all fields to NULL or 0
void setfoo(int inNbFoo)
{
gFoo = calloc(inNbFoo,sizeof(*gFoo));
// can the following be supressed?
if (gFoo!=NULL)
while(--inNbFoo)
gFoo[inNbFoo].fPtr = NULL;
}

int main(void)
{
setfoo(7);
if (gFoo!=NULL && gFoo[0].fPtr!=NULL)
printf("wrongo\n");
return 0;
}

/*
For strict portability, it appears that the while loop is mandatory,
in case NULL is not represented by the all-zero bit pattern.
But is there a portable way to determine at compile time that it
is not needed, which is the case on most machines, and/or
otherwise help the compiler to remove the while loop ? */

The short answer is no, either because there is no guarantee
of a suitable scalar type that can exclude the possibility
of trap representations, or because there is no way to do
an (at least in theory) unbounded length check on a pointer
variable in a way that a compiler is likely to optimize
away.

Turning to the practical rather than the theoretical,
here are some slightly longer "other" or "yes" answers.


First: the supplied example looks like it's called only
during initialization. If that's true, then don't worry
about it, because it won't matter anyway.


Second: if the code in question is being called multiple
times rather than just during initialization, one way
to work around the problem, completely portably I believe,
is as follows (sketch only; code not compiled or tested):

extern tFoo * (*some_foos)( int );

tFoo*
fast_some_foos( int n ){
return calloc( n, sizeof *some_foos(n) );
}

tFoo*
slow_some_foos( int n ){
tFoo *result = fast_some_foos( n );
if( result ){
while( n > 0 ){
result[--n].fPtr = NULL;
}
}
return result;
}

tFoo*
initial_some_foos( int n ){
tFoo foo;
unsigned i, i_limit = sizeof foo.fPtr;
foo.fPtr = NULL;
for( i = 0; i < i_limit; i++ ){
if( i[ (unsigned char*) &foo.fPtr ] != 0 ) break;
}
some_foos = i == i_limit ? fast_some_foos : slow_some_foos;
return some_foos( n );
}

tFoo *(*some_foos)(int) = initial_some_foos;

All calls to some_foos() after the first will automatically
land at the right optimized place.


Third: if you're using C99, there is this:

tFoo*
some_foos( int n ){
tFoo *result = calloc( n, sizeof *result );
if( 0 != *(int**) (unsigned char [sizeof(int*)]){0} ){
while( n > 0 ) ... etc ...
}
return result;
}

Technically, the expression after the == is undefined behavior, not
once but twice; practically speaking, it will almost certainly
work, and the rares cases where it doesn't work (I don't know of
any -- anyone?) most likely will fail by exhibiting a program
crash.[1] And saving the best news for last -- on gcc at least,
this test does get completely optimized away by the compiler,
eliminating code inside the if() body.


[1] One of the UB's could be eliminated by using a union, but
it's the other UB that's the more serious one. Life is hard.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,904
Latest member
HealthyVisionsCBDPrice

Latest Threads

Top