Skip to content

Conversation

@melver
Copy link
Contributor

@melver melver commented Oct 15, 2025

Refactor the logic for inferring allocated types out of CodeGen and
into a new reusable component in clang/AST/InferAlloc.h.

This is a preparatory step for implementing __builtin_infer_alloc_token. By
moving the type inference heuristics into a common place, it can be shared
between the existing allocation-call instrumentation and the new builtin's
implementation.


This change is part of the following series:

  1. [AllocToken] Introduce llvm.alloc.token.id intrinsic #163632
  2. [AllocToken] Refactor stateless token calculation into Support #163633
  3. [AllocToken] Make token mode a pass parameter #163634
  4. [Clang] Move AllocToken frontend options to LangOptions #163635
  5. [Clang] Refactor allocation type inference logic #163636
  6. [Clang][Sema] Add __builtin_infer_alloc_token() declaration and semantic checks #163638
  7. [Clang] Implement constexpr evaluation for __builtin_infer_alloc_token() #163639
  8. [Clang][CodeGen] Implement code generation for __builtin_infer_alloc_token() #156842
Created using spr 1.3.8-beta.1 [skip ci]
Created using spr 1.3.8-beta.1
Created using spr 1.3.8-beta.1 [skip ci]
Created using spr 1.3.8-beta.1
@llvmbot
Copy link
Member

llvmbot commented Oct 15, 2025

@llvm/pr-subscribers-clang-codegen

Author: Marco Elver (melver)

Changes

Refactor the logic for inferring allocated types out of CodeGen and
into a new reusable component in clang/AST/InferAlloc.h.

This is a preparatory step for implementing __builtin_infer_alloc_token. By
moving the type inference heuristics into a common place, it can be shared
between the existing allocation-call instrumentation and the new builtin's
implementation.


This change is part of the following series:

  1. [AllocToken] Introduce llvm.alloc.token.id intrinsic #163632
  2. [AllocToken] Refactor stateless token calculation into Support #163633
  3. [AllocToken] Make token mode a pass parameter #163634
  4. [Clang] Move AllocToken frontend options to LangOptions #163635
  5. [Clang] Refactor allocation type inference logic #163636
  6. [Clang][Sema] Add __builtin_infer_alloc_token() declaration and semantic checks #163638
  7. [Clang] Implement constexpr evaluation for __builtin_infer_alloc_token() #163639
  8. [Clang][CodeGen] Implement code generation for __builtin_infer_alloc_token() #156842

Full diff: https://github.com/llvm/llvm-project/pull/163636.diff

6 Files Affected:

  • (added) clang/include/clang/AST/InferAlloc.h (+35)
  • (modified) clang/lib/AST/CMakeLists.txt (+1)
  • (added) clang/lib/AST/InferAlloc.cpp (+204)
  • (modified) clang/lib/CodeGen/CGExpr.cpp (+22-176)
  • (modified) clang/lib/CodeGen/CodeGenFunction.h (+7-2)
  • (modified) llvm/utils/gn/secondary/clang/lib/AST/BUILD.gn (+1)
