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