在阅读 Objc 库源码时常常会遇到很多宏定义,比如宏 SUPPORT_INDEXED_ISA、SUPPORT_PACKED_ISA,代码如下所示:

  1. // Define SUPPORT_INDEXED_ISA=1 on platforms that store the class in the isa
  2. // field as an index into a class table.
  3. // Note, keep this in sync with any .s files which also define it.
  4. // Be sure to edit objc-abi.h as well.
  5.  
  6. #if __ARM_ARCH_7K__ >= 2 || (__arm64__ && !__LP64__)
  7. # define SUPPORT_INDEXED_ISA 1
  8.  
  9. #else
  10. # define SUPPORT_INDEXED_ISA 0
  11.  
  12. #endif
  13.  
  14.  
  15. // Define SUPPORT_PACKED_ISA=1 on platforms that store the class in the isa
  16. // field as a maskable pointer with other data around it.
  17.  
  18. #if (!__LP64__ || TARGET_OS_WIN32 || \
  19. (TARGET_OS_SIMULATOR && !TARGET_OS_MACCATALYST && !__arm64__))
  20. # define SUPPORT_PACKED_ISA 0
  21.  
  22. #else
  23. # define SUPPORT_PACKED_ISA 1
  24.  
  25. #endif

在上面的宏定义中,__ARM_ARCH_7K__、__arm64__、__LP64__ 这些宏在 Objc 库中找不到定义的源码。如果不清楚这些宏的意义,对阅读源码会带来一定的障碍。实际上,这些宏都定义在 LLVM 源码中(基本上找不到定义的宏,都可以在 LLVM 源码中找到)。

__ARM_ARCH_7K__

在 LLVM 源码 ARM.cpp 中,可以看到对 __ARM_ARCH_7K__ 的定义,源码如下:

  1. // file: ARM.cpp
  2. // Unfortunately, __ARM_ARCH_7K__ is now more of an ABI descriptor. The CPU
  3. // happens to be Cortex-A7 though, so it should still get __ARM_ARCH_7A__.
  4. if (getTriple().isWatchABI()) // 判断是否是 Watch 的ABI
  5. Builder.defineMacro("__ARM_ARCH_7K__", "2");

从源码看到,这个宏是在 Apple Watch 下生效,在 iPhone 设备上该宏不会生效。

__arm64__

在 LLVM 源码 AArch64.cpp 中,可以看到对 __arm64__ 的定义,源码如下:

  1. void DarwinAArch64TargetInfo::getOSDefines(const LangOptions &Opts,
  2. const llvm::Triple &Triple,
  3. MacroBuilder &Builder) const {
  4. Builder.defineMacro("__AARCH64_SIMD__");
  5. if (Triple.isArch32Bit())
  6. Builder.defineMacro("__ARM64_ARCH_8_32__");
  7. else
  8. Builder.defineMacro("__ARM64_ARCH_8__");
  9. Builder.defineMacro("__ARM_NEON__");
  10. Builder.defineMacro("__LITTLE_ENDIAN__");
  11. Builder.defineMacro("__REGISTER_PREFIX__", "");
  12. Builder.defineMacro("__arm64", "1");
  13. Builder.defineMacro("__arm64__", "1"); // __arm64__ 定义
  14.  
  15. if (Triple.isArm64e())
  16. Builder.defineMacro("__arm64e__", "1");
  17. getDarwinDefines(Builder, Opts, Triple, PlatformName, PlatformMinVersion);
  18. }

从源码上可以看到,只要 ARM CPU 是 64bit,就会定义 __arm64__ 宏,虽然可能这个 CPU 使用的是 ILP32(见下文)。

__LP64__

