Skip to content

Commit 57f2183

Browse files
committed
[𝘀𝗽𝗿] initial version
Created using spr 1.3.8-beta.1
2 parents 0fc05aa + 2ea2825 commit 57f2183

File tree

29 files changed

+652
-276
lines changed

29 files changed

+652
-276
lines changed

clang/docs/AllocToken.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@ The default mode to calculate tokens is:
3737
pointers.
3838

3939
Other token ID assignment modes are supported, but they may be subject to
40-
change or removal. These may (experimentally) be selected with ``-mllvm
41-
-alloc-token-mode=<mode>``:
40+
change or removal. These may (experimentally) be selected with ``-Xclang
41+
-falloc-token-mode=<mode>``:
4242

4343
* ``typehash``: This mode assigns a token ID based on the hash of the allocated
4444
type's name.
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
//===--- InferAlloc.h - Allocation type inference ---------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// This file defines interfaces for allocation-related type inference.
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#ifndef LLVM_CLANG_AST_INFERALLOC_H
14+
#define LLVM_CLANG_AST_INFERALLOC_H
15+
16+
#include "clang/AST/ASTContext.h"
17+
#include "clang/AST/Expr.h"
18+
#include "llvm/Support/AllocToken.h"
19+
#include <optional>
20+
21+
namespace clang {
22+
namespace infer_alloc {
23+
24+
/// Infer the possible allocated type from an allocation call expression.
25+
QualType inferPossibleType(const CallExpr *E, const ASTContext &Ctx,
26+
const CastExpr *CastE);
27+
28+
/// Get the information required for construction of an allocation token ID.
29+
std::optional<llvm::AllocTokenMetadata>
30+
getAllocTokenMetadata(QualType T, const ASTContext &Ctx);
31+
32+
} // namespace infer_alloc
33+
} // namespace clang
34+
35+
#endif // LLVM_CLANG_AST_INFERALLOC_H

