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