Skip to content

Commit 9e0a05d

Browse files
committed
[𝘀𝗽𝗿] changes to main this commit is based on
Created using spr 1.3.8-beta.1 [skip ci]
1 parent 0fc05aa commit 9e0a05d

File tree

7 files changed

+255
-52
lines changed

7 files changed

+255
-52
lines changed

llvm/include/llvm/IR/Intrinsics.td

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2853,7 +2853,15 @@ def int_ptrauth_blend :
28532853
def int_ptrauth_sign_generic :
28542854
DefaultAttrsIntrinsic<[llvm_i64_ty], [llvm_i64_ty, llvm_i64_ty], [IntrNoMem]>;
28552855

2856+
//===----------------- AllocToken Intrinsics ------------------------------===//
2857+
2858+
// Return the token ID for the given !alloc_token metadata.
2859+
def int_alloc_token_id :
2860+
DefaultAttrsIntrinsic<[llvm_anyint_ty], [llvm_metadata_ty],
2861+
[IntrNoMem, NoUndef<RetIndex>]>;
2862+
28562863
//===----------------------------------------------------------------------===//
2864+
28572865
//===------- Convergence Intrinsics ---------------------------------------===//
28582866

28592867
def int_experimental_convergence_entry
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
//===- llvm/Support/AllocToken.h - Allocation Token Calculation -----*- 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+
// Definition of AllocToken modes and shared calculation of stateless token IDs.
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#ifndef LLVM_SUPPORT_ALLOCTOKEN_H
14+
#define LLVM_SUPPORT_ALLOCTOKEN_H
15+
16+
#include "llvm/ADT/SmallString.h"
17+
#include <cstdint>
18+
#include <optional>
19+
20+
namespace llvm {
21+
22+
/// Modes for generating allocation token IDs.
23+
enum class AllocTokenMode {
24+
/// Incrementally increasing token ID.
25+
Increment,
26+
27+
/// Simple mode that returns a statically-assigned random token ID.
28+
Random,
29+
30+
/// Token ID based on allocated type hash.
31+
TypeHash,
32+
33+
/// Token ID based on allocated type hash, where the top half ID-space is
34+
/// reserved for types that contain pointers and the bottom half for types
35+
/// that do not contain pointers.
36+
TypeHashPointerSplit,
37+
};
38+
39+
/// Metadata about an allocation used to generate a token ID.
40+
struct AllocTokenMetadata {
41+
SmallString<64> TypeName;
42+
bool ContainsPointer;
43+
};
44+
45+
/// Calculates stable allocation token ID. Returns std::nullopt for stateful
46+
/// modes that are only available in the AllocToken pass.
47+
///
48+
/// \param Mode The token generation mode.
49+
/// \param Metadata The metadata about the allocation.
50+
/// \param MaxTokens The maximum number of tokens (must not be 0)
51+
/// \return The calculated allocation token ID, or std::nullopt.
52+
std::optional<uint64_t> getAllocTokenHash(AllocTokenMode Mode,
53+
const AllocTokenMetadata &Metadata,
54+
uint64_t MaxTokens);
55+
56+
} // end namespace llvm
57+
58+
#endif // LLVM_SUPPORT_ALLOCTOKEN_H

llvm/lib/Support/AllocToken.cpp

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
//===- AllocToken.cpp - Allocation Token Calculation ----------------------===//
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+
// Definition of AllocToken modes and shared calculation of stateless token IDs.
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#include "llvm/Support/AllocToken.h"
14+
#include "llvm/Support/ErrorHandling.h"
15+
#include "llvm/Support/SipHash.h"
16+
17+
namespace llvm {
18+
std::optional<uint64_t> getAllocTokenHash(AllocTokenMode Mode,
19+
const AllocTokenMetadata &Metadata,
20+
uint64_t MaxTokens) {
21+
assert(MaxTokens && "Must provide concrete max tokens");
22+
23+
switch (Mode) {
24+
case AllocTokenMode::Increment:
25+
case AllocTokenMode::Random:
26+
// Stateful modes cannot be implemented as a pure function.
27+
return std::nullopt;
28+
29+
case AllocTokenMode::TypeHash: {
30+
return getStableSipHash(Metadata.TypeName) % MaxTokens;
31+
}
32+
33+
case AllocTokenMode::TypeHashPointerSplit: {
34+
if (MaxTokens == 1)
35+
return 0;
36+
const uint64_t HalfTokens = MaxTokens / 2;
37+
uint64_t Hash = getStableSipHash(Metadata.TypeName) % HalfTokens;
38+
if (Metadata.ContainsPointer)
39+
Hash += HalfTokens;
40+
return Hash;
41+
}
42+
}
43+
44+
llvm_unreachable("");
45+
}
46+
} // namespace llvm

