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/Support/ErrorHandling.h" 20 #include "llvm/Support/HashBuilder.h" 21 #include "llvm/Support/VersionTuple.h" 22 #include "llvm/TargetParser/Triple.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) { 104 switch (Arch) { 105 case llvm::Triple::arm: 106 case llvm::Triple::x86: 107 case llvm::Triple::x86_64: 108 return !(getVersion() >= VersionTuple(1, 6)); 109 case llvm::Triple::aarch64: 110 case llvm::Triple::mips64: 111 return !(getVersion() >= VersionTuple(1, 9)); 112 case llvm::Triple::riscv64: 113 return !(getVersion() >= VersionTuple(2, 2)); 114 default: 115 return true; 116 } 117 } else if ((getKind() == MacOSX) && isNonFragile() && 118 (getVersion() >= VersionTuple(10, 0)) && 119 (getVersion() < VersionTuple(10, 6))) 120 return Arch != llvm::Triple::x86_64; 121 // Except for deployment target of 10.5 or less, 122 // Mac runtimes use legacy dispatch everywhere now. 123 return true; 124 } 125 126 /// Is this runtime basically of the GNU family of runtimes? isGNUFamily()127 bool isGNUFamily() const { 128 switch (getKind()) { 129 case FragileMacOSX: 130 case MacOSX: 131 case iOS: 132 case WatchOS: 133 return false; 134 case GCC: 135 case GNUstep: 136 case ObjFW: 137 return true; 138 } 139 llvm_unreachable("bad kind"); 140 } 141 142 /// Is this runtime basically of the NeXT family of runtimes? isNeXTFamily()143 bool isNeXTFamily() const { 144 // For now, this is just the inverse of isGNUFamily(), but that's 145 // not inherently true. 146 return !isGNUFamily(); 147 } 148 149 /// Does this runtime allow ARC at all? allowsARC()150 bool allowsARC() const { 151 switch (getKind()) { 152 case FragileMacOSX: 153 // No stub library for the fragile runtime. 154 return getVersion() >= VersionTuple(10, 7); 155 case MacOSX: return true; 156 case iOS: return true; 157 case WatchOS: return true; 158 case GCC: return false; 159 case GNUstep: return true; 160 case ObjFW: return true; 161 } 162 llvm_unreachable("bad kind"); 163 } 164 165 /// Does this runtime natively provide the ARC entrypoints? 166 /// 167 /// ARC cannot be directly supported on a platform that does not provide 168 /// these entrypoints, although it may be supportable via a stub 169 /// library. hasNativeARC()170 bool hasNativeARC() const { 171 switch (getKind()) { 172 case FragileMacOSX: return getVersion() >= VersionTuple(10, 7); 173 case MacOSX: return getVersion() >= VersionTuple(10, 7); 174 case iOS: return getVersion() >= VersionTuple(5); 175 case WatchOS: return true; 176 177 case GCC: return false; 178 case GNUstep: return getVersion() >= VersionTuple(1, 6); 179 case ObjFW: return true; 180 } 181 llvm_unreachable("bad kind"); 182 } 183 184 /// Does this runtime provide ARC entrypoints that are likely to be faster 185 /// than an ordinary message send of the appropriate selector? 186 /// 187 /// The ARC entrypoints are guaranteed to be equivalent to just sending the 188 /// corresponding message. If the entrypoint is implemented naively as just a 189 /// message send, using it is a trade-off: it sacrifices a few cycles of 190 /// overhead to save a small amount of code. However, it's possible for 191 /// runtimes to detect and special-case classes that use "standard" 192 /// retain/release behavior; if that's dynamically a large proportion of all 193 /// retained objects, using the entrypoint will also be faster than using a 194 /// message send. 195 /// 196 /// When this method returns true, Clang will turn non-super message sends of 197 /// certain selectors into calls to the correspond entrypoint: 198 /// retain => objc_retain 199 /// release => objc_release 200 /// autorelease => objc_autorelease shouldUseARCFunctionsForRetainRelease()201 bool shouldUseARCFunctionsForRetainRelease() const { 202 switch (getKind()) { 203 case FragileMacOSX: 204 return false; 205 case MacOSX: 206 return getVersion() >= VersionTuple(10, 10); 207 case iOS: 208 return getVersion() >= VersionTuple(8); 209 case WatchOS: 210 return true; 211 case GCC: 212 return false; 213 case GNUstep: 214 // This could be enabled for all versions, except for the fact that the 215 // implementation of `objc_retain` and friends prior to 2.2 call [object 216 // retain] in their fall-back paths, which leads to infinite recursion if 217 // the runtime is built with this enabled. Since distributions typically 218 // build all Objective-C things with the same compiler version and flags, 219 // it's better to be conservative here. 220 return (getVersion() >= VersionTuple(2, 2)); 221 case ObjFW: 222 return false; 223 } 224 llvm_unreachable("bad kind"); 225 } 226 227 /// Does this runtime provide entrypoints that are likely to be faster 228 /// than an ordinary message send of the "alloc" selector? 229 /// 230 /// The "alloc" entrypoint is guaranteed to be equivalent to just sending the 231 /// corresponding message. If the entrypoint is implemented naively as just a 232 /// message send, using it is a trade-off: it sacrifices a few cycles of 233 /// overhead to save a small amount of code. However, it's possible for 234 /// runtimes to detect and special-case classes that use "standard" 235 /// alloc behavior; if that's dynamically a large proportion of all 236 /// objects, using the entrypoint will also be faster than using a message 237 /// send. 238 /// 239 /// When this method returns true, Clang will turn non-super message sends of 240 /// certain selectors into calls to the corresponding entrypoint: 241 /// alloc => objc_alloc 242 /// allocWithZone:nil => objc_allocWithZone shouldUseRuntimeFunctionsForAlloc()243 bool shouldUseRuntimeFunctionsForAlloc() const { 244 switch (getKind()) { 245 case FragileMacOSX: 246 return false; 247 case MacOSX: 248 return getVersion() >= VersionTuple(10, 10); 249 case iOS: 250 return getVersion() >= VersionTuple(8); 251 case WatchOS: 252 return true; 253 254 case GCC: 255 return false; 256 case GNUstep: 257 return getVersion() >= VersionTuple(2, 2); 258 case ObjFW: 259 return false; 260 } 261 llvm_unreachable("bad kind"); 262 } 263 264 /// Does this runtime provide the objc_alloc_init entrypoint? This can apply 265 /// the same optimization as objc_alloc, but also sends an -init message, 266 /// reducing code size on the caller. shouldUseRuntimeFunctionForCombinedAllocInit()267 bool shouldUseRuntimeFunctionForCombinedAllocInit() const { 268 switch (getKind()) { 269 case MacOSX: 270 return getVersion() >= VersionTuple(10, 14, 4); 271 case iOS: 272 return getVersion() >= VersionTuple(12, 2); 273 case WatchOS: 274 return getVersion() >= VersionTuple(5, 2); 275 case GNUstep: 276 return getVersion() >= VersionTuple(2, 2); 277 default: 278 return false; 279 } 280 } 281 282 /// Does this runtime supports optimized setter entrypoints? hasOptimizedSetter()283 bool hasOptimizedSetter() const { 284 switch (getKind()) { 285 case MacOSX: 286 return getVersion() >= VersionTuple(10, 8); 287 case iOS: 288 return (getVersion() >= VersionTuple(6)); 289 case WatchOS: 290 return true; 291 case GNUstep: 292 return getVersion() >= VersionTuple(1, 7); 293 default: 294 return false; 295 } 296 } 297 298 /// Does this runtime allow the use of __weak? allowsWeak()299 bool allowsWeak() const { 300 return hasNativeWeak(); 301 } 302 303 /// Does this runtime natively provide ARC-compliant 'weak' 304 /// entrypoints? hasNativeWeak()305 bool hasNativeWeak() const { 306 // Right now, this is always equivalent to whether the runtime 307 // natively supports ARC decision. 308 return hasNativeARC(); 309 } 310 311 /// Does this runtime directly support the subscripting methods? 312 /// 313 /// This is really a property of the library, not the runtime. hasSubscripting()314 bool hasSubscripting() const { 315 switch (getKind()) { 316 case FragileMacOSX: return false; 317 case MacOSX: return getVersion() >= VersionTuple(10, 11); 318 case iOS: return getVersion() >= VersionTuple(9); 319 case WatchOS: return true; 320 321 // This is really a lie, because some implementations and versions 322 // of the runtime do not support ARC. Probably -fgnu-runtime 323 // should imply a "maximal" runtime or something? 324 case GCC: return true; 325 case GNUstep: return true; 326 case ObjFW: return true; 327 } 328 llvm_unreachable("bad kind"); 329 } 330 331 /// Does this runtime allow sizeof or alignof on object types? allowsSizeofAlignof()332 bool allowsSizeofAlignof() const { 333 return isFragile(); 334 } 335 336 /// Does this runtime allow pointer arithmetic on objects? 337 /// 338 /// This covers +, -, ++, --, and (if isSubscriptPointerArithmetic() 339 /// yields true) []. allowsPointerArithmetic()340 bool allowsPointerArithmetic() const { 341 switch (getKind()) { 342 case FragileMacOSX: 343 case GCC: 344 return true; 345 case MacOSX: 346 case iOS: 347 case WatchOS: 348 case GNUstep: 349 case ObjFW: 350 return false; 351 } 352 llvm_unreachable("bad kind"); 353 } 354 355 /// Is subscripting pointer arithmetic? isSubscriptPointerArithmetic()356 bool isSubscriptPointerArithmetic() const { 357 return allowsPointerArithmetic(); 358 } 359 360 /// Does this runtime provide an objc_terminate function? 361 /// 362 /// This is used in handlers for exceptions during the unwind process; 363 /// without it, abort() must be used in pure ObjC files. hasTerminate()364 bool hasTerminate() const { 365 switch (getKind()) { 366 case FragileMacOSX: return getVersion() >= VersionTuple(10, 8); 367 case MacOSX: return getVersion() >= VersionTuple(10, 8); 368 case iOS: return getVersion() >= VersionTuple(5); 369 case WatchOS: return true; 370 case GCC: return false; 371 case GNUstep: return false; 372 case ObjFW: return false; 373 } 374 llvm_unreachable("bad kind"); 375 } 376 377 /// Does this runtime support weakly importing classes? hasWeakClassImport()378 bool hasWeakClassImport() const { 379 switch (getKind()) { 380 case MacOSX: return true; 381 case iOS: return true; 382 case WatchOS: return true; 383 case FragileMacOSX: return false; 384 case GCC: return true; 385 case GNUstep: return true; 386 case ObjFW: return true; 387 } 388 llvm_unreachable("bad kind"); 389 } 390 391 /// Does this runtime use zero-cost exceptions? hasUnwindExceptions()392 bool hasUnwindExceptions() const { 393 switch (getKind()) { 394 case MacOSX: return true; 395 case iOS: return true; 396 case WatchOS: return true; 397 case FragileMacOSX: return false; 398 case GCC: return true; 399 case GNUstep: return true; 400 case ObjFW: return true; 401 } 402 llvm_unreachable("bad kind"); 403 } 404 hasAtomicCopyHelper()405 bool hasAtomicCopyHelper() const { 406 switch (getKind()) { 407 case FragileMacOSX: 408 case MacOSX: 409 case iOS: 410 case WatchOS: 411 return true; 412 case GNUstep: 413 return getVersion() >= VersionTuple(1, 7); 414 default: return false; 415 } 416 } 417 418 /// Is objc_unsafeClaimAutoreleasedReturnValue available? hasARCUnsafeClaimAutoreleasedReturnValue()419 bool hasARCUnsafeClaimAutoreleasedReturnValue() const { 420 switch (getKind()) { 421 case MacOSX: 422 case FragileMacOSX: 423 return getVersion() >= VersionTuple(10, 11); 424 case iOS: 425 return getVersion() >= VersionTuple(9); 426 case WatchOS: 427 return getVersion() >= VersionTuple(2); 428 case GNUstep: 429 return false; 430 default: 431 return false; 432 } 433 } 434 435 /// Are the empty collection symbols available? hasEmptyCollections()436 bool hasEmptyCollections() const { 437 switch (getKind()) { 438 default: 439 return false; 440 case MacOSX: 441 return getVersion() >= VersionTuple(10, 11); 442 case iOS: 443 return getVersion() >= VersionTuple(9); 444 case WatchOS: 445 return getVersion() >= VersionTuple(2); 446 } 447 } 448 449 /// Returns true if this Objective-C runtime supports Objective-C class 450 /// stubs. allowsClassStubs()451 bool allowsClassStubs() const { 452 switch (getKind()) { 453 case FragileMacOSX: 454 case GCC: 455 case GNUstep: 456 case ObjFW: 457 return false; 458 case MacOSX: 459 case iOS: 460 case WatchOS: 461 return true; 462 } 463 llvm_unreachable("bad kind"); 464 } 465 466 /// Does this runtime supports direct dispatch allowsDirectDispatch()467 bool allowsDirectDispatch() const { 468 switch (getKind()) { 469 case FragileMacOSX: return false; 470 case MacOSX: return true; 471 case iOS: return true; 472 case WatchOS: return true; 473 case GCC: return false; 474 case GNUstep: 475 return (getVersion() >= VersionTuple(2, 2)); 476 case ObjFW: return false; 477 } 478 llvm_unreachable("bad kind"); 479 } 480 481 /// Try to parse an Objective-C runtime specification from the given 482 /// string. 483 /// 484 /// \return true on error. 485 bool tryParse(StringRef input); 486 487 std::string getAsString() const; 488 489 friend bool operator==(const ObjCRuntime &left, const ObjCRuntime &right) { 490 return left.getKind() == right.getKind() && 491 left.getVersion() == right.getVersion(); 492 } 493 494 friend bool operator!=(const ObjCRuntime &left, const ObjCRuntime &right) { 495 return !(left == right); 496 } 497 hash_value(const ObjCRuntime & OCR)498 friend llvm::hash_code hash_value(const ObjCRuntime &OCR) { 499 return llvm::hash_combine(OCR.getKind(), OCR.getVersion()); 500 } 501 502 template <typename HasherT, llvm::endianness Endianness> addHash(llvm::HashBuilder<HasherT,Endianness> & HBuilder,const ObjCRuntime & OCR)503 friend void addHash(llvm::HashBuilder<HasherT, Endianness> &HBuilder, 504 const ObjCRuntime &OCR) { 505 HBuilder.add(OCR.getKind(), OCR.getVersion()); 506 } 507 }; 508 509 raw_ostream &operator<<(raw_ostream &out, const ObjCRuntime &value); 510 511 } // namespace clang 512 513 #endif // LLVM_CLANG_BASIC_OBJCRUNTIME_H 514