diff --git a/clang/include/clang/AST/InferAlloc.h b/clang/include/clang/AST/InferAlloc.h new file mode 100644 index 0000000000000..c3dc30204feaf --- /dev/null +++ b/clang/include/clang/AST/InferAlloc.h @@ -0,0 +1,35 @@ +//===--- InferAlloc.h - Allocation type inference ---------------*- 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 +// +//===----------------------------------------------------------------------===// +// +// This file defines interfaces for allocation-related type inference. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_AST_INFERALLOC_H +#define LLVM_CLANG_AST_INFERALLOC_H + +#include "clang/AST/ASTContext.h" +#include "clang/AST/Expr.h" +#include "llvm/Support/AllocToken.h" +#include <optional> + +namespace clang { +namespace infer_alloc { + +/// Infer the possible allocated type from an allocation call expression. +QualType inferPossibleType(const CallExpr *E, const ASTContext &Ctx, + const CastExpr *CastE); + +/// Get the information required for construction of an allocation token ID. +std::optional<llvm::AllocTokenMetadata> +getAllocTokenMetadata(QualType T, const ASTContext &Ctx); + +} // namespace infer_alloc +} // namespace clang + +#endif // LLVM_CLANG_AST_INFERALLOC_H diff --git a/clang/lib/AST/CMakeLists.txt b/clang/lib/AST/CMakeLists.txt index d4fd7a7f16d53..fd50e956bb865 100644 --- a/clang/lib/AST/CMakeLists.txt +++ b/clang/lib/AST/CMakeLists.txt @@ -66,6 +66,7 @@ add_clang_library(clangAST ExternalASTMerger.cpp ExternalASTSource.cpp FormatString.cpp + InferAlloc.cpp InheritViz.cpp ByteCode/BitcastBuffer.cpp ByteCode/ByteCodeEmitter.cpp diff --git a/clang/lib/AST/InferAlloc.cpp b/clang/lib/AST/InferAlloc.cpp new file mode 100644 index 0000000000000..c21fcfccaef0f --- /dev/null +++ b/clang/lib/AST/InferAlloc.cpp @@ -0,0 +1,204 @@ +//===--- InferAlloc.cpp - Allocation type inference -----------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// This file implements allocation-related type inference. +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/InferAlloc.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/Expr.h" +#include "clang/AST/Type.h" +#include "clang/Basic/IdentifierTable.h" +#include "llvm/ADT/SmallPtrSet.h" + +namespace clang { +namespace { +bool typeContainsPointer(QualType T, + llvm::SmallPtrSet<const RecordDecl *, 4> &VisitedRD, + bool &IncompleteType) { + QualType CanonicalType = T.getCanonicalType(); + if (CanonicalType->isPointerType()) + return true; // base case + + // Look through typedef chain to check for special types. + for (QualType CurrentT = T; const auto *TT = CurrentT->getAs<TypedefType>(); + CurrentT = TT->getDecl()->getUnderlyingType()) { + const IdentifierInfo *II = TT->getDecl()->getIdentifier(); + // Special Case: Syntactically uintptr_t is not a pointer; semantically, + // however, very likely used as such. Therefore, classify uintptr_t as a + // pointer, too. + if (II && II->isStr("uintptr_t")) + return true; + } + + // The type is an array; check the element type. + if (const ArrayType *AT = dyn_cast<ArrayType>(CanonicalType)) + return typeContainsPointer(AT->getElementType(), VisitedRD, IncompleteType); + // The type is a struct, class, or union. + if (const RecordDecl *RD = CanonicalType->getAsRecordDecl()) { + if (!RD->isCompleteDefinition()) { + IncompleteType = true; + return false; + } + if (!VisitedRD.insert(RD).second) + return false; // already visited + // Check all fields. + for (const FieldDecl *Field : RD->fields()) { + if (typeContainsPointer(Field->getType(), VisitedRD, IncompleteType)) + return true; + } + // For C++ classes, also check base classes. + if (const CXXRecordDecl *CXXRD = dyn_cast<CXXRecordDecl>(RD)) { + // Polymorphic types require a vptr. + if (CXXRD->isDynamicClass()) + return true; + for (const CXXBaseSpecifier &Base : CXXRD->bases()) { + if (typeContainsPointer(Base.getType(), VisitedRD, IncompleteType)) + return true; + } + } + } + return false; +} + +/// Infer type from a simple sizeof expression. +QualType inferTypeFromSizeofExpr(const Expr *E) { + const Expr *Arg = E->IgnoreParenImpCasts(); + if (const auto *UET = dyn_cast<UnaryExprOrTypeTraitExpr>(Arg)) { + if (UET->getKind() == UETT_SizeOf) { + if (UET->isArgumentType()) + return UET->getArgumentTypeInfo()->getType(); + else + return UET->getArgumentExpr()->getType(); + } + } + return QualType(); +} + +/// Infer type from an arithmetic expression involving a sizeof. For example: +/// +/// malloc(sizeof(MyType) + padding); // infers 'MyType' +/// malloc(sizeof(MyType) * 32); // infers 'MyType' +/// malloc(32 * sizeof(MyType)); // infers 'MyType' +/// malloc(sizeof(MyType) << 1); // infers 'MyType' +/// ... +/// +/// More complex arithmetic expressions are supported, but are a heuristic, e.g. +/// when considering allocations for structs with flexible array members: +/// +/// malloc(sizeof(HasFlexArray) + sizeof(int) * 32); // infers 'HasFlexArray' +/// +QualType inferPossibleTypeFromArithSizeofExpr(const Expr *E) { + const Expr *Arg = E->IgnoreParenImpCasts(); + // The argument is a lone sizeof expression. + if (QualType T = inferTypeFromSizeofExpr(Arg); !T.isNull()) + return T; + if (const auto *BO = dyn_cast<BinaryOperator>(Arg)) { + // Argument is an arithmetic expression. Cover common arithmetic patterns + // involving sizeof. + switch (BO->getOpcode()) { + case BO_Add: + case BO_Div: + case BO_Mul: + case BO_Shl: + case BO_Shr: + case BO_Sub: + if (QualType T = inferPossibleTypeFromArithSizeofExpr(BO->getLHS()); + !T.isNull()) + return T; + if (QualType T = inferPossibleTypeFromArithSizeofExpr(BO->getRHS()); + !T.isNull()) + return T; + break; + default: + break; + } + } + return QualType(); +} + +/// If the expression E is a reference to a variable, infer the type from a +/// variable's initializer if it contains a sizeof. Beware, this is a heuristic +/// and ignores if a variable is later reassigned. For example: +/// +/// size_t my_size = sizeof(MyType); +/// void *x = malloc(my_size); // infers 'MyType' +/// +QualType inferPossibleTypeFromVarInitSizeofExpr(const Expr *E) { + const Expr *Arg = E->IgnoreParenImpCasts(); + if (const auto *DRE = dyn_cast<DeclRefExpr>(Arg)) { + if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl())) { + if (const Expr *Init = VD->getInit()) + return inferPossibleTypeFromArithSizeofExpr(Init); + } + } + return QualType(); +} + +/// Deduces the allocated type by checking if the allocation call's result +/// is immediately used in a cast expression. For example: +/// +/// MyType *x = (MyType *)malloc(4096); // infers 'MyType' +/// +QualType inferPossibleTypeFromCastExpr(const CallExpr *CallE, + const CastExpr *CastE) { + if (!CastE) + return QualType(); + QualType PtrType = CastE->getType(); + if (PtrType->isPointerType()) + return PtrType->getPointeeType(); + return QualType(); +} +} // anonymous namespace + +namespace infer_alloc { + +QualType inferPossibleType(const CallExpr *E, const ASTContext &Ctx, + const CastExpr *CastE) { + QualType AllocType; + // First check arguments. + for (const Expr *Arg : E->arguments()) { + AllocType = inferPossibleTypeFromArithSizeofExpr(Arg); + if (AllocType.isNull()) + AllocType = inferPossibleTypeFromVarInitSizeofExpr(Arg); + if (!AllocType.isNull()) + break; + } + // Then check later casts. + if (AllocType.isNull()) + AllocType = inferPossibleTypeFromCastExpr(E, CastE); + return AllocType; +} + +std::optional<llvm::AllocTokenMetadata> +getAllocTokenMetadata(QualType T, const ASTContext &Ctx) { + llvm::AllocTokenMetadata ATMD; + + // Get unique type name. + PrintingPolicy Policy(Ctx.getLangOpts()); + Policy.SuppressTagKeyword = true; + Policy.FullyQualifiedName = true; + llvm::raw_svector_ostream TypeNameOS(ATMD.TypeName); + T.getCanonicalType().print(TypeNameOS, Policy); + + // Check if QualType contains a pointer. Implements a simple DFS to + // recursively check if a type contains a pointer type. + llvm::SmallPtrSet<const RecordDecl *, 4> VisitedRD; + bool IncompleteType = false; + ATMD.ContainsPointer = typeContainsPointer(T, VisitedRD, IncompleteType); + if (!ATMD.ContainsPointer && IncompleteType) + return std::nullopt; + + return ATMD; +} + +} // namespace infer_alloc +} // namespace clang diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index e8255b0554da8..0a2f57dc119b6 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -29,6 +29,7 @@ #include "clang/AST/ASTLambda.h" #include "clang/AST/Attr.h" #include "clang/AST/DeclObjC.h" +#include "clang/AST/InferAlloc.h" #include "clang/AST/NSAPI.h" #include "clang/AST/ParentMapContext.h" #include "clang/AST/StmtVisitor.h" @@ -1273,194 +1274,39 @@ void CodeGenFunction::EmitBoundsCheckImpl(const Expr *E, llvm::Value *Bound, EmitCheck(std::make_pair(Check, CheckKind), CheckHandler, StaticData, Index); } -static bool -typeContainsPointer(QualType T, - llvm::SmallPtrSet<const RecordDecl *, 4> &VisitedRD, - bool &IncompleteType) { - QualType CanonicalType = T.getCanonicalType(); - if (CanonicalType->isPointerType()) - return true; // base case - - // Look through typedef chain to check for special types. - for (QualType CurrentT = T; const auto *TT = CurrentT->getAs<TypedefType>(); - CurrentT = TT->getDecl()->getUnderlyingType()) { - const IdentifierInfo *II = TT->getDecl()->getIdentifier(); - // Special Case: Syntactically uintptr_t is not a pointer; semantically, - // however, very likely used as such. Therefore, classify uintptr_t as a - // pointer, too. - if (II && II->isStr("uintptr_t")) - return true; - } - - // The type is an array; check the element type. - if (const ArrayType *AT = dyn_cast<ArrayType>(CanonicalType)) - return typeContainsPointer(AT->getElementType(), VisitedRD, IncompleteType); - // The type is a struct, class, or union. - if (const RecordDecl *RD = CanonicalType->getAsRecordDecl()) { - if (!RD->isCompleteDefinition()) { - IncompleteType = true; - return false; - } - if (!VisitedRD.insert(RD).second) - return false; // already visited - // Check all fields. - for (const FieldDecl *Field : RD->fields()) { - if (typeContainsPointer(Field->getType(), VisitedRD, IncompleteType)) - return true; - } - // For C++ classes, also check base classes. - if (const CXXRecordDecl *CXXRD = dyn_cast<CXXRecordDecl>(RD)) { - // Polymorphic types require a vptr. - if (CXXRD->isDynamicClass()) - return true; - for (const CXXBaseSpecifier &Base : CXXRD->bases()) { - if (typeContainsPointer(Base.getType(), VisitedRD, IncompleteType)) - return true; - } - } - } - return false; -} - -void CodeGenFunction::EmitAllocToken(llvm::CallBase *CB, QualType AllocType) { - assert(SanOpts.has(SanitizerKind::AllocToken) && - "Only needed with -fsanitize=alloc-token"); +llvm::MDNode *CodeGenFunction::buildAllocToken(QualType AllocType) { + auto ATMD = infer_alloc::getAllocTokenMetadata(AllocType, getContext()); + if (!ATMD) + return nullptr; llvm::MDBuilder MDB(getLLVMContext()); - - // Get unique type name. - PrintingPolicy Policy(CGM.getContext().getLangOpts()); - Policy.SuppressTagKeyword = true; - Policy.FullyQualifiedName = true; - SmallString<64> TypeName; - llvm::raw_svector_ostream TypeNameOS(TypeName); - AllocType.getCanonicalType().print(TypeNameOS, Policy); - auto *TypeNameMD = MDB.createString(TypeNameOS.str()); - - // Check if QualType contains a pointer. Implements a simple DFS to - // recursively check if a type contains a pointer type. - llvm::SmallPtrSet<const RecordDecl *, 4> VisitedRD; - bool IncompleteType = false; - const bool ContainsPtr = - typeContainsPointer(AllocType, VisitedRD, IncompleteType); - if (!ContainsPtr && IncompleteType) - return; - auto *ContainsPtrC = Builder.getInt1(ContainsPtr); + auto *TypeNameMD = MDB.createString(ATMD->TypeName); + auto *ContainsPtrC = Builder.getInt1(ATMD->ContainsPointer); auto *ContainsPtrMD = MDB.createConstant(ContainsPtrC); // Format: !{<type-name>, <contains-pointer>} - auto *MDN = - llvm::MDNode::get(CGM.getLLVMContext(), {TypeNameMD, ContainsPtrMD}); - CB->setMetadata(llvm::LLVMContext::MD_alloc_token, MDN); -} - -namespace { -/// Infer type from a simple sizeof expression. -QualType inferTypeFromSizeofExpr(const Expr *E) { - const Expr *Arg = E->IgnoreParenImpCasts(); - if (const auto *UET = dyn_cast<UnaryExprOrTypeTraitExpr>(Arg)) { - if (UET->getKind() == UETT_SizeOf) { - if (UET->isArgumentType()) - return UET->getArgumentTypeInfo()->getType(); - else - return UET->getArgumentExpr()->getType(); - } - } - return QualType(); -} - -/// Infer type from an arithmetic expression involving a sizeof. For example: -/// -/// malloc(sizeof(MyType) + padding); // infers 'MyType' -/// malloc(sizeof(MyType) * 32); // infers 'MyType' -/// malloc(32 * sizeof(MyType)); // infers 'MyType' -/// malloc(sizeof(MyType) << 1); // infers 'MyType' -/// ... -/// -/// More complex arithmetic expressions are supported, but are a heuristic, e.g. -/// when considering allocations for structs with flexible array members: -/// -/// malloc(sizeof(HasFlexArray) + sizeof(int) * 32); // infers 'HasFlexArray' -/// -QualType inferPossibleTypeFromArithSizeofExpr(const Expr *E) { - const Expr *Arg = E->IgnoreParenImpCasts(); - // The argument is a lone sizeof expression. - if (QualType T = inferTypeFromSizeofExpr(Arg); !T.isNull()) - return T; - if (const auto *BO = dyn_cast<BinaryOperator>(Arg)) { - // Argument is an arithmetic expression. Cover common arithmetic patterns - // involving sizeof. - switch (BO->getOpcode()) { - case BO_Add: - case BO_Div: - case BO_Mul: - case BO_Shl: - case BO_Shr: - case BO_Sub: - if (QualType T = inferPossibleTypeFromArithSizeofExpr(BO->getLHS()); - !T.isNull()) - return T; - if (QualType T = inferPossibleTypeFromArithSizeofExpr(BO->getRHS()); - !T.isNull()) - return T; - break; - default: - break; - } - } - return QualType(); + return llvm::MDNode::get(CGM.getLLVMContext(), {TypeNameMD, ContainsPtrMD}); } -/// If the expression E is a reference to a variable, infer the type from a -/// variable's initializer if it contains a sizeof. Beware, this is a heuristic -/// and ignores if a variable is later reassigned. For example: -/// -/// size_t my_size = sizeof(MyType); -/// void *x = malloc(my_size); // infers 'MyType' -/// -QualType inferPossibleTypeFromVarInitSizeofExpr(const Expr *E) { - const Expr *Arg = E->IgnoreParenImpCasts(); - if (const auto *DRE = dyn_cast<DeclRefExpr>(Arg)) { - if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl())) { - if (const Expr *Init = VD->getInit()) - return inferPossibleTypeFromArithSizeofExpr(Init); - } - } - return QualType(); +void CodeGenFunction::EmitAllocToken(llvm::CallBase *CB, QualType AllocType) { + assert(SanOpts.has(SanitizerKind::AllocToken) && + "Only needed with -fsanitize=alloc-token"); + CB->setMetadata(llvm::LLVMContext::MD_alloc_token, + buildAllocToken(AllocType)); } -/// Deduces the allocated type by checking if the allocation call's result -/// is immediately used in a cast expression. For example: -/// -/// MyType *x = (MyType *)malloc(4096); // infers 'MyType' -/// -QualType inferPossibleTypeFromCastExpr(const CallExpr *CallE, - const CastExpr *CastE) { - if (!CastE) - return QualType(); - QualType PtrType = CastE->getType(); - if (PtrType->isPointerType()) - return PtrType->getPointeeType(); - return QualType(); +llvm::MDNode *CodeGenFunction::buildAllocToken(const CallExpr *E) { + QualType AllocType = infer_alloc::inferPossibleType(E, getContext(), CurCast); + if (!AllocType.isNull()) + return buildAllocToken(AllocType); + return nullptr; } -} // end anonymous namespace void CodeGenFunction::EmitAllocToken(llvm::CallBase *CB, const CallExpr *E) { - QualType AllocType; - // First check arguments. - for (const Expr *Arg : E->arguments()) { - AllocType = inferPossibleTypeFromArithSizeofExpr(Arg); - if (AllocType.isNull()) - AllocType = inferPossibleTypeFromVarInitSizeofExpr(Arg); - if (!AllocType.isNull()) - break; - } - // Then check later casts. - if (AllocType.isNull()) - AllocType = inferPossibleTypeFromCastExpr(E, CurCast); - // Emit if we were able to infer the type. - if (!AllocType.isNull()) - EmitAllocToken(CB, AllocType); + assert(SanOpts.has(SanitizerKind::AllocToken) && + "Only needed with -fsanitize=alloc-token"); + if (llvm::MDNode *MDN = buildAllocToken(E)) + CB->setMetadata(llvm::LLVMContext::MD_alloc_token, MDN); } CodeGenFunction::ComplexPairTy CodeGenFunction:: diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h index 1f0be2d8756de..8c4c1c8c2dc95 100644 --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -3352,9 +3352,14 @@ class CodeGenFunction : public CodeGenTypeCache { SanitizerAnnotateDebugInfo(ArrayRef<SanitizerKind::SanitizerOrdinal> Ordinals, SanitizerHandler Handler); - /// Emit additional metadata used by the AllocToken instrumentation. + /// Build metadata used by the AllocToken instrumentation. + llvm::MDNode *buildAllocToken(QualType AllocType); + /// Emit and set additional metadata used by the AllocToken instrumentation. void EmitAllocToken(llvm::CallBase *CB, QualType AllocType); - /// Emit additional metadata used by the AllocToken instrumentation, + /// Build additional metadata used by the AllocToken instrumentation, + /// inferring the type from an allocation call expression. + llvm::MDNode *buildAllocToken(const CallExpr *E); + /// Emit and set additional metadata used by the AllocToken instrumentation, /// inferring the type from an allocation call expression. void EmitAllocToken(llvm::CallBase *CB, const CallExpr *E); diff --git a/llvm/utils/gn/secondary/clang/lib/AST/BUILD.gn b/llvm/utils/gn/secondary/clang/lib/AST/BUILD.gn index 9981d100fd555..4da907cbdd938 100644 --- a/llvm/utils/gn/secondary/clang/lib/AST/BUILD.gn +++ b/llvm/utils/gn/secondary/clang/lib/AST/BUILD.gn @@ -121,6 +121,7 @@ static_library("AST") { "ExternalASTMerger.cpp", "ExternalASTSource.cpp", "FormatString.cpp", + "InferAlloc.cpp", "InheritViz.cpp", "ItaniumCXXABI.cpp", "ItaniumMangle.cpp", 
@llvmbot
Copy link
Member

