1327952Sdim//===- PreISelIntrinsicLowering.cpp - Pre-ISel intrinsic lowering pass ----===//
2303231Sdim//
3353358Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4353358Sdim// See https://llvm.org/LICENSE.txt for license information.
5353358Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6303231Sdim//
7303231Sdim//===----------------------------------------------------------------------===//
8303231Sdim//
9344779Sdim// This pass implements IR lowering for the llvm.load.relative and llvm.objc.*
10344779Sdim// intrinsics.
11303231Sdim//
12303231Sdim//===----------------------------------------------------------------------===//
13303231Sdim
14303231Sdim#include "llvm/CodeGen/PreISelIntrinsicLowering.h"
15360784Sdim#include "llvm/Analysis/ObjCARCInstKind.h"
16303231Sdim#include "llvm/CodeGen/Passes.h"
17303231Sdim#include "llvm/IR/Function.h"
18303231Sdim#include "llvm/IR/IRBuilder.h"
19303231Sdim#include "llvm/IR/Instructions.h"
20360784Sdim#include "llvm/IR/Intrinsics.h"
21303231Sdim#include "llvm/IR/Module.h"
22327952Sdim#include "llvm/IR/Type.h"
23327952Sdim#include "llvm/IR/User.h"
24360784Sdim#include "llvm/InitializePasses.h"
25303231Sdim#include "llvm/Pass.h"
26327952Sdim#include "llvm/Support/Casting.h"
27303231Sdim
28303231Sdimusing namespace llvm;
29303231Sdim
30327952Sdimstatic bool lowerLoadRelative(Function &F) {
31303231Sdim  if (F.use_empty())
32303231Sdim    return false;
33303231Sdim
34303231Sdim  bool Changed = false;
35303231Sdim  Type *Int32Ty = Type::getInt32Ty(F.getContext());
36303231Sdim  Type *Int32PtrTy = Int32Ty->getPointerTo();
37303231Sdim  Type *Int8Ty = Type::getInt8Ty(F.getContext());
38303231Sdim
39303231Sdim  for (auto I = F.use_begin(), E = F.use_end(); I != E;) {
40303231Sdim    auto CI = dyn_cast<CallInst>(I->getUser());
41303231Sdim    ++I;
42303231Sdim    if (!CI || CI->getCalledValue() != &F)
43303231Sdim      continue;
44303231Sdim
45303231Sdim    IRBuilder<> B(CI);
46303231Sdim    Value *OffsetPtr =
47303231Sdim        B.CreateGEP(Int8Ty, CI->getArgOperand(0), CI->getArgOperand(1));
48303231Sdim    Value *OffsetPtrI32 = B.CreateBitCast(OffsetPtr, Int32PtrTy);
49353358Sdim    Value *OffsetI32 = B.CreateAlignedLoad(Int32Ty, OffsetPtrI32, 4);
50303231Sdim
51303231Sdim    Value *ResultPtr = B.CreateGEP(Int8Ty, CI->getArgOperand(0), OffsetI32);
52303231Sdim
53303231Sdim    CI->replaceAllUsesWith(ResultPtr);
54303231Sdim    CI->eraseFromParent();
55303231Sdim    Changed = true;
56303231Sdim  }
57303231Sdim
58303231Sdim  return Changed;
59303231Sdim}
60303231Sdim
61360784Sdim// ObjCARC has knowledge about whether an obj-c runtime function needs to be
62360784Sdim// always tail-called or never tail-called.
63360784Sdimstatic CallInst::TailCallKind getOverridingTailCallKind(const Function &F) {
64360784Sdim  objcarc::ARCInstKind Kind = objcarc::GetFunctionClass(&F);
65360784Sdim  if (objcarc::IsAlwaysTail(Kind))
66360784Sdim    return CallInst::TCK_Tail;
67360784Sdim  else if (objcarc::IsNeverTail(Kind))
68360784Sdim    return CallInst::TCK_NoTail;
69360784Sdim  return CallInst::TCK_None;
70360784Sdim}
71360784Sdim
72344779Sdimstatic bool lowerObjCCall(Function &F, const char *NewFn,
73344779Sdim                          bool setNonLazyBind = false) {
74344779Sdim  if (F.use_empty())
75344779Sdim    return false;
76344779Sdim
77344779Sdim  // If we haven't already looked up this function, check to see if the
78344779Sdim  // program already contains a function with this name.
79344779Sdim  Module *M = F.getParent();
80353358Sdim  FunctionCallee FCache = M->getOrInsertFunction(NewFn, F.getFunctionType());
81344779Sdim
82353358Sdim  if (Function *Fn = dyn_cast<Function>(FCache.getCallee())) {
83344779Sdim    Fn->setLinkage(F.getLinkage());
84344779Sdim    if (setNonLazyBind && !Fn->isWeakForLinker()) {
85344779Sdim      // If we have Native ARC, set nonlazybind attribute for these APIs for
86344779Sdim      // performance.
87344779Sdim      Fn->addFnAttr(Attribute::NonLazyBind);
88344779Sdim    }
89344779Sdim  }
90344779Sdim
91360784Sdim  CallInst::TailCallKind OverridingTCK = getOverridingTailCallKind(F);
92360784Sdim
93344779Sdim  for (auto I = F.use_begin(), E = F.use_end(); I != E;) {
94360784Sdim    auto *CI = cast<CallInst>(I->getUser());
95344779Sdim    assert(CI->getCalledFunction() && "Cannot lower an indirect call!");
96344779Sdim    ++I;
97344779Sdim
98344779Sdim    IRBuilder<> Builder(CI->getParent(), CI->getIterator());
99344779Sdim    SmallVector<Value *, 8> Args(CI->arg_begin(), CI->arg_end());
100344779Sdim    CallInst *NewCI = Builder.CreateCall(FCache, Args);
101344779Sdim    NewCI->setName(CI->getName());
102360784Sdim
103360784Sdim    // Try to set the most appropriate TailCallKind based on both the current
104360784Sdim    // attributes and the ones that we could get from ObjCARC's special
105360784Sdim    // knowledge of the runtime functions.
106360784Sdim    //
107360784Sdim    // std::max respects both requirements of notail and tail here:
108360784Sdim    // * notail on either the call or from ObjCARC becomes notail
109360784Sdim    // * tail on either side is stronger than none, but not notail
110360784Sdim    CallInst::TailCallKind TCK = CI->getTailCallKind();
111360784Sdim    NewCI->setTailCallKind(std::max(TCK, OverridingTCK));
112360784Sdim
113344779Sdim    if (!CI->use_empty())
114344779Sdim      CI->replaceAllUsesWith(NewCI);
115344779Sdim    CI->eraseFromParent();
116344779Sdim  }
117344779Sdim
118344779Sdim  return true;
119344779Sdim}
120344779Sdim
121327952Sdimstatic bool lowerIntrinsics(Module &M) {
122303231Sdim  bool Changed = false;
123303231Sdim  for (Function &F : M) {
124344779Sdim    if (F.getName().startswith("llvm.load.relative.")) {
125303231Sdim      Changed |= lowerLoadRelative(F);
126344779Sdim      continue;
127344779Sdim    }
128344779Sdim    switch (F.getIntrinsicID()) {
129344779Sdim    default:
130344779Sdim      break;
131344779Sdim    case Intrinsic::objc_autorelease:
132344779Sdim      Changed |= lowerObjCCall(F, "objc_autorelease");
133344779Sdim      break;
134344779Sdim    case Intrinsic::objc_autoreleasePoolPop:
135344779Sdim      Changed |= lowerObjCCall(F, "objc_autoreleasePoolPop");
136344779Sdim      break;
137344779Sdim    case Intrinsic::objc_autoreleasePoolPush:
138344779Sdim      Changed |= lowerObjCCall(F, "objc_autoreleasePoolPush");
139344779Sdim      break;
140344779Sdim    case Intrinsic::objc_autoreleaseReturnValue:
141344779Sdim      Changed |= lowerObjCCall(F, "objc_autoreleaseReturnValue");
142344779Sdim      break;
143344779Sdim    case Intrinsic::objc_copyWeak:
144344779Sdim      Changed |= lowerObjCCall(F, "objc_copyWeak");
145344779Sdim      break;
146344779Sdim    case Intrinsic::objc_destroyWeak:
147344779Sdim      Changed |= lowerObjCCall(F, "objc_destroyWeak");
148344779Sdim      break;
149344779Sdim    case Intrinsic::objc_initWeak:
150344779Sdim      Changed |= lowerObjCCall(F, "objc_initWeak");
151344779Sdim      break;
152344779Sdim    case Intrinsic::objc_loadWeak:
153344779Sdim      Changed |= lowerObjCCall(F, "objc_loadWeak");
154344779Sdim      break;
155344779Sdim    case Intrinsic::objc_loadWeakRetained:
156344779Sdim      Changed |= lowerObjCCall(F, "objc_loadWeakRetained");
157344779Sdim      break;
158344779Sdim    case Intrinsic::objc_moveWeak:
159344779Sdim      Changed |= lowerObjCCall(F, "objc_moveWeak");
160344779Sdim      break;
161344779Sdim    case Intrinsic::objc_release:
162344779Sdim      Changed |= lowerObjCCall(F, "objc_release", true);
163344779Sdim      break;
164344779Sdim    case Intrinsic::objc_retain:
165344779Sdim      Changed |= lowerObjCCall(F, "objc_retain", true);
166344779Sdim      break;
167344779Sdim    case Intrinsic::objc_retainAutorelease:
168344779Sdim      Changed |= lowerObjCCall(F, "objc_retainAutorelease");
169344779Sdim      break;
170344779Sdim    case Intrinsic::objc_retainAutoreleaseReturnValue:
171344779Sdim      Changed |= lowerObjCCall(F, "objc_retainAutoreleaseReturnValue");
172344779Sdim      break;
173344779Sdim    case Intrinsic::objc_retainAutoreleasedReturnValue:
174344779Sdim      Changed |= lowerObjCCall(F, "objc_retainAutoreleasedReturnValue");
175344779Sdim      break;
176344779Sdim    case Intrinsic::objc_retainBlock:
177344779Sdim      Changed |= lowerObjCCall(F, "objc_retainBlock");
178344779Sdim      break;
179344779Sdim    case Intrinsic::objc_storeStrong:
180344779Sdim      Changed |= lowerObjCCall(F, "objc_storeStrong");
181344779Sdim      break;
182344779Sdim    case Intrinsic::objc_storeWeak:
183344779Sdim      Changed |= lowerObjCCall(F, "objc_storeWeak");
184344779Sdim      break;
185344779Sdim    case Intrinsic::objc_unsafeClaimAutoreleasedReturnValue:
186344779Sdim      Changed |= lowerObjCCall(F, "objc_unsafeClaimAutoreleasedReturnValue");
187344779Sdim      break;
188344779Sdim    case Intrinsic::objc_retainedObject:
189344779Sdim      Changed |= lowerObjCCall(F, "objc_retainedObject");
190344779Sdim      break;
191344779Sdim    case Intrinsic::objc_unretainedObject:
192344779Sdim      Changed |= lowerObjCCall(F, "objc_unretainedObject");
193344779Sdim      break;
194344779Sdim    case Intrinsic::objc_unretainedPointer:
195344779Sdim      Changed |= lowerObjCCall(F, "objc_unretainedPointer");
196344779Sdim      break;
197344779Sdim    case Intrinsic::objc_retain_autorelease:
198344779Sdim      Changed |= lowerObjCCall(F, "objc_retain_autorelease");
199344779Sdim      break;
200344779Sdim    case Intrinsic::objc_sync_enter:
201344779Sdim      Changed |= lowerObjCCall(F, "objc_sync_enter");
202344779Sdim      break;
203344779Sdim    case Intrinsic::objc_sync_exit:
204344779Sdim      Changed |= lowerObjCCall(F, "objc_sync_exit");
205344779Sdim      break;
206344779Sdim    }
207303231Sdim  }
208303231Sdim  return Changed;
209303231Sdim}
210303231Sdim
211327952Sdimnamespace {
212327952Sdim
213303231Sdimclass PreISelIntrinsicLoweringLegacyPass : public ModulePass {
214303231Sdimpublic:
215303231Sdim  static char ID;
216327952Sdim
217303231Sdim  PreISelIntrinsicLoweringLegacyPass() : ModulePass(ID) {}
218303231Sdim
219327952Sdim  bool runOnModule(Module &M) override { return lowerIntrinsics(M); }
220303231Sdim};
221303231Sdim
222327952Sdim} // end anonymous namespace
223327952Sdim
224303231Sdimchar PreISelIntrinsicLoweringLegacyPass::ID;
225303231Sdim
226303231SdimINITIALIZE_PASS(PreISelIntrinsicLoweringLegacyPass,
227303231Sdim                "pre-isel-intrinsic-lowering", "Pre-ISel Intrinsic Lowering",
228303231Sdim                false, false)
229303231Sdim
230327952SdimModulePass *llvm::createPreISelIntrinsicLoweringPass() {
231303231Sdim  return new PreISelIntrinsicLoweringLegacyPass;
232303231Sdim}
233303231Sdim
234303231SdimPreservedAnalyses PreISelIntrinsicLoweringPass::run(Module &M,
235303231Sdim                                                    ModuleAnalysisManager &AM) {
236303231Sdim  if (!lowerIntrinsics(M))
237303231Sdim    return PreservedAnalyses::all();
238303231Sdim  else
239303231Sdim    return PreservedAnalyses::none();
240303231Sdim}
241