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; |
69 | ObjCRuntime(Kind kind, const VersionTuple &version) |
70 | : TheKind(kind), Version(version) {} |
71 | |
72 | void set(Kind kind, VersionTuple version) { |
73 | TheKind = kind; |
74 | Version = version; |
75 | } |
76 | |
77 | Kind getKind() const { return TheKind; } |
78 | const VersionTuple &getVersion() const { return Version; } |
79 | |
80 | /// Does this runtime follow the set of implied behaviors for a |
81 | /// "non-fragile" ABI? |
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? |
97 | bool isFragile() const { return !isNonFragile(); } |
98 | |
99 | /// The default dispatch mechanism to use for the specified architecture |
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? |
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? |
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? |
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. |
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 |
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 |
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. |
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? |
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? |
299 | bool allowsWeak() const { |
300 | return hasNativeWeak(); |
301 | } |
302 | |
303 | /// Does this runtime natively provide ARC-compliant 'weak' |
304 | /// entrypoints? |
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. |
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? |
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) []. |
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? |
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. |
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? |
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? |
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 | |
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? |
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? |
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. |
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 |
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 | |
498 | friend llvm::hash_code hash_value(const ObjCRuntime &OCR) { |
499 | return llvm::hash_combine(args: OCR.getKind(), args: OCR.getVersion()); |
500 | } |
501 | |
502 | template <typename HasherT, llvm::endianness Endianness> |
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 | |