llvmbot commented Oct 15, 2025

@llvm/pr-subscribers-clang

Author: Marco Elver (melver)

Changes

Refactor the logic for inferring allocated types out of CodeGen and
into a new reusable component in clang/AST/InferAlloc.h.

This is a preparatory step for implementing __builtin_infer_alloc_token. By
moving the type inference heuristics into a common place, it can be shared
between the existing allocation-call instrumentation and the new builtin's
implementation.


This change is part of the following series:

  1. [AllocToken] Introduce llvm.alloc.token.id intrinsic #163632
  2. [AllocToken] Refactor stateless token calculation into Support #163633
  3. [AllocToken] Make token mode a pass parameter #163634
  4. [Clang] Move AllocToken frontend options to LangOptions #163635
  5. [Clang] Refactor allocation type inference logic #163636
  6. [Clang][Sema] Add __builtin_infer_alloc_token() declaration and semantic checks #163638
  7. [Clang] Implement constexpr evaluation for __builtin_infer_alloc_token() #163639
  8. [Clang][CodeGen] Implement code generation for __builtin_infer_alloc_token() #156842

Full diff: https://github.com/llvm/llvm-project/pull/163636.diff

6 Files Affected:

  • (added) clang/include/clang/AST/InferAlloc.h (+35)
  • (modified) clang/lib/AST/CMakeLists.txt (+1)
  • (added) clang/lib/AST/InferAlloc.cpp (+204)
  • (modified) clang/lib/CodeGen/CGExpr.cpp (+22-176)
  • (modified) clang/lib/CodeGen/CodeGenFunction.h (+7-2)
  • (modified) llvm/utils/gn/secondary/clang/lib/AST/BUILD.gn (+1)