在 LLVM 源码 InitPreprocessor.cpp 中,可以看到对 __LP64__ 的定义,源码如下:

  1. // file: InitPreprocessor.cpp

    static
    void InitializePredefinedMacros(const TargetInfo &TI,
  2. const LangOptions &LangOpts,
  3. const FrontendOptions &FEOpts,
  4. const PreprocessorOptions &PPOpts,
  5. MacroBuilder &Builder) {
  6. ...
  7. if (TI.getPointerWidth(0) == 64 && TI.getLongWidth() == 64
  8. && TI.getIntWidth() == 32) {
  9. Builder.defineMacro("_LP64");
  10. Builder.defineMacro("__LP64__"); // 定义 __LP64__
  11. }
  12. if (TI.getPointerWidth(0) == 32 && TI.getLongWidth() == 32
  13. && TI.getIntWidth() == 32) {
  14. Builder.defineMacro("_ILP32");
  15. Builder.defineMacro("__ILP32__"); // 定义 __ILP32__
  16. }
  17. ...
  18. // Get other target #defines.
  19. TI.getTargetDefines(LangOpts, Builder); // 该方法会重新定义 __LP64__
  20. }

从上面源码可以看到,如果指针 pointer 的长度是 64bit,long 类型的长度是 64bit,int 类型的长度是 32bit,那么就定义宏 __LP64__。

如果指针 pointer 的长度是 32bit,long 类型的长度是 32bit,int 类型的长度是 32bit,那么就定义宏 __ILP32__。

源码最后一行 TI.getTargetDefines(LangOpts, Builder) 在 ARM 架构下重新定义 __LP64__,相关源码位于 AArch64.cpp:

  1. // file: AArch64.cpp

    void
    AArch64TargetInfo::getTargetDefines(const LangOptions &Opts,
  2. MacroBuilder &Builder) const {
  3. // Target identification.
  4. Builder.defineMacro("__aarch64__");
  5. // For bare-metal.
  6. if (getTriple().getOS() == llvm::Triple::UnknownOS &&
  7. getTriple().isOSBinFormatELF())
  8. Builder.defineMacro("__ELF__");
  9. // Target properties.
  10. if (!getTriple().isOSWindows() && getTriple().isArch64Bit()) { // 在非 Windows 下,并且真正支持 64bit 指针的 CPU 架构下才定义 __LP64__
  11. Builder.defineMacro("_LP64");
  12. Builder.defineMacro("__LP64__");
  13. }
  14. ...
  15. }

从上面的源码可以看到,非 Windows 系统 & 真正支持 64bit 指针的 CPU 架构才会定义 __LP64__。那么哪些类型的 CPU 支持 64bit 的指针呢? 通过查看 Triple::isArch64Bit 方法可以得到答案:

  1. // file: Triple.cpp
  2.  
  3. bool Triple::isArch64Bit() const {
  4. return getArchPointerBitWidth(getArch()) == 64;
  5. }
  6. static unsigned getArchPointerBitWidth(llvm::Triple::ArchType Arch) {
  7. switch (Arch) {
  8. case llvm::Triple::UnknownArch:
  9. return 0;
  10. case llvm::Triple::avr:
  11. case llvm::Triple::msp430:
  12. return 16;
  13. case llvm::Triple::aarch64_32:
  14. case llvm::Triple::amdil:
  15. case llvm::Triple::arc:
  16. case llvm::Triple::arm:
  17. case llvm::Triple::armeb:
  18. case llvm::Triple::csky:
  19. case llvm::Triple::dxil:
  20. case llvm::Triple::hexagon:
  21. case llvm::Triple::hsail:
  22. case llvm::Triple::kalimba:
  23. case llvm::Triple::lanai:
  24. case llvm::Triple::le32:
  25. case llvm::Triple::loongarch32:
  26. case llvm::Triple::m68k:
  27. case llvm::Triple::mips:
  28. case llvm::Triple::mipsel:
  29. case llvm::Triple::nvptx:
  30. case llvm::Triple::ppc:
  31. case llvm::Triple::ppcle:
  32. case llvm::Triple::r600:
  33. case llvm::Triple::renderscript32:
  34. case llvm::Triple::riscv32:
  35. case llvm::Triple::shave:
  36. case llvm::Triple::sparc:
  37. case llvm::Triple::sparcel:
  38. case llvm::Triple::spir:
  39. case llvm::Triple::spirv32:
  40. case llvm::Triple::tce:
  41. case llvm::Triple::tcele:
  42. case llvm::Triple::thumb:
  43. case llvm::Triple::thumbeb:
  44. case llvm::Triple::wasm32:
  45. case llvm::Triple::x86:
  46. case llvm::Triple::xcore:
  47. return 32;
  48. case llvm::Triple::aarch64:
  49. case llvm::Triple::aarch64_be:
  50. case llvm::Triple::amdgcn:
  51. case llvm::Triple::amdil64:
  52. case llvm::Triple::bpfeb:
  53. case llvm::Triple::bpfel:
  54. case llvm::Triple::hsail64:
  55. case llvm::Triple::le64:
  56. case llvm::Triple::loongarch64:
  57. case llvm::Triple::mips64:
  58. case llvm::Triple::mips64el:
  59. case llvm::Triple::nvptx64:
  60. case llvm::Triple::ppc64:
  61. case llvm::Triple::ppc64le:
  62. case llvm::Triple::renderscript64:
  63. case llvm::Triple::riscv64:
  64. case llvm::Triple::sparcv9:
  65. case llvm::Triple::spir64:
  66. case llvm::Triple::spirv64:
  67. case llvm::Triple::systemz:
  68. case llvm::Triple::ve:
  69. case llvm::Triple::wasm64:
  70. case llvm::Triple::x86_64:
  71. return 64;
  72. }
  73. llvm_unreachable("Invalid architecture value");
  74. }

