I have a question about strict-aliasing and clang optimizations for one example.
Let's consider the following example (1):
typedef void (*FTy)(void);
FTy F = 0;
(*F)();
It is an undefined behavior.
Let's consider the example (2) below:
typedef void (*FTy)(void);
static const FTy F = 0;
void g( int flag)
{
if ( flag )
{
(*F)();
}
}
int main( void)
{
g( 0);
return (0);
}
Indirect call by F is still "undefined behavior", but stands under always false condtion. So programm must be correct.
Now let's consider the main example (3):
(second edition: Thanks to #Antti Haapala simplified version)
(third edition: using of always_inline)
#include <stdio.h>
#ifndef BUGGY
#define BUGGY 1
#endif
static inline void __attribute__((always_inline)) longLongAssign( int cond, char *ptr)
{
if ( cond )
{
*((long long *)ptr) = 0;
}
}
void funcA(int s, int g)
{
int i, j = 0, k;
int grp[4] = {-1, -1};
void *ptr[2] = {(void *)&(grp[0]), 0};
for (i = 0, k = 0; i < 1; ++i) {
for (j = 0; j < 1; ++j) {
if ( grp[g] > 0 )
{
if ( g > 5 )
{
continue;
} else
{
longLongAssign( g > 3, (char *)ptr[0]);
}
}
grp[k++] = 0;
}
printf("this should be zero: %d\n", grp[0]);
}
}
int main(void) {
funcA(0, 1);
}
Compile by gcc and execute
this should be zero: 0
Compile by "clang-7.0 -O0" and execute
this should be zero: 0
Compile by "clang-7.0 -O1 -fno-strict-aliasing" and execute
this should be zero: 0
Compile by "clang-7.0 -O1" and execute
this should be zero: -1
In the main example one of stores to grp formally violates strict-aliasing
*((long long *)ptr) = 0;
But this store stands under always false condition.
The question here is: how a store operation
violating breaking strict-aliasing rule
but located in unreachable statement
may affect any way to program execution?
Is it correct by C language standart?
Is an example (4) below correct, well defined and hasn't undefined behavior?
void assign( int type, char *ptr)
{
if ( ptr )
{
if ( (type == 0) )
{
*((int *)ptr) = 1;
} else if ( (type == 1) )
{
*((float *)ptr) = 1;
} else
{
// unknown type
}
}
}
int main( void)
{
int a;
float b;
assign( 0, (char *)&a);
assign( 1, (char *)&b);
assign( 0, (char *)0);
return (0);
}
Inline and constant propagation optimizations in function main gives
...
if ( &a )
{
if ( (0 == 0) )
{
*((int *)&a) = 1;
} else if ( (0 == 1) )
{
*((float *)&a) = 1;
} else
{
// unknown type
}
}
...
In one hand store operation
*((float *)&a) = 1;
formally violates strict-aliasing, but stands in unreacheble location.
Which reasons example (4) may be incorrect?
If example (4) is correct so why example (3) gives by clang compilation different results?
The expression statement
*(long long *)grp = 0;
has undefined behavior on account of accessing an object of type int[4] via an lvalue of different, incompatible type (long long) -- a strict-aliasing violation, as you observe in the question. But that doesn't have to be limited to runtime behavior. The (potential) issue being visible at translation time, the translation-time behavior is undefined, too, and therefore so is the result of every execution.
Or at minimum, that's an interpretation of the standard to which at least some compiler developers subscribe. Some folks around here object to such interpretations, but that doesn't change the fact that you have to deal with them.
With regard to the update
Your example (4) has perfectly well-defined behavior. The major considerations here are
It is explicitly permitted to convert a value of one object-pointer type to a different object-pointer type. There are caveats about alignment of the result, but C requires it always to work for conversion to char *, and it requires the reverse conversion to reproduce the original pointer value (which has no alignment issue if it was valid to begin with).
It is permitted to access any object's representation via an lvalue of character type. In other words, a char * is permitted to alias any part of any object, so even though you don't access anything directly through the char * values passed to assign(), a conforming compiler must assume that those pointers could alias any object in the program.
A null pointer of any type can be converted to another object-pointer type, resulting in a null pointer of the target type.
By use of the type argument to function assign() in a manner consistent with that function's implementation, the program ensures that each object involved is ultimately accessed (only) via an lvalue of its correct type.
What optimizations the compiler might apply are irrelevant to this analysis. It is the code you present to the compiler by which behavior, if defined, is established. Supposing that the program has defined behavior, it is the compiler's responsibility to ensure that that behavior is exhibited by the program resulting from translation to an executable, and it may and does use its knowledge about its own implementation to provide for that.
Thank you all for your comments! You've helped me to understand the problem much better.
Just to clarify why I do this and what I really wanted:
I am porting clang on some specific platform. So my goal here was to understand if this test (from our autogen test system) contains error, or rather it's clang compilation bug. On results of this discussion, I've submitted an llvm bug
(https://bugs.llvm.org/show_bug.cgi?id=41178).
Thanks again!
Related
Is the option -Wdeclaration-after-statement stylistic only? By that I mean, if I macro'd all cases in my C code where a variable was defined and I initialized them in them in the same fashion migrating from this older style C90 to the newer C99 style, would that code be byte-for-byte the same?
Here is how the option -Wdeclaration-after-statement is documented (from man gcc):
Warn when a declaration is found after a statement in a block. This construct, known from C++, was introduced with ISO C99 and is by default allowed in GCC. It is not supported by ISO C90.
And it allows you to take code like
int a;
{
a = 42;
printf( "%d", a );
}
and turn it into
int a = 42;
printf( "%d", a );
This is a follow-up to my question here.
I may be confused here, but I think that we are missing something.
Prior to C99, all variable declarations had to occur before any statements in a block. It did not matter where you assigned it a value (except maybe in generated assembly code).
int a;
do_something();
a = 7;
do_something_else();
What you could not do in C but has always been perfectly legal in C++ is mix declarations and statements:
do_something();
int a = 7;
do_something_else();
With the advent of C99 you can now do the same thing in C as you can in C++, and declare a variable anywhere in a block, even after non-declaration statements.
Ultimately it was a design decision based on making it easier to write a compiler that leaked into the language specification. Compilers are a little more sophisticated now (and much larger).
Statement in this context either refers to a full expression ending with ; or alternatively a compound statement, { }. These terms are taken from the formal C grammar.
Declaration after statement does not apply to the code in your question. Because... you placed the declaration before a (compound) statement. Your two examples have nothing to do with this gcc setting, so it would appear that you have misunderstood what it does.
Rather, the relevant example would be:
{
int a = 42;
}
versus
{
puts("hello");
int a = 42;
}
The former example is fine in any C version. The latter is fine in standard C, but not in the deprecated C90 standard. So the only purpose of the warning nowadays would be to give a diagnostic message for standard C programs where a certain coding style is enforced.
The vast majority of programmers out there should not use this warning and stick to standard C as defined in ISO 9899:2018.
EDIT:
This is merely about whether or not you can rewrite code from a C90 to C99 conventions and obtain binary compatibility
Yes you can rewrite to C99 style without any consequences. None of these affect how the variable is treated in the "abstract machine" as C calls it. Here are some examples, 2 relevant and 1 irrelevant:
void relevant_c99_example (void) {
if ( 1 ) {
int a = 42;
printf( "%d", a );
}
else {
int a = 42;
printf( "%d", a );
}
}
void relevant_c90_example (void) {
int a = 42;
if ( 1 ) {
printf( "%d", a );
}
else {
printf( "%d", a );
}
}
void irrelevant_example (void) {
int a;
if ( 1 ) {
a = 42;
printf( "%d", a );
}
else {
a = 42;
printf( "%d", a );
}
}
https://godbolt.org/z/1oqvoesx7
These 3 produce 100% identical machine code when tested on both gcc and clang for x86_64. They even produce 100% identical machine code when optimizations are disabled! Which one shouldn't expect, since that isn't how you benchmark C code correctly. But it happens to be the case here.
No, it's not. For example, the above two snippets will compile byte-for-byte the same. So will the foo and bar below, but baz will not. Here is a link to GodBolt. This demonstrates that hoisting the initialization to the declaration may NOT produce the same code
void foo () {
int a;
{
if ( 1 ) {
a = 42;
printf( "%d", a );
}
else {
a = 42;
printf( "%d", a );
}
}
}
void bar () {
int a = 42;
{
if ( 1 ) {
printf( "%d", a );
}
else {
printf( "%d", a );
}
}
}
void baz () {
int a;
{
if ( rand() > 0 ) {
a = 42;
printf( "%d", a );
}
else {
a = 42;
printf( "%d", a );
}
}
}
The example is taken from Wikipedia:
void updatePtrs(size_t *restrict ptrA, size_t *restrict ptrB, size_t *restrict val)
{
*ptrA += *val;
*ptrB += *val;
}
I call this function in the main():
int main(void)
{
size_t i = 10;
size_t j = 0;
updatePtrs(&i, &j, &i);
printf("i = %lu\n", i);
printf("j = %lu\n", j);
return 0;
}
The val pointer is not be loaded twice according to the Wikipedia's description, so the value of j should be 10, but it's 20 in fact.
Is my comprehension about this keyword not correct? Should I utilize some specific options of gcc?
Thanks in advance.
Your code causes undefined behaviour. restrict is a promise from you to the compiler that all of the pointer parameters point to different memory areas.
You break this promise by putting &i for two of the arguments.
(In fact, with restrict it is allowed to pass overlapping pointers, but only if no writes are done through any of the overlapping pointers within the function. But typically you would not bother with restrict if there is no writing happening).
FWIW, on my system with gcc 4.9.2, output is j = 20 at -O0 and j = 10 at -O1 or higher, which suggests that the compiler is indeed taking note of the restrict. Of course, since it is undefined behaviour, your results may vary.
I get lvalue required as increment operand error message with gcc-4.7 for
void func1()
{
u32 *src;
hwrcolor c;
c = *(((u32*)src)++);
}
and
void func2()
{
u8 *dest;
hwrcolor c;
u32 * u32Dest;
*(((u32 *)dest)++) = c;
}
I change them to
void func1()
{
u32 *src;
hwrcolor c;
u32Dest = ((u32*)src);
//c = *(u32Dest ++);
*u32Dest = *u32Dest + 1;
c = *u32Dest;
}
and
void func2()
{
u8 *dest;
hwrcolor c;
u32 * u32Dest;
u32Dest = ((u32 *)dest);
//*(u32Dest++) = c;
*u32Dest = *u32Dest + 1;
*u32Dest = c;
}
source code can be compiled but application not work as expected. Are conversions true?
The code:
((T *)p)++
is illegal because ++ may only be applied to an lvalue.
This is because the side-effect of ++ is to increment the value stored in a variable, but (T *)p isn't a variable; it's a temporary result of converting p to a new type.
This is the same sort of error as int x = 5; (x + 3)++; . It doesn't make sense.
In some older compilers, the code ((T *)p)++; was accepted and treated like:
(*(T **)&p)++;
In other words, the memory location storing p was treated as if it actually stored a pointer of another type, and then that pointer is incremented. This worked on those compilers because those compilers only ran on hardware where all pointers are stored in the same way.
The fix for your func1 is simply to write c = *src++; . No casts are necessary because src is already the right type.
In func2 the code *(((u32 *)dest)++) = c; could be replaced with:
*(*(u32 **)&dest)++ = c;
Your version doesn't work because you never increment the original dest. In fact in both of your attempts you increment the data being pointed to, whereas the original code increments the pointer, leaving the data untouched.
Note that func2 violates the strict aliasing rule. Older compilers did not optimize based on the strict aliasing rule, but recent versions of gcc do. You should use the flag -fno-strict-aliasing when compiling this code.
It is common to assign pointers with allocations using an implicit function-return void * conversion, just like malloc()'s:
void *malloc(size_t size);
int *pi = malloc(sizeof *pi);
I would like to perform the same assignment while passing the address of the target pointer, and without explicitly casting its type from within the function (not within its body, nor arguments).
The following code seems to achieve just that.
I would like to know whether the code fully conforms with (any of)
the C standards.
If it doesn't conform, I would like to know if it's possible to
achieve my requirement while conforming to (any of) the C standards.
.
#include <stdio.h>
#include <stdlib.h>
int allocate_memory(void *p, size_t s) {
void *pv;
if ( ( pv = malloc(s) ) == NULL ) {
fprintf(stderr, "Error: malloc();");
return -1;
}
printf("pv: %p;\n", pv);
*((void **) p) = pv;
return 0;
}
int main(void) {
int *pi = NULL;
allocate_memory(&pi, sizeof *pi);
printf("pi: %p;\n", (void *) pi);
return 0;
}
Result:
pv: 0x800103a8;
pi: 0x800103a8;
Types int** and void** are not compatible
You are casting p, whose real type is int**, to void** and then dereferencing it here:
*((void **) p) = pv;
which will break aliasing rules.
You can either pass a void pointer and then cast it correctly:
void *pi = NULL;
int* ipi = NULL ;
allocate_memory(&pi, sizeof *ipi );
ipi = pi ;
or return a void pointer.
int *pi = allocate_memory(sizeof *pi);
There is an option to use a union:
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
union Pass
{
void** p ;
int** pi ;
} ;
int allocate_memory(union Pass u , size_t s) {
void *pv;
if ( ( pv = malloc(s) ) == NULL ) {
fprintf(stderr, "Error: malloc();");
return -1;
}
printf("pv: %p;\n", pv);
*(u.p) = pv;
return 0;
}
int main()
{
int* pi = NULL ;
printf("%p\n" , pi ) ;
allocate_memory( ( union Pass ){ .pi = &pi } , sizeof( *pi ) ) ;
printf("%p\n" , pi ) ;
return 0;
}
As far as I understand it, this example conforms to standard.
Use static asserts to guarantee that the sizes and alignment are the same.
_Static_assert( sizeof( int** ) == sizeof( void** ) , "warning" ) ;
_Static_assert( _Alignof( int** ) == _Alignof( void** ) , "warning" ) ;
No, this is not compliant. You're passing an int** as void* (ok), but then you cast the void* to a void** which is not guaranteed to have the same size and layout. You can only dereference a void* (except one gotten from malloc/calloc) after you cast it back to the pointer type that it originally was, and this rule does not apply recursively (so a void** does not convert automatically, like a void*).
I also don't see a way to meet all your requirements. If you must pass a pointer by pointer, then you need to actually pass the address of a void* and do all the necessary casting in the caller, in this case main. That would be
int *pi;
void *pv;
allocate_memory(&pv, sizeof(int));
pi = pv;
... defeating your scheme.
I don't think it's possible to do it in a 100% standard-compliant manner, because non-void pointers are not guaranteed to have the strictly same size as a void*.
It's the same reason the standard demands explicitly casting printf("%p") arguments to void*.
Added: On the other hand, some implementations mandate that this work, such as Windows (which happily casts IUnknown** to void**).
I think your code might provide some interesting problems due to casting void* to void** and dereferencing it. According to GCC this is not a problem but sometimes GCC lies. You can try
#include <stdio.h>
#include <stdlib.h>
int allocate_memory(void **p, size_t s) {
if ( ( *p = malloc(s) ) == NULL ) {
fprintf(stderr, "Error: malloc();");
return -1;
}
return 0;
}
int main(void) {
int *pi = NULL;
if ( allocate_memory((void**) &pi, sizeof *pi) == 0 ) {
printf("pi: %p;\n", (void *) pi);
}
return 0;
}
Note that in your original code you had to cast int** to void* (implicit) and then explicitly cast to void** which could really confuse your compiler. There might still be an aliasing problem due to the fact that main's int *pi is accessed as and assigned a void pointer. However, a quick scan of the C11 standard is inconclusive in that regard (see http://open-std.org/JTC1/SC22/WG14/).
Some platforms are capable of storing pointers that can only identify coarsely-aligned objects (e.g. those of type int*) more compactly than pointers that can access arbitrary bytes (e.g. those of type void* or char*). The Standard allows implementations targeting such platforms to reserve less space for int* than for void*. On implementations that do that, would generally be impractical to allow a void** to be capable of updating either an int* or a char* interchangeably; consequently, the Standard does not require that implementations support such usage.
On the other hand, the vast majority of implementations target platforms where int* and char* have the same size and representation, and where it would cost essentially nothing to regard a void* as being capable of manipulating both types interchangeably. According to the published Rationale document, the Spirit of C indicates that implementations should not "prevent programmers from doing what needs to be done". Consequently, if an implementation claims to be suitable for purposes like low-level programming that may involve processing pointers to different kinds of objects interchangeably, it should support such constructs whether or not the Standard would require it to do so; those that don't support such constructs on platforms where they would cost essentially nothing should be recognized as unsuitable for any purposes that would benefit from them.
Compilers like gcc and clang would require using -fno-strict-aliasing to make them support such constructs; getting good performance would then likely require using restrict in many cases when appropriate. On the other hand, since code which exploits the semantics available via -nno-strict-aliasing and properly uses restrict may achieve better performance than would be possible with strictly conforming code, and support for such code should be viewed as one of the "popular extension" alluded to on line 27 of page 11 of the published Rationale.
I got this message:
expected 'void **' but argument is of type 'char **'
when I tried to compile something similar to this:
void myfree( void **v )
{
if( !v || !*v )
return;
free( *v );
*v = NULL;
return;
}
I found what I think is a solution after reading this question on stack overflow:
Avoid incompatible pointer warning when dealing with double-indirection - Stack Overflow
So I adjusted to something like this:
#include <stdio.h>
#include <stdlib.h>
void myfree( void *x )
{
void **v = x;
if( !v || !*v )
return;
free( *v );
*v = NULL;
return;
}
int main( int argc, char *argv[] )
{
char *test;
if( ( test = malloc( 1 ) ) )
{
printf( "before: %p\n", test );
myfree( &test );
printf( "after: %p\n", test );
}
return 0;
}
Is this legal C? I am dereferencing a void pointer aren't I?
Thanks guys
EDIT 12/10/2010 4:45PM EST:
As it has been pointed out free(NULL) is safe and covered by the C standard. Also, as discussed below my example above is not legal C. See caf's answer, Zack's answer, and my own answer.
Therefore it's going to be easier for me to initalize any to-be-malloc'd pointers as NULL and then later on to just free() and NULL out directly in the code:
free( pointer );
pointer = NULL;
The reason I was checking for NULL in myfree() like I did was because of my experiences with fclose(). fclose(NULL) can segfault depending on platform (eg xpsp3 msvcrt.dll 7.0.2600.5512) and so I had assumed (mistakenly) the same thing could happen with free(). I had figured rather than clutter up my code with if statements I could better implement in a function.
Thanks everyone for all the good discussion
No, this is not legal C, unless you pass the address of a void * object to myfree() (so you might as well just keep your original definition).
The reason is that in your example, an object of type char * (the object declared as test in main()) is modified through an lvalue of type void * (the lvalue *v in myfree()). §6.5 of the C standard states:
7 An object shall have its stored value accessed only by an lvalue
expression that has one of
the following types:
— a type compatible with the effective type of the object,
— a qualified version of a type compatible with the effective type of
the object,
— a type that is the signed or unsigned type corresponding to the effective
type of the object,
— a type that is the signed or unsigned type corresponding to a qualified
version of the effective type of the object,
— an aggregate or union type that includes one of the aforementioned
types among its members (including, recursively, a member of a subaggregate
or contained union), or
— a character type.
Since void * and char * are not compatible types, this constraint has been broken. The condition for two pointer types to be compatible is described in §6.7.5.1:
For two pointer types to be
compatible, both shall be identically
qualified and both shall be pointers
to compatible types.
To achieve the effect you want, you must use a macro:
#define MYFREE(p) (free(p), (p) = NULL)
(There is no need to check for NULL, since free(NULL) is legal. Note that this macro evaluates p twice).
This is perfectly legal but can get confusing for other people who read your code.
You could also use casting to eliminate the warning:
myfree((void **)&rest);
This is more readable and understandable.
In C you have no choice but to introduce a cast somewhere in here. I would use a macro to ensure that things were done correctly at the call site:
void
myfree_(void **ptr)
{
if (!ptr || !*ptr) return;
free(*ptr);
*ptr = 0;
}
#define myfree(ptr) myfree_((void **)&(ptr))
[You could actually name both the function and the macro "myfree", thanks to C's no-infinite-macro-recursion rules! But it would be confusing for human readers. Per the long discussion below caf's answer, I will also stipulate that the statement *ptr = 0 here modifies an object of unknown type through a void** alias, which is runtime-undefined behavior -- however, my informed opinion is, it will not cause problems in practice, and it's the least bad option available in plain C; caf's macro that evaluates its argument twice seems far more likely (to me) to cause real problems.]
In C++ you could use a template function, which is better on three counts: it avoids needing to take the address of anything at the call site, it doesn't break type correctness, and you will get a compile-time error instead of a run-time crash if you accidentally pass a non-pointer to myfree.
template <typename T>
void
myfree(T*& ptr)
{
free((void *)ptr);
ptr = 0;
}
But of course in C++ you have even better options available, such as smart pointer and container classes.
It should, finally, be mentioned that skilled C programmers eschew this kind of wrapper, because it does not help you when there's another copy of the pointer to the memory you just freed hanging around somewhere -- and that's exactly when you need help.
caf's answer is correct: No, it's not legal. And as Zack points out breaking the law in this way is apparently least likely to cause problems.
I found what appears to be another solution in the comp.lang.c FAQ list · Question 4.9, which notes that an intermediate void value has to be used.
#include <stdio.h>
#include <stdlib.h>
void myfree( void **v )
{
if( !v )
return;
free( *v );
*v = NULL;
return;
}
int main( int argc, char *argv[] )
{
double *num;
if( ( num = malloc( sizeof( double ) ) ) )
{
printf( "before: %p\n", num );
{
void *temp = num;
myfree( &temp );
num = temp;
}
printf( "after: %p\n", num );
}
return 0;
}