reference, declarationdefinition
definition → references, declarations, derived classes, virtual overrides
reference to multiple definitions → definitions
unreferenced
    1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130
  131
  132
  133
  134
  135
  136
  137
  138
  139
  140
  141
  142
  143
  144
  145
  146
  147
  148
  149
  150
  151
  152
  153
  154
  155
  156
  157
  158
  159
  160
  161
  162
  163
  164
  165
  166
  167
  168
  169
  170
  171
  172
  173
  174
  175
  176
  177
  178
  179
  180
  181
  182
  183
  184
  185
  186
  187
  188
  189
  190
  191
  192
  193
  194
  195
  196
  197
  198
  199
  200
  201
  202
  203
  204
  205
  206
  207
  208
  209
  210
  211
  212
  213
  214
  215
  216
  217
  218
  219
  220
  221
  222
  223
  224
  225
  226
  227
  228
  229
  230
  231
  232
  233
  234
  235
  236
  237
  238
  239
  240
  241
  242
  243
  244
  245
  246
  247
  248
  249
  250
  251
  252
  253
  254
  255
  256
  257
  258
  259
  260
  261
  262
  263
  264
  265
  266
  267
  268
  269
  270
  271
  272
  273
  274
  275
  276
  277
  278
  279
  280
  281
  282
  283
  284
  285
  286
  287
  288
  289
  290
  291
  292
  293
  294
  295
  296
  297
  298
  299
  300
//===- ObjCARCAnalysisUtils.h - ObjC ARC Analysis Utilities -----*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
/// \file
/// This file defines common analysis utilities used by the ObjC ARC Optimizer.
/// ARC stands for Automatic Reference Counting and is a system for managing
/// reference counts for objects in Objective C.
///
/// WARNING: This file knows about certain library functions. It recognizes them
/// by name, and hardwires knowledge of their semantics.
///
/// WARNING: This file knows about how certain Objective-C library functions are
/// used. Naive LLVM IR transformations which would otherwise be
/// behavior-preserving may break these assumptions.
///
//===----------------------------------------------------------------------===//

#ifndef LLVM_LIB_ANALYSIS_OBJCARCANALYSISUTILS_H
#define LLVM_LIB_ANALYSIS_OBJCARCANALYSISUTILS_H

#include "llvm/ADT/Optional.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/Analysis/AliasAnalysis.h"
#include "llvm/Analysis/ObjCARCInstKind.h"
#include "llvm/Analysis/Passes.h"
#include "llvm/Analysis/ValueTracking.h"
#include "llvm/IR/CallSite.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/InstIterator.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/ValueHandle.h"
#include "llvm/Pass.h"

namespace llvm {
class raw_ostream;
}

