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/Analysis/ObjCARCUtil.h"
17 #include "llvm/CodeGen/Passes.h"
18 #include "llvm/IR/Function.h"
19 #include "llvm/IR/IRBuilder.h"
20 #include "llvm/IR/Instructions.h"
21 #include "llvm/IR/IntrinsicInst.h"
22 #include "llvm/IR/Module.h"
23 #include "llvm/IR/Type.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 (Use &U : llvm::make_early_inc_range(F.uses())) {
40 auto CI = dyn_cast<CallInst>(U.getUser());
41 if (!CI || CI->getCalledOperand() != &F)
42 continue;
43
44 IRBuilder<> B(CI);
45 Value *OffsetPtr =
46 B.CreateGEP(Int8Ty, CI->getArgOperand(0), CI->getArgOperand(1));
47 Value *OffsetPtrI32 = B.CreateBitCast(OffsetPtr, Int32PtrTy);
48 Value *OffsetI32 = B.CreateAlignedLoad(Int32Ty, OffsetPtrI32, Align(4));
49
50 Value *ResultPtr = B.CreateGEP(Int8Ty, CI->getArgOperand(0), OffsetI32);
51
52 CI->replaceAllUsesWith(ResultPtr);
53 CI->eraseFromParent();
54 Changed = true;
55 }
56
57 return Changed;
58 }
59
60 // ObjCARC has knowledge about whether an obj-c runtime function needs to be
61 // always tail-called or never tail-called.
getOverridingTailCallKind(const Function & F)62 static CallInst::TailCallKind getOverridingTailCallKind(const Function &F) {
63 objcarc::ARCInstKind Kind = objcarc::GetFunctionClass(&F);
64 if (objcarc::IsAlwaysTail(Kind))
65 return CallInst::TCK_Tail;
66 else if (objcarc::IsNeverTail(Kind))
67 return CallInst::TCK_NoTail;
68 return CallInst::TCK_None;
69 }
70
lowerObjCCall(Function & F,const char * NewFn,bool setNonLazyBind=false)71 static bool lowerObjCCall(Function &F, const char *NewFn,
72 bool setNonLazyBind = false) {
73 assert(IntrinsicInst::mayLowerToFunctionCall(F.getIntrinsicID()) &&
74 "Pre-ISel intrinsics do lower into regular function calls");
75 if (F.use_empty())
76 return false;
77
78 // If we haven't already looked up this function, check to see if the
79 // program already contains a function with this name.
80 Module *M = F.getParent();
81 FunctionCallee FCache = M->getOrInsertFunction(NewFn, F.getFunctionType());
82
83 if (Function *Fn = dyn_cast<Function>(FCache.getCallee())) {
84 Fn->setLinkage(F.getLinkage());
85 if (setNonLazyBind && !Fn->isWeakForLinker()) {
86 // If we have Native ARC, set nonlazybind attribute for these APIs for
87 // performance.
88 Fn->addFnAttr(Attribute::NonLazyBind);
89 }
90 }
91
92 CallInst::TailCallKind OverridingTCK = getOverridingTailCallKind(F);
93
94 for (Use &U : llvm::make_early_inc_range(F.uses())) {
95 auto *CB = cast<CallBase>(U.getUser());
96
97 if (CB->getCalledFunction() != &F) {
98 objcarc::ARCInstKind Kind = objcarc::getAttachedARCFunctionKind(CB);
99 (void)Kind;
100 assert((Kind == objcarc::ARCInstKind::RetainRV ||
101 Kind == objcarc::ARCInstKind::UnsafeClaimRV) &&
102 "use expected to be the argument of operand bundle "
103 "\"clang.arc.attachedcall\"");
104 U.set(FCache.getCallee());
105 continue;
106 }
107
108 auto *CI = cast<CallInst>(CB);
109 assert(CI->getCalledFunction() && "Cannot lower an indirect call!");
110
111 IRBuilder<> Builder(CI->getParent(), CI->getIterator());
112 SmallVector<Value *, 8> Args(CI->args());
113 SmallVector<llvm::OperandBundleDef, 1> BundleList;
114 CI->getOperandBundlesAsDefs(BundleList);
115 CallInst *NewCI = Builder.CreateCall(FCache, Args, BundleList);
116 NewCI->setName(CI->getName());
117
118 // Try to set the most appropriate TailCallKind based on both the current
119 // attributes and the ones that we could get from ObjCARC's special
120 // knowledge of the runtime functions.
121 //
122 // std::max respects both requirements of notail and tail here:
123 // * notail on either the call or from ObjCARC becomes notail
124 // * tail on either side is stronger than none, but not notail
125 CallInst::TailCallKind TCK = CI->getTailCallKind();
126 NewCI->setTailCallKind(std::max(TCK, OverridingTCK));
127
128 if (!CI->use_empty())
129 CI->replaceAllUsesWith(NewCI);
130 CI->eraseFromParent();
131 }
132
133 return true;
134 }
135
lowerIntrinsics(Module & M)136 static bool lowerIntrinsics(Module &M) {
137 bool Changed = false;
138 for (Function &F : M) {
139 if (F.getName().startswith("llvm.load.relative.")) {
140 Changed |= lowerLoadRelative(F);
141 continue;
142 }
143 switch (F.getIntrinsicID()) {
144 default:
145 break;
146 case Intrinsic::objc_autorelease:
147 Changed |= lowerObjCCall(F, "objc_autorelease");
148 break;
149 case Intrinsic::objc_autoreleasePoolPop:
150 Changed |= lowerObjCCall(F, "objc_autoreleasePoolPop");
151 break;
152 case Intrinsic::objc_autoreleasePoolPush:
153 Changed |= lowerObjCCall(F, "objc_autoreleasePoolPush");
154 break;
155 case Intrinsic::objc_autoreleaseReturnValue:
156 Changed |= lowerObjCCall(F, "objc_autoreleaseReturnValue");
157 break;
158 case Intrinsic::objc_copyWeak:
159 Changed |= lowerObjCCall(F, "objc_copyWeak");
160 break;
161 case Intrinsic::objc_destroyWeak:
162 Changed |= lowerObjCCall(F, "objc_destroyWeak");
163 break;
164 case Intrinsic::objc_initWeak:
165 Changed |= lowerObjCCall(F, "objc_initWeak");
166 break;
167 case Intrinsic::objc_loadWeak:
168 Changed |= lowerObjCCall(F, "objc_loadWeak");
169 break;
170 case Intrinsic::objc_loadWeakRetained:
171 Changed |= lowerObjCCall(F, "objc_loadWeakRetained");
172 break;
173 case Intrinsic::objc_moveWeak:
174 Changed |= lowerObjCCall(F, "objc_moveWeak");
175 break;
176 case Intrinsic::objc_release:
177 Changed |= lowerObjCCall(F, "objc_release", true);
178 break;
179 case Intrinsic::objc_retain:
180 Changed |= lowerObjCCall(F, "objc_retain", true);
181 break;
182 case Intrinsic::objc_retainAutorelease:
183 Changed |= lowerObjCCall(F, "objc_retainAutorelease");
184 break;
185 case Intrinsic::objc_retainAutoreleaseReturnValue:
186 Changed |= lowerObjCCall(F, "objc_retainAutoreleaseReturnValue");
187 break;
188 case Intrinsic::objc_retainAutoreleasedReturnValue:
189 Changed |= lowerObjCCall(F, "objc_retainAutoreleasedReturnValue");
190 break;
191 case Intrinsic::objc_retainBlock:
192 Changed |= lowerObjCCall(F, "objc_retainBlock");
193 break;
194 case Intrinsic::objc_storeStrong:
195 Changed |= lowerObjCCall(F, "objc_storeStrong");
196 break;
197 case Intrinsic::objc_storeWeak:
198 Changed |= lowerObjCCall(F, "objc_storeWeak");
199 break;
200 case Intrinsic::objc_unsafeClaimAutoreleasedReturnValue:
201 Changed |= lowerObjCCall(F, "objc_unsafeClaimAutoreleasedReturnValue");
202 break;
203 case Intrinsic::objc_retainedObject:
204 Changed |= lowerObjCCall(F, "objc_retainedObject");
205 break;
206 case Intrinsic::objc_unretainedObject:
207 Changed |= lowerObjCCall(F, "objc_unretainedObject");
208 break;
209 case Intrinsic::objc_unretainedPointer:
210 Changed |= lowerObjCCall(F, "objc_unretainedPointer");
211 break;
212 case Intrinsic::objc_retain_autorelease:
213 Changed |= lowerObjCCall(F, "objc_retain_autorelease");
214 break;
215 case Intrinsic::objc_sync_enter:
216 Changed |= lowerObjCCall(F, "objc_sync_enter");
217 break;
218 case Intrinsic::objc_sync_exit:
219 Changed |= lowerObjCCall(F, "objc_sync_exit");
220 break;
221 }
222 }
223 return Changed;
224 }
225
226 namespace {
227
228 class PreISelIntrinsicLoweringLegacyPass : public ModulePass {
229 public:
230 static char ID;
231
PreISelIntrinsicLoweringLegacyPass()232 PreISelIntrinsicLoweringLegacyPass() : ModulePass(ID) {}
233
runOnModule(Module & M)234 bool runOnModule(Module &M) override { return lowerIntrinsics(M); }
235 };
236
237 } // end anonymous namespace
238
239 char PreISelIntrinsicLoweringLegacyPass::ID;
240
241 INITIALIZE_PASS(PreISelIntrinsicLoweringLegacyPass,
242 "pre-isel-intrinsic-lowering", "Pre-ISel Intrinsic Lowering",
243 false, false)
244
createPreISelIntrinsicLoweringPass()245 ModulePass *llvm::createPreISelIntrinsicLoweringPass() {
246 return new PreISelIntrinsicLoweringLegacyPass;
247 }
248
run(Module & M,ModuleAnalysisManager & AM)249 PreservedAnalyses PreISelIntrinsicLoweringPass::run(Module &M,
250 ModuleAnalysisManager &AM) {
251 if (!lowerIntrinsics(M))
252 return PreservedAnalyses::all();
253 else
254 return PreservedAnalyses::none();
255 }
256