Hi - I've been trying to track down why a function pointer isn't being
inlined in some cases, and have nailed it down to the following repro:
struct s
{
int (*cmp) (const void *, const void *);
int its[2];
};
static int __uint_compare(const void *e1, const void *e2)
{
const int *i1 = e1;
const int *i2 = e2;
return *i2 - *i1;
}
int main()
{
struct s hp;
hp.cmp = __uint_compare;
hp.its[0] = 1;
hp.its[1] = 2;
for (int i = 0; i < 1; i++)
{
int cmp = hp.cmp(&hp.its[i], &hp.its[i + 1]);
if(cmp < 0)
{
return 1;
}
}
return 0;
}
I've compiled it (as test2.c) with the following commands, and have
attached the unoptimised and optimised outputs.
clang -O0 -S -emit-llvm -o test2.ll test2.c
opt -O3 -debug -S test2.ll > test2.opt.ll 2>log2
However, if I replace the main function with the code below (test.c),
the function call to __uint_compare is inlined and the whole function
is correctly replaced with a "ret i32 0".
int main()
{
struct s hp;
hp.cmp = __uint_compare;
hp.its[0] = 1;
hp.its[1] = 2;
int cmp = hp.cmp(&hp.its[0], &hp.its[1]);
if(cmp < 0)
{
return 1;
}
return 0;
}
As you can see, the first example always executes the for-loop exactly
once, so the semantics of both examples are identical. From looking at
the differences in the two (attached) log files, it seems like the
problem's in the GVN pass, as only the log for the second example has:
GVN iteration: 0
GVN removed: %5 = load i32 (i8*, i8*)** %1, align 8
GVN iteration: 1
Is this analysis correct? Or is some other pass getting tricked by the
for loop (maybe because the load & store are no longer in the same
basic block)? I was thinking of trying to fix it myself, but am not
sure exactly where in the codebase I should get started.
Thanks -
Nick
test.c (368 Bytes)
test.ll (2.72 KB)
test.opt.ll (716 Bytes)
test2.c (412 Bytes)
test2.ll (3.4 KB)
test2.opt.ll (2.35 KB)
log (32.6 KB)
log2 (48.3 KB)