Hello,
I am adding function calls to an LLVM link-time optimization (LTO) pass,
using the IRBuilder::CreateCall method. I want these calls to remain in the
final x86 binary at any optimization level, but on levels -O2 and -O3, some
of these calls are being optimized out.
So far, I've tried adding each function in the program (excluding LLVM
intrinsics) to the llvm.used set, and I've also set noinline and optnone
attributes on each function in the program. This has allowed me to
retain *most,
*but not all, of the calls I've added with IRBuilder::CreateCall.
Furthermore, I have confirmed that all the calls I've created are present
in the LLVM IR that results immediately after my pass. Thus, I know some
future LTO pass is optimizing out some of these calls.
How can I ensure that none of the calls I add are optimized out? Thanks for
your help!
The answer, in short, is:
Don't run any optimizations/normalizations or, better, only place calls where they are not statically dead.
Basically, if you want optimization, O2/O3, you get them. We even have "reaosnable"
way to tell optimizations to ignore some part of the code, e.g., optnone, but even then,
some trivial things will be removed (think `if (false) foo();`). That said, you can
make the code "conditionally life" to avoid this. There are many ways, one would be
something like:
Before:
int foo(int a) {
if (a > 3)
return a-1;
return 0;
}
After:
```
int return_zero() __attribute__((pure)) // linked in as object file
int foo(int a) {
if \(return\_zero\(\) \!= 0\)
goto calls;
if \(a > 3\) \{
call1:
my\_new\_call\(1, a\);
return a\-1;
\}
call2:
my\_new\_call\(2, a\);
return 0;
calls:
switch \(return\_zero\(\)\) \{
case 1: goto call1;
case 2: goto call2;
default:
goto call2;
\};
}
Now even if you inline the above for a call site like
`foo(0)`, the `my_new_call(1, a)` call site will still
be considered life, thus not be removed.
Hope this helps.