clang/include/clang/Basic/CodeGenOptions.h

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -447,10 +447,6 @@ class CodeGenOptions : public CodeGenOptionsBase {
447447

448448
std::optional<double> AllowRuntimeCheckSkipHotCutoff;
449449

450-
/// Maximum number of allocation tokens (0 = no max), nullopt if none set (use
451-
/// pass default).
452-
std::optional<uint64_t> AllocTokenMax;
453-
454450
/// List of backend command-line options for -fembed-bitcode.
455451
std::vector<uint8_t> CmdArgs;
456452

clang/include/clang/Basic/LangOptions.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include "llvm/ADT/FloatingPointMode.h"
2626
#include "llvm/ADT/StringRef.h"
2727
#include "llvm/BinaryFormat/DXContainer.h"
28+
#include "llvm/Support/AllocToken.h"
2829
#include "llvm/TargetParser/Triple.h"
2930
#include <optional>
3031
#include <string>
@@ -565,6 +566,13 @@ class LangOptions : public LangOptionsBase {
565566
bool AtomicFineGrainedMemory = false;
566567
bool AtomicIgnoreDenormalMode = false;
567568

569+
/// Maximum number of allocation tokens (0 = no max), nullopt if none set (use
570+
/// target default).
571+
std::optional<uint64_t> AllocTokenMax;
572+
573+
/// The allocation token mode.
574+
std::optional<llvm::AllocTokenMode> AllocTokenMode;
575+
568576
LangOptions();
569577

570578
/// Set language defaults for the given input language and

clang/include/clang/Driver/Options.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2751,6 +2751,10 @@ def falloc_token_max_EQ : Joined<["-"], "falloc-token-max=">,
27512751
MetaVarName<"<N>">,
27522752
HelpText<"Limit to maximum N allocation tokens (0 = no max)">;
27532753

2754+
def falloc_token_mode_EQ : Joined<["-"], "falloc-token-mode=">,
2755+
Group<f_Group>, Visibility<[CC1Option]>,
2756+
HelpText<"Set the allocation token mode (experimental)">;
2757+
27542758
def fallow_runtime_check_skip_hot_cutoff_EQ
27552759
: Joined<["-"], "fallow-runtime-check-skip-hot-cutoff=">,
27562760
Group<f_clang_Group>,

clang/lib/AST/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ add_clang_library(clangAST
6666
ExternalASTMerger.cpp
6767
ExternalASTSource.cpp
6868
FormatString.cpp
69+
InferAlloc.cpp
6970
InheritViz.cpp
7071
ByteCode/BitcastBuffer.cpp
7172
ByteCode/ByteCodeEmitter.cpp

clang/lib/AST/InferAlloc.cpp

Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
//===--- InferAlloc.cpp - Allocation type inference -----------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// This file implements allocation-related type inference.
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#include "clang/AST/InferAlloc.h"
14+
#include "clang/AST/ASTContext.h"
15+
#include "clang/AST/Decl.h"
16+
#include "clang/AST/DeclCXX.h"
17+
#include "clang/AST/Expr.h"
18+
#include "clang/AST/Type.h"
19+
#include "clang/Basic/IdentifierTable.h"
20+
#include "llvm/ADT/SmallPtrSet.h"
21+
22+
namespace clang {
23+
namespace {
24+
bool typeContainsPointer(QualType T,
25+
llvm::SmallPtrSet<const RecordDecl *, 4> &VisitedRD,
26+
bool &IncompleteType) {
27+
QualType CanonicalType = T.getCanonicalType();
28+
if (CanonicalType->isPointerType())
29+
return true; // base case
30+
31+
// Look through typedef chain to check for special types.
32+
for (QualType CurrentT = T; const auto *TT = CurrentT->getAs<TypedefType>();
33+
CurrentT = TT->getDecl()->getUnderlyingType()) {
34+
const IdentifierInfo *II = TT->getDecl()->getIdentifier();
35+
// Special Case: Syntactically uintptr_t is not a pointer; semantically,
36+
// however, very likely used as such. Therefore, classify uintptr_t as a
37+
// pointer, too.
38+
if (II && II->isStr("uintptr_t"))
39+
return true;
40+
}
41+
42+
// The type is an array; check the element type.
43+
if (const ArrayType *AT = dyn_cast<ArrayType>(CanonicalType))
44+
return typeContainsPointer(AT->getElementType(), VisitedRD, IncompleteType);
45+
// The type is a struct, class, or union.
46+
if (const RecordDecl *RD = CanonicalType->getAsRecordDecl()) {
47+
if (!RD->isCompleteDefinition()) {
48+
IncompleteType = true;
49+
return false;
50+
}
51+
if (!VisitedRD.insert(RD).second)
52+
return false; // already visited
53+
// Check all fields.
54+
for (const FieldDecl *Field : RD->fields()) {
55+
if (typeContainsPointer(Field->getType(), VisitedRD, IncompleteType))
56+
return true;
57+
}
58+
// For C++ classes, also check base classes.
59+
if (const CXXRecordDecl *CXXRD = dyn_cast<CXXRecordDecl>(RD)) {
60+
// Polymorphic types require a vptr.
61+
if (CXXRD->isDynamicClass())
62+
return true;
63+
for (const CXXBaseSpecifier &Base : CXXRD->bases()) {
64+
if (typeContainsPointer(Base.getType(), VisitedRD, IncompleteType))
65+
return true;
66+
}
67+
}
68+
}
69+
return false;
70+
}
71+
72+
/// Infer type from a simple sizeof expression.
73+
QualType inferTypeFromSizeofExpr(const Expr *E) {
74+
const Expr *Arg = E->IgnoreParenImpCasts();
75+
if (const auto *UET = dyn_cast<UnaryExprOrTypeTraitExpr>(Arg)) {
76+
if (UET->getKind() == UETT_SizeOf) {
77+
if (UET->isArgumentType())
78+
return UET->getArgumentTypeInfo()->getType();
79+
else
80+
return UET->getArgumentExpr()->getType();
81+
}
82+
}
83+
return QualType();
84+
}
85+
86+
/// Infer type from an arithmetic expression involving a sizeof. For example:
87+
///
88+
/// malloc(sizeof(MyType) + padding); // infers 'MyType'
89+
/// malloc(sizeof(MyType) * 32); // infers 'MyType'
90+
/// malloc(32 * sizeof(MyType)); // infers 'MyType'
91+
/// malloc(sizeof(MyType) << 1); // infers 'MyType'
92+
/// ...
93+
///
94+
/// More complex arithmetic expressions are supported, but are a heuristic, e.g.
95+
/// when considering allocations for structs with flexible array members:
96+
///
97+
/// malloc(sizeof(HasFlexArray) + sizeof(int) * 32); // infers 'HasFlexArray'
98+
///
99+
QualType inferPossibleTypeFromArithSizeofExpr(const Expr *E) {
100+
const Expr *Arg = E->IgnoreParenImpCasts();
101+
// The argument is a lone sizeof expression.
102+
if (QualType T = inferTypeFromSizeofExpr(Arg); !T.isNull())
103+
return T;
104+
if (const auto *BO = dyn_cast<BinaryOperator>(Arg)) {
105+
// Argument is an arithmetic expression. Cover common arithmetic patterns
106+
// involving sizeof.
107+
switch (BO->getOpcode()) {
108+
case BO_Add:
109+
case BO_Div:
110+
case BO_Mul:
111+
case BO_Shl:
112+
case BO_Shr:
113+
case BO_Sub:
114+
if (QualType T = inferPossibleTypeFromArithSizeofExpr(BO->getLHS());
115+
!T.isNull())
116+
return T;
117+
if (QualType T = inferPossibleTypeFromArithSizeofExpr(BO->getRHS());
118+
!T.isNull())
119+
return T;
120+
break;
121+
default:
122+
break;
123+
}
124+
}
125+
return QualType();
126+
}
127+
128+
/// If the expression E is a reference to a variable, infer the type from a
129+
/// variable's initializer if it contains a sizeof. Beware, this is a heuristic
130+
/// and ignores if a variable is later reassigned. For example:
131+
///
132+
/// size_t my_size = sizeof(MyType);
133+
/// void *x = malloc(my_size); // infers 'MyType'
134+
///
135+
QualType inferPossibleTypeFromVarInitSizeofExpr(const Expr *E) {
136+
const Expr *Arg = E->IgnoreParenImpCasts();
137+
if (const auto *DRE = dyn_cast<DeclRefExpr>(Arg)) {
138+
if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl())) {
139+
if (const Expr *Init = VD->getInit())
140+
return inferPossibleTypeFromArithSizeofExpr(Init);
141+
}
142+
}
143+
return QualType();
144+
}
145+
146+
/// Deduces the allocated type by checking if the allocation call's result
147+
/// is immediately used in a cast expression. For example:
148+
///
149+
/// MyType *x = (MyType *)malloc(4096); // infers 'MyType'
150+
///
151+
QualType inferPossibleTypeFromCastExpr(const CallExpr *CallE,
152+
const CastExpr *CastE) {
153+
if (!CastE)
154+
return QualType();
155+
QualType PtrType = CastE->getType();
156+
if (PtrType->isPointerType())
157+
return PtrType->getPointeeType();
158+
return QualType();
159+
}
160+
} // anonymous namespace
161+
162+
namespace infer_alloc {
163+
164+
QualType inferPossibleType(const CallExpr *E, const ASTContext &Ctx,
165+
const CastExpr *CastE) {
166+
QualType AllocType;
167+
// First check arguments.
168+
for (const Expr *Arg : E->arguments()) {
169+
AllocType = inferPossibleTypeFromArithSizeofExpr(Arg);
170+
if (AllocType.isNull())
171+
AllocType = inferPossibleTypeFromVarInitSizeofExpr(Arg);
172+
if (!AllocType.isNull())
173+
break;
174+
}
175+
// Then check later casts.
176+
if (AllocType.isNull())
177+
AllocType = inferPossibleTypeFromCastExpr(E, CastE);
178+
return AllocType;
179+
}
180+
181+
std::optional<llvm::AllocTokenMetadata>
182+
getAllocTokenMetadata(QualType T, const ASTContext &Ctx) {
183+
llvm::AllocTokenMetadata ATMD;
184+
185+
// Get unique type name.
186+
PrintingPolicy Policy(Ctx.getLangOpts());
187+
Policy.SuppressTagKeyword = true;
188+
Policy.FullyQualifiedName = true;
189+
llvm::raw_svector_ostream TypeNameOS(ATMD.TypeName);
190+
T.getCanonicalType().print(TypeNameOS, Policy);
191+
192+
// Check if QualType contains a pointer. Implements a simple DFS to
193+
// recursively check if a type contains a pointer type.
194+
llvm::SmallPtrSet<const RecordDecl *, 4> VisitedRD;
195+
bool IncompleteType = false;
196+
ATMD.ContainsPointer = typeContainsPointer(T, VisitedRD, IncompleteType);
197+
if (!ATMD.ContainsPointer && IncompleteType)
198+
return std::nullopt;
199+
200+
return ATMD;
201+
}
202+
203+
} // namespace infer_alloc
204+
} // namespace clang

clang/lib/CodeGen/BackendUtil.cpp

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -234,9 +234,12 @@ class EmitAssemblyHelper {
234234
};
235235
} // namespace
236236

