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