llvm/lib/Support/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@ add_llvm_component_library(LLVMSupport
147147
ARMBuildAttributes.cpp
148148
AArch64AttributeParser.cpp
149149
AArch64BuildAttributes.cpp
150+
AllocToken.cpp
150151
ARMAttributeParser.cpp
151152
ARMWinEH.cpp
152153
Allocator.cpp

llvm/lib/Transforms/Instrumentation/AllocToken.cpp

Lines changed: 78 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,12 @@
3131
#include "llvm/IR/InstIterator.h"
3232
#include "llvm/IR/InstrTypes.h"
3333
#include "llvm/IR/Instructions.h"
34+
#include "llvm/IR/IntrinsicInst.h"
3435
#include "llvm/IR/Metadata.h"
3536
#include "llvm/IR/Module.h"
3637
#include "llvm/IR/PassManager.h"
3738
#include "llvm/IR/Type.h"
39+
#include "llvm/Support/AllocToken.h"
3840
#include "llvm/Support/Casting.h"
3941
#include "llvm/Support/CommandLine.h"
4042
#include "llvm/Support/Compiler.h"
@@ -53,29 +55,12 @@
5355
#include <variant>
5456

5557
using namespace llvm;
58+
using TokenMode = AllocTokenMode;
5659

5760
#define DEBUG_TYPE "alloc-token"
5861

5962
namespace {
6063

61-
//===--- Constants --------------------------------------------------------===//
62-
63-
enum class TokenMode : unsigned {
64-
/// Incrementally increasing token ID.
65-
Increment = 0,
66-
67-
/// Simple mode that returns a statically-assigned random token ID.
68-
Random = 1,
69-
70-
/// Token ID based on allocated type hash.
71-
TypeHash = 2,
72-
73-
/// Token ID based on allocated type hash, where the top half ID-space is
74-
/// reserved for types that contain pointers and the bottom half for types
75-
/// that do not contain pointers.
76-
TypeHashPointerSplit = 3,
77-
};
78-
7964
//===--- Command-line options ---------------------------------------------===//
8065

8166
cl::opt<TokenMode> ClMode(
@@ -131,7 +116,7 @@ cl::opt<uint64_t> ClFallbackToken(
131116

132117
//===--- Statistics -------------------------------------------------------===//
133118

134-
STATISTIC(NumFunctionsInstrumented, "Functions instrumented");
119+
STATISTIC(NumFunctionsModified, "Functions modified");
135120
STATISTIC(NumAllocationsInstrumented, "Allocations instrumented");
136121

137122
//===----------------------------------------------------------------------===//
@@ -140,9 +125,19 @@ STATISTIC(NumAllocationsInstrumented, "Allocations instrumented");
140125
///
141126
/// Expected format is: !{<type-name>, <contains-pointer>}
142127
MDNode *getAllocTokenMetadata(const CallBase &CB) {
143-
MDNode *Ret = CB.getMetadata(LLVMContext::MD_alloc_token);
144-
if (!Ret)
145-
return nullptr;
128+
MDNode *Ret = nullptr;
129+
if (auto *II = dyn_cast<IntrinsicInst>(&CB);
130+
II && II->getIntrinsicID() == Intrinsic::alloc_token_id) {
131+
auto *MDV = cast<MetadataAsValue>(II->getArgOperand(0));
132+
Ret = cast<MDNode>(MDV->getMetadata());
133+
// If the intrinsic has an empty MDNode, type inference failed.
134+
if (Ret->getNumOperands() == 0)
135+
return nullptr;
136+
} else {
137+
Ret = CB.getMetadata(LLVMContext::MD_alloc_token);
138+
if (!Ret)
139+
return nullptr;
140+
}
146141
assert(Ret->getNumOperands() == 2 && "bad !alloc_token");
147142
assert(isa<MDString>(Ret->getOperand(0)));
148143
assert(isa<ConstantAsMetadata>(Ret->getOperand(1)));
@@ -206,22 +201,20 @@ class TypeHashMode : public ModeBase {
206201
using ModeBase::ModeBase;
207202

208203
uint64_t operator()(const CallBase &CB, OptimizationRemarkEmitter &ORE) {
209-
const auto [N, H] = getHash(CB, ORE);
210-
return N ? boundedToken(H) : H;
211-
}
212204

213-
protected:
214-
std::pair<MDNode *, uint64_t> getHash(const CallBase &CB,
215-
OptimizationRemarkEmitter &ORE) {
216205
if (MDNode *N = getAllocTokenMetadata(CB)) {
217206
MDString *S = cast<MDString>(N->getOperand(0));
218-
return {N, getStableSipHash(S->getString())};
207+
AllocTokenMetadata Metadata{S->getString(), containsPointer(N)};
208+
if (auto Token =
209+
getAllocTokenHash(TokenMode::TypeHash, Metadata, MaxTokens))
210+
return *Token;
219211
}
220212
// Fallback.
221213
remarkNoMetadata(CB, ORE);
222-
return {nullptr, ClFallbackToken};
214+
return ClFallbackToken;
223215
}
224216

217+
protected:
225218
/// Remark that there was no precise type information.
226219
static void remarkNoMetadata(const CallBase &CB,
227220
OptimizationRemarkEmitter &ORE) {
@@ -242,20 +235,18 @@ class TypeHashPointerSplitMode : public TypeHashMode {
242235
using TypeHashMode::TypeHashMode;
243236

244237
uint64_t operator()(const CallBase &CB, OptimizationRemarkEmitter &ORE) {
245-
if (MaxTokens == 1)
246-
return 0;
247-
const uint64_t HalfTokens = MaxTokens / 2;
248-
const auto [N, H] = getHash(CB, ORE);
249-
if (!N) {
250-
// Pick the fallback token (ClFallbackToken), which by default is 0,
251-
// meaning it'll fall into the pointer-less bucket. Override by setting
252-
// -alloc-token-fallback if that is the wrong choice.
253-
return H;
238+
if (MDNode *N = getAllocTokenMetadata(CB)) {
239+
MDString *S = cast<MDString>(N->getOperand(0));
240+
AllocTokenMetadata Metadata{S->getString(), containsPointer(N)};
241+
if (auto Token = getAllocTokenHash(TokenMode::TypeHashPointerSplit,
242+
Metadata, MaxTokens))
243+
return *Token;
254244
}
255-
uint64_t Hash = H % HalfTokens; // base hash
256-
if (containsPointer(N))
257-
Hash += HalfTokens;
258-
return Hash;
245+
// Pick the fallback token (ClFallbackToken), which by default is 0, meaning
246+
// it'll fall into the pointer-less bucket. Override by setting
247+
// -alloc-token-fallback if that is the wrong choice.
248+
remarkNoMetadata(CB, ORE);
249+
return ClFallbackToken;
259250
}
260251
};
261252

@@ -315,6 +306,9 @@ class AllocToken {
315306
FunctionCallee getTokenAllocFunction(const CallBase &CB, uint64_t TokenID,
316307
LibFunc OriginalFunc);
317308

309+
/// Lower alloc_token_* intrinsics.
310+
void replaceIntrinsicInst(IntrinsicInst *II, OptimizationRemarkEmitter &ORE);
311+
318312
/// Return the token ID from metadata in the call.
319313
uint64_t getToken(const CallBase &CB, OptimizationRemarkEmitter &ORE) {
320314
return std::visit([&](auto &&Mode) { return Mode(CB, ORE); }, Mode);
@@ -336,21 +330,32 @@ bool AllocToken::instrumentFunction(Function &F) {
336330
// Do not apply any instrumentation for naked functions.
337331
if (F.hasFnAttribute(Attribute::Naked))
338332
return false;
339-
if (F.hasFnAttribute(Attribute::DisableSanitizerInstrumentation))
340-
return false;
341333
// Don't touch available_externally functions, their actual body is elsewhere.
342334
if (F.getLinkage() == GlobalValue::AvailableExternallyLinkage)
343335
return false;
344-
// Only instrument functions that have the sanitize_alloc_token attribute.
345-
if (!F.hasFnAttribute(Attribute::SanitizeAllocToken))
346-
return false;
347336

348337
auto &ORE = FAM.getResult<OptimizationRemarkEmitterAnalysis>(F);
349338
auto &TLI = FAM.getResult<TargetLibraryAnalysis>(F);
350339
SmallVector<std::pair<CallBase *, LibFunc>, 4> AllocCalls;
340+
SmallVector<IntrinsicInst *, 4> IntrinsicInsts;
341+
342+
// Only instrument functions that have the sanitize_alloc_token attribute.
343+
const bool InstrumentFunction =
344+
F.hasFnAttribute(Attribute::SanitizeAllocToken) &&
345+
!F.hasFnAttribute(Attribute::DisableSanitizerInstrumentation);
351346

352347
// Collect all allocation calls to avoid iterator invalidation.
353348
for (Instruction &I : instructions(F)) {
349+
// Collect all alloc_token_* intrinsics.
350+
if (auto *II = dyn_cast<IntrinsicInst>(&I);
351+
II && II->getIntrinsicID() == Intrinsic::alloc_token_id) {
352+
IntrinsicInsts.emplace_back(II);
353+
continue;
354+
}
355+
356+
if (!InstrumentFunction)
357+
continue;
358+
354359
auto *CB = dyn_cast<CallBase>(&I);
355360
if (!CB)
356361
continue;
@@ -359,11 +364,22 @@ bool AllocToken::instrumentFunction(Function &F) {
359364
}
360365

361366
bool Modified = false;
362-
for (auto &[CB, Func] : AllocCalls)
363-
Modified |= replaceAllocationCall(CB, Func, ORE, TLI);
364367

365-
if (Modified)
366-
NumFunctionsInstrumented++;
368+
if (!AllocCalls.empty()) {
369+
for (auto &[CB, Func] : AllocCalls)
370+
Modified |= replaceAllocationCall(CB, Func, ORE, TLI);
371+
if (Modified)
372+
NumFunctionsModified++;
373+
}
374+
375+
if (!IntrinsicInsts.empty()) {
376+
for (auto *II : IntrinsicInsts) {
377+
replaceIntrinsicInst(II, ORE);
378+
}
379+
Modified = true;
380+
NumFunctionsModified++;
381+
}
382+
367383
return Modified;
368384
}
369385

@@ -528,6 +544,16 @@ FunctionCallee AllocToken::getTokenAllocFunction(const CallBase &CB,
528544
return TokenAlloc;
529545
}
530546

547+
void AllocToken::replaceIntrinsicInst(IntrinsicInst *II,
548+
OptimizationRemarkEmitter &ORE) {
549+
assert(II->getIntrinsicID() == Intrinsic::alloc_token_id);
550+
551+
uint64_t TokenID = getToken(*II, ORE);
552+
Value *V = ConstantInt::get(IntPtrTy, TokenID);
553+
II->replaceAllUsesWith(V);
554+
II->eraseFromParent();
555+
}
556+
531557
} // namespace
532558

533559
AllocTokenPass::AllocTokenPass(AllocTokenOptions Opts)
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
2+
; Test that the alloc-token pass lowers the intrinsic to a constant token ID.
3+
;
4+
; RUN: opt < %s -passes=alloc-token -alloc-token-mode=typehashpointersplit -alloc-token-max=2 -S | FileCheck %s
5+
6+
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
7+
target triple = "x86_64-unknown-linux-gnu"
8+
9+
declare i64 @llvm.alloc.token.id.i64(metadata)
10+
11+
define i64 @test_intrinsic_lowering() {
12+
; CHECK-LABEL: define i64 @test_intrinsic_lowering() {
13+
; CHECK-NEXT: [[ENTRY:.*:]]
14+
; CHECK-NEXT: ret i64 0
15+
;
16+
entry:
17+
%token_no_ptr = call i64 @llvm.alloc.token.id.i64(metadata !0)
18+
ret i64 %token_no_ptr
19+
}
20+
21+
define i64 @test_intrinsic_lowering_ptr() {
22+
; CHECK-LABEL: define i64 @test_intrinsic_lowering_ptr() {
23+
; CHECK-NEXT: [[ENTRY:.*:]]
24+
; CHECK-NEXT: ret i64 1
25+
;
26+
entry:
27+
%token_with_ptr = call i64 @llvm.alloc.token.id.i64(metadata !1)
28+
ret i64 %token_with_ptr
29+
}
30+
31+
!0 = !{!"NoPointerType", i1 false}
32+
!1 = !{!"PointerType", i1 true}

0 commit comments

Comments
 (0)