Files
UniXNU/scripts/linux/llvm-xnu-builtin-type.patch
smeng9andClaude Opus 5.5 6dc20ebe08 build(linux): install the kernel step toolchain on a Linux host
scripts/linux/setup-toolchain.py, run once as root, builds into
/opt/unixnu-linux (or PREFIX, or $UNIXNU_TOOLCHAIN):

- clang from llvm-project 58c46bae2118 with llvm-xnu-builtin-type.patch,
  which adds __builtin_xnu_type_signature, __builtin_xnu_type_summary,
  __builtin_xnu_types_compatible and xnu_usage_semantics; kalloc.h uses
  the builtins with no fallback;
- cctools-port 1030.6.3-ld64-956.6 (its master, ld64-609, corrupts the
  kernel link), with libdispatch for ld64;
- mig and migcom from bootstrap_cmds-138;
- iig-port, with iig-port-darwin-target.patch;
- xnu-host-cc, the host compiler for xnu's SETUP tools;
- lzfse for the kernelcache.

It installs the Command Line Tools shims (clang, clang++, cc, install,
gm4) under the prefix and xcrun, plutil and codesign in /usr/bin, where
xnu's makefiles call them. libmacho.a and libstuff.a go to /usr/lib and
libdispatch to /usr/local. Each step is skipped when its output exists.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 19:48:06 +08:00

649 lines
24 KiB
Diff

