1 //===- PreISelIntrinsicLowering.cpp - Pre-ISel intrinsic lowering pass ----===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // This pass implements IR lowering for the llvm.load.relative and llvm.objc.*
10 // intrinsics.
11 //
12 //===----------------------------------------------------------------------===//
13
14 #include "llvm/CodeGen/PreISelIntrinsicLowering.h"
15 #include "llvm/Analysis/ObjCARCInstKind.h"
16 #include "llvm/CodeGen/Passes.h"
17 #include "llvm/IR/Function.h"
18 #include "llvm/IR/IRBuilder.h"
19 #include "llvm/IR/Instructions.h"
20 #include "llvm/IR/Intrinsics.h"
21 #include "llvm/IR/Module.h"
22 #include "llvm/IR/Type.h"
23 #include "llvm/IR/User.h"
24 #include "llvm/InitializePasses.h"
25 #include "llvm/Pass.h"
26 #include "llvm/Support/Casting.h"
27
28 using namespace llvm;
29
lowerLoadRelative(Function & F)30 static bool lowerLoadRelative(Function &F) {
31 if (F.use_empty())
32 return false;
33
34 bool Changed = false;
35 Type *Int32Ty = Type::getInt32Ty(F.getContext());
36 Type *Int32PtrTy = Int32Ty->getPointerTo();
37 Type *Int8Ty = Type::getInt8Ty(F.getContext());
38
39 for (auto I = F.use_begin(), E = F.use_end(); I != E;) {
40 auto CI = dyn_cast<CallInst>(I->getUser());
41 ++I;
42 if (!CI || CI->getCalledValue() != &F)
43 continue;
44
45 IRBuilder<> B(CI);
46 Value *OffsetPtr =
47 B.CreateGEP(Int8Ty, CI->getArgOperand(0), CI->getArgOperand(1));
48 Value *OffsetPtrI32 = B.CreateBitCast(OffsetPtr, Int32PtrTy);
49 Value *OffsetI32 = B.CreateAlignedLoad(Int32Ty, OffsetPtrI32, 4);
50
51 Value *ResultPtr = B.CreateGEP(Int8Ty, CI->getArgOperand(0), OffsetI32);
52
53 CI->replaceAllUsesWith(ResultPtr);
54 CI->eraseFromParent();
55 Changed = true;
56 }
57
58 return Changed;
59 }
60
61 // ObjCARC has knowledge about whether an obj-c runtime function needs to be
62 // always tail-called or never tail-called.
getOverridingTailCallKind(const Function & F)63 static CallInst::TailCallKind getOverridingTailCallKind(const Function &F) {
64 objcarc::ARCInstKind Kind = objcarc::GetFunctionClass(&F);
65 if (objcarc::IsAlwaysTail(Kind))
66 return CallInst::TCK_Tail;
67 else if (objcarc::IsNeverTail(Kind))
68 return CallInst::TCK_NoTail;
69 return CallInst::TCK_None;
70 }
71
lowerObjCCall(Function & F,const char * NewFn,bool setNonLazyBind=false)72 static bool lowerObjCCall(Function &F, const char *NewFn,
73 bool setNonLazyBind = false) {
74 if (F.use_empty())
75 return false;
76
77 // If we haven't already looked up this function, check to see if the
78 // program already contains a function with this name.
79 Module *M = F.getParent();
80 FunctionCallee FCache = M->getOrInsertFunction(NewFn, F.getFunctionType());
81
82 if (Function *Fn = dyn_cast<Function>(FCache.getCallee())) {
83 Fn->setLinkage(F.getLinkage());
84 if (setNonLazyBind && !Fn->isWeakForLinker()) {
85 // If we have Native ARC, set nonlazybind attribute for these APIs for
86 // performance.
87 Fn->addFnAttr(Attribute::NonLazyBind);
88 }
89 }
90
91 CallInst::TailCallKind OverridingTCK = getOverridingTailCallKind(F);
92
93 for (auto I = F.use_begin(), E = F.use_end(); I != E;) {
94 auto *CI = cast<CallInst>(I->getUser());
95 assert(CI->getCalledFunction() && "Cannot lower an indirect call!");
96 ++I;
97
98 IRBuilder<> Builder(CI->getParent(), CI->getIterator());
99 SmallVector<Value *, 8> Args(CI->arg_begin(), CI->arg_end());
100 CallInst *NewCI = Builder.CreateCall(FCache, Args);
101 NewCI->setName(CI->getName());
102
103 // Try to set the most appropriate TailCallKind based on both the current
104 // attributes and the ones that we could get from ObjCARC's special
105 // knowledge of the runtime functions.
106 //
107 // std::max respects both requirements of notail and tail here:
108 // * notail on either the call or from ObjCARC becomes notail
109 // * tail on either side is stronger than none, but not notail
110 CallInst::TailCallKind TCK = CI->getTailCallKind();
111 NewCI->setTailCallKind(std::max(TCK, OverridingTCK));
112
113 if (!CI->use_empty())
114 CI->replaceAllUsesWith(NewCI);
115 CI->eraseFromParent();
116 }
117
118 return true;
119 }
120
lowerIntrinsics(Module & M)121 static bool lowerIntrinsics(Module &M) {
122 bool Changed = false;
123 for (Function &F : M) {
124 if (F.getName().startswith("llvm.load.relative.")) {
125 Changed |= lowerLoadRelative(F);
126 continue;
127 }
128 switch (F.getIntrinsicID()) {
129 default:
130 break;
131 case Intrinsic::objc_autorelease:
132 Changed |= lowerObjCCall(F, "objc_autorelease");
133 break;
134 case Intrinsic::objc_autoreleasePoolPop:
135 Changed |= lowerObjCCall(F, "objc_autoreleasePoolPop");
136 break;
137 case Intrinsic::objc_autoreleasePoolPush:
138 Changed |= lowerObjCCall(F, "objc_autoreleasePoolPush");
139 break;
140 case Intrinsic::objc_autoreleaseReturnValue:
141 Changed |= lowerObjCCall(F, "objc_autoreleaseReturnValue");
142 break;
143 case Intrinsic::objc_copyWeak:
144 Changed |= lowerObjCCall(F, "objc_copyWeak");
145 break;
146 case Intrinsic::objc_destroyWeak:
147 Changed |= lowerObjCCall(F, "objc_destroyWeak");
148 break;
149 case Intrinsic::objc_initWeak:
150 Changed |= lowerObjCCall(F, "objc_initWeak");
151 break;
152 case Intrinsic::objc_loadWeak:
153 Changed |= lowerObjCCall(F, "objc_loadWeak");
154 break;
155 case Intrinsic::objc_loadWeakRetained:
156 Changed |= lowerObjCCall(F, "objc_loadWeakRetained");
157 break;
158 case Intrinsic::objc_moveWeak:
159 Changed |= lowerObjCCall(F, "objc_moveWeak");
160 break;
161 case Intrinsic::objc_release:
162 Changed |= lowerObjCCall(F, "objc_release", true);
163 break;
164 case Intrinsic::objc_retain:
165 Changed |= lowerObjCCall(F, "objc_retain", true);
166 break;
167 case Intrinsic::objc_retainAutorelease:
168 Changed |= lowerObjCCall(F, "objc_retainAutorelease");
169 break;
170 case Intrinsic::objc_retainAutoreleaseReturnValue:
171 Changed |= lowerObjCCall(F, "objc_retainAutoreleaseReturnValue");
172 break;
173 case Intrinsic::objc_retainAutoreleasedReturnValue:
174 Changed |= lowerObjCCall(F, "objc_retainAutoreleasedReturnValue");
175 break;
176 case Intrinsic::objc_retainBlock:
177 Changed |= lowerObjCCall(F, "objc_retainBlock");
178 break;
179 case Intrinsic::objc_storeStrong:
180 Changed |= lowerObjCCall(F, "objc_storeStrong");
181 break;
182 case Intrinsic::objc_storeWeak:
183 Changed |= lowerObjCCall(F, "objc_storeWeak");
184 break;
185 case Intrinsic::objc_unsafeClaimAutoreleasedReturnValue:
186 Changed |= lowerObjCCall(F, "objc_unsafeClaimAutoreleasedReturnValue");
187 break;
188 case Intrinsic::objc_retainedObject:
189 Changed |= lowerObjCCall(F, "objc_retainedObject");
190 break;
191 case Intrinsic::objc_unretainedObject:
192 Changed |= lowerObjCCall(F, "objc_unretainedObject");
193 break;
194 case Intrinsic::objc_unretainedPointer:
195 Changed |= lowerObjCCall(F, "objc_unretainedPointer");
196 break;
197 case Intrinsic::objc_retain_autorelease:
198 Changed |= lowerObjCCall(F, "objc_retain_autorelease");
199 break;
200 case Intrinsic::objc_sync_enter:
201 Changed |= lowerObjCCall(F, "objc_sync_enter");
202 break;
203 case Intrinsic::objc_sync_exit:
204 Changed |= lowerObjCCall(F, "objc_sync_exit");
205 break;
206 }
207 }
208 return Changed;
209 }
210
211 namespace {
212
213 class PreISelIntrinsicLoweringLegacyPass : public ModulePass {
214 public:
215 static char ID;
216
PreISelIntrinsicLoweringLegacyPass()217 PreISelIntrinsicLoweringLegacyPass() : ModulePass(ID) {}
218
runOnModule(Module & M)219 bool runOnModule(Module &M) override { return lowerIntrinsics(M); }
220 };
221
222 } // end anonymous namespace
223
224 char PreISelIntrinsicLoweringLegacyPass::ID;
225
226 INITIALIZE_PASS(PreISelIntrinsicLoweringLegacyPass,
227 "pre-isel-intrinsic-lowering", "Pre-ISel Intrinsic Lowering",
228 false, false)
229
createPreISelIntrinsicLoweringPass()230 ModulePass *llvm::createPreISelIntrinsicLoweringPass() {
231 return new PreISelIntrinsicLoweringLegacyPass;
232 }
233
run(Module & M,ModuleAnalysisManager & AM)234 PreservedAnalyses PreISelIntrinsicLoweringPass::run(Module &M,
235 ModuleAnalysisManager &AM) {
236 if (!lowerIntrinsics(M))
237 return PreservedAnalyses::all();
238 else
239 return PreservedAnalyses::none();
240 }
241