Open mushenoy opened 1 day ago
The missing branch has undefined behavior, because type
is read in it while being uninitialized. That is UB for automatic storage duration objects which do not have their address taken.
However, this modification does not have undefined behavior and still results in the same assembly:
struct sample {
struct sample *next;
char a;
};
char function (char);
void test(void);
void* test_function (const struct sample *c)
{
char type;
char* _ = &type;
if (c) {
type = c->a;
test();
}
function(type);
return (void *)0;
}
In C (but not C++), taking the address of the automatic storage duration variable should avoid the UB of using the indeterminate representation, at least for character type variables.
In C++ copying an unsigned char
(but not char
) with an indeterminate value has defined behavior, but Clang also incorrectly drops the branch in the following variation of the program when compiled as C++ with -Os
:
struct sample {
struct sample *next;
char a;
};
char function (unsigned char);
void test(void);
void* test_function (const struct sample *c)
{
unsigned char type;
if (c) {
type = c->a;
test();
}
function(type);
return (void *)0;
}
@EugeneZelenko While the original code does have UB, the modifications in my comment don't and Clang compiles them incorrectly. Also I don't know to what degree indeterminate value/representation UB is considered undefined per Clang's policies. I am a bit surprised that it compiles away the branch completely, rather than just considering the indeterminate representation unstable, as well.
@EugeneZelenko I think this is a frontend issue.
For example, for the C++ variation that has defined behavior from my previous comment the following is produced for the call to function
:
%call = call noundef signext i8 @function(unsigned char)(i8 noundef zeroext %3)
noundef
on the function argument is generally incorrect. Or alternatively the variable's initial state musn't be undefined.
The key point is that clang sets noundef
on the function argument of function
. Then SimplifyCFGPass
will eliminate the null check:
https://github.com/llvm/llvm-project/blob/287781c7c9dbd7674cf7cbab8a8fe8a49a4b9317/llvm/lib/Transforms/Utils/SimplifyCFG.cpp#L8045-L8080
https://github.com/llvm/llvm-project/blob/287781c7c9dbd7674cf7cbab8a8fe8a49a4b9317/llvm/lib/Transforms/Utils/SimplifyCFG.cpp#L8009-L8037
With -Xclang -no-enable-noundef-analysis
, the null pointer check will be kept: https://godbolt.org/z/3Eb4rhens
@llvm/issue-subscribers-clang-codegen
Author: Mukund Shenoy (mushenoy)
For the below sample source file - the assembly code generated is incorrect.
Sample.c:
Compilation command:
In the assembly:
c
is non-null.Assembly code:
x86_64:
Observations:
type
variable is initialized with some value.