上面源码需要注意的一个 CPU 架构是 aarch64_32,这种 ARM 架构的 CPU 虽然是 64bit 的,但是 int、long、pointer 都使用 32bit 表示(即 ILP32)。这种 CPU 通常用在嵌入式里面,Apple Watch Series 4/5 就是使用的这种 CPU:

 

 

由于 Apple 从 iPhone 5S 就开始支持 64bit 的 CPU,因此在 >= iPhone 5S 的设备上,SUPPORT_INDEXED_ISA 定义为0,SUPPORT_PACKED_ISA 定义为1。

__OBJC__

___OBJC__ 宏定义在 LLVM 源码的 InitPreprocessor.cpp 文件,源码如下:

  1. // file: InitPreprocessor.cpp

    static
    void InitializeStandardPredefinedMacros(const TargetInfo &TI,
  2. const LangOptions &LangOpts,
  3. const FrontendOptions &FEOpts,
  4. MacroBuilder &Builder) {
  5. ...
  6. if (LangOpts.ObjC)
  7. Builder.defineMacro("__OBJC__"); // 定义 __OBJC__ 宏
  8. ...
  9. }

从源码可以看到,如果编译的语言是Objective-C,那么这个宏就会被定义。

__OBJC2__

__OBJC2__ 宏定义在 LLVM 源码的 InitPreprocessor.cpp 文件,源码如下:

  1. // file: InitPreprocessor.cpp
  2.  
  3. static void InitializePredefinedMacros(const TargetInfo &TI,
  4. const LangOptions &LangOpts,
  5. const FrontendOptions &FEOpts,
  6. const PreprocessorOptions &PPOpts,
  7. MacroBuilder &Builder) {
  8. ...
  9. if (LangOpts.ObjC) {
  10. if (LangOpts.ObjCRuntime.isNonFragile()) {
  11. Builder.defineMacro("__OBJC2__"); // 如果是 Objective-C 语言,并且满足 non fragile,就定义 __OBJC2__
  12. ...
  13. }

对于 __OBJC2__ 宏的定义中,除了判断是 Objectvie-C 语言,还需要判断 non-fragile 条件。该条件判断的源码如下:

  1. // file: ObjcRuntime.h
  2.  
  3. bool isNonFragile() const {
  4. switch (getKind()) {
  5. case FragileMacOSX: return false;
  6. case GCC: return false;
  7. case MacOSX: return true; // Mac
  8. case GNUstep: return true;
  9. case ObjFW: return true;
  10. case iOS: return true; // iOS
  11. case WatchOS: return true; // Watch
  12. }
  13. llvm_unreachable("bad kind");
  14. }

从源码可以看到,对于 iOS 系统和 Watch OS 系统,__OBJC2__ 宏是一定会定义的。但是对于 MAC 系统就要区分 MacOSX 与 FragileMacOSX。这些类型的定义源码如下:

  1. // file: ObjcRuntime.h
  2.  
  3. class ObjCRuntime {
  4. public:
  5. /// The basic Objective-C runtimes that we know about.
  6. enum Kind {
  7. /// 'macosx' is the Apple-provided NeXT-derived runtime on Mac OS
  8. /// X platforms that use the non-fragile ABI; the version is a
  9. /// release of that OS.
  10. MacOSX,
  11. /// 'macosx-fragile' is the Apple-provided NeXT-derived runtime on
  12. /// Mac OS X platforms that use the fragile ABI; the version is a
  13. /// release of that OS.
  14. FragileMacOSX,
  15. /// 'ios' is the Apple-provided NeXT-derived runtime on iOS or the iOS
  16. /// simulator; it is always non-fragile. The version is a release
  17. /// version of iOS.
  18. iOS,
  19. /// 'watchos' is a variant of iOS for Apple's watchOS. The version
  20. /// is a release version of watchOS.
  21. WatchOS,
  22. /// 'gcc' is the Objective-C runtime shipped with GCC, implementing a
  23. /// fragile Objective-C ABI
  24. GCC,
  25. /// 'gnustep' is the modern non-fragile GNUstep runtime.
  26. GNUstep,
  27. /// 'objfw' is the Objective-C runtime included in ObjFW
  28. ObjFW
  29. };
  30. ...
  31. }

__has_feature

__has_feature 宏可以帮助我们判断一个功能是否可以由 Clang 编译器支持,Clang 文档原文如下:

These function-like macros take a single identifier argument that is the name of a feature. __has_feature evaluates to 1 if the feature is both supported by Clang and standardized in the current language standard or 0 if not

  1.  

那么它的实现是怎样的呢?

首先 Clang 会注册 __has_feature 宏,注册的结果被保存在 Preprocessor 对象的实例变量 Ident__has_feature 中。源码如下所示:

  1. /// file: PPMacroExpansion.cpp
  2. /// RegisterBuiltinMacros - Register builtin macros, such as __LINE__ with the
  3. /// identifier table.
  4. void Preprocessor::RegisterBuiltinMacros() {
  5. // 注入了许多常见的内置宏
  6. Ident__LINE__ = RegisterBuiltinMacro(*this, "__LINE__");
  7. Ident__FILE__ = RegisterBuiltinMacro(*this, "__FILE__");
  8. Ident__DATE__ = RegisterBuiltinMacro(*this, "__DATE__");
  9. Ident__TIME__ = RegisterBuiltinMacro(*this, "__TIME__");
  10. Ident__COUNTER__ = RegisterBuiltinMacro(*this, "__COUNTER__");
  11. Ident_Pragma = RegisterBuiltinMacro(*this, "_Pragma");
  12. ...
  13. // Clang Extensions.
  14. Ident__FILE_NAME__ = RegisterBuiltinMacro(*this, "__FILE_NAME__");
  15. Ident__has_feature = RegisterBuiltinMacro(*this, "__has_feature"); // __has_feature 被注入
  16. Ident__has_extension = RegisterBuiltinMacro(*this, "__has_extension"); // __has_extension 被注入
  17. Ident__has_builtin = RegisterBuiltinMacro(*this, "__has_builtin"); // __has_builtin 被注入
  18. ...
  19. }

当 Clang 预编译源文件时如果遇到了 __has_feature 标识符,就会进行扩展,扩展的代码如下所示:

  1. // file: PPMacroExpansion.cpp
  2.  
  3. void Preprocessor::ExpandBuiltinMacro(Token &Tok) {
  4. ...
  5. } else if (II == Ident__has_feature) {
  6. EvaluateFeatureLikeBuiltinMacro(OS, Tok, II, *this, false,
  7. [this](Token &Tok, bool &HasLexedNextToken) -> int {
  8. IdentifierInfo *II = ExpectFeatureIdentifierInfo(Tok, *this,
  9. diag::err_feature_check_malformed);
  10. return II && HasFeature(*this, II->getName()); // 最终 Clang 编译器调用 HasFeature 函数进行判断
  11. });
  12. } else if (II == Ident__has_extension) {
  13. ...
  14. }
  15. ...
  16. }

从源码可以看到,Clang 的扩展结果通过调用 HasFeature 函数获取,HasFeature 函数接收要检测的功能名作为参数,源码如下:

  1. // file: PPMacroExpansion.cpp
  2.  
  3. static bool HasFeature(const Preprocessor &PP, StringRef Feature) {
  4. const LangOptions &LangOpts = PP.getLangOpts();
  5. // Normalize the feature name, __foo__ becomes foo.
  6. if (Feature.startswith("__") && Feature.endswith("__") && Feature.size() >= 4)
  7. Feature = Feature.substr(2, Feature.size() - 4);
  8. #define FEATURE(Name, Predicate) .Case(#Name, Predicate) // 下面的 Feature.def 里面使用了大量的 FEATURE 宏
  9. return llvm::StringSwitch<bool>(Feature) // StringSwitch 是一个类,它支持对字符串进行 switch-case 操作
  10. #include "clang/Basic/Features.def" // 所有 Clang 支持的功能都定义在这个文件
  11. .Default(false); // 默认返回 false
  12. #undef FEATURE
  13. }

从源码可以看到,HasFeature 函数内部定义了一个 FEATURE 宏,这个宏在 Feature.def 文件中被大量使用,下面截取部分 Feature.def 文件内容:

  1. // file: Feature.def
  2. ...
  3. // Objective-C features
  4. FEATURE(objc_arr, LangOpts.ObjCAutoRefCount) // FIXME: REMOVE?
  5. FEATURE(objc_arc, LangOpts.ObjCAutoRefCount)
  6. FEATURE(objc_arc_fields, true) // ARC
  7. FEATURE(objc_arc_weak, LangOpts.ObjCWeak) // weak
  8. FEATURE(objc_default_synthesize_properties, LangOpts.ObjC)
  9. FEATURE(objc_fixed_enum, LangOpts.ObjC)
  10. FEATURE(objc_instancetype, LangOpts.ObjC) // instancetype
  11. FEATURE(objc_kindof, LangOpts.ObjC)
  12. ...

经过宏扩展之后,HasFeature 函数最后的 return 语句实际上变成为:

  1. return llvm::StringSwitch<bool>(Feature)
  2. ...
  3. .Case("objc_arr", LangOpts.ObjCAutoRefCount)
  4. .Case("objc_arc", LangOpts.ObjCAutoRefCount)
  5. .Case("objc_arc_fields", true)
  6. .Case("objc_arc_weak", LangOpts.ObjCWeak)
  7. .Case("objc_default_synthesize_properties", LangOpts.ObjC)
  8. .Case("objc_fixed_enum", LangOpts.ObjC)
  9. .Case("objc_instancetype", LangOpts.ObjC)
  10. .Case("objc_kindof", LangOpts.ObjC)
  11. ...
  12. .Default(false)

return 语句首先传入待检测的功能名,调用 StringSwitch 的构造函数生成一个 StringSwitch 对象,这个 StringSwitch 对象用来对 string 进行 switch-case 操作,它内部有 Case 和 Default 两个方法,定义如下:

  1. template<typename T, typename R = T>
  2. class StringSwitch {
  3. ...
  4. // Case-sensitive case matchers
  5. StringSwitch &Case(StringLiteral S, T Value) {
  6. if (!Result && Str == S) { // 如果 switch-case 没有匹配的结果,本次 Case 方法才进行比较,否则如果已经匹配出结果,直接返回对象本身
  7. Result = std::move(Value);
  8. }
  9. return *this; // 返回对象本身,形成链式调用
  10. }
  11. ..
  12. R Default(T Value) {
  13. if (Result)
  14. return std::move(*Result); // 匹配除了结果,直接返回结果
  15. return Value; // 未匹配出结果,返回默认值
  16. }
  17. ...
  18. }

从源码可以看到,Case 方法只有在未匹配出结果时,才进行匹配操作,如果结果已经匹配,Case 方法直接返回对象本身,这样就可以形成链式调用。链式调用最后,会调用到 Default 方法,如果已经匹配到结果,Default 方法直接返回对应的匹配结果,否则就返回默认值。

版权声明:本文为chaoguo1234原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/chaoguo1234/p/16535651.html