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
25namespace clang {
26
27/// The basic abstraction for the target Objective-C runtime.
28class ObjCRuntime {
29public:
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
62private:
63 Kind TheKind = MacOSX;
64 VersionTuple Version;
65
66public:
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
509raw_ostream &operator<<(raw_ostream &out, const ObjCRuntime &value);
510
511} // namespace clang
512
513#endif // LLVM_CLANG_BASIC_OBJCRUNTIME_H
514

source code of clang/include/clang/Basic/ObjCRuntime.h