Adding intrinsics to LLVM’s backend
An LLVM intrinsic is a compiler’s built-in function. The compiler implements the
functionality of an LLVM intrinsic in the most optimized way possible to avoid
performance overheads. The names for the intrinsics are reserved, starting with
the prefix “llvm.”. These functions are introduced at the LLVM IR level to hold
additional information related to the program. These functions can only appear
in a call or invoke instructions. The advantage of using an intrinsic is that
the information present in it gets updated with every IR transformation. LLVM
users can contribute to LLVM by adding custom intrinsics to its backend. One
method of adding a custom intrinsic to LLVM’s backend is:
1) Create a new case in the switch case inside the function unsigned getIntrinsicCost(Intrinsic::ID IID, Type *RetTy, ArrayRef
ParamTys, const User *U) present in the file
include/llvm/Analysis/TargetTransformInfoImpl.h.
case Intrinsic::< intrinsic_name >:
2) Create a new case in the switch case inside the function classof() in the file include/llvm/IR/IntrinsicInst.h.
case Intrinsic::< intrinsic_name >:
3) Add the properties and type information related to the intrinsic in the file IR/Intrinsics.td. For example,
let IntrProperties = [list_the_properties] in {
def intrinsic_name : Intrinsic< [return_type], [intrinsic_arguments] >
}
4) Add a new if-condition to the function static bool isAlwaysLive(Instruction *I), which belongs to the file lib/Analysis/DemandedBits.cpp.
if (isa< CallInst >(*I) && cast < CallInst >(*I).getIntrinsicID() == Intrinsic::intrinsic_name)
return true;
5) Create a new case in the switch case inside the function bool isAssumeLikeIntrinsic(const Instruction *I) present in the file lib/Analysis/ValueTracking.cpp.
case Intrinsic::< intrinsic_name >:
6) Create a new case in the switch case inside the function void LowerIntrinsicCall(CallInst *CI) present in the file lib/CodeGen/IntrinsicLowering.cpp.
case Intrinsic::< intrinsic_name >:
break;
7) Create a new case in the switch case inside the function void visitIntrinsicCall(const CallInst &I, unsigned Intrinsic) present in the file lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp.
case Intrinsic::< intrinsic_name >:
8) Add a new if-condition to the function static bool isAlwaysLive(Instruction &I), which belongs to the file lib/Transforms/Scalar/ADCE.cpp.
if (isa< CallInst > (*I) && cast< CallInst >(*I).getIntrinsicID() == Intrinsic::intrinsic_name)
return true;
9) Add a new if-condition to the function static bool bitTrackingDCE(Function &F, DemandedBits &DB), which belongs to the file lib/Transforms/Scalar/BDCE.cpp.
if (isa< CallInst > (*I) && cast< CallInst > (*I).getIntrinsicID() == Intrinsic::intrinsic_name)
continue;
10) Add a new if-condition to the function bool isInstructionTriviallyDead(Instruction *I, const TargetLibraryInfo *TLI), which belongs to the file lib/Transforms/Utils/Local.cpp.
if (isa< CallInst > (*I) && cast< CallInst >(*I).getIntrinsicID() == Intrinsic::intrinsic_name)
return false;
11) Create a new case in the switch case inside the function static bool removeEmptyCleanup(CleanupReturnInst *RI) present in the file lib/Transforms/Utils/SimplifyCFG.cpp.
case Intrinsic::< intrinsic_name >:
The newly created intrinsic can be instrumented to the LLVM IR using the following statements:
Function NewIntrinsic = Intrinsic::getDeclaration(&M, Intrinsic::intrinsic_name);
std::vector<Value *> Args;
Args.push(...);
Args.push(...);
…
CallInst::Create(NewIntrinsic, Args, "", InsertBefore);
References
1) https://llvm.org/docs/LangRef.html#intrinsic-functions
2) https://llvm.org/docs/ExtendingLLVM.html
1) Create a new case in the switch case inside the function unsigned getIntrinsicCost(Intrinsic::ID IID, Type *RetTy, ArrayRef
case Intrinsic::< intrinsic_name >:
2) Create a new case in the switch case inside the function classof() in the file include/llvm/IR/IntrinsicInst.h.
case Intrinsic::< intrinsic_name >:
3) Add the properties and type information related to the intrinsic in the file IR/Intrinsics.td. For example,
let IntrProperties = [list_the_properties] in {
def intrinsic_name : Intrinsic< [return_type], [intrinsic_arguments] >
}
4) Add a new if-condition to the function static bool isAlwaysLive(Instruction *I), which belongs to the file lib/Analysis/DemandedBits.cpp.
if (isa< CallInst >(*I) && cast < CallInst >(*I).getIntrinsicID() == Intrinsic::intrinsic_name)
return true;
5) Create a new case in the switch case inside the function bool isAssumeLikeIntrinsic(const Instruction *I) present in the file lib/Analysis/ValueTracking.cpp.
case Intrinsic::< intrinsic_name >:
6) Create a new case in the switch case inside the function void LowerIntrinsicCall(CallInst *CI) present in the file lib/CodeGen/IntrinsicLowering.cpp.
case Intrinsic::< intrinsic_name >:
break;
7) Create a new case in the switch case inside the function void visitIntrinsicCall(const CallInst &I, unsigned Intrinsic) present in the file lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp.
case Intrinsic::< intrinsic_name >:
8) Add a new if-condition to the function static bool isAlwaysLive(Instruction &I), which belongs to the file lib/Transforms/Scalar/ADCE.cpp.
if (isa< CallInst > (*I) && cast< CallInst >(*I).getIntrinsicID() == Intrinsic::intrinsic_name)
return true;
9) Add a new if-condition to the function static bool bitTrackingDCE(Function &F, DemandedBits &DB), which belongs to the file lib/Transforms/Scalar/BDCE.cpp.
if (isa< CallInst > (*I) && cast< CallInst > (*I).getIntrinsicID() == Intrinsic::intrinsic_name)
continue;
10) Add a new if-condition to the function bool isInstructionTriviallyDead(Instruction *I, const TargetLibraryInfo *TLI), which belongs to the file lib/Transforms/Utils/Local.cpp.
if (isa< CallInst > (*I) && cast< CallInst >(*I).getIntrinsicID() == Intrinsic::intrinsic_name)
return false;
11) Create a new case in the switch case inside the function static bool removeEmptyCleanup(CleanupReturnInst *RI) present in the file lib/Transforms/Utils/SimplifyCFG.cpp.
case Intrinsic::< intrinsic_name >:
The newly created intrinsic can be instrumented to the LLVM IR using the following statements:
Function NewIntrinsic = Intrinsic::getDeclaration(&M, Intrinsic::intrinsic_name);
std::vector<Value *> Args;
Args.push(...);
Args.push(...);
…
CallInst::Create(NewIntrinsic, Args, "", InsertBefore);
References
1) https://llvm.org/docs/LangRef.html#intrinsic-functions
2) https://llvm.org/docs/ExtendingLLVM.html
Comments
Post a Comment