Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
14 | pmbaty | 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/HashBuilder.h" |
||
22 | #include "llvm/Support/VersionTuple.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 && getVersion() >= VersionTuple(1, 6)) { |
||
104 | if (Arch == llvm::Triple::arm || |
||
105 | Arch == llvm::Triple::x86 || |
||
106 | Arch == llvm::Triple::x86_64) |
||
107 | return false; |
||
108 | } |
||
109 | else if ((getKind() == MacOSX) && isNonFragile() && |
||
110 | (getVersion() >= VersionTuple(10, 0)) && |
||
111 | (getVersion() < VersionTuple(10, 6))) |
||
112 | return Arch != llvm::Triple::x86_64; |
||
113 | // Except for deployment target of 10.5 or less, |
||
114 | // Mac runtimes use legacy dispatch everywhere now. |
||
115 | return true; |
||
116 | } |
||
117 | |||
118 | /// Is this runtime basically of the GNU family of runtimes? |
||
119 | bool isGNUFamily() const { |
||
120 | switch (getKind()) { |
||
121 | case FragileMacOSX: |
||
122 | case MacOSX: |
||
123 | case iOS: |
||
124 | case WatchOS: |
||
125 | return false; |
||
126 | case GCC: |
||
127 | case GNUstep: |
||
128 | case ObjFW: |
||
129 | return true; |
||
130 | } |
||
131 | llvm_unreachable("bad kind"); |
||
132 | } |
||
133 | |||
134 | /// Is this runtime basically of the NeXT family of runtimes? |
||
135 | bool isNeXTFamily() const { |
||
136 | // For now, this is just the inverse of isGNUFamily(), but that's |
||
137 | // not inherently true. |
||
138 | return !isGNUFamily(); |
||
139 | } |
||
140 | |||
141 | /// Does this runtime allow ARC at all? |
||
142 | bool allowsARC() const { |
||
143 | switch (getKind()) { |
||
144 | case FragileMacOSX: |
||
145 | // No stub library for the fragile runtime. |
||
146 | return getVersion() >= VersionTuple(10, 7); |
||
147 | case MacOSX: return true; |
||
148 | case iOS: return true; |
||
149 | case WatchOS: return true; |
||
150 | case GCC: return false; |
||
151 | case GNUstep: return true; |
||
152 | case ObjFW: return true; |
||
153 | } |
||
154 | llvm_unreachable("bad kind"); |
||
155 | } |
||
156 | |||
157 | /// Does this runtime natively provide the ARC entrypoints? |
||
158 | /// |
||
159 | /// ARC cannot be directly supported on a platform that does not provide |
||
160 | /// these entrypoints, although it may be supportable via a stub |
||
161 | /// library. |
||
162 | bool hasNativeARC() const { |
||
163 | switch (getKind()) { |
||
164 | case FragileMacOSX: return getVersion() >= VersionTuple(10, 7); |
||
165 | case MacOSX: return getVersion() >= VersionTuple(10, 7); |
||
166 | case iOS: return getVersion() >= VersionTuple(5); |
||
167 | case WatchOS: return true; |
||
168 | |||
169 | case GCC: return false; |
||
170 | case GNUstep: return getVersion() >= VersionTuple(1, 6); |
||
171 | case ObjFW: return true; |
||
172 | } |
||
173 | llvm_unreachable("bad kind"); |
||
174 | } |
||
175 | |||
176 | /// Does this runtime provide ARC entrypoints that are likely to be faster |
||
177 | /// than an ordinary message send of the appropriate selector? |
||
178 | /// |
||
179 | /// The ARC entrypoints are guaranteed to be equivalent to just sending the |
||
180 | /// corresponding message. If the entrypoint is implemented naively as just a |
||
181 | /// message send, using it is a trade-off: it sacrifices a few cycles of |
||
182 | /// overhead to save a small amount of code. However, it's possible for |
||
183 | /// runtimes to detect and special-case classes that use "standard" |
||
184 | /// retain/release behavior; if that's dynamically a large proportion of all |
||
185 | /// retained objects, using the entrypoint will also be faster than using a |
||
186 | /// message send. |
||
187 | /// |
||
188 | /// When this method returns true, Clang will turn non-super message sends of |
||
189 | /// certain selectors into calls to the correspond entrypoint: |
||
190 | /// retain => objc_retain |
||
191 | /// release => objc_release |
||
192 | /// autorelease => objc_autorelease |
||
193 | bool shouldUseARCFunctionsForRetainRelease() const { |
||
194 | switch (getKind()) { |
||
195 | case FragileMacOSX: |
||
196 | return false; |
||
197 | case MacOSX: |
||
198 | return getVersion() >= VersionTuple(10, 10); |
||
199 | case iOS: |
||
200 | return getVersion() >= VersionTuple(8); |
||
201 | case WatchOS: |
||
202 | return true; |
||
203 | case GCC: |
||
204 | return false; |
||
205 | case GNUstep: |
||
206 | return false; |
||
207 | case ObjFW: |
||
208 | return false; |
||
209 | } |
||
210 | llvm_unreachable("bad kind"); |
||
211 | } |
||
212 | |||
213 | /// Does this runtime provide entrypoints that are likely to be faster |
||
214 | /// than an ordinary message send of the "alloc" selector? |
||
215 | /// |
||
216 | /// The "alloc" entrypoint is guaranteed to be equivalent to just sending the |
||
217 | /// corresponding message. If the entrypoint is implemented naively as just a |
||
218 | /// message send, using it is a trade-off: it sacrifices a few cycles of |
||
219 | /// overhead to save a small amount of code. However, it's possible for |
||
220 | /// runtimes to detect and special-case classes that use "standard" |
||
221 | /// alloc behavior; if that's dynamically a large proportion of all |
||
222 | /// objects, using the entrypoint will also be faster than using a message |
||
223 | /// send. |
||
224 | /// |
||
225 | /// When this method returns true, Clang will turn non-super message sends of |
||
226 | /// certain selectors into calls to the corresponding entrypoint: |
||
227 | /// alloc => objc_alloc |
||
228 | /// allocWithZone:nil => objc_allocWithZone |
||
229 | bool shouldUseRuntimeFunctionsForAlloc() const { |
||
230 | switch (getKind()) { |
||
231 | case FragileMacOSX: |
||
232 | return false; |
||
233 | case MacOSX: |
||
234 | return getVersion() >= VersionTuple(10, 10); |
||
235 | case iOS: |
||
236 | return getVersion() >= VersionTuple(8); |
||
237 | case WatchOS: |
||
238 | return true; |
||
239 | |||
240 | case GCC: |
||
241 | return false; |
||
242 | case GNUstep: |
||
243 | return false; |
||
244 | case ObjFW: |
||
245 | return false; |
||
246 | } |
||
247 | llvm_unreachable("bad kind"); |
||
248 | } |
||
249 | |||
250 | /// Does this runtime provide the objc_alloc_init entrypoint? This can apply |
||
251 | /// the same optimization as objc_alloc, but also sends an -init message, |
||
252 | /// reducing code size on the caller. |
||
253 | bool shouldUseRuntimeFunctionForCombinedAllocInit() const { |
||
254 | switch (getKind()) { |
||
255 | case MacOSX: |
||
256 | return getVersion() >= VersionTuple(10, 14, 4); |
||
257 | case iOS: |
||
258 | return getVersion() >= VersionTuple(12, 2); |
||
259 | case WatchOS: |
||
260 | return getVersion() >= VersionTuple(5, 2); |
||
261 | default: |
||
262 | return false; |
||
263 | } |
||
264 | } |
||
265 | |||
266 | /// Does this runtime supports optimized setter entrypoints? |
||
267 | bool hasOptimizedSetter() const { |
||
268 | switch (getKind()) { |
||
269 | case MacOSX: |
||
270 | return getVersion() >= VersionTuple(10, 8); |
||
271 | case iOS: |
||
272 | return (getVersion() >= VersionTuple(6)); |
||
273 | case WatchOS: |
||
274 | return true; |
||
275 | case GNUstep: |
||
276 | return getVersion() >= VersionTuple(1, 7); |
||
277 | default: |
||
278 | return false; |
||
279 | } |
||
280 | } |
||
281 | |||
282 | /// Does this runtime allow the use of __weak? |
||
283 | bool allowsWeak() const { |
||
284 | return hasNativeWeak(); |
||
285 | } |
||
286 | |||
287 | /// Does this runtime natively provide ARC-compliant 'weak' |
||
288 | /// entrypoints? |
||
289 | bool hasNativeWeak() const { |
||
290 | // Right now, this is always equivalent to whether the runtime |
||
291 | // natively supports ARC decision. |
||
292 | return hasNativeARC(); |
||
293 | } |
||
294 | |||
295 | /// Does this runtime directly support the subscripting methods? |
||
296 | /// |
||
297 | /// This is really a property of the library, not the runtime. |
||
298 | bool hasSubscripting() const { |
||
299 | switch (getKind()) { |
||
300 | case FragileMacOSX: return false; |
||
301 | case MacOSX: return getVersion() >= VersionTuple(10, 11); |
||
302 | case iOS: return getVersion() >= VersionTuple(9); |
||
303 | case WatchOS: return true; |
||
304 | |||
305 | // This is really a lie, because some implementations and versions |
||
306 | // of the runtime do not support ARC. Probably -fgnu-runtime |
||
307 | // should imply a "maximal" runtime or something? |
||
308 | case GCC: return true; |
||
309 | case GNUstep: return true; |
||
310 | case ObjFW: return true; |
||
311 | } |
||
312 | llvm_unreachable("bad kind"); |
||
313 | } |
||
314 | |||
315 | /// Does this runtime allow sizeof or alignof on object types? |
||
316 | bool allowsSizeofAlignof() const { |
||
317 | return isFragile(); |
||
318 | } |
||
319 | |||
320 | /// Does this runtime allow pointer arithmetic on objects? |
||
321 | /// |
||
322 | /// This covers +, -, ++, --, and (if isSubscriptPointerArithmetic() |
||
323 | /// yields true) []. |
||
324 | bool allowsPointerArithmetic() const { |
||
325 | switch (getKind()) { |
||
326 | case FragileMacOSX: |
||
327 | case GCC: |
||
328 | return true; |
||
329 | case MacOSX: |
||
330 | case iOS: |
||
331 | case WatchOS: |
||
332 | case GNUstep: |
||
333 | case ObjFW: |
||
334 | return false; |
||
335 | } |
||
336 | llvm_unreachable("bad kind"); |
||
337 | } |
||
338 | |||
339 | /// Is subscripting pointer arithmetic? |
||
340 | bool isSubscriptPointerArithmetic() const { |
||
341 | return allowsPointerArithmetic(); |
||
342 | } |
||
343 | |||
344 | /// Does this runtime provide an objc_terminate function? |
||
345 | /// |
||
346 | /// This is used in handlers for exceptions during the unwind process; |
||
347 | /// without it, abort() must be used in pure ObjC files. |
||
348 | bool hasTerminate() const { |
||
349 | switch (getKind()) { |
||
350 | case FragileMacOSX: return getVersion() >= VersionTuple(10, 8); |
||
351 | case MacOSX: return getVersion() >= VersionTuple(10, 8); |
||
352 | case iOS: return getVersion() >= VersionTuple(5); |
||
353 | case WatchOS: return true; |
||
354 | case GCC: return false; |
||
355 | case GNUstep: return false; |
||
356 | case ObjFW: return false; |
||
357 | } |
||
358 | llvm_unreachable("bad kind"); |
||
359 | } |
||
360 | |||
361 | /// Does this runtime support weakly importing classes? |
||
362 | bool hasWeakClassImport() const { |
||
363 | switch (getKind()) { |
||
364 | case MacOSX: return true; |
||
365 | case iOS: return true; |
||
366 | case WatchOS: return true; |
||
367 | case FragileMacOSX: return false; |
||
368 | case GCC: return true; |
||
369 | case GNUstep: return true; |
||
370 | case ObjFW: return true; |
||
371 | } |
||
372 | llvm_unreachable("bad kind"); |
||
373 | } |
||
374 | |||
375 | /// Does this runtime use zero-cost exceptions? |
||
376 | bool hasUnwindExceptions() const { |
||
377 | switch (getKind()) { |
||
378 | case MacOSX: return true; |
||
379 | case iOS: return true; |
||
380 | case WatchOS: return true; |
||
381 | case FragileMacOSX: return false; |
||
382 | case GCC: return true; |
||
383 | case GNUstep: return true; |
||
384 | case ObjFW: return true; |
||
385 | } |
||
386 | llvm_unreachable("bad kind"); |
||
387 | } |
||
388 | |||
389 | bool hasAtomicCopyHelper() const { |
||
390 | switch (getKind()) { |
||
391 | case FragileMacOSX: |
||
392 | case MacOSX: |
||
393 | case iOS: |
||
394 | case WatchOS: |
||
395 | return true; |
||
396 | case GNUstep: |
||
397 | return getVersion() >= VersionTuple(1, 7); |
||
398 | default: return false; |
||
399 | } |
||
400 | } |
||
401 | |||
402 | /// Is objc_unsafeClaimAutoreleasedReturnValue available? |
||
403 | bool hasARCUnsafeClaimAutoreleasedReturnValue() const { |
||
404 | switch (getKind()) { |
||
405 | case MacOSX: |
||
406 | case FragileMacOSX: |
||
407 | return getVersion() >= VersionTuple(10, 11); |
||
408 | case iOS: |
||
409 | return getVersion() >= VersionTuple(9); |
||
410 | case WatchOS: |
||
411 | return getVersion() >= VersionTuple(2); |
||
412 | case GNUstep: |
||
413 | return false; |
||
414 | default: |
||
415 | return false; |
||
416 | } |
||
417 | } |
||
418 | |||
419 | /// Are the empty collection symbols available? |
||
420 | bool hasEmptyCollections() const { |
||
421 | switch (getKind()) { |
||
422 | default: |
||
423 | return false; |
||
424 | case MacOSX: |
||
425 | return getVersion() >= VersionTuple(10, 11); |
||
426 | case iOS: |
||
427 | return getVersion() >= VersionTuple(9); |
||
428 | case WatchOS: |
||
429 | return getVersion() >= VersionTuple(2); |
||
430 | } |
||
431 | } |
||
432 | |||
433 | /// Returns true if this Objective-C runtime supports Objective-C class |
||
434 | /// stubs. |
||
435 | bool allowsClassStubs() const { |
||
436 | switch (getKind()) { |
||
437 | case FragileMacOSX: |
||
438 | case GCC: |
||
439 | case GNUstep: |
||
440 | case ObjFW: |
||
441 | return false; |
||
442 | case MacOSX: |
||
443 | case iOS: |
||
444 | case WatchOS: |
||
445 | return true; |
||
446 | } |
||
447 | llvm_unreachable("bad kind"); |
||
448 | } |
||
449 | |||
450 | /// Does this runtime supports direct dispatch |
||
451 | bool allowsDirectDispatch() const { |
||
452 | switch (getKind()) { |
||
453 | case FragileMacOSX: return false; |
||
454 | case MacOSX: return true; |
||
455 | case iOS: return true; |
||
456 | case WatchOS: return true; |
||
457 | case GCC: return false; |
||
458 | case GNUstep: return false; |
||
459 | case ObjFW: return false; |
||
460 | } |
||
461 | llvm_unreachable("bad kind"); |
||
462 | } |
||
463 | |||
464 | /// Try to parse an Objective-C runtime specification from the given |
||
465 | /// string. |
||
466 | /// |
||
467 | /// \return true on error. |
||
468 | bool tryParse(StringRef input); |
||
469 | |||
470 | std::string getAsString() const; |
||
471 | |||
472 | friend bool operator==(const ObjCRuntime &left, const ObjCRuntime &right) { |
||
473 | return left.getKind() == right.getKind() && |
||
474 | left.getVersion() == right.getVersion(); |
||
475 | } |
||
476 | |||
477 | friend bool operator!=(const ObjCRuntime &left, const ObjCRuntime &right) { |
||
478 | return !(left == right); |
||
479 | } |
||
480 | |||
481 | friend llvm::hash_code hash_value(const ObjCRuntime &OCR) { |
||
482 | return llvm::hash_combine(OCR.getKind(), OCR.getVersion()); |
||
483 | } |
||
484 | |||
485 | template <typename HasherT, llvm::support::endianness Endianness> |
||
486 | friend void addHash(llvm::HashBuilderImpl<HasherT, Endianness> &HBuilder, |
||
487 | const ObjCRuntime &OCR) { |
||
488 | HBuilder.add(OCR.getKind(), OCR.getVersion()); |
||
489 | } |
||
490 | }; |
||
491 | |||
492 | raw_ostream &operator<<(raw_ostream &out, const ObjCRuntime &value); |
||
493 | |||
494 | } // namespace clang |
||
495 | |||
496 | #endif // LLVM_CLANG_BASIC_OBJCRUNTIME_H |