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