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