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 } 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? isGNUFamily()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? isNeXTFamily()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? allowsARC()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. hasNativeARC()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 shouldUseARCFunctionsForRetainRelease()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 shouldUseRuntimeFunctionsForAlloc()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. shouldUseRuntimeFunctionForCombinedAllocInit()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? hasOptimizedSetter()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? allowsWeak()282 bool allowsWeak() const { 283 return hasNativeWeak(); 284 } 285 286 /// Does this runtime natively provide ARC-compliant 'weak' 287 /// entrypoints? hasNativeWeak()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. hasSubscripting()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? allowsSizeofAlignof()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) []. allowsPointerArithmetic()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? isSubscriptPointerArithmetic()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. hasTerminate()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? hasWeakClassImport()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? hasUnwindExceptions()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 hasAtomicCopyHelper()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? hasARCUnsafeClaimAutoreleasedReturnValue()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? hasEmptyCollections()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. allowsClassStubs()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 /// Try to parse an Objective-C runtime specification from the given 450 /// string. 451 /// 452 /// \return true on error. 453 bool tryParse(StringRef input); 454 455 std::string getAsString() const; 456 457 friend bool operator==(const ObjCRuntime &left, const ObjCRuntime &right) { 458 return left.getKind() == right.getKind() && 459 left.getVersion() == right.getVersion(); 460 } 461 462 friend bool operator!=(const ObjCRuntime &left, const ObjCRuntime &right) { 463 return !(left == right); 464 } 465 }; 466 467 raw_ostream &operator<<(raw_ostream &out, const ObjCRuntime &value); 468 469 } // namespace clang 470 471 #endif // LLVM_CLANG_BASIC_OBJCRUNTIME_H 472