diff --git a/clang/include/clang/AST/InferAlloc.h b/clang/include/clang/AST/InferAlloc.h new file mode 100644 index 0000000000000..c3dc30204feaf --- /dev/null +++ b/clang/include/clang/AST/InferAlloc.h @@ -0,0 +1,35 @@ +//===--- InferAlloc.h - Allocation type inference ---------------*- 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 +// +//===----------------------------------------------------------------------===// +// +// This file defines interfaces for allocation-related type inference. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_AST_INFERALLOC_H +#define LLVM_CLANG_AST_INFERALLOC_H + +#include "clang/AST/ASTContext.h" +#include "clang/AST/Expr.h" +#include "llvm/Support/AllocToken.h" +#include <optional> + +namespace clang { +namespace infer_alloc { + +/// Infer the possible allocated type from an allocation call expression. +QualType inferPossibleType(const CallExpr *E, const ASTContext &Ctx, + const CastExpr *CastE); + +/// Get the information required for construction of an allocation token ID. +std::optional<llvm::AllocTokenMetadata> +getAllocTokenMetadata(QualType T, const ASTContext &Ctx); + +} // namespace infer_alloc +} // namespace clang + +#endif // LLVM_CLANG_AST_INFERALLOC_H diff --git a/clang/lib/AST/CMakeLists.txt b/clang/lib/AST/CMakeLists.txt index d4fd7a7f16d53..fd50e956bb865 100644 --- a/clang/lib/AST/CMakeLists.txt +++ b/clang/lib/AST/CMakeLists.txt @@ -66,6 +66,7 @@ add_clang_library(clangAST ExternalASTMerger.cpp ExternalASTSource.cpp FormatString.cpp + InferAlloc.cpp InheritViz.cpp ByteCode/BitcastBuffer.cpp ByteCode/ByteCodeEmitter.cpp diff --git a/clang/lib/AST/InferAlloc.cpp b/clang/lib/AST/InferAlloc.cpp new file mode 100644 index 0000000000000..c21fcfccaef0f --- /dev/null +++ b/clang/lib/AST/InferAlloc.cpp @@ -0,0 +1,204 @@ +//===--- InferAlloc.cpp - Allocation type inference -----------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// This file implements allocation-related type inference. +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/InferAlloc.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/Expr.h" +#include "clang/AST/Type.h" +#include "clang/Basic/IdentifierTable.h" +#include "llvm/ADT/SmallPtrSet.h" + +namespace clang { +namespace { +bool typeContainsPointer(QualType T, + llvm::SmallPtrSet<const RecordDecl *, 4> &VisitedRD, + bool &IncompleteType) { + QualType CanonicalType = T.getCanonicalType(); + if (CanonicalType->isPointerType()) + return true; // base case + + // Look through typedef chain to check for special types. + for (QualType CurrentT = T; const auto *TT = CurrentT->getAs<TypedefType>(); + CurrentT = TT->getDecl()->getUnderlyingType()) { + const IdentifierInfo *II = TT->getDecl()->getIdentifier(); + // Special Case: Syntactically uintptr_t is not a pointer; semantically, + // however, very likely used as such. Therefore, classify uintptr_t as a + // pointer, too. + if (II && II->isStr("uintptr_t")) + return true; + } + + // The type is an array; check the element type. + if (const ArrayType *AT = dyn_cast<ArrayType>(CanonicalType)) + return typeContainsPointer(AT->getElementType(), VisitedRD, IncompleteType); + // The type is a struct, class, or union. + if (const RecordDecl *RD = CanonicalType->getAsRecordDecl()) { + if (!RD->isCompleteDefinition()) { + IncompleteType = true; + return false; + } + if (!VisitedRD.insert(RD).second) + return false; // already visited + // Check all fields. + for (const FieldDecl *Field : RD->fields()) { + if (typeContainsPointer(Field->getType(), VisitedRD, IncompleteType)) + return true; + } + // For C++ classes, also check base classes. + if (const CXXRecordDecl *CXXRD = dyn_cast<CXXRecordDecl>(RD)) { + // Polymorphic types require a vptr. + if (CXXRD->isDynamicClass()) + return true; + for (const CXXBaseSpecifier &Base : CXXRD->bases()) { + if (typeContainsPointer(Base.getType(), VisitedRD, IncompleteType)) + return true; + } + } + } + return false; +} + +/// Infer type from a simple sizeof expression. +QualType inferTypeFromSizeofExpr(const Expr *E) { + const Expr *Arg = E->IgnoreParenImpCasts(); + if (const auto *UET = dyn_cast<UnaryExprOrTypeTraitExpr>(Arg)) { + if (UET->getKind() == UETT_SizeOf) { + if (UET->isArgumentType()) + return UET->getArgumentTypeInfo()->getType(); + else + return UET->getArgumentExpr()->getType(); + } + } + return QualType(); +} + +/// Infer type from an arithmetic expression involving a sizeof. For example: +/// +/// malloc(sizeof(MyType) + padding); // infers 'MyType' +/// malloc(sizeof(MyType) * 32); // infers 'MyType' +/// malloc(32 * sizeof(MyType)); // infers 'MyType' +/// malloc(sizeof(MyType) << 1); // infers 'MyType' +/// ... +/// +/// More complex arithmetic expressions are supported, but are a heuristic, e.g. +/// when considering allocations for structs with flexible array members: +/// +/// malloc(sizeof(HasFlexArray) + sizeof(int) * 32); // infers 'HasFlexArray' +/// +QualType inferPossibleTypeFromArithSizeofExpr(const Expr *E) { + const Expr *Arg = E->IgnoreParenImpCasts(); + // The argument is a lone sizeof expression. + if (QualType T = inferTypeFromSizeofExpr(Arg); !T.isNull()) + return T; + if (const auto *BO = dyn_cast<BinaryOperator>(Arg)) { + // Argument is an arithmetic expression. Cover common arithmetic patterns + // involving sizeof. + switch (BO->getOpcode()) { + case BO_Add: + case BO_Div: + case BO_Mul: + case BO_Shl: + case BO_Shr: + case BO_Sub: + if (QualType T = inferPossibleTypeFromArithSizeofExpr(BO->getLHS()); + !T.isNull()) + return T; + if (QualType T = inferPossibleTypeFromArithSizeofExpr(BO->getRHS()); + !T.isNull()) + return T; + break; + default: + break; + } + } + return QualType(); +} + +/// If the expression E is a reference to a variable, infer the type from a +/// variable's initializer if it contains a sizeof. Beware, this is a heuristic +/// and ignores if a variable is later reassigned. For example: +/// +/// size_t my_size = sizeof(MyType); +/// void *x = malloc(my_size); // infers 'MyType' +/// +QualType inferPossibleTypeFromVarInitSizeofExpr(const Expr *E) { + const Expr *Arg = E->IgnoreParenImpCasts(); + if (const auto *DRE = dyn_cast<DeclRefExpr>(Arg)) { + if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl())) { + if (const Expr *Init = VD->getInit()) + return inferPossibleTypeFromArithSizeofExpr(Init); + } + } + return QualType(); +} + +/// Deduces the allocated type by checking if the allocation call's result +/// is immediately used in a cast expression. For example: +/// +/// MyType *x = (MyType *)malloc(4096); // infers 'MyType' +/// +QualType inferPossibleTypeFromCastExpr(const CallExpr *CallE, + const CastExpr *CastE) { + if (!CastE) + return QualType(); + QualType PtrType = CastE->getType(); + if (PtrType->isPointerType()) + return PtrType->getPointeeType(); + return QualType(); +} +} // anonymous namespace + +namespace infer_alloc { + +QualType inferPossibleType(const CallExpr *E, const ASTContext &Ctx, + const CastExpr *CastE) { + QualType AllocType; + // First check arguments. + for (const Expr *Arg : E->arguments()) { + AllocType = inferPossibleTypeFromArithSizeofExpr(Arg); + if (AllocType.isNull()) + AllocType = inferPossibleTypeFromVarInitSizeofExpr(Arg); + if (!AllocType.isNull()) + break; + } + // Then check later casts. + if (AllocType.isNull()) + AllocType = inferPossibleTypeFromCastExpr(E, CastE); + return AllocType; +} + +std::optional<llvm::AllocTokenMetadata> +getAllocTokenMetadata(QualType T, const ASTContext &Ctx) { + llvm::AllocTokenMetadata ATMD; + + // Get unique type name. + PrintingPolicy Policy(Ctx.getLangOpts()); + Policy.SuppressTagKeyword = true; + Policy.FullyQualifiedName = true; + llvm::raw_svector_ostream TypeNameOS(ATMD.TypeName); + T.getCanonicalType().print(TypeNameOS, Policy); + + // Check if QualType contains a pointer. Implements a simple DFS to + // recursively check if a type contains a pointer type. + llvm::SmallPtrSet<const RecordDecl *, 4> VisitedRD; + bool IncompleteType = false; + ATMD.ContainsPointer = typeContainsPointer(T, VisitedRD, IncompleteType); + if (!ATMD.ContainsPointer && IncompleteType) + return std::nullopt; + + return ATMD; +} + +} // namespace infer_alloc +} // namespace clang diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index e8255b0554da8..0a2f57dc119b6 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -29,6 +29,7 @@ #include "clang/AST/ASTLambda.h" #include "clang/AST/Attr.h" #include "clang/AST/DeclObjC.h" +#include "clang/AST/InferAlloc.h" #include "clang/AST/NSAPI.h" #include "clang/AST/ParentMapContext.h" #include "clang/AST/StmtVisitor.h" @@ -1273,194 +1274,39 @@ void CodeGenFunction::EmitBoundsCheckImpl(const Expr *E, llvm::Value *Bound, EmitCheck(std::make_pair(Check, CheckKind), CheckHandler, StaticData, Index); } -static bool -typeContainsPointer(QualType T, - llvm::SmallPtrSet<const RecordDecl *, 4> &VisitedRD, - bool &IncompleteType) { - QualType CanonicalType = T.getCanonicalType(); - if (CanonicalType->isPointerType()) - return true; // base case - - // Look through typedef chain to check for special types. - for (QualType CurrentT = T; const auto *TT = CurrentT->getAs<TypedefType>(); - CurrentT = TT->getDecl()->getUnderlyingType()) { - const IdentifierInfo *II = TT->getDecl()->getIdentifier(); - // Special Case: Syntactically uintptr_t is not a pointer; semantically, - // however, very likely used as such. Therefore, classify uintptr_t as a - // pointer, too. - if (II && II->isStr("uintptr_t")) - return true; - } - - // The type is an array; check the element type. - if (const ArrayType *AT = dyn_cast<ArrayType>(CanonicalType)) - return typeContainsPointer(AT->getElementType(), VisitedRD, IncompleteType); - // The type is a struct, class, or union. - if (const RecordDecl *RD = CanonicalType->getAsRecordDecl()) { - if (!RD->isCompleteDefinition()) { - IncompleteType = true; - return false; - } - if (!VisitedRD.insert(RD).second) - return false; // already visited - // Check all fields. - for (const FieldDecl *Field : RD->fields()) { - if (typeContainsPointer(Field->getType(), VisitedRD, IncompleteType)) - return true; - } - // For C++ classes, also check base classes. - if (const CXXRecordDecl *CXXRD = dyn_cast<CXXRecordDecl>(RD)) { - // Polymorphic types require a vptr. - if (CXXRD->isDynamicClass()) - return true; - for (const CXXBaseSpecifier &Base : CXXRD->bases()) { - if (typeContainsPointer(Base.getType(), VisitedRD, IncompleteType)) - return true; - } - } - } - return false; -} - -void CodeGenFunction::EmitAllocToken(llvm::CallBase *CB, QualType AllocType) { - assert(SanOpts.has(SanitizerKind::AllocToken) && - "Only needed with -fsanitize=alloc-token"); +llvm::MDNode *CodeGenFunction::buildAllocToken(QualType AllocType) { + auto ATMD = infer_alloc::getAllocTokenMetadata(AllocType, getContext()); + if (!ATMD) + return nullptr; llvm::MDBuilder MDB(getLLVMContext()); - - // Get unique type name. - PrintingPolicy Policy(CGM.getContext().getLangOpts()); - Policy.SuppressTagKeyword = true; - Policy.FullyQualifiedName = true; - SmallString<64> TypeName; - llvm::raw_svector_ostream TypeNameOS(TypeName); - AllocType.getCanonicalType().print(TypeNameOS, Policy); - auto *TypeNameMD = MDB.createString(TypeNameOS.str()); - - // Check if QualType contains a pointer. Implements a simple DFS to - // recursively check if a type contains a pointer type. - llvm::SmallPtrSet<const RecordDecl *, 4> VisitedRD; - bool IncompleteType = false; - const bool ContainsPtr = - typeContainsPointer(AllocType, VisitedRD, IncompleteType); - if (!ContainsPtr && IncompleteType) - return; - auto *ContainsPtrC = Builder.getInt1(ContainsPtr); + auto *TypeNameMD = MDB.createString(ATMD->TypeName); + auto *ContainsPtrC = Builder.getInt1(ATMD->ContainsPointer); auto *ContainsPtrMD = MDB.createConstant(ContainsPtrC); // Format: !{<type-name>, <contains-pointer>} - auto *MDN = - llvm::MDNode::get(CGM.getLLVMContext(), {TypeNameMD, ContainsPtrMD}); - CB->setMetadata(llvm::LLVMContext::MD_alloc_token, MDN); -} - -namespace { -/// Infer type from a simple sizeof expression. -QualType inferTypeFromSizeofExpr(const Expr *E) { - const Expr *Arg = E->IgnoreParenImpCasts(); - if (const auto *UET = dyn_cast<UnaryExprOrTypeTraitExpr>(Arg)) { - if (UET->getKind() == UETT_SizeOf) { - if (UET->isArgumentType()) - return UET->getArgumentTypeInfo()->getType(); - else - return UET->getArgumentExpr()->getType(); - } - } - return QualType(); -} - -/// Infer type from an arithmetic expression involving a sizeof. For example: -/// -/// malloc(sizeof(MyType) + padding); // infers 'MyType' -/// malloc(sizeof(MyType) * 32); // infers 'MyType' -/// malloc(32 * sizeof(MyType)); // infers 'MyType' -/// malloc(sizeof(MyType) << 1); // infers 'MyType' -/// ... -/// -/// More complex arithmetic expressions are supported, but are a heuristic, e.g. -/// when considering allocations for structs with flexible array members: -/// -/// malloc(sizeof(HasFlexArray) + sizeof(int) * 32); // infers 'HasFlexArray' -/// -QualType inferPossibleTypeFromArithSizeofExpr(const Expr *E) { - const Expr *Arg = E->IgnoreParenImpCasts(); - // The argument is a lone sizeof expression. - if (QualType T = inferTypeFromSizeofExpr(Arg); !T.isNull()) - return T; - if (const auto *BO = dyn_cast<BinaryOperator>(Arg)) { - // Argument is an arithmetic expression. Cover common arithmetic patterns - // involving sizeof. - switch (BO->getOpcode()) { - case BO_Add: - case BO_Div: - case BO_Mul: - case BO_Shl: - case BO_Shr: - case BO_Sub: - if (QualType T = inferPossibleTypeFromArithSizeofExpr(BO->getLHS()); - !T.isNull()) - return T; - if (QualType T = inferPossibleTypeFromArithSizeofExpr(BO->getRHS()); - !T.isNull()) - return T; - break; - default: - break; - } - } - return QualType(); + return llvm::MDNode::get(CGM.getLLVMContext(), {TypeNameMD, ContainsPtrMD}); } -/// If the expression E is a reference to a variable, infer the type from a -/// variable's initializer if it contains a sizeof. Beware, this is a heuristic -/// and ignores if a variable is later reassigned. For example: -/// -/// size_t my_size = sizeof(MyType); -/// void *x = malloc(my_size); // infers 'MyType' -/// -QualType inferPossibleTypeFromVarInitSizeofExpr(const Expr *E) { - const Expr *Arg = E->IgnoreParenImpCasts(); - if (const auto *DRE = dyn_cast<DeclRefExpr>(Arg)) { - if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl())) { - if (const Expr *Init = VD->getInit()) - return inferPossibleTypeFromArithSizeofExpr(Init); - } - } - return QualType(); +void CodeGenFunction::EmitAllocToken(llvm::CallBase *CB, QualType AllocType) { + assert(SanOpts.has(SanitizerKind::AllocToken) && + "Only needed with -fsanitize=alloc-token"); + CB->setMetadata(llvm::LLVMContext::MD_alloc_token, + buildAllocToken(AllocType)); } -/// Deduces the allocated type by checking if the allocation call's result -/// is immediately used in a cast expression. For example: -/// -/// MyType *x = (MyType *)malloc(4096); // infers 'MyType' -/// -QualType inferPossibleTypeFromCastExpr(const CallExpr *CallE, - const CastExpr *CastE) { - if (!CastE) - return QualType(); - QualType PtrType = CastE->getType(); - if (PtrType->isPointerType()) - return PtrType->getPointeeType(); - return QualType(); +llvm::MDNode *CodeGenFunction::buildAllocToken(const CallExpr *E) { + QualType AllocType = infer_alloc::inferPossibleType(E, getContext(), CurCast); + if (!AllocType.isNull()) + return buildAllocToken(AllocType); + return nullptr; } -} // end anonymous namespace void CodeGenFunction::EmitAllocToken(llvm::CallBase *CB, const CallExpr *E) { - QualType AllocType; - // First check arguments. - for (const Expr *Arg : E->arguments()) { - AllocType = inferPossibleTypeFromArithSizeofExpr(Arg); - if (AllocType.isNull()) - AllocType = inferPossibleTypeFromVarInitSizeofExpr(Arg); - if (!AllocType.isNull()) - break; - } - // Then check later casts. - if (AllocType.isNull()) - AllocType = inferPossibleTypeFromCastExpr(E, CurCast); - // Emit if we were able to infer the type. - if (!AllocType.isNull()) - EmitAllocToken(CB, AllocType); + assert(SanOpts.has(SanitizerKind::AllocToken) && + "Only needed with -fsanitize=alloc-token"); + if (llvm::MDNode *MDN = buildAllocToken(E)) + CB->setMetadata(llvm::LLVMContext::MD_alloc_token, MDN); } CodeGenFunction::ComplexPairTy CodeGenFunction:: diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h index 1f0be2d8756de..8c4c1c8c2dc95 100644 --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -3352,9 +3352,14 @@ class CodeGenFunction : public CodeGenTypeCache { SanitizerAnnotateDebugInfo(ArrayRef<SanitizerKind::SanitizerOrdinal> Ordinals, SanitizerHandler Handler); - /// Emit additional metadata used by the AllocToken instrumentation. + /// Build metadata used by the AllocToken instrumentation. + llvm::MDNode *buildAllocToken(QualType AllocType); + /// Emit and set additional metadata used by the AllocToken instrumentation. void EmitAllocToken(llvm::CallBase *CB, QualType AllocType); - /// Emit additional metadata used by the AllocToken instrumentation, + /// Build additional metadata used by the AllocToken instrumentation, + /// inferring the type from an allocation call expression. + llvm::MDNode *buildAllocToken(const CallExpr *E); + /// Emit and set additional metadata used by the AllocToken instrumentation, /// inferring the type from an allocation call expression. void EmitAllocToken(llvm::CallBase *CB, const CallExpr *E); diff --git a/llvm/utils/gn/secondary/clang/lib/AST/BUILD.gn b/llvm/utils/gn/secondary/clang/lib/AST/BUILD.gn index 9981d100fd555..4da907cbdd938 100644 --- a/llvm/utils/gn/secondary/clang/lib/AST/BUILD.gn +++ b/llvm/utils/gn/secondary/clang/lib/AST/BUILD.gn @@ -121,6 +121,7 @@ static_library("AST") { "ExternalASTMerger.cpp", "ExternalASTSource.cpp", "FormatString.cpp", + "InferAlloc.cpp", "InheritViz.cpp", "ItaniumCXXABI.cpp", "ItaniumMangle.cpp", 
Created using spr 1.3.8-beta.1 [skip ci]
Created using spr 1.3.8-beta.1
@melver melver requested a review from fmayer October 17, 2025 18:16
Created using spr 1.3.8-beta.1 [skip ci]
Created using spr 1.3.8-beta.1
Created using spr 1.3.8-beta.1 [skip ci]
Created using spr 1.3.8-beta.1
Created using spr 1.3.8-beta.1 [skip ci]
Created using spr 1.3.8-beta.1
Created using spr 1.3.8-beta.1 [skip ci]
Created using spr 1.3.8-beta.1
@melver melver requested a review from fmayer October 22, 2025 12:58
@melver melver changed the base branch from users/melver/spr/main.clang-refactor-allocation-type-inference-logic to main October 22, 2025 12:59
Created using spr 1.3.8-beta.1
@melver melver merged commit 0fddaf0 into main Oct 23, 2025
10 checks passed
@melver melver deleted the users/melver/spr/clang-refactor-allocation-type-inference-logic branch October 23, 2025 09:22
mikolaj-pirog pushed a commit to mikolaj-pirog/llvm-project that referenced this pull request Oct 23, 2025
Refactor the logic for inferring allocated types out of `CodeGen` and into a new reusable component in `clang/AST/InferAlloc.h`. This is a preparatory step for implementing `__builtin_infer_alloc_token`. By moving the type inference heuristics into a common place, it can be shared between the existing allocation-call instrumentation and the new builtin's implementation.
@llvm-ci
Copy link
Collaborator