237-
static AllocTokenOptions getAllocTokenOptions(const CodeGenOptions &CGOpts) {
237+
static AllocTokenOptions getAllocTokenOptions(const LangOptions &LangOpts,
238+
const CodeGenOptions &CGOpts) {
238239
AllocTokenOptions Opts;
239-
Opts.MaxTokens = CGOpts.AllocTokenMax;
240+
if (LangOpts.AllocTokenMode)
241+
Opts.Mode = *LangOpts.AllocTokenMode;
242+
Opts.MaxTokens = LangOpts.AllocTokenMax;
240243
Opts.Extended = CGOpts.SanitizeAllocTokenExtended;
241244
Opts.FastABI = CGOpts.SanitizeAllocTokenFastABI;
242245
return Opts;
@@ -808,7 +811,7 @@ static void addSanitizers(const Triple &TargetTriple,
808811
// memory allocation function detection.
809812
MPM.addPass(InferFunctionAttrsPass());
810813
}
811-
MPM.addPass(AllocTokenPass(getAllocTokenOptions(CodeGenOpts)));
814+
MPM.addPass(AllocTokenPass(getAllocTokenOptions(LangOpts, CodeGenOpts)));
812815
}
813816
};
814817
if (ClSanitizeOnOptimizerEarlyEP) {

0 commit comments

Comments
 (0)