How can I make llvm intrinsic functions declarations survive from optimizations.

Dear LLVM community.

I have a function pass. This pass will insert a llvm intrinsic function
prototype (llvm.memset.p0i8.i32) to the module at the doInitialize
stage. Then at runOnFunction Stage, this pass will find the insert
points and insert calls of this intrinsic function. However, when the
-O2 flag is turned on, the strip unused function optimization will
automatically delete this intrinsic function because there's no use of
this function found. (Strip unused function happens before runOnFunction
of my pass).

I used to solve this kind of problems on non-intrinsic functions by
inserting a record to llvm.compiler.used. However, after I append this
intrinsic function to llvm.compiler.used global, I got a fatal error

Invalid user of intrinsic instruction!
i8* bitcast (void (i8*, i8, i32, i32, i1)* @llvm.memset.p0i8.i32 to i8*)
fatal error: error in backend: Broken module found, compilation aborted!

I noticed that, only user of direct call/invokes are allowed for
intrinsic functions. So is it impossible to solve this problem by
inserting intrinsic function prototype?

Could you please give me some ideas on this , to make these intrinsic
functions prototypes survive from llvm optimizations?

Thank you.

Best regards,
Zhengyang.

Hi Zhengyang,

Do you mind sharing _why_ you need the intrinsic declarations to stay
around? It is possible that there is a better way of solving your
problem.

Thanks!
-- Sanjoy

Dear Sanjoy Das and community.

I was tried to fix a bug in the pass InitAllocas from SAFECode. This is a function pass and will insert a prototype of ‘llvm.memset.p0i8.i32’ in the module at doInitialize() stage of the pass. But, this prototype will be eliminated by strip unused function optimization since there is no call on this function after doInitialization(). Therefore there will be no prototype at the runOnFunction() stage, and this will cause a fail. Previously I solve this kind of bug by inserting the prototype of the function to llvm.compiler.used. In this way, the compiler will not leave the prototype alone. This time, the situation is a bit complicated, inserting the intrinsic function to llvm.compiler.used will cause a fail because only users of direct call/invokes are allowed on intrinsic functions.

Fortunately, after some discussion with my GSoC mentor, Prof. Criswell, we chose to rewrite the pass to a module pass, this solves this issue perfectly. Thanks for your patience.

Best regards,

Zhengyang.

Dear All,

To add to what Zhengyang has written, the choice to optimize away unused function declarations within the standard optimization pipeline changes how instrumentation passes are written.

Many instrumentation passes just add calls to functions. Therefore, they could be written as a FunctionPass, and this worked because any global changes that needed to be made could be done within the doInitialization() method. To the best of my recollection, the LLVM optimization passes would not remove these declarations.

Now these declarations are removed before the runOnFunction() methods are called within the FunctionPasses. For regular functions, this can be worked around by adding the functions to the llvm.compiler.used array, but it does not work for intrinsics (such as memset). This means that some instrumentation passes that could be written as a FunctionPass can no longer be written this way (at least not without modifying the pass pipeline, which we try to avoid doing in SAFECode).

I suspect this is an untended consequence of a change to either the pass pipeline or the global optimization pass. It probably affects very little code within LLVM, but it can affect a lot of external code that uses LLVM for instrumentation purposes. I thought we should mention this behavior in case it is something that we want to change.

Regards,

John Criswell