Skip to content

Commit 5baf61b

Browse files
melveraokblast
authored andcommitted
[AllocToken] Introduce llvm.alloc.token.id intrinsic (llvm#163632)
Introduce a new intrinsic, `llvm.alloc.token.id`, to allow compile-time querying of allocation token IDs. The `AllocToken` pass is taught to recognize and lower this intrinsic. It extracts the `!alloc_token` metadata from the intrinsic's argument, feeds it into the same token-generation logic used for instrumenting allocation calls, and replaces the intrinsic with the resulting constant integer token ID. This is a prerequisite for `__builtin_infer_alloc_token`. The pass now runs on all functions to ensure intrinsics are lowered, but continues to only instrument allocation calls in functions with the `sanitize_alloc_token` attribute.
1 parent add85d4 commit 5baf61b

File tree

4 files changed

+131
-14
lines changed

4 files changed

+131
-14
lines changed

llvm/include/llvm/IR/Intrinsics.td

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

2854+
//===----------------- AllocToken Intrinsics ------------------------------===//
2855+
2856+
// Return the token ID for the given !alloc_token metadata.
2857+
def int_alloc_token_id :
2858+
DefaultAttrsIntrinsic<[llvm_anyint_ty], [llvm_metadata_ty],
2859+
[IntrNoMem, NoUndef<RetIndex>]>;
2860+
28542861
//===----------------------------------------------------------------------===//
2862+
28552863
//===------- Convergence Intrinsics ---------------------------------------===//
28562864

28572865
def int_experimental_convergence_entry

llvm/lib/Transforms/Instrumentation/AllocToken.cpp

Lines changed: 59 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
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"
@@ -131,7 +132,7 @@ cl::opt<uint64_t> ClFallbackToken(
131132

132133
//===--- Statistics -------------------------------------------------------===//
133134

134-
STATISTIC(NumFunctionsInstrumented, "Functions instrumented");
135+
STATISTIC(NumFunctionsModified, "Functions modified");
135136
STATISTIC(NumAllocationsInstrumented, "Allocations instrumented");
136137

137138
//===----------------------------------------------------------------------===//
@@ -140,9 +141,19 @@ STATISTIC(NumAllocationsInstrumented, "Allocations instrumented");
140141
///
141142
/// Expected format is: !{<type-name>, <contains-pointer>}
142143
MDNode *getAllocTokenMetadata(const CallBase &CB) {
143-
MDNode *Ret = CB.getMetadata(LLVMContext::MD_alloc_token);
144-
if (!Ret)
145-
return nullptr;
144+
MDNode *Ret = nullptr;
145+
if (auto *II = dyn_cast<IntrinsicInst>(&CB);
146+
II && II->getIntrinsicID() == Intrinsic::alloc_token_id) {
147+
auto *MDV = cast<MetadataAsValue>(II->getArgOperand(0));
148+
Ret = cast<MDNode>(MDV->getMetadata());
149+
// If the intrinsic has an empty MDNode, type inference failed.
150+
if (Ret->getNumOperands() == 0)
151+
return nullptr;
152+
} else {
153+
Ret = CB.getMetadata(LLVMContext::MD_alloc_token);
154+
if (!Ret)
155+
return nullptr;
156+
}
146157
assert(Ret->getNumOperands() == 2 && "bad !alloc_token");
147158
assert(isa<MDString>(Ret->getOperand(0)));
148159
assert(isa<ConstantAsMetadata>(Ret->getOperand(1)));
@@ -315,6 +326,9 @@ class AllocToken {
315326
FunctionCallee getTokenAllocFunction(const CallBase &CB, uint64_t TokenID,
316327
LibFunc OriginalFunc);
317328

329+
/// Lower alloc_token_* intrinsics.
330+
void replaceIntrinsicInst(IntrinsicInst *II, OptimizationRemarkEmitter &ORE);
331+
318332
/// Return the token ID from metadata in the call.
319333
uint64_t getToken(const CallBase &CB, OptimizationRemarkEmitter &ORE) {
320334
return std::visit([&](auto &&Mode) { return Mode(CB, ORE); }, Mode);
@@ -336,21 +350,32 @@ bool AllocToken::instrumentFunction(Function &F) {
336350
// Do not apply any instrumentation for naked functions.
337351
if (F.hasFnAttribute(Attribute::Naked))
338352
return false;
339-
if (F.hasFnAttribute(Attribute::DisableSanitizerInstrumentation))
340-
return false;
341353
// Don't touch available_externally functions, their actual body is elsewhere.
342354
if (F.getLinkage() == GlobalValue::AvailableExternallyLinkage)
343355
return false;
344-
// Only instrument functions that have the sanitize_alloc_token attribute.
345-
if (!F.hasFnAttribute(Attribute::SanitizeAllocToken))
346-
return false;
347356

348357
auto &ORE = FAM.getResult<OptimizationRemarkEmitterAnalysis>(F);
349358
auto &TLI = FAM.getResult<TargetLibraryAnalysis>(F);
350359
SmallVector<std::pair<CallBase *, LibFunc>, 4> AllocCalls;
360+
SmallVector<IntrinsicInst *, 4> IntrinsicInsts;
361+
362+
// Only instrument functions that have the sanitize_alloc_token attribute.
363+
const bool InstrumentFunction =
364+
F.hasFnAttribute(Attribute::SanitizeAllocToken) &&
365+
!F.hasFnAttribute(Attribute::DisableSanitizerInstrumentation);
351366

352367
// Collect all allocation calls to avoid iterator invalidation.
353368
for (Instruction &I : instructions(F)) {
369+
// Collect all alloc_token_* intrinsics.
370+
if (auto *II = dyn_cast<IntrinsicInst>(&I);
371+
II && II->getIntrinsicID() == Intrinsic::alloc_token_id) {
372+
IntrinsicInsts.emplace_back(II);
373+
continue;
374+
}
375+
376+
if (!InstrumentFunction)
377+
continue;
378+
354379
auto *CB = dyn_cast<CallBase>(&I);
355380
if (!CB)
356381
continue;
@@ -359,11 +384,21 @@ bool AllocToken::instrumentFunction(Function &F) {
359384
}
360385

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

365-
if (Modified)
366-
NumFunctionsInstrumented++;
388+
if (!AllocCalls.empty()) {
389+
for (auto &[CB, Func] : AllocCalls)
390+
Modified |= replaceAllocationCall(CB, Func, ORE, TLI);
391+
if (Modified)
392+
NumFunctionsModified++;
393+
}
394+
395+
if (!IntrinsicInsts.empty()) {
396+
for (auto *II : IntrinsicInsts)
397+
replaceIntrinsicInst(II, ORE);
398+
Modified = true;
399+
NumFunctionsModified++;
400+
}
401+
367402
return Modified;
368403
}
369404

@@ -381,7 +416,7 @@ AllocToken::shouldInstrumentCall(const CallBase &CB,
381416
if (TLI.getLibFunc(*Callee, Func)) {
382417
if (isInstrumentableLibFunc(Func, CB, TLI))
383418
return Func;
384-
} else if (Options.Extended && getAllocTokenMetadata(CB)) {
419+
} else if (Options.Extended && CB.getMetadata(LLVMContext::MD_alloc_token)) {
385420
return NotLibFunc;
386421
}
387422

@@ -528,6 +563,16 @@ FunctionCallee AllocToken::getTokenAllocFunction(const CallBase &CB,
528563
return TokenAlloc;
529564
}
530565

566+
void AllocToken::replaceIntrinsicInst(IntrinsicInst *II,
567+
OptimizationRemarkEmitter &ORE) {
568+
assert(II->getIntrinsicID() == Intrinsic::alloc_token_id);
569+
570+
uint64_t TokenID = getToken(*II, ORE);
571+
Value *V = ConstantInt::get(IntPtrTy, TokenID);
572+
II->replaceAllUsesWith(V);
573+
II->eraseFromParent();
574+
}
575+
531576
} // namespace
532577

533578
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}
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-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:32:64-v64:64:64-v128:128:128-a0:0:64-f80:32:32-n8:16:32-S128"
7+
target triple = "i386-pc-linux-gnu"
8+
9+
declare i32 @llvm.alloc.token.id.i32(metadata)
10+
11+
define i32 @test_intrinsic_lowering() {
12+
; CHECK-LABEL: define i32 @test_intrinsic_lowering() {
13+
; CHECK-NEXT: [[ENTRY:.*:]]
14+
; CHECK-NEXT: ret i32 0
15+
;
16+
entry:
17+
%token_no_ptr = call i32 @llvm.alloc.token.id.i32(metadata !0)
18+
ret i32 %token_no_ptr
19+
}
20+
21+
define i32 @test_intrinsic_lowering_ptr() {
22+
; CHECK-LABEL: define i32 @test_intrinsic_lowering_ptr() {
23+
; CHECK-NEXT: [[ENTRY:.*:]]
24+
; CHECK-NEXT: ret i32 1
25+
;
26+
entry:
27+
%token_with_ptr = call i32 @llvm.alloc.token.id.i32(metadata !1)
28+
ret i32 %token_with_ptr
29+
}
30+
31+
!0 = !{!"NoPointerType", i1 false}
32+
!1 = !{!"PointerType", i1 true}

0 commit comments

Comments
 (0)