10b57cec5SDimitry Andric //===- PreISelIntrinsicLowering.cpp - Pre-ISel intrinsic lowering pass ----===// 20b57cec5SDimitry Andric // 30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 60b57cec5SDimitry Andric // 70b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 80b57cec5SDimitry Andric // 90b57cec5SDimitry Andric // This pass implements IR lowering for the llvm.load.relative and llvm.objc.* 100b57cec5SDimitry Andric // intrinsics. 110b57cec5SDimitry Andric // 120b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 130b57cec5SDimitry Andric 140b57cec5SDimitry Andric #include "llvm/CodeGen/PreISelIntrinsicLowering.h" 15480093f4SDimitry Andric #include "llvm/Analysis/ObjCARCInstKind.h" 16349cc55cSDimitry Andric #include "llvm/Analysis/ObjCARCUtil.h" 170b57cec5SDimitry Andric #include "llvm/CodeGen/Passes.h" 180b57cec5SDimitry Andric #include "llvm/IR/Function.h" 190b57cec5SDimitry Andric #include "llvm/IR/IRBuilder.h" 200b57cec5SDimitry Andric #include "llvm/IR/Instructions.h" 21*972a253aSDimitry Andric #include "llvm/IR/IntrinsicInst.h" 220b57cec5SDimitry Andric #include "llvm/IR/Module.h" 230b57cec5SDimitry Andric #include "llvm/IR/Type.h" 24480093f4SDimitry Andric #include "llvm/InitializePasses.h" 250b57cec5SDimitry Andric #include "llvm/Pass.h" 260b57cec5SDimitry Andric #include "llvm/Support/Casting.h" 270b57cec5SDimitry Andric 280b57cec5SDimitry Andric using namespace llvm; 290b57cec5SDimitry Andric 300b57cec5SDimitry Andric static bool lowerLoadRelative(Function &F) { 310b57cec5SDimitry Andric if (F.use_empty()) 320b57cec5SDimitry Andric return false; 330b57cec5SDimitry Andric 340b57cec5SDimitry Andric bool Changed = false; 350b57cec5SDimitry Andric Type *Int32Ty = Type::getInt32Ty(F.getContext()); 360b57cec5SDimitry Andric Type *Int32PtrTy = Int32Ty->getPointerTo(); 370b57cec5SDimitry Andric Type *Int8Ty = Type::getInt8Ty(F.getContext()); 380b57cec5SDimitry Andric 39349cc55cSDimitry Andric for (Use &U : llvm::make_early_inc_range(F.uses())) { 40349cc55cSDimitry Andric auto CI = dyn_cast<CallInst>(U.getUser()); 415ffd83dbSDimitry Andric if (!CI || CI->getCalledOperand() != &F) 420b57cec5SDimitry Andric continue; 430b57cec5SDimitry Andric 440b57cec5SDimitry Andric IRBuilder<> B(CI); 450b57cec5SDimitry Andric Value *OffsetPtr = 460b57cec5SDimitry Andric B.CreateGEP(Int8Ty, CI->getArgOperand(0), CI->getArgOperand(1)); 470b57cec5SDimitry Andric Value *OffsetPtrI32 = B.CreateBitCast(OffsetPtr, Int32PtrTy); 485ffd83dbSDimitry Andric Value *OffsetI32 = B.CreateAlignedLoad(Int32Ty, OffsetPtrI32, Align(4)); 490b57cec5SDimitry Andric 500b57cec5SDimitry Andric Value *ResultPtr = B.CreateGEP(Int8Ty, CI->getArgOperand(0), OffsetI32); 510b57cec5SDimitry Andric 520b57cec5SDimitry Andric CI->replaceAllUsesWith(ResultPtr); 530b57cec5SDimitry Andric CI->eraseFromParent(); 540b57cec5SDimitry Andric Changed = true; 550b57cec5SDimitry Andric } 560b57cec5SDimitry Andric 570b57cec5SDimitry Andric return Changed; 580b57cec5SDimitry Andric } 590b57cec5SDimitry Andric 60480093f4SDimitry Andric // ObjCARC has knowledge about whether an obj-c runtime function needs to be 61480093f4SDimitry Andric // always tail-called or never tail-called. 62480093f4SDimitry Andric static CallInst::TailCallKind getOverridingTailCallKind(const Function &F) { 63480093f4SDimitry Andric objcarc::ARCInstKind Kind = objcarc::GetFunctionClass(&F); 64480093f4SDimitry Andric if (objcarc::IsAlwaysTail(Kind)) 65480093f4SDimitry Andric return CallInst::TCK_Tail; 66480093f4SDimitry Andric else if (objcarc::IsNeverTail(Kind)) 67480093f4SDimitry Andric return CallInst::TCK_NoTail; 68480093f4SDimitry Andric return CallInst::TCK_None; 69480093f4SDimitry Andric } 70480093f4SDimitry Andric 710b57cec5SDimitry Andric static bool lowerObjCCall(Function &F, const char *NewFn, 720b57cec5SDimitry Andric bool setNonLazyBind = false) { 73*972a253aSDimitry Andric assert(IntrinsicInst::mayLowerToFunctionCall(F.getIntrinsicID()) && 74*972a253aSDimitry Andric "Pre-ISel intrinsics do lower into regular function calls"); 750b57cec5SDimitry Andric if (F.use_empty()) 760b57cec5SDimitry Andric return false; 770b57cec5SDimitry Andric 780b57cec5SDimitry Andric // If we haven't already looked up this function, check to see if the 790b57cec5SDimitry Andric // program already contains a function with this name. 800b57cec5SDimitry Andric Module *M = F.getParent(); 810b57cec5SDimitry Andric FunctionCallee FCache = M->getOrInsertFunction(NewFn, F.getFunctionType()); 820b57cec5SDimitry Andric 830b57cec5SDimitry Andric if (Function *Fn = dyn_cast<Function>(FCache.getCallee())) { 840b57cec5SDimitry Andric Fn->setLinkage(F.getLinkage()); 850b57cec5SDimitry Andric if (setNonLazyBind && !Fn->isWeakForLinker()) { 860b57cec5SDimitry Andric // If we have Native ARC, set nonlazybind attribute for these APIs for 870b57cec5SDimitry Andric // performance. 880b57cec5SDimitry Andric Fn->addFnAttr(Attribute::NonLazyBind); 890b57cec5SDimitry Andric } 900b57cec5SDimitry Andric } 910b57cec5SDimitry Andric 92480093f4SDimitry Andric CallInst::TailCallKind OverridingTCK = getOverridingTailCallKind(F); 93480093f4SDimitry Andric 94349cc55cSDimitry Andric for (Use &U : llvm::make_early_inc_range(F.uses())) { 95349cc55cSDimitry Andric auto *CB = cast<CallBase>(U.getUser()); 96349cc55cSDimitry Andric 97349cc55cSDimitry Andric if (CB->getCalledFunction() != &F) { 98349cc55cSDimitry Andric objcarc::ARCInstKind Kind = objcarc::getAttachedARCFunctionKind(CB); 99349cc55cSDimitry Andric (void)Kind; 100349cc55cSDimitry Andric assert((Kind == objcarc::ARCInstKind::RetainRV || 10104eeddc0SDimitry Andric Kind == objcarc::ARCInstKind::UnsafeClaimRV) && 102349cc55cSDimitry Andric "use expected to be the argument of operand bundle " 103349cc55cSDimitry Andric "\"clang.arc.attachedcall\""); 104349cc55cSDimitry Andric U.set(FCache.getCallee()); 105349cc55cSDimitry Andric continue; 106349cc55cSDimitry Andric } 107349cc55cSDimitry Andric 108349cc55cSDimitry Andric auto *CI = cast<CallInst>(CB); 1090b57cec5SDimitry Andric assert(CI->getCalledFunction() && "Cannot lower an indirect call!"); 1100b57cec5SDimitry Andric 1110b57cec5SDimitry Andric IRBuilder<> Builder(CI->getParent(), CI->getIterator()); 112e8d8bef9SDimitry Andric SmallVector<Value *, 8> Args(CI->args()); 113*972a253aSDimitry Andric SmallVector<llvm::OperandBundleDef, 1> BundleList; 114*972a253aSDimitry Andric CI->getOperandBundlesAsDefs(BundleList); 115*972a253aSDimitry Andric CallInst *NewCI = Builder.CreateCall(FCache, Args, BundleList); 1160b57cec5SDimitry Andric NewCI->setName(CI->getName()); 117480093f4SDimitry Andric 118480093f4SDimitry Andric // Try to set the most appropriate TailCallKind based on both the current 119480093f4SDimitry Andric // attributes and the ones that we could get from ObjCARC's special 120480093f4SDimitry Andric // knowledge of the runtime functions. 121480093f4SDimitry Andric // 122480093f4SDimitry Andric // std::max respects both requirements of notail and tail here: 123480093f4SDimitry Andric // * notail on either the call or from ObjCARC becomes notail 124480093f4SDimitry Andric // * tail on either side is stronger than none, but not notail 125480093f4SDimitry Andric CallInst::TailCallKind TCK = CI->getTailCallKind(); 126480093f4SDimitry Andric NewCI->setTailCallKind(std::max(TCK, OverridingTCK)); 127480093f4SDimitry Andric 1280b57cec5SDimitry Andric if (!CI->use_empty()) 1290b57cec5SDimitry Andric CI->replaceAllUsesWith(NewCI); 1300b57cec5SDimitry Andric CI->eraseFromParent(); 1310b57cec5SDimitry Andric } 1320b57cec5SDimitry Andric 1330b57cec5SDimitry Andric return true; 1340b57cec5SDimitry Andric } 1350b57cec5SDimitry Andric 1360b57cec5SDimitry Andric static bool lowerIntrinsics(Module &M) { 1370b57cec5SDimitry Andric bool Changed = false; 1380b57cec5SDimitry Andric for (Function &F : M) { 1390b57cec5SDimitry Andric if (F.getName().startswith("llvm.load.relative.")) { 1400b57cec5SDimitry Andric Changed |= lowerLoadRelative(F); 1410b57cec5SDimitry Andric continue; 1420b57cec5SDimitry Andric } 1430b57cec5SDimitry Andric switch (F.getIntrinsicID()) { 1440b57cec5SDimitry Andric default: 1450b57cec5SDimitry Andric break; 1460b57cec5SDimitry Andric case Intrinsic::objc_autorelease: 1470b57cec5SDimitry Andric Changed |= lowerObjCCall(F, "objc_autorelease"); 1480b57cec5SDimitry Andric break; 1490b57cec5SDimitry Andric case Intrinsic::objc_autoreleasePoolPop: 1500b57cec5SDimitry Andric Changed |= lowerObjCCall(F, "objc_autoreleasePoolPop"); 1510b57cec5SDimitry Andric break; 1520b57cec5SDimitry Andric case Intrinsic::objc_autoreleasePoolPush: 1530b57cec5SDimitry Andric Changed |= lowerObjCCall(F, "objc_autoreleasePoolPush"); 1540b57cec5SDimitry Andric break; 1550b57cec5SDimitry Andric case Intrinsic::objc_autoreleaseReturnValue: 1560b57cec5SDimitry Andric Changed |= lowerObjCCall(F, "objc_autoreleaseReturnValue"); 1570b57cec5SDimitry Andric break; 1580b57cec5SDimitry Andric case Intrinsic::objc_copyWeak: 1590b57cec5SDimitry Andric Changed |= lowerObjCCall(F, "objc_copyWeak"); 1600b57cec5SDimitry Andric break; 1610b57cec5SDimitry Andric case Intrinsic::objc_destroyWeak: 1620b57cec5SDimitry Andric Changed |= lowerObjCCall(F, "objc_destroyWeak"); 1630b57cec5SDimitry Andric break; 1640b57cec5SDimitry Andric case Intrinsic::objc_initWeak: 1650b57cec5SDimitry Andric Changed |= lowerObjCCall(F, "objc_initWeak"); 1660b57cec5SDimitry Andric break; 1670b57cec5SDimitry Andric case Intrinsic::objc_loadWeak: 1680b57cec5SDimitry Andric Changed |= lowerObjCCall(F, "objc_loadWeak"); 1690b57cec5SDimitry Andric break; 1700b57cec5SDimitry Andric case Intrinsic::objc_loadWeakRetained: 1710b57cec5SDimitry Andric Changed |= lowerObjCCall(F, "objc_loadWeakRetained"); 1720b57cec5SDimitry Andric break; 1730b57cec5SDimitry Andric case Intrinsic::objc_moveWeak: 1740b57cec5SDimitry Andric Changed |= lowerObjCCall(F, "objc_moveWeak"); 1750b57cec5SDimitry Andric break; 1760b57cec5SDimitry Andric case Intrinsic::objc_release: 1770b57cec5SDimitry Andric Changed |= lowerObjCCall(F, "objc_release", true); 1780b57cec5SDimitry Andric break; 1790b57cec5SDimitry Andric case Intrinsic::objc_retain: 1800b57cec5SDimitry Andric Changed |= lowerObjCCall(F, "objc_retain", true); 1810b57cec5SDimitry Andric break; 1820b57cec5SDimitry Andric case Intrinsic::objc_retainAutorelease: 1830b57cec5SDimitry Andric Changed |= lowerObjCCall(F, "objc_retainAutorelease"); 1840b57cec5SDimitry Andric break; 1850b57cec5SDimitry Andric case Intrinsic::objc_retainAutoreleaseReturnValue: 1860b57cec5SDimitry Andric Changed |= lowerObjCCall(F, "objc_retainAutoreleaseReturnValue"); 1870b57cec5SDimitry Andric break; 1880b57cec5SDimitry Andric case Intrinsic::objc_retainAutoreleasedReturnValue: 1890b57cec5SDimitry Andric Changed |= lowerObjCCall(F, "objc_retainAutoreleasedReturnValue"); 1900b57cec5SDimitry Andric break; 1910b57cec5SDimitry Andric case Intrinsic::objc_retainBlock: 1920b57cec5SDimitry Andric Changed |= lowerObjCCall(F, "objc_retainBlock"); 1930b57cec5SDimitry Andric break; 1940b57cec5SDimitry Andric case Intrinsic::objc_storeStrong: 1950b57cec5SDimitry Andric Changed |= lowerObjCCall(F, "objc_storeStrong"); 1960b57cec5SDimitry Andric break; 1970b57cec5SDimitry Andric case Intrinsic::objc_storeWeak: 1980b57cec5SDimitry Andric Changed |= lowerObjCCall(F, "objc_storeWeak"); 1990b57cec5SDimitry Andric break; 2000b57cec5SDimitry Andric case Intrinsic::objc_unsafeClaimAutoreleasedReturnValue: 2010b57cec5SDimitry Andric Changed |= lowerObjCCall(F, "objc_unsafeClaimAutoreleasedReturnValue"); 2020b57cec5SDimitry Andric break; 2030b57cec5SDimitry Andric case Intrinsic::objc_retainedObject: 2040b57cec5SDimitry Andric Changed |= lowerObjCCall(F, "objc_retainedObject"); 2050b57cec5SDimitry Andric break; 2060b57cec5SDimitry Andric case Intrinsic::objc_unretainedObject: 2070b57cec5SDimitry Andric Changed |= lowerObjCCall(F, "objc_unretainedObject"); 2080b57cec5SDimitry Andric break; 2090b57cec5SDimitry Andric case Intrinsic::objc_unretainedPointer: 2100b57cec5SDimitry Andric Changed |= lowerObjCCall(F, "objc_unretainedPointer"); 2110b57cec5SDimitry Andric break; 2120b57cec5SDimitry Andric case Intrinsic::objc_retain_autorelease: 2130b57cec5SDimitry Andric Changed |= lowerObjCCall(F, "objc_retain_autorelease"); 2140b57cec5SDimitry Andric break; 2150b57cec5SDimitry Andric case Intrinsic::objc_sync_enter: 2160b57cec5SDimitry Andric Changed |= lowerObjCCall(F, "objc_sync_enter"); 2170b57cec5SDimitry Andric break; 2180b57cec5SDimitry Andric case Intrinsic::objc_sync_exit: 2190b57cec5SDimitry Andric Changed |= lowerObjCCall(F, "objc_sync_exit"); 2200b57cec5SDimitry Andric break; 2210b57cec5SDimitry Andric } 2220b57cec5SDimitry Andric } 2230b57cec5SDimitry Andric return Changed; 2240b57cec5SDimitry Andric } 2250b57cec5SDimitry Andric 2260b57cec5SDimitry Andric namespace { 2270b57cec5SDimitry Andric 2280b57cec5SDimitry Andric class PreISelIntrinsicLoweringLegacyPass : public ModulePass { 2290b57cec5SDimitry Andric public: 2300b57cec5SDimitry Andric static char ID; 2310b57cec5SDimitry Andric 2320b57cec5SDimitry Andric PreISelIntrinsicLoweringLegacyPass() : ModulePass(ID) {} 2330b57cec5SDimitry Andric 2340b57cec5SDimitry Andric bool runOnModule(Module &M) override { return lowerIntrinsics(M); } 2350b57cec5SDimitry Andric }; 2360b57cec5SDimitry Andric 2370b57cec5SDimitry Andric } // end anonymous namespace 2380b57cec5SDimitry Andric 2390b57cec5SDimitry Andric char PreISelIntrinsicLoweringLegacyPass::ID; 2400b57cec5SDimitry Andric 2410b57cec5SDimitry Andric INITIALIZE_PASS(PreISelIntrinsicLoweringLegacyPass, 2420b57cec5SDimitry Andric "pre-isel-intrinsic-lowering", "Pre-ISel Intrinsic Lowering", 2430b57cec5SDimitry Andric false, false) 2440b57cec5SDimitry Andric 2450b57cec5SDimitry Andric ModulePass *llvm::createPreISelIntrinsicLoweringPass() { 2460b57cec5SDimitry Andric return new PreISelIntrinsicLoweringLegacyPass; 2470b57cec5SDimitry Andric } 2480b57cec5SDimitry Andric 2490b57cec5SDimitry Andric PreservedAnalyses PreISelIntrinsicLoweringPass::run(Module &M, 2500b57cec5SDimitry Andric ModuleAnalysisManager &AM) { 2510b57cec5SDimitry Andric if (!lowerIntrinsics(M)) 2520b57cec5SDimitry Andric return PreservedAnalyses::all(); 2530b57cec5SDimitry Andric else 2540b57cec5SDimitry Andric return PreservedAnalyses::none(); 2550b57cec5SDimitry Andric } 256