namespace llvm {
namespace objcarc {

/// A handy option to enable/disable all ARC Optimizations.
extern bool EnableARCOpts;

/// Test if the given module looks interesting to run ARC optimization
/// on.
inline bool ModuleHasARC(const Module &M) {
  return
    M.getNamedValue("llvm.objc.retain") ||
    M.getNamedValue("llvm.objc.release") ||
    M.getNamedValue("llvm.objc.autorelease") ||
    M.getNamedValue("llvm.objc.retainAutoreleasedReturnValue") ||
    M.getNamedValue("llvm.objc.unsafeClaimAutoreleasedReturnValue") ||
    M.getNamedValue("llvm.objc.retainBlock") ||
    M.getNamedValue("llvm.objc.autoreleaseReturnValue") ||
    M.getNamedValue("llvm.objc.autoreleasePoolPush") ||
    M.getNamedValue("llvm.objc.loadWeakRetained") ||
    M.getNamedValue("llvm.objc.loadWeak") ||
    M.getNamedValue("llvm.objc.destroyWeak") ||
    M.getNamedValue("llvm.objc.storeWeak") ||
    M.getNamedValue("llvm.objc.initWeak") ||
    M.getNamedValue("llvm.objc.moveWeak") ||
    M.getNamedValue("llvm.objc.copyWeak") ||
    M.getNamedValue("llvm.objc.retainedObject") ||
    M.getNamedValue("llvm.objc.unretainedObject") ||
    M.getNamedValue("llvm.objc.unretainedPointer") ||
    M.getNamedValue("llvm.objc.clang.arc.use");
}

/// This is a wrapper around getUnderlyingObject which also knows how to
/// look through objc_retain and objc_autorelease calls, which we know to return
/// their argument verbatim.
inline const Value *GetUnderlyingObjCPtr(const Value *V,
                                                const DataLayout &DL) {
  for (;;) {
    V = GetUnderlyingObject(V, DL);
    if (!IsForwarding(GetBasicARCInstKind(V)))
      break;
    V = cast<CallInst>(V)->getArgOperand(0);
  }

  return V;
}

/// A wrapper for GetUnderlyingObjCPtr used for results memoization.
inline const Value *
GetUnderlyingObjCPtrCached(const Value *V, const DataLayout &DL,
                           DenseMap<const Value *, WeakTrackingVH> &Cache) {
  if (auto InCache = Cache.lookup(V))
    return InCache;

  const Value *Computed = GetUnderlyingObjCPtr(V, DL);
  Cache[V] = const_cast<Value *>(Computed);
  return Computed;
}

/// The RCIdentity root of a value \p V is a dominating value U for which
/// retaining or releasing U is equivalent to retaining or releasing V. In other
/// words, ARC operations on \p V are equivalent to ARC operations on \p U.
///
/// We use this in the ARC optimizer to make it easier to match up ARC
/// operations by always mapping ARC operations to RCIdentityRoots instead of
/// pointers themselves.
///
/// The two ways that we see RCIdentical values in ObjC are via:
///
///   1. PointerCasts
///   2. Forwarding Calls that return their argument verbatim.
///
/// Thus this function strips off pointer casts and forwarding calls. *NOTE*
/// This implies that two RCIdentical values must alias.
inline const Value *GetRCIdentityRoot(const Value *V) {
  for (;;) {
    V = V->stripPointerCasts();
    if (!IsForwarding(GetBasicARCInstKind(V)))
      break;
    V = cast<CallInst>(V)->getArgOperand(0);
  }
  return V;
}

/// Helper which calls const Value *GetRCIdentityRoot(const Value *V) and just
/// casts away the const of the result. For documentation about what an
/// RCIdentityRoot (and by extension GetRCIdentityRoot is) look at that
/// function.
inline Value *GetRCIdentityRoot(Value *V) {
  return const_cast<Value *>(GetRCIdentityRoot((const Value *)V));
}

/// Assuming the given instruction is one of the special calls such as
/// objc_retain or objc_release, return the RCIdentity root of the argument of
/// the call.
inline Value *GetArgRCIdentityRoot(Value *Inst) {
  return GetRCIdentityRoot(cast<CallInst>(Inst)->getArgOperand(0));
}

inline bool IsNullOrUndef(const Value *V) {
  return isa<ConstantPointerNull>(V) || isa<UndefValue>(V);
}

inline bool IsNoopInstruction(const Instruction *I) {
  return isa<BitCastInst>(I) ||
    (isa<GetElementPtrInst>(I) &&
     cast<GetElementPtrInst>(I)->hasAllZeroIndices());
}

/// Test whether the given value is possible a retainable object pointer.
inline bool IsPotentialRetainableObjPtr(const Value *Op) {
  // Pointers to static or stack storage are not valid retainable object
  // pointers.
  if (isa<Constant>(Op) || isa<AllocaInst>(Op))
    return false;
  // Special arguments can not be a valid retainable object pointer.
  if (const Argument *Arg = dyn_cast<Argument>(Op))
    if (Arg->hasByValAttr() ||
        Arg->hasInAllocaAttr() ||
        Arg->hasNestAttr() ||
        Arg->hasStructRetAttr())
      return false;
  // Only consider values with pointer types.
  //
  // It seemes intuitive to exclude function pointer types as well, since
  // functions are never retainable object pointers, however clang occasionally
  // bitcasts retainable object pointers to function-pointer type temporarily.
  PointerType *Ty = dyn_cast<PointerType>(Op->getType());
  if (!Ty)
    return false;
  // Conservatively assume anything else is a potential retainable object
  // pointer.
  return true;
}

inline bool IsPotentialRetainableObjPtr(const Value *Op,
                                               AliasAnalysis &AA) {
  // First make the rudimentary check.
  if (!IsPotentialRetainableObjPtr(Op))
    return false;

  // Objects in constant memory are not reference-counted.
  if (AA.pointsToConstantMemory(Op))
    return false;

  // Pointers in constant memory are not pointing to reference-counted objects.
  if (const LoadInst *LI = dyn_cast<LoadInst>(Op))
    if (AA.pointsToConstantMemory(LI->getPointerOperand()))
      return false;

  // Otherwise assume the worst.
  return true;
}

/// Helper for GetARCInstKind. Determines what kind of construct CS
/// is.
inline ARCInstKind GetCallSiteClass(ImmutableCallSite CS) {
  for (ImmutableCallSite::arg_iterator I = CS.arg_begin(), E = CS.arg_end();
       I != E; ++I)
    if (IsPotentialRetainableObjPtr(*I))
      return CS.onlyReadsMemory() ? ARCInstKind::User : ARCInstKind::CallOrUser;

  return CS.onlyReadsMemory() ? ARCInstKind::None : ARCInstKind::Call;
}

/// Return true if this value refers to a distinct and identifiable
/// object.
///
/// This is similar to AliasAnalysis's isIdentifiedObject, except that it uses
/// special knowledge of ObjC conventions.
inline bool IsObjCIdentifiedObject(const Value *V) {
  // Assume that call results and arguments have their own "provenance".
  // Constants (including GlobalVariables) and Allocas are never
  // reference-counted.
  if (isa<CallInst>(V) || isa<InvokeInst>(V) ||
      isa<Argument>(V) || isa<Constant>(V) ||
      isa<AllocaInst>(V))
    return true;

  if (const LoadInst *LI = dyn_cast<LoadInst>(V)) {
    const Value *Pointer =
      GetRCIdentityRoot(LI->getPointerOperand());
    if (const GlobalVariable *GV = dyn_cast<GlobalVariable>(Pointer)) {
      // A constant pointer can't be pointing to an object on the heap. It may
      // be reference-counted, but it won't be deleted.
      if (GV->isConstant())
        return true;
      StringRef Name = GV->getName();
      // These special variables are known to hold values which are not
      // reference-counted pointers.
      if (Name.startswith("\01l_objc_msgSend_fixup_"))
        return true;

      StringRef Section = GV->getSection();
      if (Section.find("__message_refs") != StringRef::npos ||
          Section.find("__objc_classrefs") != StringRef::npos ||
          Section.find("__objc_superrefs") != StringRef::npos ||
          Section.find("__objc_methname") != StringRef::npos ||
          Section.find("__cstring") != StringRef::npos)
        return true;
    }
  }

  return false;
}

enum class ARCMDKindID {
  ImpreciseRelease,
  CopyOnEscape,
  NoObjCARCExceptions,
};

/// A cache of MDKinds used by various ARC optimizations.
class ARCMDKindCache {
  Module *M;

