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
28using namespace llvm;
29
30static 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->getCalledOperand() != &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, Align(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.
63static 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
72static 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
121static 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
211namespace {
212
213class PreISelIntrinsicLoweringLegacyPass : public ModulePass {
214public:
215  static char ID;
216
217  PreISelIntrinsicLoweringLegacyPass() : ModulePass(ID) {}
218
219  bool runOnModule(Module &M) override { return lowerIntrinsics(M); }
220};
221
222} // end anonymous namespace
223
224char PreISelIntrinsicLoweringLegacyPass::ID;
225
226INITIALIZE_PASS(PreISelIntrinsicLoweringLegacyPass,
227                "pre-isel-intrinsic-lowering", "Pre-ISel Intrinsic Lowering",
228                false, false)
229
230ModulePass *llvm::createPreISelIntrinsicLoweringPass() {
231  return new PreISelIntrinsicLoweringLegacyPass;
232}
233
234PreservedAnalyses PreISelIntrinsicLoweringPass::run(Module &M,
235                                                    ModuleAnalysisManager &AM) {
236  if (!lowerIntrinsics(M))
237    return PreservedAnalyses::all();
238  else
239    return PreservedAnalyses::none();
240}
241