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   /// Does this runtime supports direct dispatch
allowsDirectDispatch()450   bool allowsDirectDispatch() const {
451     switch (getKind()) {
452     case FragileMacOSX: return false;
453     case MacOSX: return true;
454     case iOS: return true;
455     case WatchOS: return true;
456     case GCC: return false;
457     case GNUstep: return false;
458     case ObjFW: return false;
459     }
460     llvm_unreachable("bad kind");
461   }
462 
463   /// Try to parse an Objective-C runtime specification from the given
464   /// string.
465   ///
466   /// \return true on error.
467   bool tryParse(StringRef input);
468 
469   std::string getAsString() const;
470 
471   friend bool operator==(const ObjCRuntime &left, const ObjCRuntime &right) {
472     return left.getKind() == right.getKind() &&
473            left.getVersion() == right.getVersion();
474   }
475 
476   friend bool operator!=(const ObjCRuntime &left, const ObjCRuntime &right) {
477     return !(left == right);
478   }
479 
hash_value(const ObjCRuntime & OCR)480   friend llvm::hash_code hash_value(const ObjCRuntime &OCR) {
481     return llvm::hash_combine(OCR.getKind(), OCR.getVersion());
482   }
483 };
484 
485 raw_ostream &operator<<(raw_ostream &out, const ObjCRuntime &value);
486 
487 } // namespace clang
488 
489 #endif // LLVM_CLANG_BASIC_OBJCRUNTIME_H
490