  /// The Metadata Kind for clang.imprecise_release metadata.
  llvm::Optional<unsigned> ImpreciseReleaseMDKind;

  /// The Metadata Kind for clang.arc.copy_on_escape metadata.
  llvm::Optional<unsigned> CopyOnEscapeMDKind;

  /// The Metadata Kind for clang.arc.no_objc_arc_exceptions metadata.
  llvm::Optional<unsigned> NoObjCARCExceptionsMDKind;

public:
  void init(Module *Mod) {
    M = Mod;
    ImpreciseReleaseMDKind = NoneType::None;
    CopyOnEscapeMDKind = NoneType::None;
    NoObjCARCExceptionsMDKind = NoneType::None;
  }

  unsigned get(ARCMDKindID ID) {
    switch (ID) {
    case ARCMDKindID::ImpreciseRelease:
      if (!ImpreciseReleaseMDKind)
        ImpreciseReleaseMDKind =
            M->getContext().getMDKindID("clang.imprecise_release");
      return *ImpreciseReleaseMDKind;
    case ARCMDKindID::CopyOnEscape:
      if (!CopyOnEscapeMDKind)
        CopyOnEscapeMDKind =
            M->getContext().getMDKindID("clang.arc.copy_on_escape");
      return *CopyOnEscapeMDKind;
    case ARCMDKindID::NoObjCARCExceptions:
      if (!NoObjCARCExceptionsMDKind)
        NoObjCARCExceptionsMDKind =
            M->getContext().getMDKindID("clang.arc.no_objc_arc_exceptions");
      return *NoObjCARCExceptionsMDKind;
    }
    llvm_unreachable("Covered switch isn't covered?!");
  }
};

} // end namespace objcarc
} // end namespace llvm

#endif