Home | History | Annotate | Line # | Download | only in Basic
      1 //===- ObjCRuntime.h - Objective-C Runtime Configuration --------*- C++ -*-===//
      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 /// \file
     10 /// Defines types useful for describing an Objective-C runtime.
     11 //
     12 //===----------------------------------------------------------------------===//
     13 
     14 #ifndef LLVM_CLANG_BASIC_OBJCRUNTIME_H
     15 #define LLVM_CLANG_BASIC_OBJCRUNTIME_H
     16 
     17 #include "clang/Basic/LLVM.h"
     18 #include "llvm/ADT/StringRef.h"
     19 #include "llvm/ADT/Triple.h"
     20 #include "llvm/Support/ErrorHandling.h"
     21 #include "llvm/Support/VersionTuple.h"
     22 #include <string>
     23 
     24 namespace clang {
     25 
     26 /// The basic abstraction for the target Objective-C runtime.
     27 class ObjCRuntime {
     28 public:
     29   /// The basic Objective-C runtimes that we know about.
     30   enum Kind {
     31     /// 'macosx' is the Apple-provided NeXT-derived runtime on Mac OS
     32     /// X platforms that use the non-fragile ABI; the version is a
     33     /// release of that OS.
     34     MacOSX,
     35 
     36     /// 'macosx-fragile' is the Apple-provided NeXT-derived runtime on
     37     /// Mac OS X platforms that use the fragile ABI; the version is a
     38     /// release of that OS.
     39     FragileMacOSX,
     40 
     41     /// 'ios' is the Apple-provided NeXT-derived runtime on iOS or the iOS
     42     /// simulator;  it is always non-fragile.  The version is a release
     43     /// version of iOS.
     44     iOS,
     45 
     46     /// 'watchos' is a variant of iOS for Apple's watchOS. The version
     47     /// is a release version of watchOS.
     48     WatchOS,
     49 
     50     /// 'gcc' is the Objective-C runtime shipped with GCC, implementing a
     51     /// fragile Objective-C ABI
     52     GCC,
     53 
     54     /// 'gnustep' is the modern non-fragile GNUstep runtime.
     55     GNUstep,
     56 
     57     /// 'objfw' is the Objective-C runtime included in ObjFW
     58     ObjFW
     59   };
     60 
     61 private:
     62   Kind TheKind = MacOSX;
     63   VersionTuple Version;
     64 
     65 public:
     66   /// A bogus initialization of the runtime.
     67   ObjCRuntime() = default;
     68   ObjCRuntime(Kind kind, const VersionTuple &version)
     69       : TheKind(kind), Version(version) {}
     70 
     71   void set(Kind kind, VersionTuple version) {
     72     TheKind = kind;
     73     Version = version;
     74   }
     75 
     76   Kind getKind() const { return TheKind; }
     77   const VersionTuple &getVersion() const { return Version; }
     78 
     79   /// Does this runtime follow the set of implied behaviors for a
     80   /// "non-fragile" ABI?
     81   bool isNonFragile() const {
     82     switch (getKind()) {
     83     case FragileMacOSX: return false;
     84     case GCC: return false;
     85     case MacOSX: return true;
     86     case GNUstep: return true;
     87     case ObjFW: return true;
     88     case iOS: return true;
     89     case WatchOS: return true;
     90     }
     91     llvm_unreachable("bad kind");
     92   }
     93 
     94   /// The inverse of isNonFragile():  does this runtime follow the set of
     95   /// implied behaviors for a "fragile" ABI?
     96   bool isFragile() const { return !isNonFragile(); }
     97 
     98   /// The default dispatch mechanism to use for the specified architecture
     99   bool isLegacyDispatchDefaultForArch(llvm::Triple::ArchType Arch) {
    100     // The GNUstep runtime uses a newer dispatch method by default from
    101     // version 1.6 onwards
    102     if (getKind() == GNUstep && getVersion() >= VersionTuple(1, 6)) {
    103       if (Arch == llvm::Triple::arm ||
    104           Arch == llvm::Triple::x86 ||
    105           Arch == llvm::Triple::x86_64)
    106         return false;
    107     }
    108     else if ((getKind() ==  MacOSX) && isNonFragile() &&
    109              (getVersion() >= VersionTuple(10, 0)) &&
    110              (getVersion() < VersionTuple(10, 6)))
    111         return Arch != llvm::Triple::x86_64;
    112     // Except for deployment target of 10.5 or less,
    113     // Mac runtimes use legacy dispatch everywhere now.
    114     return true;
    115   }
    116 
    117   /// Is this runtime basically of the GNU family of runtimes?
    118   bool isGNUFamily() const {
    119     switch (getKind()) {
    120     case FragileMacOSX:
    121     case MacOSX:
    122     case iOS:
    123     case WatchOS:
    124       return false;
    125     case GCC:
    126     case GNUstep:
    127     case ObjFW:
    128       return true;
    129     }
    130     llvm_unreachable("bad kind");
    131   }
    132 
    133   /// Is this runtime basically of the NeXT family of runtimes?
    134   bool isNeXTFamily() const {
    135     // For now, this is just the inverse of isGNUFamily(), but that's
    136     // not inherently true.
    137     return !isGNUFamily();
    138   }
    139 
    140   /// Does this runtime allow ARC at all?
    141   bool allowsARC() const {
    142     switch (getKind()) {
    143     case FragileMacOSX:
    144       // No stub library for the fragile runtime.
    145       return getVersion() >= VersionTuple(10, 7);
    146     case MacOSX: return true;
    147     case iOS: return true;
    148     case WatchOS: return true;
    149     case GCC: return false;
    150     case GNUstep: return true;
    151     case ObjFW: return true;
    152     }
    153     llvm_unreachable("bad kind");
    154   }
    155 
    156   /// Does this runtime natively provide the ARC entrypoints?
    157   ///
    158   /// ARC cannot be directly supported on a platform that does not provide
    159   /// these entrypoints, although it may be supportable via a stub
    160   /// library.
    161   bool hasNativeARC() const {
    162     switch (getKind()) {
    163     case FragileMacOSX: return getVersion() >= VersionTuple(10, 7);
    164     case MacOSX: return getVersion() >= VersionTuple(10, 7);
    165     case iOS: return getVersion() >= VersionTuple(5);
    166     case WatchOS: return true;
    167 
    168     case GCC: return false;
    169     case GNUstep: return getVersion() >= VersionTuple(1, 6);
    170     case ObjFW: return true;
    171     }
    172     llvm_unreachable("bad kind");
    173   }
    174 
    175   /// Does this runtime provide ARC entrypoints that are likely to be faster
    176   /// than an ordinary message send of the appropriate selector?
    177   ///
    178   /// The ARC entrypoints are guaranteed to be equivalent to just sending the
    179   /// corresponding message.  If the entrypoint is implemented naively as just a
    180   /// message send, using it is a trade-off: it sacrifices a few cycles of
    181   /// overhead to save a small amount of code.  However, it's possible for
    182   /// runtimes to detect and special-case classes that use "standard"
    183   /// retain/release behavior; if that's dynamically a large proportion of all
    184   /// retained objects, using the entrypoint will also be faster than using a
    185   /// message send.
    186   ///
    187   /// When this method returns true, Clang will turn non-super message sends of
    188   /// certain selectors into calls to the correspond entrypoint:
    189   ///   retain => objc_retain
    190   ///   release => objc_release
    191   ///   autorelease => objc_autorelease
    192   bool shouldUseARCFunctionsForRetainRelease() const {
    193     switch (getKind()) {
    194     case FragileMacOSX:
    195       return false;
    196     case MacOSX:
    197       return getVersion() >= VersionTuple(10, 10);
    198     case iOS:
    199       return getVersion() >= VersionTuple(8);
    200     case WatchOS:
    201       return true;
    202     case GCC:
    203       return false;
    204     case GNUstep:
    205       return false;
    206     case ObjFW:
    207       return false;
    208     }
    209     llvm_unreachable("bad kind");
    210   }
    211 
    212   /// Does this runtime provide entrypoints that are likely to be faster
    213   /// than an ordinary message send of the "alloc" selector?
    214   ///
    215   /// The "alloc" entrypoint is guaranteed to be equivalent to just sending the
    216   /// corresponding message.  If the entrypoint is implemented naively as just a
    217   /// message send, using it is a trade-off: it sacrifices a few cycles of
    218   /// overhead to save a small amount of code.  However, it's possible for
    219   /// runtimes to detect and special-case classes that use "standard"
    220   /// alloc behavior; if that's dynamically a large proportion of all
    221   /// objects, using the entrypoint will also be faster than using a message
    222   /// send.
    223   ///
    224   /// When this method returns true, Clang will turn non-super message sends of
    225   /// certain selectors into calls to the corresponding entrypoint:
    226   ///   alloc => objc_alloc
    227   ///   allocWithZone:nil => objc_allocWithZone
    228   bool shouldUseRuntimeFunctionsForAlloc() const {
    229     switch (getKind()) {
    230     case FragileMacOSX:
    231       return false;
    232     case MacOSX:
    233       return getVersion() >= VersionTuple(10, 10);
    234     case iOS:
    235       return getVersion() >= VersionTuple(8);
    236     case WatchOS:
    237       return true;
    238 
    239     case GCC:
    240       return false;
    241     case GNUstep:
    242       return false;
    243     case ObjFW:
    244       return false;
    245     }
    246     llvm_unreachable("bad kind");
    247   }
    248 
    249   /// Does this runtime provide the objc_alloc_init entrypoint? This can apply
    250   /// the same optimization as objc_alloc, but also sends an -init message,
    251   /// reducing code size on the caller.
    252   bool shouldUseRuntimeFunctionForCombinedAllocInit() const {
    253     switch (getKind()) {
    254     case MacOSX:
    255       return getVersion() >= VersionTuple(10, 14, 4);
    256     case iOS:
    257       return getVersion() >= VersionTuple(12, 2);
    258     case WatchOS:
    259       return getVersion() >= VersionTuple(5, 2);
    260     default:
    261       return false;
    262     }
    263   }
    264 
    265   /// Does this runtime supports optimized setter entrypoints?
    266   bool hasOptimizedSetter() const {
    267     switch (getKind()) {
    268       case MacOSX:
    269         return getVersion() >= VersionTuple(10, 8);
    270       case iOS:
    271         return (getVersion() >= VersionTuple(6));
    272       case WatchOS:
    273         return true;
    274       case GNUstep:
    275         return getVersion() >= VersionTuple(1, 7);
    276       default:
    277         return false;
    278     }
    279   }
    280 
    281   /// Does this runtime allow the use of __weak?
    282   bool allowsWeak() const {
    283     return hasNativeWeak();
    284   }
    285 
    286   /// Does this runtime natively provide ARC-compliant 'weak'
    287   /// entrypoints?
    288   bool hasNativeWeak() const {
    289     // Right now, this is always equivalent to whether the runtime
    290     // natively supports ARC decision.
    291     return hasNativeARC();
    292   }
    293 
    294   /// Does this runtime directly support the subscripting methods?
    295   ///
    296   /// This is really a property of the library, not the runtime.
    297   bool hasSubscripting() const {
    298     switch (getKind()) {
    299     case FragileMacOSX: return false;
    300     case MacOSX: return getVersion() >= VersionTuple(10, 11);
    301     case iOS: return getVersion() >= VersionTuple(9);
    302     case WatchOS: return true;
    303 
    304     // This is really a lie, because some implementations and versions
    305     // of the runtime do not support ARC.  Probably -fgnu-runtime
    306     // should imply a "maximal" runtime or something?
    307     case GCC: return true;
    308     case GNUstep: return true;
    309     case ObjFW: return true;
    310     }
    311     llvm_unreachable("bad kind");
    312   }
    313 
    314   /// Does this runtime allow sizeof or alignof on object types?
    315   bool allowsSizeofAlignof() const {
    316     return isFragile();
    317   }
    318 
    319   /// Does this runtime allow pointer arithmetic on objects?
    320   ///
    321   /// This covers +, -, ++, --, and (if isSubscriptPointerArithmetic()
    322   /// yields true) [].
    323   bool allowsPointerArithmetic() const {
    324     switch (getKind()) {
    325     case FragileMacOSX:
    326     case GCC:
    327       return true;
    328     case MacOSX:
    329     case iOS:
    330     case WatchOS:
    331     case GNUstep:
    332     case ObjFW:
    333       return false;
    334     }
    335     llvm_unreachable("bad kind");
    336   }
    337 
    338   /// Is subscripting pointer arithmetic?
    339   bool isSubscriptPointerArithmetic() const {
    340     return allowsPointerArithmetic();
    341   }
    342 
    343   /// Does this runtime provide an objc_terminate function?
    344   ///
    345   /// This is used in handlers for exceptions during the unwind process;
    346   /// without it, abort() must be used in pure ObjC files.
    347   bool hasTerminate() const {
    348     switch (getKind()) {
    349     case FragileMacOSX: return getVersion() >= VersionTuple(10, 8);
    350     case MacOSX: return getVersion() >= VersionTuple(10, 8);
    351     case iOS: return getVersion() >= VersionTuple(5);
    352     case WatchOS: return true;
    353     case GCC: return false;
    354     case GNUstep: return false;
    355     case ObjFW: return false;
    356     }
    357     llvm_unreachable("bad kind");
    358   }
    359 
    360   /// Does this runtime support weakly importing classes?
    361   bool hasWeakClassImport() const {
    362     switch (getKind()) {
    363     case MacOSX: return true;
    364     case iOS: return true;
    365     case WatchOS: return true;
    366     case FragileMacOSX: return false;
    367     case GCC: return true;
    368     case GNUstep: return true;
    369     case ObjFW: return true;
    370     }
    371     llvm_unreachable("bad kind");
    372   }
    373 
    374   /// Does this runtime use zero-cost exceptions?
    375   bool hasUnwindExceptions() const {
    376     switch (getKind()) {
    377     case MacOSX: return true;
    378     case iOS: return true;
    379     case WatchOS: return true;
    380     case FragileMacOSX: return false;
    381     case GCC: return true;
    382     case GNUstep: return true;
    383     case ObjFW: return true;
    384     }
    385     llvm_unreachable("bad kind");
    386   }
    387 
    388   bool hasAtomicCopyHelper() const {
    389     switch (getKind()) {
    390     case FragileMacOSX:
    391     case MacOSX:
    392     case iOS:
    393     case WatchOS:
    394       return true;
    395     case GNUstep:
    396       return getVersion() >= VersionTuple(1, 7);
    397     default: return false;
    398     }
    399   }
    400 
    401   /// Is objc_unsafeClaimAutoreleasedReturnValue available?
    402   bool hasARCUnsafeClaimAutoreleasedReturnValue() const {
    403     switch (getKind()) {
    404     case MacOSX:
    405     case FragileMacOSX:
    406       return getVersion() >= VersionTuple(10, 11);
    407     case iOS:
    408       return getVersion() >= VersionTuple(9);
    409     case WatchOS:
    410       return getVersion() >= VersionTuple(2);
    411     case GNUstep:
    412       return false;
    413     default:
    414       return false;
    415     }
    416   }
    417 
    418   /// Are the empty collection symbols available?
    419   bool hasEmptyCollections() const {
    420     switch (getKind()) {
    421     default:
    422       return false;
    423     case MacOSX:
    424       return getVersion() >= VersionTuple(10, 11);
    425     case iOS:
    426       return getVersion() >= VersionTuple(9);
    427     case WatchOS:
    428       return getVersion() >= VersionTuple(2);
    429     }
    430   }
    431 
    432   /// Returns true if this Objective-C runtime supports Objective-C class
    433   /// stubs.
    434   bool allowsClassStubs() const {
    435     switch (getKind()) {
    436     case FragileMacOSX:
    437     case GCC:
    438     case GNUstep:
    439     case ObjFW:
    440       return false;
    441     case MacOSX:
    442     case iOS:
    443     case WatchOS:
    444       return true;
    445     }
    446     llvm_unreachable("bad kind");
    447   }
    448 
    449   /// Does this runtime supports direct dispatch
    450   bool allowsDirectDispatch() const {
    451     switch (getKind()) {
    452     case FragileMacOSX: return false;
    453     case MacOSX: return true;
    454     case iOS: return true;
    455     case WatchOS: return true;
    456     case GCC: return false;
    457     case GNUstep: return false;
    458     case ObjFW: return false;
    459     }
    460     llvm_unreachable("bad kind");
    461   }
    462 
    463   /// Try to parse an Objective-C runtime specification from the given
    464   /// string.
    465   ///
    466   /// \return true on error.
    467   bool tryParse(StringRef input);
    468 
    469   std::string getAsString() const;
    470 
    471   friend bool operator==(const ObjCRuntime &left, const ObjCRuntime &right) {
    472     return left.getKind() == right.getKind() &&
    473            left.getVersion() == right.getVersion();
    474   }
    475 
    476   friend bool operator!=(const ObjCRuntime &left, const ObjCRuntime &right) {
    477     return !(left == right);
    478   }
    479 
    480   friend llvm::hash_code hash_value(const ObjCRuntime &OCR) {
    481     return llvm::hash_combine(OCR.getKind(), OCR.getVersion());
    482   }
    483 };
    484 
    485 raw_ostream &operator<<(raw_ostream &out, const ObjCRuntime &value);
    486 
    487 } // namespace clang
    488 
    489 #endif // LLVM_CLANG_BASIC_OBJCRUNTIME_H
    490