1327952Sdim//===- PreISelIntrinsicLowering.cpp - Pre-ISel intrinsic lowering pass ----===// 2303231Sdim// 3353358Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4353358Sdim// See https://llvm.org/LICENSE.txt for license information. 5353358Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6303231Sdim// 7303231Sdim//===----------------------------------------------------------------------===// 8303231Sdim// 9344779Sdim// This pass implements IR lowering for the llvm.load.relative and llvm.objc.* 10344779Sdim// intrinsics. 11303231Sdim// 12303231Sdim//===----------------------------------------------------------------------===// 13303231Sdim 14303231Sdim#include "llvm/CodeGen/PreISelIntrinsicLowering.h" 15360784Sdim#include "llvm/Analysis/ObjCARCInstKind.h" 16303231Sdim#include "llvm/CodeGen/Passes.h" 17303231Sdim#include "llvm/IR/Function.h" 18303231Sdim#include "llvm/IR/IRBuilder.h" 19303231Sdim#include "llvm/IR/Instructions.h" 20360784Sdim#include "llvm/IR/Intrinsics.h" 21303231Sdim#include "llvm/IR/Module.h" 22327952Sdim#include "llvm/IR/Type.h" 23327952Sdim#include "llvm/IR/User.h" 24360784Sdim#include "llvm/InitializePasses.h" 25303231Sdim#include "llvm/Pass.h" 26327952Sdim#include "llvm/Support/Casting.h" 27303231Sdim 28303231Sdimusing namespace llvm; 29303231Sdim 30327952Sdimstatic bool lowerLoadRelative(Function &F) { 31303231Sdim if (F.use_empty()) 32303231Sdim return false; 33303231Sdim 34303231Sdim bool Changed = false; 35303231Sdim Type *Int32Ty = Type::getInt32Ty(F.getContext()); 36303231Sdim Type *Int32PtrTy = Int32Ty->getPointerTo(); 37303231Sdim Type *Int8Ty = Type::getInt8Ty(F.getContext()); 38303231Sdim 39303231Sdim for (auto I = F.use_begin(), E = F.use_end(); I != E;) { 40303231Sdim auto CI = dyn_cast<CallInst>(I->getUser()); 41303231Sdim ++I; 42303231Sdim if (!CI || CI->getCalledValue() != &F) 43303231Sdim continue; 44303231Sdim 45303231Sdim IRBuilder<> B(CI); 46303231Sdim Value *OffsetPtr = 47303231Sdim B.CreateGEP(Int8Ty, CI->getArgOperand(0), CI->getArgOperand(1)); 48303231Sdim Value *OffsetPtrI32 = B.CreateBitCast(OffsetPtr, Int32PtrTy); 49353358Sdim Value *OffsetI32 = B.CreateAlignedLoad(Int32Ty, OffsetPtrI32, 4); 50303231Sdim 51303231Sdim Value *ResultPtr = B.CreateGEP(Int8Ty, CI->getArgOperand(0), OffsetI32); 52303231Sdim 53303231Sdim CI->replaceAllUsesWith(ResultPtr); 54303231Sdim CI->eraseFromParent(); 55303231Sdim Changed = true; 56303231Sdim } 57303231Sdim 58303231Sdim return Changed; 59303231Sdim} 60303231Sdim 61360784Sdim// ObjCARC has knowledge about whether an obj-c runtime function needs to be 62360784Sdim// always tail-called or never tail-called. 63360784Sdimstatic CallInst::TailCallKind getOverridingTailCallKind(const Function &F) { 64360784Sdim objcarc::ARCInstKind Kind = objcarc::GetFunctionClass(&F); 65360784Sdim if (objcarc::IsAlwaysTail(Kind)) 66360784Sdim return CallInst::TCK_Tail; 67360784Sdim else if (objcarc::IsNeverTail(Kind)) 68360784Sdim return CallInst::TCK_NoTail; 69360784Sdim return CallInst::TCK_None; 70360784Sdim} 71360784Sdim 72344779Sdimstatic bool lowerObjCCall(Function &F, const char *NewFn, 73344779Sdim bool setNonLazyBind = false) { 74344779Sdim if (F.use_empty()) 75344779Sdim return false; 76344779Sdim 77344779Sdim // If we haven't already looked up this function, check to see if the 78344779Sdim // program already contains a function with this name. 79344779Sdim Module *M = F.getParent(); 80353358Sdim FunctionCallee FCache = M->getOrInsertFunction(NewFn, F.getFunctionType()); 81344779Sdim 82353358Sdim if (Function *Fn = dyn_cast<Function>(FCache.getCallee())) { 83344779Sdim Fn->setLinkage(F.getLinkage()); 84344779Sdim if (setNonLazyBind && !Fn->isWeakForLinker()) { 85344779Sdim // If we have Native ARC, set nonlazybind attribute for these APIs for 86344779Sdim // performance. 87344779Sdim Fn->addFnAttr(Attribute::NonLazyBind); 88344779Sdim } 89344779Sdim } 90344779Sdim 91360784Sdim CallInst::TailCallKind OverridingTCK = getOverridingTailCallKind(F); 92360784Sdim 93344779Sdim for (auto I = F.use_begin(), E = F.use_end(); I != E;) { 94360784Sdim auto *CI = cast<CallInst>(I->getUser()); 95344779Sdim assert(CI->getCalledFunction() && "Cannot lower an indirect call!"); 96344779Sdim ++I; 97344779Sdim 98344779Sdim IRBuilder<> Builder(CI->getParent(), CI->getIterator()); 99344779Sdim SmallVector<Value *, 8> Args(CI->arg_begin(), CI->arg_end()); 100344779Sdim CallInst *NewCI = Builder.CreateCall(FCache, Args); 101344779Sdim NewCI->setName(CI->getName()); 102360784Sdim 103360784Sdim // Try to set the most appropriate TailCallKind based on both the current 104360784Sdim // attributes and the ones that we could get from ObjCARC's special 105360784Sdim // knowledge of the runtime functions. 106360784Sdim // 107360784Sdim // std::max respects both requirements of notail and tail here: 108360784Sdim // * notail on either the call or from ObjCARC becomes notail 109360784Sdim // * tail on either side is stronger than none, but not notail 110360784Sdim CallInst::TailCallKind TCK = CI->getTailCallKind(); 111360784Sdim NewCI->setTailCallKind(std::max(TCK, OverridingTCK)); 112360784Sdim 113344779Sdim if (!CI->use_empty()) 114344779Sdim CI->replaceAllUsesWith(NewCI); 115344779Sdim CI->eraseFromParent(); 116344779Sdim } 117344779Sdim 118344779Sdim return true; 119344779Sdim} 120344779Sdim 121327952Sdimstatic bool lowerIntrinsics(Module &M) { 122303231Sdim bool Changed = false; 123303231Sdim for (Function &F : M) { 124344779Sdim if (F.getName().startswith("llvm.load.relative.")) { 125303231Sdim Changed |= lowerLoadRelative(F); 126344779Sdim continue; 127344779Sdim } 128344779Sdim switch (F.getIntrinsicID()) { 129344779Sdim default: 130344779Sdim break; 131344779Sdim case Intrinsic::objc_autorelease: 132344779Sdim Changed |= lowerObjCCall(F, "objc_autorelease"); 133344779Sdim break; 134344779Sdim case Intrinsic::objc_autoreleasePoolPop: 135344779Sdim Changed |= lowerObjCCall(F, "objc_autoreleasePoolPop"); 136344779Sdim break; 137344779Sdim case Intrinsic::objc_autoreleasePoolPush: 138344779Sdim Changed |= lowerObjCCall(F, "objc_autoreleasePoolPush"); 139344779Sdim break; 140344779Sdim case Intrinsic::objc_autoreleaseReturnValue: 141344779Sdim Changed |= lowerObjCCall(F, "objc_autoreleaseReturnValue"); 142344779Sdim break; 143344779Sdim case Intrinsic::objc_copyWeak: 144344779Sdim Changed |= lowerObjCCall(F, "objc_copyWeak"); 145344779Sdim break; 146344779Sdim case Intrinsic::objc_destroyWeak: 147344779Sdim Changed |= lowerObjCCall(F, "objc_destroyWeak"); 148344779Sdim break; 149344779Sdim case Intrinsic::objc_initWeak: 150344779Sdim Changed |= lowerObjCCall(F, "objc_initWeak"); 151344779Sdim break; 152344779Sdim case Intrinsic::objc_loadWeak: 153344779Sdim Changed |= lowerObjCCall(F, "objc_loadWeak"); 154344779Sdim break; 155344779Sdim case Intrinsic::objc_loadWeakRetained: 156344779Sdim Changed |= lowerObjCCall(F, "objc_loadWeakRetained"); 157344779Sdim break; 158344779Sdim case Intrinsic::objc_moveWeak: 159344779Sdim Changed |= lowerObjCCall(F, "objc_moveWeak"); 160344779Sdim break; 161344779Sdim case Intrinsic::objc_release: 162344779Sdim Changed |= lowerObjCCall(F, "objc_release", true); 163344779Sdim break; 164344779Sdim case Intrinsic::objc_retain: 165344779Sdim Changed |= lowerObjCCall(F, "objc_retain", true); 166344779Sdim break; 167344779Sdim case Intrinsic::objc_retainAutorelease: 168344779Sdim Changed |= lowerObjCCall(F, "objc_retainAutorelease"); 169344779Sdim break; 170344779Sdim case Intrinsic::objc_retainAutoreleaseReturnValue: 171344779Sdim Changed |= lowerObjCCall(F, "objc_retainAutoreleaseReturnValue"); 172344779Sdim break; 173344779Sdim case Intrinsic::objc_retainAutoreleasedReturnValue: 174344779Sdim Changed |= lowerObjCCall(F, "objc_retainAutoreleasedReturnValue"); 175344779Sdim break; 176344779Sdim case Intrinsic::objc_retainBlock: 177344779Sdim Changed |= lowerObjCCall(F, "objc_retainBlock"); 178344779Sdim break; 179344779Sdim case Intrinsic::objc_storeStrong: 180344779Sdim Changed |= lowerObjCCall(F, "objc_storeStrong"); 181344779Sdim break; 182344779Sdim case Intrinsic::objc_storeWeak: 183344779Sdim Changed |= lowerObjCCall(F, "objc_storeWeak"); 184344779Sdim break; 185344779Sdim case Intrinsic::objc_unsafeClaimAutoreleasedReturnValue: 186344779Sdim Changed |= lowerObjCCall(F, "objc_unsafeClaimAutoreleasedReturnValue"); 187344779Sdim break; 188344779Sdim case Intrinsic::objc_retainedObject: 189344779Sdim Changed |= lowerObjCCall(F, "objc_retainedObject"); 190344779Sdim break; 191344779Sdim case Intrinsic::objc_unretainedObject: 192344779Sdim Changed |= lowerObjCCall(F, "objc_unretainedObject"); 193344779Sdim break; 194344779Sdim case Intrinsic::objc_unretainedPointer: 195344779Sdim Changed |= lowerObjCCall(F, "objc_unretainedPointer"); 196344779Sdim break; 197344779Sdim case Intrinsic::objc_retain_autorelease: 198344779Sdim Changed |= lowerObjCCall(F, "objc_retain_autorelease"); 199344779Sdim break; 200344779Sdim case Intrinsic::objc_sync_enter: 201344779Sdim Changed |= lowerObjCCall(F, "objc_sync_enter"); 202344779Sdim break; 203344779Sdim case Intrinsic::objc_sync_exit: 204344779Sdim Changed |= lowerObjCCall(F, "objc_sync_exit"); 205344779Sdim break; 206344779Sdim } 207303231Sdim } 208303231Sdim return Changed; 209303231Sdim} 210303231Sdim 211327952Sdimnamespace { 212327952Sdim 213303231Sdimclass PreISelIntrinsicLoweringLegacyPass : public ModulePass { 214303231Sdimpublic: 215303231Sdim static char ID; 216327952Sdim 217303231Sdim PreISelIntrinsicLoweringLegacyPass() : ModulePass(ID) {} 218303231Sdim 219327952Sdim bool runOnModule(Module &M) override { return lowerIntrinsics(M); } 220303231Sdim}; 221303231Sdim 222327952Sdim} // end anonymous namespace 223327952Sdim 224303231Sdimchar PreISelIntrinsicLoweringLegacyPass::ID; 225303231Sdim 226303231SdimINITIALIZE_PASS(PreISelIntrinsicLoweringLegacyPass, 227303231Sdim "pre-isel-intrinsic-lowering", "Pre-ISel Intrinsic Lowering", 228303231Sdim false, false) 229303231Sdim 230327952SdimModulePass *llvm::createPreISelIntrinsicLoweringPass() { 231303231Sdim return new PreISelIntrinsicLoweringLegacyPass; 232303231Sdim} 233303231Sdim 234303231SdimPreservedAnalyses PreISelIntrinsicLoweringPass::run(Module &M, 235303231Sdim ModuleAnalysisManager &AM) { 236303231Sdim if (!lowerIntrinsics(M)) 237303231Sdim return PreservedAnalyses::all(); 238303231Sdim else 239303231Sdim return PreservedAnalyses::none(); 240303231Sdim} 241