[clang] [llvm] [Clang] Add __builtin_allow_sanitize_check() (PR #172030)
Marco Elver via cfe-commits
cfe-commits at lists.llvm.org
Mon Jan 5 07:02:37 PST 2026
https://github.com/melver updated https://github.com/llvm/llvm-project/pull/172030
>From 6368450bba995b8c936f419f46d274c9cce84282 Mon Sep 17 00:00:00 2001
From: Marco Elver <elver at google.com>
Date: Fri, 12 Dec 2025 16:57:20 +0100
Subject: [PATCH 1/4] =?UTF-8?q?[=F0=9D=98=80=F0=9D=97=BD=F0=9D=97=BF]=20ch?=
=?UTF-8?q?anges=20to=20main=20this=20commit=20is=20based=20on?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Created using spr 1.3.8-beta.1
[skip ci]
---
llvm/include/llvm/IR/Intrinsics.td | 14 ++++
.../Instrumentation/LowerAllowCheckPass.h | 2 +
.../Instrumentation/LowerAllowCheckPass.cpp | 28 ++++++--
.../lower-builtin-allow-check-pipeline.ll | 0
.../lower-builtin-allow-check-remarks.ll | 0
.../lower-builtin-allow-check.ll | 0
.../LowerAllowCheck/sanitize-check.ll | 70 +++++++++++++++++++
7 files changed, 108 insertions(+), 6 deletions(-)
rename llvm/test/Transforms/{ => LowerAllowCheck}/lower-builtin-allow-check-pipeline.ll (100%)
rename llvm/test/Transforms/{ => LowerAllowCheck}/lower-builtin-allow-check-remarks.ll (100%)
rename llvm/test/Transforms/{ => LowerAllowCheck}/lower-builtin-allow-check.ll (100%)
create mode 100644 llvm/test/Transforms/LowerAllowCheck/sanitize-check.ll
diff --git a/llvm/include/llvm/IR/Intrinsics.td b/llvm/include/llvm/IR/Intrinsics.td
index 35a4158a56da9..c8e16178b7a02 100644
--- a/llvm/include/llvm/IR/Intrinsics.td
+++ b/llvm/include/llvm/IR/Intrinsics.td
@@ -1884,6 +1884,20 @@ def int_allow_runtime_check : DefaultAttrsIntrinsic<[llvm_i1_ty], [llvm_metadata
[IntrInaccessibleMemOnly, NoUndef<RetIndex>]>,
ClangBuiltin<"__builtin_allow_runtime_check">;
+// Return true if the specific sanitizer is enabled for the function.
+def int_allow_sanitize_address
+ : DefaultAttrsIntrinsic<[llvm_i1_ty], [],
+ [IntrInaccessibleMemOnly, NoUndef<RetIndex>]>;
+def int_allow_sanitize_thread
+ : DefaultAttrsIntrinsic<[llvm_i1_ty], [],
+ [IntrInaccessibleMemOnly, NoUndef<RetIndex>]>;
+def int_allow_sanitize_memory
+ : DefaultAttrsIntrinsic<[llvm_i1_ty], [],
+ [IntrInaccessibleMemOnly, NoUndef<RetIndex>]>;
+def int_allow_sanitize_hwaddress
+ : DefaultAttrsIntrinsic<[llvm_i1_ty], [],
+ [IntrInaccessibleMemOnly, NoUndef<RetIndex>]>;
+
// Support for dynamic deoptimization (or de-specialization)
def int_experimental_deoptimize : Intrinsic<[llvm_any_ty], [llvm_vararg_ty],
[Throws]>;
diff --git a/llvm/include/llvm/Transforms/Instrumentation/LowerAllowCheckPass.h b/llvm/include/llvm/Transforms/Instrumentation/LowerAllowCheckPass.h
index 37bc728779836..7b5e4a21c057c 100644
--- a/llvm/include/llvm/Transforms/Instrumentation/LowerAllowCheckPass.h
+++ b/llvm/include/llvm/Transforms/Instrumentation/LowerAllowCheckPass.h
@@ -34,6 +34,8 @@ class LowerAllowCheckPass : public PassInfoMixin<LowerAllowCheckPass> {
: Opts(std::move(Opts)) {};
LLVM_ABI PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM);
+ static bool isRequired() { return true; }
+
LLVM_ABI static bool IsRequested();
LLVM_ABI void
printPipeline(raw_ostream &OS,
diff --git a/llvm/lib/Transforms/Instrumentation/LowerAllowCheckPass.cpp b/llvm/lib/Transforms/Instrumentation/LowerAllowCheckPass.cpp
index 2486e77ab0137..d4c23ffe9a723 100644
--- a/llvm/lib/Transforms/Instrumentation/LowerAllowCheckPass.cpp
+++ b/llvm/lib/Transforms/Instrumentation/LowerAllowCheckPass.cpp
@@ -76,6 +76,7 @@ static bool lowerAllowChecks(Function &F, const BlockFrequencyInfo &BFI,
const ProfileSummaryInfo *PSI,
OptimizationRemarkEmitter &ORE,
const LowerAllowCheckPass::Options &Opts) {
+ // List of intrinsics and the constant value they should be lowered to.
SmallVector<std::pair<IntrinsicInst *, bool>, 16> ReplaceWithValue;
std::unique_ptr<RandomNumberGenerator> Rng;
@@ -123,26 +124,41 @@ static bool lowerAllowChecks(Function &F, const BlockFrequencyInfo &BFI,
switch (ID) {
case Intrinsic::allow_ubsan_check:
case Intrinsic::allow_runtime_check: {
- ++NumChecksTotal;
-
bool ToRemove = ShouldRemove(II);
ReplaceWithValue.push_back({
II,
- ToRemove,
+ !ToRemove,
});
- if (ToRemove)
- ++NumChecksRemoved;
emitRemark(II, ORE, ToRemove);
break;
}
+ case Intrinsic::allow_sanitize_address:
+ ReplaceWithValue.push_back(
+ {II, F.hasFnAttribute(Attribute::SanitizeAddress)});
+ break;
+ case Intrinsic::allow_sanitize_thread:
+ ReplaceWithValue.push_back(
+ {II, F.hasFnAttribute(Attribute::SanitizeThread)});
+ break;
+ case Intrinsic::allow_sanitize_memory:
+ ReplaceWithValue.push_back(
+ {II, F.hasFnAttribute(Attribute::SanitizeMemory)});
+ break;
+ case Intrinsic::allow_sanitize_hwaddress:
+ ReplaceWithValue.push_back(
+ {II, F.hasFnAttribute(Attribute::SanitizeHWAddress)});
+ break;
default:
break;
}
}
for (auto [I, V] : ReplaceWithValue) {
- I->replaceAllUsesWith(ConstantInt::getBool(I->getType(), !V));
+ ++NumChecksTotal;
+ if (!V) // If the final value is false, the check is considered removed
+ ++NumChecksRemoved;
+ I->replaceAllUsesWith(ConstantInt::getBool(I->getType(), V));
I->eraseFromParent();
}
diff --git a/llvm/test/Transforms/lower-builtin-allow-check-pipeline.ll b/llvm/test/Transforms/LowerAllowCheck/lower-builtin-allow-check-pipeline.ll
similarity index 100%
rename from llvm/test/Transforms/lower-builtin-allow-check-pipeline.ll
rename to llvm/test/Transforms/LowerAllowCheck/lower-builtin-allow-check-pipeline.ll
diff --git a/llvm/test/Transforms/lower-builtin-allow-check-remarks.ll b/llvm/test/Transforms/LowerAllowCheck/lower-builtin-allow-check-remarks.ll
similarity index 100%
rename from llvm/test/Transforms/lower-builtin-allow-check-remarks.ll
rename to llvm/test/Transforms/LowerAllowCheck/lower-builtin-allow-check-remarks.ll
diff --git a/llvm/test/Transforms/lower-builtin-allow-check.ll b/llvm/test/Transforms/LowerAllowCheck/lower-builtin-allow-check.ll
similarity index 100%
rename from llvm/test/Transforms/lower-builtin-allow-check.ll
rename to llvm/test/Transforms/LowerAllowCheck/lower-builtin-allow-check.ll
diff --git a/llvm/test/Transforms/LowerAllowCheck/sanitize-check.ll b/llvm/test/Transforms/LowerAllowCheck/sanitize-check.ll
new file mode 100644
index 0000000000000..eb3c6073aabe5
--- /dev/null
+++ b/llvm/test/Transforms/LowerAllowCheck/sanitize-check.ll
@@ -0,0 +1,70 @@
+; RUN: opt < %s -passes=lower-allow-check -S | FileCheck %s
+; RUN: opt < %s -passes=lower-allow-check -lower-allow-check-random-rate=0 -S | FileCheck %s
+
+declare i1 @llvm.allow.sanitize.address()
+declare i1 @llvm.allow.sanitize.thread()
+declare i1 @llvm.allow.sanitize.memory()
+declare i1 @llvm.allow.sanitize.hwaddress()
+
+define i1 @test_address() sanitize_address {
+; CHECK-LABEL: @test_address(
+; CHECK-NEXT: ret i1 true
+ %1 = call i1 @llvm.allow.sanitize.address()
+ ret i1 %1
+}
+
+define i1 @test_no_sanitize_address() {
+; CHECK-LABEL: @test_no_sanitize_address(
+; CHECK-NEXT: ret i1 false
+ %1 = call i1 @llvm.allow.sanitize.address()
+ ret i1 %1
+}
+
+define i1 @test_address_but_no_thread() sanitize_address {
+; CHECK-LABEL: @test_address_but_no_thread(
+; CHECK-NEXT: ret i1 false
+ %1 = call i1 @llvm.allow.sanitize.thread()
+ ret i1 %1
+}
+
+define i1 @test_thread() sanitize_thread {
+; CHECK-LABEL: @test_thread(
+; CHECK-NEXT: ret i1 true
+ %1 = call i1 @llvm.allow.sanitize.thread()
+ ret i1 %1
+}
+
+define i1 @test_no_sanitize_thread() {
+; CHECK-LABEL: @test_no_sanitize_thread(
+; CHECK-NEXT: ret i1 false
+ %1 = call i1 @llvm.allow.sanitize.thread()
+ ret i1 %1
+}
+
+define i1 @test_memory() sanitize_memory {
+; CHECK-LABEL: @test_memory(
+; CHECK-NEXT: ret i1 true
+ %1 = call i1 @llvm.allow.sanitize.memory()
+ ret i1 %1
+}
+
+define i1 @test_no_sanitize_memory() {
+; CHECK-LABEL: @test_no_sanitize_memory(
+; CHECK-NEXT: ret i1 false
+ %1 = call i1 @llvm.allow.sanitize.memory()
+ ret i1 %1
+}
+
+define i1 @test_hwaddress() sanitize_hwaddress {
+; CHECK-LABEL: @test_hwaddress(
+; CHECK-NEXT: ret i1 true
+ %1 = call i1 @llvm.allow.sanitize.hwaddress()
+ ret i1 %1
+}
+
+define i1 @test_no_sanitize_hwaddress() {
+; CHECK-LABEL: @test_no_sanitize_hwaddress(
+; CHECK-NEXT: ret i1 false
+ %1 = call i1 @llvm.allow.sanitize.hwaddress()
+ ret i1 %1
+}
>From d4f149dbb21fd7f5e706560b1aa3b8f0b9fa5ae9 Mon Sep 17 00:00:00 2001
From: Marco Elver <elver at google.com>
Date: Fri, 12 Dec 2025 17:18:15 +0100
Subject: [PATCH 2/4] tweak test
Created using spr 1.3.8-beta.1
---
clang/test/CodeGen/builtin-allow-sanitize-check-lower.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/clang/test/CodeGen/builtin-allow-sanitize-check-lower.c b/clang/test/CodeGen/builtin-allow-sanitize-check-lower.c
index 05a0295799f55..5e52f77f55573 100644
--- a/clang/test/CodeGen/builtin-allow-sanitize-check-lower.c
+++ b/clang/test/CodeGen/builtin-allow-sanitize-check-lower.c
@@ -9,9 +9,9 @@ _Bool check() {
return __builtin_allow_sanitize_check("address");
}
-// CHECK-LABEL: @test
+// CHECK-LABEL: @test_sanitize
// CHECK: ret i1 true
-_Bool test() {
+_Bool test_sanitize() {
return check();
}
>From f4c6cfded128616e2c8d119e19174bbed1298781 Mon Sep 17 00:00:00 2001
From: Marco Elver <elver at google.com>
Date: Wed, 17 Dec 2025 20:47:40 +0100
Subject: [PATCH 3/4] fixes
Created using spr 1.3.8-beta.1
---
clang/lib/CodeGen/CGBuiltin.cpp | 37 ++++++++++---------
.../test/Sema/builtin-allow-sanitize-check.c | 6 +--
2 files changed, 23 insertions(+), 20 deletions(-)
diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp
index 98d80620b44a5..9de085379882c 100644
--- a/clang/lib/CodeGen/CGBuiltin.cpp
+++ b/clang/lib/CodeGen/CGBuiltin.cpp
@@ -3551,25 +3551,28 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
}
case Builtin::BI__builtin_allow_sanitize_check: {
Intrinsic::ID IntrID = Intrinsic::not_intrinsic;
- StringRef SanitizerName =
+ StringRef Name =
cast<StringLiteral>(E->getArg(0)->IgnoreParenCasts())->getString();
- if (SanitizerName == "address" || SanitizerName == "kernel-address") {
- if (CGM.getLangOpts().Sanitize.hasOneOf(SanitizerKind::Address |
- SanitizerKind::KernelAddress))
- IntrID = Intrinsic::allow_sanitize_address;
- } else if (SanitizerName == "thread") {
- if (CGM.getLangOpts().Sanitize.has(SanitizerKind::Thread))
- IntrID = Intrinsic::allow_sanitize_thread;
- } else if (SanitizerName == "memory" || SanitizerName == "kernel-memory") {
- if (CGM.getLangOpts().Sanitize.hasOneOf(SanitizerKind::Memory |
- SanitizerKind::KernelMemory))
- IntrID = Intrinsic::allow_sanitize_memory;
- } else if (SanitizerName == "hwaddress" ||
- SanitizerName == "kernel-hwaddress") {
- if (CGM.getLangOpts().Sanitize.hasOneOf(SanitizerKind::HWAddress |
- SanitizerKind::KernelHWAddress))
- IntrID = Intrinsic::allow_sanitize_hwaddress;
+ // We deliberately allow the use of kernel- and non-kernel names
+ // interchangably, even when one or the other is enabled. This is consistent
+ // with the no_sanitize-attribute, which allows either kernel- or non-kernel
+ // name to disable instrumentation (see CodeGenFunction::StartFunction).
+ if (getLangOpts().Sanitize.hasOneOf(SanitizerKind::Address |
+ SanitizerKind::KernelAddress) &&
+ (Name == "address" || Name == "kernel-address")) {
+ IntrID = Intrinsic::allow_sanitize_address;
+ } else if (getLangOpts().Sanitize.has(SanitizerKind::Thread) &&
+ Name == "thread") {
+ IntrID = Intrinsic::allow_sanitize_thread;
+ } else if (getLangOpts().Sanitize.hasOneOf(SanitizerKind::Memory |
+ SanitizerKind::KernelMemory) &&
+ (Name == "memory" || Name == "kernel-memory")) {
+ IntrID = Intrinsic::allow_sanitize_memory;
+ } else if (getLangOpts().Sanitize.hasOneOf(
+ SanitizerKind::HWAddress | SanitizerKind::KernelHWAddress) &&
+ (Name == "hwaddress" || Name == "kernel-hwaddress")) {
+ IntrID = Intrinsic::allow_sanitize_hwaddress;
}
if (IntrID != Intrinsic::not_intrinsic) {
diff --git a/clang/test/Sema/builtin-allow-sanitize-check.c b/clang/test/Sema/builtin-allow-sanitize-check.c
index 94deb16dd89f9..6e0e21a869461 100644
--- a/clang/test/Sema/builtin-allow-sanitize-check.c
+++ b/clang/test/Sema/builtin-allow-sanitize-check.c
@@ -1,15 +1,15 @@
// RUN: %clang_cc1 -fsyntax-only -verify %s
void test_builtin_allow_sanitize_check() {
- // Test with non-string literal argument
+ // Test with non-string literal argument.
char str[] = "address";
(void)__builtin_allow_sanitize_check(str); // expected-error {{expression is not a string literal}}
(void)__builtin_allow_sanitize_check(123); // expected-error {{expression is not a string literal}}
- // Test with unsupported sanitizer name
+ // Test with unsupported sanitizer name.
(void)__builtin_allow_sanitize_check("unsupported"); // expected-error {{invalid argument 'unsupported' to __builtin_allow_sanitize_check}}
- // Test with supported sanitizer names
+ // Test with supported sanitizer names.
(void)__builtin_allow_sanitize_check("address");
(void)__builtin_allow_sanitize_check("thread");
(void)__builtin_allow_sanitize_check("memory");
>From 14461fb01021f5a9689d55c1e848b8eb85ad78b2 Mon Sep 17 00:00:00 2001
From: Marco Elver <elver at google.com>
Date: Thu, 18 Dec 2025 12:05:13 +0100
Subject: [PATCH 4/4] =?UTF-8?q?[=F0=9D=98=80=F0=9D=97=BD=F0=9D=97=BF]=20ch?=
=?UTF-8?q?anges=20introduced=20through=20rebase?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Created using spr 1.3.8-beta.1
[skip ci]
---
llvm/lib/Transforms/Instrumentation/LowerAllowCheckPass.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/llvm/lib/Transforms/Instrumentation/LowerAllowCheckPass.cpp b/llvm/lib/Transforms/Instrumentation/LowerAllowCheckPass.cpp
index d4c23ffe9a723..7a950036d9b6b 100644
--- a/llvm/lib/Transforms/Instrumentation/LowerAllowCheckPass.cpp
+++ b/llvm/lib/Transforms/Instrumentation/LowerAllowCheckPass.cpp
@@ -156,7 +156,7 @@ static bool lowerAllowChecks(Function &F, const BlockFrequencyInfo &BFI,
for (auto [I, V] : ReplaceWithValue) {
++NumChecksTotal;
- if (!V) // If the final value is false, the check is considered removed
+ if (!V) // If the final value is false, the check is considered removed.
++NumChecksRemoved;
I->replaceAllUsesWith(ConstantInt::getBool(I->getType(), V));
I->eraseFromParent();
More information about the cfe-commits
mailing list