Implement Apple xnu type traits in upstream clang
Adds __builtin_xnu_type_signature, __builtin_xnu_type_summary,
__builtin_xnu_types_compatible and __attribute__((xnu_usage_semantics())).
Upstream base: https://github.com/llvm/llvm-project
branch main, commit 58c46bae21181fb4240cf964461bea738d96d50e (apt.llvm.org snapshot 24~++20260911085248)
diff --git a/clang/include/clang/AST/XnuTypeSignature.h b/clang/include/clang/AST/XnuTypeSignature.h
new file mode 100644
index 0000000..e0a3c85
--- /dev/null
+++ b/clang/include/clang/AST/XnuTypeSignature.h
@@ -0,0 +1,58 @@
+//===--- XnuTypeSignature.h - XNU kalloc type signatures --------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Implements the granule classification used by XNU's signature based
+/// kalloc type segregation, as exposed by __builtin_xnu_type_signature,
+/// __builtin_xnu_type_summary and __builtin_xnu_types_compatible.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_AST_XNUTYPESIGNATURE_H
+#define LLVM_CLANG_AST_XNUTYPESIGNATURE_H
+
+#include "clang/AST/Type.h"
+#include "llvm/ADT/SmallVector.h"
+#include <cstdint>
+
+namespace clang {
+class ASTContext;
+
+/// The KT_GRANULE_* encodings from XNU's osfmk/kern/kalloc.h. A granule's
+/// encoding is the bitwise OR of the encodings of every leaf overlapping it.
+enum XnuGranuleKind : unsigned {
+ XNU_GRANULE_PADDING = 0,
+ XNU_GRANULE_POINTER = 1,
+ XNU_GRANULE_DATA = 2,
+ XNU_GRANULE_DUAL = 4,
+ XNU_GRANULE_PAC = 8,
+};
+
+/// Compute the per-granule (8 byte) encoding sequence of \p T.
+///
+/// \p Granules is resized to ceil(sizeof(T) / 8) and filled in, except that a
+/// type which contains no scalar leaf at all (an empty record, a record whose
+/// only members are empty records or zero-length arrays, ...) produces an
+/// empty sequence regardless of its size.
+///
+/// \p T must be a complete, non variably-modified type.
+void computeXnuTypeGranules(const ASTContext &Ctx, QualType T,
+ llvm::SmallVectorImpl<uint8_t> &Granules);
+
+/// Render the granule sequence of \p T as lowercase hexadecimal digits; this
+/// is the value of __builtin_xnu_type_signature(T).
+void getXnuTypeSignature(const ASTContext &Ctx, QualType T,
+ llvm::SmallVectorImpl<char> &Out);
+
+/// Compute __builtin_xnu_type_summary(T): the OR over all granules g of
+/// (1 << g), i.e. KT_SUMMARY_GRANULE_TO_IDX applied to every granule.
+uint64_t getXnuTypeSummary(const ASTContext &Ctx, QualType T);
+
+} // namespace clang
+
+#endif // LLVM_CLANG_AST_XNUTYPESIGNATURE_H
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index 61ef3fb..8c6730e 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -5539,3 +5539,29 @@ def Personality : InheritableAttr {
let Subjects = SubjectList<[Function]>;
let Documentation = [PersonalityDocs];
}
+
+def XnuUsageSemantics : InheritableAttr {
+ let Spellings = [Clang<"xnu_usage_semantics">];
+ let Args = [StringArgument<"Kind0">, StringArgument<"Kind1", 1>];
+ let Documentation = [Undocumented];
+ let AdditionalMembers = [{
+ /// The KT_GRANULE_* encoding (see clang/AST/XnuTypeSignature.h) that this
+ /// annotation forces for the annotated typedef or field.
+ unsigned getGranuleKind() const {
+ bool IsPointer = false, IsData = false;
+ for (llvm::StringRef S : {getKind0(), getKind1()}) {
+ if (S == "pointer")
+ IsPointer = true;
+ else if (S == "data")
+ IsData = true;
+ }
+ if (IsPointer && IsData)
+ return 4; // KT_GRANULE_DUAL
+ if (IsPointer)
+ return 1; // KT_GRANULE_POINTER
+ if (IsData)
+ return 2; // KT_GRANULE_DATA
+ return 0; // KT_GRANULE_PADDING
+ }
+ }];
+}
diff --git a/clang/include/clang/Basic/BuiltinTraits.td b/clang/include/clang/Basic/BuiltinTraits.td
index f80234d..9a1b105 100644
--- a/clang/include/clang/Basic/BuiltinTraits.td
+++ b/clang/include/clang/Basic/BuiltinTraits.td
@@ -125,6 +125,18 @@ def TypeCompatible : BinaryTrait {
let Spelling = "__builtin_types_compatible_p";
let KeyFlags = [KEYNOCXX];
}
+def XnuTypeSignature : UnaryTrait {
+ let Spelling = "__builtin_xnu_type_signature";
+ let KeyFlags = [KEYALL];
+}
+def XnuTypeSummary : UnaryTrait {
+ let Spelling = "__builtin_xnu_type_summary";
+ let KeyFlags = [KEYALL];
+}
+def XnuTypesCompatible : BinaryTrait {
+ let Spelling = "__builtin_xnu_types_compatible";
+ let KeyFlags = [KEYALL];
+}
def TypeOrder : BinaryTrait {
let Spelling = "__builtin_type_order";
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index a7b40a7..aac9b47 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -10033,6 +10033,19 @@ def warn_unused_comparison : Warning<
def note_inequality_comparison_to_or_assign : Note<
"use '|=' to turn this inequality comparison into an or-assignment">;
+// XNU kalloc type signature traits (__builtin_xnu_type_signature and friends).
+def err_xnu_type_trait_variable_array : Error<
+ "invalid application of __builtin_xnu_type_%select{signature|summary}0 to "
+ "variable array type %1">;
+def err_xnu_usage_semantics_subject : Error<
+ "%0 attribute can only be applied to a typedef or a field declaration">;
+def err_xnu_usage_semantics_record : Error<
+ "%0 attribute can only be applied to non-record types">;
+def err_xnu_usage_semantics_invalid_kind : Error<
+ "invalid type signature semantic kind %0">;
+def err_xnu_usage_semantics_repeated_kind : Error<
+ "semantics kind '%0' repeated more than once">;
+
def err_incomplete_type_used_in_type_trait_expr : Error<
"incomplete type %0 used in type trait expression">, NoSFINAE;
diff --git a/clang/lib/AST/CMakeLists.txt b/clang/lib/AST/CMakeLists.txt
index c703151..ee5f369 100644
--- a/clang/lib/AST/CMakeLists.txt
+++ b/clang/lib/AST/CMakeLists.txt
@@ -137,6 +137,7 @@ add_clang_library(clangAST
TypePrinter.cpp
VTableBuilder.cpp
VTTBuilder.cpp
+ XnuTypeSignature.cpp
PRECOMPILE_HEADERS
[["clang/AST/pch.h"]]
diff --git a/clang/lib/AST/XnuTypeSignature.cpp b/clang/lib/AST/XnuTypeSignature.cpp
new file mode 100644
index 0000000..6e0ad52
--- /dev/null
+++ b/clang/lib/AST/XnuTypeSignature.cpp
@@ -0,0 +1,222 @@
+//===--- XnuTypeSignature.cpp - XNU kalloc type signatures ----------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/AST/XnuTypeSignature.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Attr.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/RecordLayout.h"
+#include "clang/Basic/TargetInfo.h"
+#include "llvm/Support/SaveAndRestore.h"
+
+using namespace clang;
+
+namespace {
+
+/// Sentinel meaning "no xnu_usage_semantics annotation".
+static constexpr unsigned NoKind = ~0u;
+
+static unsigned getAnnotatedKind(const Decl *D) {
+ if (const auto *A = D->getAttr<XnuUsageSemanticsAttr>())
+ return A->getGranuleKind();
+ return NoKind;
+}
+
+class XnuGranuleComputer {
+ const ASTContext &Ctx;
+ llvm::SmallVectorImpl<uint8_t> &G;
+ /// Whether any scalar leaf (including a bit-field of any width) was reached.
+ bool SawLeaf = false;
+ /// Set while walking the elements of an array. Apple stops honouring field
+ /// level xnu_usage_semantics annotations below an array, though typedef
+ /// level annotations and __ptrauth still apply:
+ /// struct M { uintptr_t v __kernel_data_semantics; }; -> "2"
+ /// struct { struct M a; } -> "2"
+ /// struct { struct M a[2]; } -> "11" (!)
+ /// XNU depends on this: the opaque lck_mtx_t / lck_spin_t / lck_rw_t are
+ /// exactly of that shape, and arrays of them sit inside kalloc'd types.
+ bool IgnoreFieldAttrs = false;
+
+public:
+ XnuGranuleComputer(const ASTContext &Ctx, llvm::SmallVectorImpl<uint8_t> &G)
+ : Ctx(Ctx), G(G) {}
+
+ bool sawLeaf() const { return SawLeaf; }
+
+ /// OR \p Kind into every granule overlapping [BitOff, BitOff + BitWidth).
+ void mark(uint64_t BitOff, uint64_t BitWidth, unsigned Kind) {
+ // Reaching a leaf at all is what makes a record non-empty, even when the
+ // leaf is a zero-width bit-field that covers no granule.
+ SawLeaf = true;
+ if (BitWidth == 0 || G.empty())
+ return;
+ uint64_t First = BitOff / 64;
+ if (First >= G.size())
+ return;
+ uint64_t Last = (BitOff + BitWidth - 1) / 64;
+ if (Last >= G.size())
+ Last = G.size() - 1;
+ for (uint64_t I = First; I <= Last; ++I)
+ G[I] |= static_cast<uint8_t>(Kind);
+ }
+
+ /// Classify a scalar leaf.
+ ///
+ /// Deliberate sugar quirks, both matching Apple's implementation:
+ /// * xnu_usage_semantics is only honoured when it sits on the immediate
+ /// typedef of the leaf type, so it survives exactly one typedef level.
+ /// * __ptrauth is read off the *local* qualifiers, so it is lost through
+ /// any typedef or __typeof__.
+ unsigned classify(QualType T) const {
+ if (const auto *TT = T->getAs<TypedefType>()) {
+ unsigned K = getAnnotatedKind(TT->getDecl());
+ if (K != NoKind)
+ return K;
+ }
+
+ if (T.getLocalQualifiers().getPointerAuth())
+ return XNU_GRANULE_PAC;
+
+ // _Atomic(void *) is still a pointer.
+ QualType V = T;
+ if (const auto *AT = V->getAs<AtomicType>())
+ V = AT->getValueType();
+
+ if (V->isPointerType() || V->isReferenceType() || V->isBlockPointerType() ||
+ V->isObjCObjectPointerType() || V->isNullPtrType())
+ return XNU_GRANULE_POINTER;
+
+ // Everything else, including member pointers, is data.
+ return XNU_GRANULE_DATA;
+ }
+
+ void visitType(QualType T, uint64_t BitOff) {
+ if (const ConstantArrayType *CAT = Ctx.getAsConstantArrayType(T)) {
+ uint64_t N = CAT->getSize().getZExtValue();
+ if (N == 0)
+ return;
+ QualType ET = CAT->getElementType();
+ uint64_t ESize = Ctx.getTypeSize(ET);
+ if (!ET->isRecordType() && !ET->isArrayType()) {
+ // Elements of an array are contiguous, so a scalar element type can be
+ // handled in one go; this keeps huge arrays cheap.
+ mark(BitOff, N * ESize, classify(ET));
+ return;
+ }
+ llvm::SaveAndRestore<bool> Save(IgnoreFieldAttrs, true);
+ for (uint64_t I = 0; I != N; ++I)
+ visitType(ET, BitOff + I * ESize);
+ return;
+ }
+
+ if (const RecordType *RT = T->getAs<RecordType>()) {
+ visitRecord(RT->getDecl(), BitOff, /*MostDerived=*/true);
+ return;
+ }
+
+ mark(BitOff, Ctx.getTypeSize(T), classify(T));
+ }
+
+ void visitRecord(const RecordDecl *RD, uint64_t BitOff, bool MostDerived) {
+ RD = RD->getDefinition();
+ if (!RD)
+ return;
+
+ const ASTRecordLayout &L = Ctx.getASTRecordLayout(RD);
+
+ if (const auto *CXXRD = dyn_cast<CXXRecordDecl>(RD)) {
+ // A vtable pointer introduced by this class is an ordinary pointer.
+ if (L.hasOwnVFPtr())
+ mark(BitOff, Ctx.getTargetInfo().getPointerWidth(LangAS::Default),
+ XNU_GRANULE_POINTER);
+
+ for (const CXXBaseSpecifier &B : CXXRD->bases()) {
+ if (B.isVirtual())
+ continue;
+ if (const CXXRecordDecl *BD = B.getType()->getAsCXXRecordDecl())
+ visitRecord(BD, BitOff + Ctx.toBits(L.getBaseClassOffset(BD)),
+ /*MostDerived=*/false);
+ }
+
+ // Virtual bases only exist once, in the most derived object.
+ if (MostDerived) {
+ for (const CXXBaseSpecifier &B : CXXRD->vbases()) {
+ if (const CXXRecordDecl *BD = B.getType()->getAsCXXRecordDecl())
+ visitRecord(BD, BitOff + Ctx.toBits(L.getVBaseClassOffset(BD)),
+ /*MostDerived=*/false);
+ }
+ }
+ }
+
+ for (const FieldDecl *FD : RD->fields()) {
+ // Flexible array members are silently ignored.
+ if (FD->getType()->isIncompleteArrayType())
+ continue;
+ if (FD->getType()->isVariablyModifiedType())
+ continue;
+
+ uint64_t Off = BitOff + L.getFieldOffset(FD->getFieldIndex());
+ unsigned K = IgnoreFieldAttrs ? NoKind : getAnnotatedKind(FD);
+
+ if (FD->isBitField()) {
+ if (K == NoKind)
+ K = classify(FD->getType());
+ mark(Off, FD->getBitWidthValue(), K);
+ continue;
+ }
+
+ if (K != NoKind) {
+ mark(Off, Ctx.getTypeSize(FD->getType()), K);
+ continue;
+ }
+
+ visitType(FD->getType(), Off);
+ }
+ }
+};
+
+} // namespace
+
+void clang::computeXnuTypeGranules(const ASTContext &Ctx, QualType T,
+ llvm::SmallVectorImpl<uint8_t> &Granules) {
+ Granules.clear();
+ if (T.isNull() || T->isIncompleteType() || T->isVariablyModifiedType() ||
+ T->isFunctionType())
+ return;
+
+ uint64_t Bytes = Ctx.getTypeSizeInChars(T).getQuantity();
+ Granules.assign((Bytes + 7) / 8, 0);
+
+ XnuGranuleComputer C(Ctx, Granules);
+ C.visitType(T, 0);
+
+ // A type with no scalar leaf at all has an empty signature, whatever its
+ // size: struct {}, struct { Empty e; }, struct { int a[0]; } ...
+ if (!C.sawLeaf())
+ Granules.clear();
+}
+
+void clang::getXnuTypeSignature(const ASTContext &Ctx, QualType T,
+ llvm::SmallVectorImpl<char> &Out) {
+ llvm::SmallVector<uint8_t, 32> Granules;
+ computeXnuTypeGranules(Ctx, T, Granules);
+ Out.clear();
+ Out.reserve(Granules.size());
+ for (uint8_t G : Granules)
+ Out.push_back("0123456789abcdef"[G & 0xf]);
+}
+
+uint64_t clang::getXnuTypeSummary(const ASTContext &Ctx, QualType T) {
+ llvm::SmallVector<uint8_t, 32> Granules;
+ computeXnuTypeGranules(Ctx, T, Granules);
+ uint64_t Summary = 0;
+ for (uint8_t G : Granules)
+ Summary |= uint64_t(1) << (G & 0xf);
+ return Summary;
+}
diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp
index 860c069..702a03f 100644
--- a/clang/lib/Parse/ParseExprCXX.cpp
+++ b/clang/lib/Parse/ParseExprCXX.cpp
@@ -3550,6 +3550,12 @@ ExprResult Parser::ParseTypeTrait() {
// Add this type to the list of arguments.
Args.push_back(Ty.get());
+
+ // The unary XNU type traits reject a second argument while parsing, the
+ // way sizeof does, rather than with an arity diagnostic.
+ TypeTrait Trait = TypeTraitFromTokKind(Kind);
+ if (Trait == UTT_XnuTypeSignature || Trait == UTT_XnuTypeSummary)
+ break;
} while (TryConsumeToken(tok::comma));
if (Parens.consumeClose())
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index eb4a8c2..d7c50b8 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -7559,6 +7559,60 @@ static void handlePersonalityAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
<< AL.getAttrName();
}
+static void handleXnuUsageSemanticsAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
+ // The annotation describes the granule class of a scalar leaf type, so it is
+ // only meaningful on a typedef or on a field, and never on a record type.
+ if (isa<TagDecl>(D)) {
+ S.Diag(AL.getLoc(), diag::err_xnu_usage_semantics_record) << AL;
+ return;
+ }
+
+ QualType T;
+ if (const auto *TD = dyn_cast<TypedefNameDecl>(D))
+ T = TD->getUnderlyingType();
+ else if (const auto *FD = dyn_cast<FieldDecl>(D))
+ T = FD->getType();
+ else {
+ S.Diag(AL.getLoc(), diag::err_xnu_usage_semantics_subject) << AL;
+ return;
+ }
+
+ QualType Elem = T;
+ while (const ArrayType *AT = S.Context.getAsArrayType(Elem))
+ Elem = AT->getElementType();
+ if (Elem->isRecordType()) {
+ S.Diag(AL.getLoc(), diag::err_xnu_usage_semantics_record) << AL;
+ return;
+ }
+
+ StringRef Kinds[2];
+ bool SeenPointer = false, SeenData = false;
+ for (unsigned I = 0, N = std::min(AL.getNumArgs(), 2u); I != N; ++I) {
+ StringRef Kind;
+ if (!S.checkStringLiteralArgumentAttr(AL, I, Kind))
+ return;
+
+ bool *Seen;
+ if (Kind == "pointer")
+ Seen = &SeenPointer;
+ else if (Kind == "data")
+ Seen = &SeenData;
+ else {
+ S.Diag(AL.getLoc(), diag::err_xnu_usage_semantics_invalid_kind) << Kind;
+ return;
+ }
+ if (*Seen) {
+ S.Diag(AL.getLoc(), diag::err_xnu_usage_semantics_repeated_kind) << Kind;
+ return;
+ }
+ *Seen = true;
+ Kinds[I] = Kind;
+ }
+
+ D->addAttr(::new (S.Context)
+ XnuUsageSemanticsAttr(S.Context, AL, Kinds[0], Kinds[1]));
+}
+
/// ProcessDeclAttribute - Apply the specific attribute to the specified decl if
/// the attribute applies to decls. If the attribute is a type attribute, just
/// silently ignore it if a GNU attribute.
@@ -7765,6 +7819,9 @@ ProcessDeclAttribute(Sema &S, Decl *D, const ParsedAttr &AL,
case ParsedAttr::AT_Aligned:
handleAlignedAttr(S, D, AL);
break;
+ case ParsedAttr::AT_XnuUsageSemantics:
+ handleXnuUsageSemanticsAttr(S, D, AL);
+ break;
case ParsedAttr::AT_AlignValue:
handleAlignValueAttr(S, D, AL);
break;
diff --git a/clang/lib/Sema/SemaTypeTraits.cpp b/clang/lib/Sema/SemaTypeTraits.cpp
index cb30ff9..d52fc47 100644
--- a/clang/lib/Sema/SemaTypeTraits.cpp
+++ b/clang/lib/Sema/SemaTypeTraits.cpp
@@ -15,6 +15,7 @@
#include "clang/AST/Mangle.h"
#include "clang/AST/TemplateBase.h"
#include "clang/AST/Type.h"
+#include "clang/AST/XnuTypeSignature.h"
#include "clang/Basic/BuiltinTraits.h"
#include "clang/Basic/DiagnosticIDs.h"
#include "clang/Basic/DiagnosticParse.h"
@@ -1525,12 +1526,141 @@ static TypeTraitReturnType GetReturnType(TypeTrait Kind) {
return TypeTraitReturnType::Bool;
}
+//===----------------------------------------------------------------------===//
+// XNU kalloc type signature traits.
+//
+// __builtin_xnu_type_signature(T) -> const char * (a string literal)
+// __builtin_xnu_type_summary(T) -> unsigned long
+// __builtin_xnu_types_compatible(A, B) -> bool / int
+//
+// These fold to literals in Sema, so neither ExprConstant nor CodeGen needs to
+// know about them; the signature is an ordinary string literal, which gives
+// the pointer identity for equal signatures that XNU relies on.
+//===----------------------------------------------------------------------===//
+
+/// Diagnose an operand that cannot have a granule signature. Returns true if
+/// a diagnostic was emitted.
+static bool CheckXnuTypeTraitOperand(Sema &S, TypeTrait Kind,
+ SourceLocation KWLoc,
+ const TypeSourceInfo *TSI) {
+ QualType T = TSI->getType();
+ // Apple points these diagnostics at the trait keyword, not at the operand.
+ SourceLocation Loc = KWLoc;
+
+ bool IsSummary = Kind == UTT_XnuTypeSummary;
+ StringRef Name = IsSummary ? "__builtin_xnu_type_summary"
+ : "__builtin_xnu_type_signature";
+
+ if (T->isVariablyModifiedType()) {
+ S.Diag(Loc, diag::err_xnu_type_trait_variable_array) << IsSummary << T;
+ return true;
+ }
+
+ if (S.RequireCompleteType(
+ Loc, T, diag::err_sizeof_alignof_incomplete_or_sizeless_type, Name,
+ /*Sizeless=*/0))
+ return true;
+
+ if (T->isFunctionType()) {
+ S.Diag(Loc, diag::err_sizeof_alignof_function_type) << Name;
+ return true;
+ }
+
+ return false;
+}
+
+static ExprResult BuildXnuUnaryTypeTrait(Sema &S, TypeTrait Kind,
+ SourceLocation KWLoc,
+ ArrayRef<TypeSourceInfo *> Args,
+ SourceLocation RParenLoc) {
+ ASTContext &Ctx = S.Context;
+ QualType T = Args[0]->getType();
+ bool Dependent = T->isDependentType();
+
+ if (!Dependent && CheckXnuTypeTraitOperand(S, Kind, KWLoc, Args[0]))
+ return ExprError();
+
+ if (Kind == UTT_XnuTypeSummary) {
+ APValue Value;
+ if (!Dependent) {
+ unsigned Width = Ctx.getIntWidth(Ctx.UnsignedLongTy);
+ Value = APValue(llvm::APSInt(
+ llvm::APInt(Width, getXnuTypeSummary(Ctx, T)), /*isUnsigned=*/true));
+ }
+ return TypeTraitExpr::Create(Ctx, Ctx.UnsignedLongTy, KWLoc, Kind, Args,
+ RParenLoc, Value);
+ }
+
+ QualType StrPtrTy = Ctx.getPointerType(Ctx.CharTy.withConst());
+
+ if (Dependent) {
+ // Defer to instantiation, exactly like the standard type traits: the
+ // TypeTraitExpr is value dependent while its operand is, and
+ // TreeTransform::TransformTypeTraitExpr re-enters BuildTypeTrait with the
+ // substituted type, which then folds to the string literal below. XNU
+ // needs this: IOOverrideTypedOperators() and the SMR macros apply the
+ // trait to a template parameter inside the constant initialiser of a
+ // kalloc_type view.
+ return TypeTraitExpr::Create(Ctx, StrPtrTy, KWLoc, Kind, Args, RParenLoc,
+ APValue());
+ }
+
+ SmallString<64> Sig;
+ getXnuTypeSignature(Ctx, T, Sig);
+
+ QualType ElemTy = Ctx.CharTy.withConst();
+ llvm::APInt Len(Ctx.getTypeSize(Ctx.getSizeType()), Sig.size() + 1);
+ QualType ArrTy = Ctx.getConstantArrayType(ElemTy, Len, /*SizeExpr=*/nullptr,
+ ArraySizeModifier::Normal,
+ /*IndexTypeQuals=*/0);
+ StringLiteral *SL = StringLiteral::Create(
+ Ctx, Sig, StringLiteralKind::Ordinary, /*Pascal=*/false, ArrTy, KWLoc);
+ return ImplicitCastExpr::Create(Ctx, StrPtrTy, CK_ArrayToPointerDecay, SL,
+ /*BasePath=*/nullptr, VK_PRValue,
+ FPOptionsOverride());
+}
+
+/// __builtin_xnu_types_compatible(A, B): true when A and B have the same
+/// granule signature. void is only compatible with itself, and short circuits
+/// before the completeness check so that the wildcard idiom in XNU's
+/// KALLOC_TYPE_IS_COMPATIBLE_PTR works.
+static bool EvaluateXnuTypesCompatible(Sema &S, const TypeSourceInfo *Lhs,
+ const TypeSourceInfo *Rhs,
+ SourceLocation KeyLoc) {
+ QualType LhsT = Lhs->getType();
+ QualType RhsT = Rhs->getType();
+
+ if (S.Context.hasSameUnqualifiedType(LhsT, RhsT))
+ return true;
+
+ if (LhsT->isVoidType() || RhsT->isVoidType() || LhsT->isFunctionType() ||
+ RhsT->isFunctionType() || LhsT->isVariablyModifiedType() ||
+ RhsT->isVariablyModifiedType())
+ return false;
+
+ // Apple reports this at the closing parenthesis.
+ if (S.RequireCompleteType(KeyLoc, LhsT,
+ diag::err_incomplete_type_used_in_type_trait_expr))
+ return false;
+ if (S.RequireCompleteType(KeyLoc, RhsT,
+ diag::err_incomplete_type_used_in_type_trait_expr))
+ return false;
+
+ SmallString<64> LhsSig, RhsSig;
+ getXnuTypeSignature(S.Context, LhsT, LhsSig);
+ getXnuTypeSignature(S.Context, RhsT, RhsSig);
+ return LhsSig == RhsSig;
+}
+
ExprResult Sema::BuildTypeTrait(TypeTrait Kind, SourceLocation KWLoc,
ArrayRef<TypeSourceInfo *> Args,
SourceLocation RParenLoc) {
if (!CheckTypeTraitArity(getTypeTraitArity(Kind), KWLoc, Args.size()))
return ExprError();
+ if (Kind == UTT_XnuTypeSignature || Kind == UTT_XnuTypeSummary)
+ return BuildXnuUnaryTypeTrait(*this, Kind, KWLoc, Args, RParenLoc);
+
if (Kind <= UTT_Last && !CheckUnaryTypeTraitTypeCompleteness(
*this, Kind, KWLoc, Args[0]->getType()))
return ExprError();
@@ -1675,6 +1805,8 @@ static bool EvaluateBinaryTypeTrait(Sema &Self, TypeTrait BTT,
return cast<CXXRecordDecl>(DerivedRecord->getDecl())
->isVirtuallyDerivedFrom(cast<CXXRecordDecl>(BaseRecord->getDecl()));
}
+ case BTT_XnuTypesCompatible:
+ return EvaluateXnuTypesCompatible(Self, Lhs, Rhs, KeyLoc);
case BTT_IsSame:
return Self.Context.hasSameType(LhsT, RhsT);
case BTT_TypeCompatible: {