llvm-ci commented Oct 23, 2025

LLVM Buildbot has detected a new failure on builder sanitizer-ppc64le-linux running on ppc64le-sanitizer while building clang,llvm at step 2 "annotate".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/72/builds/15590

Here is the relevant piece of the build log for the reference
Step 2 (annotate) failure: 'python ../sanitizer_buildbot/sanitizers/zorg/buildbot/builders/sanitizers/buildbot_selector.py' (failure) ... PASS: ScudoStandalone-Unit :: ./ScudoCUnitTest-powerpc64le-Test/8/14 (1598 of 2634) PASS: SanitizerCommon-msan-powerpc64le-Linux :: symbolize_debug_argv.cpp (1599 of 2634) PASS: SanitizerCommon-msan-powerpc64le-Linux :: Posix/wcsdup.c (1600 of 2634) PASS: ScudoStandalone-Unit :: ./ScudoCUnitTest-powerpc64le-Test/4/14 (1601 of 2634) PASS: ScudoStandalone-Unit :: ./ScudoUnitTest-powerpc64le-Test/124/335 (1602 of 2634) PASS: SanitizerCommon-msan-powerpc64le-Linux :: Linux/aligned_alloc-alignment.cpp (1603 of 2634) PASS: SanitizerCommon-tsan-powerpc64le-Linux :: strcspn.c (1604 of 2634) PASS: ScudoStandalone-Unit :: ./ScudoUnitTest-powerpc64le-Test/11/335 (1605 of 2634) PASS: SanitizerCommon-msan-powerpc64le-Linux :: Linux/preadv2.cpp (1606 of 2634) PASS: SanitizerCommon-tsan-powerpc64le-Linux :: Linux/cap.c (1607 of 2634) FAIL: SanitizerCommon-msan-powerpc64le-Linux :: Linux/getpwnam_r_invalid_user.cpp (1608 of 2634) ******************** TEST 'SanitizerCommon-msan-powerpc64le-Linux :: Linux/getpwnam_r_invalid_user.cpp' FAILED ******************** Exit Code: 134 Command Output (stderr): -- /home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/build_default/./bin/clang --driver-mode=g++ -gline-tables-only -fsanitize=memory -m64 -fno-function-sections -funwind-tables -I/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/llvm-project/compiler-rt/test -ldl -O0 -g /home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/llvm-project/compiler-rt/test/sanitizer_common/TestCases/Linux/getpwnam_r_invalid_user.cpp -o /home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/build_default/runtimes/runtimes-bins/compiler-rt/test/sanitizer_common/msan-powerpc64le-Linux/Linux/Output/getpwnam_r_invalid_user.cpp.tmp && /home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/build_default/runtimes/runtimes-bins/compiler-rt/test/sanitizer_common/msan-powerpc64le-Linux/Linux/Output/getpwnam_r_invalid_user.cpp.tmp # RUN: at line 2 + /home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/build_default/./bin/clang --driver-mode=g++ -gline-tables-only -fsanitize=memory -m64 -fno-function-sections -funwind-tables -I/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/llvm-project/compiler-rt/test -ldl -O0 -g /home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/llvm-project/compiler-rt/test/sanitizer_common/TestCases/Linux/getpwnam_r_invalid_user.cpp -o /home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/build_default/runtimes/runtimes-bins/compiler-rt/test/sanitizer_common/msan-powerpc64le-Linux/Linux/Output/getpwnam_r_invalid_user.cpp.tmp + /home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/build_default/runtimes/runtimes-bins/compiler-rt/test/sanitizer_common/msan-powerpc64le-Linux/Linux/Output/getpwnam_r_invalid_user.cpp.tmp Result: 110 getpwnam_r_invalid_user.cpp.tmp: /home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/llvm-project/compiler-rt/test/sanitizer_common/TestCases/Linux/getpwnam_r_invalid_user.cpp:19: int main(): Assertion `res == 0 || res == ENOENT' failed. /home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/build_default/runtimes/runtimes-bins/compiler-rt/test/sanitizer_common/msan-powerpc64le-Linux/Linux/Output/getpwnam_r_invalid_user.cpp.script: line 1: 2235490 Aborted /home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/build_default/runtimes/runtimes-bins/compiler-rt/test/sanitizer_common/msan-powerpc64le-Linux/Linux/Output/getpwnam_r_invalid_user.cpp.tmp -- ******************** PASS: SanitizerCommon-msan-powerpc64le-Linux :: Posix/qsort.cpp (1609 of 2634) PASS: SanitizerCommon-tsan-powerpc64le-Linux :: strcasestr.c (1610 of 2634) PASS: SanitizerCommon-tsan-powerpc64le-Linux :: Linux/getservent_r.cpp (1611 of 2634) PASS: SanitizerCommon-msan-powerpc64le-Linux :: Posix/uname.c (1612 of 2634) PASS: SanitizerCommon-tsan-powerpc64le-Linux :: Posix/getpass.cpp (1613 of 2634) PASS: SanitizerCommon-tsan-powerpc64le-Linux :: fopen_nullptr.c (1614 of 2634) PASS: SanitizerCommon-lsan-powerpc64le-Linux :: options-include.cpp (1615 of 2634) PASS: SanitizerCommon-msan-powerpc64le-Linux :: Linux/cloak_signal.cpp (1616 of 2634) PASS: SanitizerCommon-msan-powerpc64le-Linux :: Posix/getrandom.c (1617 of 2634) PASS: SanitizerCommon-tsan-powerpc64le-Linux :: Posix/fputc_putc_putchar.cpp (1618 of 2634) PASS: ScudoStandalone-Unit :: ./ScudoUnitTest-powerpc64le-Test/137/335 (1619 of 2634) PASS: SanitizerCommon-tsan-powerpc64le-Linux :: Posix/wcsxfrm.c (1620 of 2634) PASS: SanitizerCommon-lsan-powerpc64le-Linux :: sanitizer_coverage_stack_depth.cpp (1621 of 2634) PASS: SanitizerCommon-tsan-powerpc64le-Linux :: Posix/fputs_puts.cpp (1622 of 2634) PASS: SanitizerCommon-tsan-powerpc64le-Linux :: symbolize_debug_argv.cpp (1623 of 2634) PASS: SanitizerCommon-msan-powerpc64le-Linux :: Posix/getpw_getgr.cpp (1624 of 2634) PASS: SanitizerCommon-tsan-powerpc64le-Linux :: wcslen_test.c (1625 of 2634) PASS: SanitizerCommon-msan-powerpc64le-Linux :: Posix/mmap_write_exec.cpp (1626 of 2634) PASS: SanitizerCommon-tsan-powerpc64le-Linux :: scandir.c (1627 of 2634) PASS: ScudoStandalone-Unit :: ./ScudoUnitTest-powerpc64le-Test/12/335 (1628 of 2634) PASS: SanitizerCommon-msan-powerpc64le-Linux :: Linux/sched_getparam.cpp (1629 of 2634) PASS: SanitizerCommon-tsan-powerpc64le-Linux :: Linux/pthread_join.cpp (1630 of 2634) PASS: SanitizerCommon-msan-powerpc64le-Linux :: Linux/malloc_usable_size.c (1631 of 2634) Step 9 (test compiler-rt debug) failure: test compiler-rt debug (failure) ... PASS: ScudoStandalone-Unit :: ./ScudoCUnitTest-powerpc64le-Test/8/14 (1598 of 2634) PASS: SanitizerCommon-msan-powerpc64le-Linux :: symbolize_debug_argv.cpp (1599 of 2634) PASS: SanitizerCommon-msan-powerpc64le-Linux :: Posix/wcsdup.c (1600 of 2634) PASS: ScudoStandalone-Unit :: ./ScudoCUnitTest-powerpc64le-Test/4/14 (1601 of 2634) PASS: ScudoStandalone-Unit :: ./ScudoUnitTest-powerpc64le-Test/124/335 (1602 of 2634) PASS: SanitizerCommon-msan-powerpc64le-Linux :: Linux/aligned_alloc-alignment.cpp (1603 of 2634) PASS: SanitizerCommon-tsan-powerpc64le-Linux :: strcspn.c (1604 of 2634) PASS: ScudoStandalone-Unit :: ./ScudoUnitTest-powerpc64le-Test/11/335 (1605 of 2634) PASS: SanitizerCommon-msan-powerpc64le-Linux :: Linux/preadv2.cpp (1606 of 2634) PASS: SanitizerCommon-tsan-powerpc64le-Linux :: Linux/cap.c (1607 of 2634) FAIL: SanitizerCommon-msan-powerpc64le-Linux :: Linux/getpwnam_r_invalid_user.cpp (1608 of 2634) ******************** TEST 'SanitizerCommon-msan-powerpc64le-Linux :: Linux/getpwnam_r_invalid_user.cpp' FAILED ******************** Exit Code: 134 Command Output (stderr): -- /home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/build_default/./bin/clang --driver-mode=g++ -gline-tables-only -fsanitize=memory -m64 -fno-function-sections -funwind-tables -I/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/llvm-project/compiler-rt/test -ldl -O0 -g /home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/llvm-project/compiler-rt/test/sanitizer_common/TestCases/Linux/getpwnam_r_invalid_user.cpp -o /home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/build_default/runtimes/runtimes-bins/compiler-rt/test/sanitizer_common/msan-powerpc64le-Linux/Linux/Output/getpwnam_r_invalid_user.cpp.tmp && /home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/build_default/runtimes/runtimes-bins/compiler-rt/test/sanitizer_common/msan-powerpc64le-Linux/Linux/Output/getpwnam_r_invalid_user.cpp.tmp # RUN: at line 2 + /home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/build_default/./bin/clang --driver-mode=g++ -gline-tables-only -fsanitize=memory -m64 -fno-function-sections -funwind-tables -I/home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/llvm-project/compiler-rt/test -ldl -O0 -g /home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/llvm-project/compiler-rt/test/sanitizer_common/TestCases/Linux/getpwnam_r_invalid_user.cpp -o /home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/build_default/runtimes/runtimes-bins/compiler-rt/test/sanitizer_common/msan-powerpc64le-Linux/Linux/Output/getpwnam_r_invalid_user.cpp.tmp + /home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/build_default/runtimes/runtimes-bins/compiler-rt/test/sanitizer_common/msan-powerpc64le-Linux/Linux/Output/getpwnam_r_invalid_user.cpp.tmp Result: 110 getpwnam_r_invalid_user.cpp.tmp: /home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/llvm-project/compiler-rt/test/sanitizer_common/TestCases/Linux/getpwnam_r_invalid_user.cpp:19: int main(): Assertion `res == 0 || res == ENOENT' failed. /home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/build_default/runtimes/runtimes-bins/compiler-rt/test/sanitizer_common/msan-powerpc64le-Linux/Linux/Output/getpwnam_r_invalid_user.cpp.script: line 1: 2235490 Aborted /home/buildbots/llvm-external-buildbots/workers/ppc64le-sanitizer/sanitizer-ppc64le/build/build_default/runtimes/runtimes-bins/compiler-rt/test/sanitizer_common/msan-powerpc64le-Linux/Linux/Output/getpwnam_r_invalid_user.cpp.tmp -- ******************** PASS: SanitizerCommon-msan-powerpc64le-Linux :: Posix/qsort.cpp (1609 of 2634) PASS: SanitizerCommon-tsan-powerpc64le-Linux :: strcasestr.c (1610 of 2634) PASS: SanitizerCommon-tsan-powerpc64le-Linux :: Linux/getservent_r.cpp (1611 of 2634) PASS: SanitizerCommon-msan-powerpc64le-Linux :: Posix/uname.c (1612 of 2634) PASS: SanitizerCommon-tsan-powerpc64le-Linux :: Posix/getpass.cpp (1613 of 2634) PASS: SanitizerCommon-tsan-powerpc64le-Linux :: fopen_nullptr.c (1614 of 2634) PASS: SanitizerCommon-lsan-powerpc64le-Linux :: options-include.cpp (1615 of 2634) PASS: SanitizerCommon-msan-powerpc64le-Linux :: Linux/cloak_signal.cpp (1616 of 2634) PASS: SanitizerCommon-msan-powerpc64le-Linux :: Posix/getrandom.c (1617 of 2634) PASS: SanitizerCommon-tsan-powerpc64le-Linux :: Posix/fputc_putc_putchar.cpp (1618 of 2634) PASS: ScudoStandalone-Unit :: ./ScudoUnitTest-powerpc64le-Test/137/335 (1619 of 2634) PASS: SanitizerCommon-tsan-powerpc64le-Linux :: Posix/wcsxfrm.c (1620 of 2634) PASS: SanitizerCommon-lsan-powerpc64le-Linux :: sanitizer_coverage_stack_depth.cpp (1621 of 2634) PASS: SanitizerCommon-tsan-powerpc64le-Linux :: Posix/fputs_puts.cpp (1622 of 2634) PASS: SanitizerCommon-tsan-powerpc64le-Linux :: symbolize_debug_argv.cpp (1623 of 2634) PASS: SanitizerCommon-msan-powerpc64le-Linux :: Posix/getpw_getgr.cpp (1624 of 2634) PASS: SanitizerCommon-tsan-powerpc64le-Linux :: wcslen_test.c (1625 of 2634) PASS: SanitizerCommon-msan-powerpc64le-Linux :: Posix/mmap_write_exec.cpp (1626 of 2634) PASS: SanitizerCommon-tsan-powerpc64le-Linux :: scandir.c (1627 of 2634) PASS: ScudoStandalone-Unit :: ./ScudoUnitTest-powerpc64le-Test/12/335 (1628 of 2634) PASS: SanitizerCommon-msan-powerpc64le-Linux :: Linux/sched_getparam.cpp (1629 of 2634) PASS: SanitizerCommon-tsan-powerpc64le-Linux :: Linux/pthread_join.cpp (1630 of 2634) PASS: SanitizerCommon-msan-powerpc64le-Linux :: Linux/malloc_usable_size.c (1631 of 2634) 
dvbuka pushed a commit to dvbuka/llvm-project that referenced this pull request Oct 27, 2025
Refactor the logic for inferring allocated types out of `CodeGen` and into a new reusable component in `clang/AST/InferAlloc.h`. This is a preparatory step for implementing `__builtin_infer_alloc_token`. By moving the type inference heuristics into a common place, it can be shared between the existing allocation-call instrumentation and the new builtin's implementation.
Lukacma pushed a commit to Lukacma/llvm-project that referenced this pull request Oct 29, 2025
Refactor the logic for inferring allocated types out of `CodeGen` and into a new reusable component in `clang/AST/InferAlloc.h`. This is a preparatory step for implementing `__builtin_infer_alloc_token`. By moving the type inference heuristics into a common place, it can be shared between the existing allocation-call instrumentation and the new builtin's implementation.
aokblast pushed a commit to aokblast/llvm-project that referenced this pull request Oct 30, 2025
Refactor the logic for inferring allocated types out of `CodeGen` and into a new reusable component in `clang/AST/InferAlloc.h`. This is a preparatory step for implementing `__builtin_infer_alloc_token`. By moving the type inference heuristics into a common place, it can be shared between the existing allocation-call instrumentation and the new builtin's implementation.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

clang:codegen IR generation bugs: mangling, exceptions, etc. clang:frontend Language frontend issues, e.g. anything involving "Sema" clang Clang issues not falling into any other category

5 participants