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/HashBuilder.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 provide the objc_alloc_init entrypoint? This can apply 251 /// the same optimization as objc_alloc, but also sends an -init message, 252 /// reducing code size on the caller. shouldUseRuntimeFunctionForCombinedAllocInit()253 bool shouldUseRuntimeFunctionForCombinedAllocInit() const { 254 switch (getKind()) { 255 case MacOSX: 256 return getVersion() >= VersionTuple(10, 14, 4); 257 case iOS: 258 return getVersion() >= VersionTuple(12, 2); 259 case WatchOS: 260 return getVersion() >= VersionTuple(5, 2); 261 default: 262 return false; 263 } 264 } 265 266 /// Does this runtime supports optimized setter entrypoints? hasOptimizedSetter()267 bool hasOptimizedSetter() const { 268 switch (getKind()) { 269 case MacOSX: 270 return getVersion() >= VersionTuple(10, 8); 271 case iOS: 272 return (getVersion() >= VersionTuple(6)); 273 case WatchOS: 274 return true; 275 case GNUstep: 276 return getVersion() >= VersionTuple(1, 7); 277 default: 278 return false; 279 } 280 } 281 282 /// Does this runtime allow the use of __weak? allowsWeak()283 bool allowsWeak() const { 284 return hasNativeWeak(); 285 } 286 287 /// Does this runtime natively provide ARC-compliant 'weak' 288 /// entrypoints? hasNativeWeak()289 bool hasNativeWeak() const { 290 // Right now, this is always equivalent to whether the runtime 291 // natively supports ARC decision. 292 return hasNativeARC(); 293 } 294 295 /// Does this runtime directly support the subscripting methods? 296 /// 297 /// This is really a property of the library, not the runtime. hasSubscripting()298 bool hasSubscripting() const { 299 switch (getKind()) { 300 case FragileMacOSX: return false; 301 case MacOSX: return getVersion() >= VersionTuple(10, 11); 302 case iOS: return getVersion() >= VersionTuple(9); 303 case WatchOS: return true; 304 305 // This is really a lie, because some implementations and versions 306 // of the runtime do not support ARC. Probably -fgnu-runtime 307 // should imply a "maximal" runtime or something? 308 case GCC: return true; 309 case GNUstep: return true; 310 case ObjFW: return true; 311 } 312 llvm_unreachable("bad kind"); 313 } 314 315 /// Does this runtime allow sizeof or alignof on object types? allowsSizeofAlignof()316 bool allowsSizeofAlignof() const { 317 return isFragile(); 318 } 319 320 /// Does this runtime allow pointer arithmetic on objects? 321 /// 322 /// This covers +, -, ++, --, and (if isSubscriptPointerArithmetic() 323 /// yields true) []. allowsPointerArithmetic()324 bool allowsPointerArithmetic() const { 325 switch (getKind()) { 326 case FragileMacOSX: 327 case GCC: 328 return true; 329 case MacOSX: 330 case iOS: 331 case WatchOS: 332 case GNUstep: 333 case ObjFW: 334 return false; 335 } 336 llvm_unreachable("bad kind"); 337 } 338 339 /// Is subscripting pointer arithmetic? isSubscriptPointerArithmetic()340 bool isSubscriptPointerArithmetic() const { 341 return allowsPointerArithmetic(); 342 } 343 344 /// Does this runtime provide an objc_terminate function? 345 /// 346 /// This is used in handlers for exceptions during the unwind process; 347 /// without it, abort() must be used in pure ObjC files. hasTerminate()348 bool hasTerminate() const { 349 switch (getKind()) { 350 case FragileMacOSX: return getVersion() >= VersionTuple(10, 8); 351 case MacOSX: return getVersion() >= VersionTuple(10, 8); 352 case iOS: return getVersion() >= VersionTuple(5); 353 case WatchOS: return true; 354 case GCC: return false; 355 case GNUstep: return false; 356 case ObjFW: return false; 357 } 358 llvm_unreachable("bad kind"); 359 } 360 361 /// Does this runtime support weakly importing classes? hasWeakClassImport()362 bool hasWeakClassImport() const { 363 switch (getKind()) { 364 case MacOSX: return true; 365 case iOS: return true; 366 case WatchOS: return true; 367 case FragileMacOSX: return false; 368 case GCC: return true; 369 case GNUstep: return true; 370 case ObjFW: return true; 371 } 372 llvm_unreachable("bad kind"); 373 } 374 375 /// Does this runtime use zero-cost exceptions? hasUnwindExceptions()376 bool hasUnwindExceptions() const { 377 switch (getKind()) { 378 case MacOSX: return true; 379 case iOS: return true; 380 case WatchOS: return true; 381 case FragileMacOSX: return false; 382 case GCC: return true; 383 case GNUstep: return true; 384 case ObjFW: return true; 385 } 386 llvm_unreachable("bad kind"); 387 } 388 hasAtomicCopyHelper()389 bool hasAtomicCopyHelper() const { 390 switch (getKind()) { 391 case FragileMacOSX: 392 case MacOSX: 393 case iOS: 394 case WatchOS: 395 return true; 396 case GNUstep: 397 return getVersion() >= VersionTuple(1, 7); 398 default: return false; 399 } 400 } 401 402 /// Is objc_unsafeClaimAutoreleasedReturnValue available? hasARCUnsafeClaimAutoreleasedReturnValue()403 bool hasARCUnsafeClaimAutoreleasedReturnValue() const { 404 switch (getKind()) { 405 case MacOSX: 406 case FragileMacOSX: 407 return getVersion() >= VersionTuple(10, 11); 408 case iOS: 409 return getVersion() >= VersionTuple(9); 410 case WatchOS: 411 return getVersion() >= VersionTuple(2); 412 case GNUstep: 413 return false; 414 default: 415 return false; 416 } 417 } 418 419 /// Are the empty collection symbols available? hasEmptyCollections()420 bool hasEmptyCollections() const { 421 switch (getKind()) { 422 default: 423 return false; 424 case MacOSX: 425 return getVersion() >= VersionTuple(10, 11); 426 case iOS: 427 return getVersion() >= VersionTuple(9); 428 case WatchOS: 429 return getVersion() >= VersionTuple(2); 430 } 431 } 432 433 /// Returns true if this Objective-C runtime supports Objective-C class 434 /// stubs. allowsClassStubs()435 bool allowsClassStubs() const { 436 switch (getKind()) { 437 case FragileMacOSX: 438 case GCC: 439 case GNUstep: 440 case ObjFW: 441 return false; 442 case MacOSX: 443 case iOS: 444 case WatchOS: 445 return true; 446 } 447 llvm_unreachable("bad kind"); 448 } 449 450 /// Does this runtime supports direct dispatch allowsDirectDispatch()451 bool allowsDirectDispatch() const { 452 switch (getKind()) { 453 case FragileMacOSX: return false; 454 case MacOSX: return true; 455 case iOS: return true; 456 case WatchOS: return true; 457 case GCC: return false; 458 case GNUstep: return false; 459 case ObjFW: return false; 460 } 461 llvm_unreachable("bad kind"); 462 } 463 464 /// Try to parse an Objective-C runtime specification from the given 465 /// string. 466 /// 467 /// \return true on error. 468 bool tryParse(StringRef input); 469 470 std::string getAsString() const; 471 472 friend bool operator==(const ObjCRuntime &left, const ObjCRuntime &right) { 473 return left.getKind() == right.getKind() && 474 left.getVersion() == right.getVersion(); 475 } 476 477 friend bool operator!=(const ObjCRuntime &left, const ObjCRuntime &right) { 478 return !(left == right); 479 } 480 hash_value(const ObjCRuntime & OCR)481 friend llvm::hash_code hash_value(const ObjCRuntime &OCR) { 482 return llvm::hash_combine(OCR.getKind(), OCR.getVersion()); 483 } 484 485 template <typename HasherT, llvm::support::endianness Endianness> addHash(llvm::HashBuilderImpl<HasherT,Endianness> & HBuilder,const ObjCRuntime & OCR)486 friend void addHash(llvm::HashBuilderImpl<HasherT, Endianness> &HBuilder, 487 const ObjCRuntime &OCR) { 488 HBuilder.add(OCR.getKind(), OCR.getVersion()); 489 } 490 }; 491 492 raw_ostream &operator<<(raw_ostream &out, const ObjCRuntime &value); 493 494 } // namespace clang 495 496 #endif // LLVM_CLANG_BASIC_OBJCRUNTIME_H 497