Author: Henrik G. Olsson Date: 2026-05-22T21:55:41-07:00 New Revision: d6be71fd1f10ad5220b69a3a09a942faabbd1a05
URL: https://github.com/llvm/llvm-project/commit/d6be71fd1f10ad5220b69a3a09a942faabbd1a05 DIFF: https://github.com/llvm/llvm-project/commit/d6be71fd1f10ad5220b69a3a09a942faabbd1a05.diff LOG: [Clang][CodeGen] map `noescape` to capture(address) (was capture(none)) (#199281) `capture(none)` has very restrictive semantics and an easy footgun to accidentally fire some UB into your code with. Most significantly it does not allow any visible side-effects of whether a pointer was null or not to escape the function. This means that the function cannot perform different side effects depending on whether a pointer marked `noescape` is null. Relax this to `captures(address)`, which allows information about the numerical address to escape the function, but no provenance (i.e. nothing that could be dereferenced) may escape. As discussed in https://discourse.llvm.org/t/rfc-updating-the-semantics-of-the-noescape-attribute/90326. Added: Modified: clang/lib/CodeGen/CGCall.cpp clang/test/CodeGenCXX/noescape.cpp clang/test/CodeGenObjC/noescape.m Removed: ################################################################################ diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp index 7194b81459922..40cc275d40273 100644 --- a/clang/lib/CodeGen/CGCall.cpp +++ b/clang/lib/CodeGen/CGCall.cpp @@ -3163,7 +3163,8 @@ void CodeGenModule::ConstructAttributeList(StringRef Name, } if (FI.getExtParameterInfo(ArgNo).isNoEscape()) - Attrs.addCapturesAttr(llvm::CaptureInfo::none()); + Attrs.addCapturesAttr( + llvm::CaptureInfo(llvm::CaptureComponents::Address)); if (Attrs.hasAttributes()) { unsigned FirstIRArg, NumIRArgs; diff --git a/clang/test/CodeGenCXX/noescape.cpp b/clang/test/CodeGenCXX/noescape.cpp index c3fc90e2ea54d..9c1ea1719bea5 100644 --- a/clang/test/CodeGenCXX/noescape.cpp +++ b/clang/test/CodeGenCXX/noescape.cpp @@ -8,26 +8,26 @@ struct S { virtual void vm1(int *, int * __attribute__((noescape))); }; -// CHECK: define{{.*}} void @_ZN1SC2EPiS0_(ptr {{.*}}, {{.*}}, {{.*}} noundef captures(none) {{%.*}}) -// CHECK: define{{.*}} void @_ZN1SC1EPiS0_(ptr {{.*}}, {{.*}}, {{.*}} noundef captures(none) {{%.*}}) {{.*}} { -// CHECK: call void @_ZN1SC2EPiS0_(ptr {{.*}}, {{.*}}, {{.*}} captures(none) {{.*}}) +// CHECK: define{{.*}} void @_ZN1SC2EPiS0_(ptr {{.*}}, {{.*}}, {{.*}} noundef captures(address) {{%.*}}) +// CHECK: define{{.*}} void @_ZN1SC1EPiS0_(ptr {{.*}}, {{.*}}, {{.*}} noundef captures(address) {{%.*}}) {{.*}} { +// CHECK: call void @_ZN1SC2EPiS0_(ptr {{.*}}, {{.*}}, {{.*}} captures(address) {{.*}}) S::S(int *, int * __attribute__((noescape))) {} -// CHECK: define {{.*}} ptr @_ZN1SaSEPi(ptr {{.*}}, {{.*}} noundef captures(none) {{%.*}}) +// CHECK: define {{.*}} ptr @_ZN1SaSEPi(ptr {{.*}}, {{.*}} noundef captures(address) {{%.*}}) S &S::operator=(int * __attribute__((noescape))) { return *this; } -// CHECK: define{{.*}} void @_ZN1S2m0EPiS0_(ptr {{.*}}, {{.*}} noundef captures(none) {{%.*}}) +// CHECK: define{{.*}} void @_ZN1S2m0EPiS0_(ptr {{.*}}, {{.*}} noundef captures(address) {{%.*}}) void S::m0(int *, int * __attribute__((noescape))) {} -// CHECK: define{{.*}} void @_ZN1S3vm1EPiS0_(ptr {{.*}}, {{.*}} noundef captures(none) {{%.*}}) +// CHECK: define{{.*}} void @_ZN1S3vm1EPiS0_(ptr {{.*}}, {{.*}} noundef captures(address) {{%.*}}) void S::vm1(int *, int * __attribute__((noescape))) {} // CHECK-LABEL: define{{.*}} void @_Z5test0P1SPiS1_( -// CHECK: call void @_ZN1SC1EPiS0_(ptr {{.*}}, {{.*}}, {{.*}} noundef captures(none) {{.*}}) -// CHECK: call {{.*}} ptr @_ZN1SaSEPi(ptr {{.*}}, {{.*}} noundef captures(none) {{.*}}) -// CHECK: call void @_ZN1S2m0EPiS0_(ptr {{.*}}, {{.*}}, {{.*}} noundef captures(none) {{.*}}) -// CHECK: call void {{.*}}(ptr {{.*}}, {{.*}}, {{.*}} noundef captures(none) {{.*}}) +// CHECK: call void @_ZN1SC1EPiS0_(ptr {{.*}}, {{.*}}, {{.*}} noundef captures(address) {{.*}}) +// CHECK: call {{.*}} ptr @_ZN1SaSEPi(ptr {{.*}}, {{.*}} noundef captures(address) {{.*}}) +// CHECK: call void @_ZN1S2m0EPiS0_(ptr {{.*}}, {{.*}}, {{.*}} noundef captures(address) {{.*}}) +// CHECK: call void {{.*}}(ptr {{.*}}, {{.*}}, {{.*}} noundef captures(address) {{.*}}) void test0(S *s, int *p0, int *p1) { S t(p0, p1); t = p1; @@ -39,27 +39,27 @@ namespace std { typedef decltype(sizeof(0)) size_t; } -// CHECK: define {{.*}} @_ZnwmPv({{.*}}, {{.*}} captures(none) {{.*}}) +// CHECK: define {{.*}} @_ZnwmPv({{.*}}, {{.*}} captures(address) {{.*}}) void *operator new(std::size_t, void * __attribute__((noescape)) p) { return p; } // CHECK-LABEL: define{{.*}} ptr @_Z5test1Pv( -// CHECK: %call = call {{.*}} @_ZnwmPv({{.*}}, {{.*}} captures(none) {{.*}}) +// CHECK: %call = call {{.*}} @_ZnwmPv({{.*}}, {{.*}} captures(address) {{.*}}) void *test1(void *p0) { return ::operator new(16, p0); } // CHECK-LABEL: define{{.*}} void @_Z5test2PiS_( -// CHECK: call void @"_ZZ5test2PiS_ENK3$_0clES_S_"({{.*}}, {{.*}}, {{.*}} captures(none) {{.*}}) -// CHECK: define internal void @"_ZZ5test2PiS_ENK3$_0clES_S_"({{.*}}, {{.*}}, {{.*}} noundef captures(none) {{%.*}}) +// CHECK: call void @"_ZZ5test2PiS_ENK3$_0clES_S_"({{.*}}, {{.*}}, {{.*}} captures(address) {{.*}}) +// CHECK: define internal void @"_ZZ5test2PiS_ENK3$_0clES_S_"({{.*}}, {{.*}}, {{.*}} noundef captures(address) {{%.*}}) void test2(int *p0, int *p1) { auto t = [](int *, int * __attribute__((noescape))){}; t(p0, p1); } // CHECK-LABEL: define{{.*}} void @_Z5test3PFvU8noescapePiES_( -// CHECK: call void {{.*}}(ptr noundef captures(none) {{.*}}) +// CHECK: call void {{.*}}(ptr noundef captures(address) {{.*}}) typedef void (*NoEscapeFunc)(__attribute__((noescape)) int *); void test3(NoEscapeFunc f, int *p) { diff --git a/clang/test/CodeGenObjC/noescape.m b/clang/test/CodeGenObjC/noescape.m index e1dbc0eb92e54..80d4bb8e1c876 100644 --- a/clang/test/CodeGenObjC/noescape.m +++ b/clang/test/CodeGenObjC/noescape.m @@ -26,37 +26,37 @@ // CHECK: @[[BLOCK_DESCIPTOR_TMP_2:.*ls32l8"]] = linkonce_odr hidden unnamed_addr constant { i64, i64, ptr, i64 } { i64 0, i64 40, ptr @{{.*}}, i64 256 }, align 8 // CHECK-LABEL: define{{.*}} void @test0( -// CHECK: call void @noescapeFunc0({{.*}}, {{.*}} captures(none) {{.*}}) -// CHECK: declare void @noescapeFunc0(ptr noundef, {{.*}} noundef captures(none)) +// CHECK: call void @noescapeFunc0({{.*}}, {{.*}} captures(address) {{.*}}) +// CHECK: declare void @noescapeFunc0(ptr noundef, {{.*}} noundef captures(address)) void test0(BlockTy b) { noescapeFunc0(0, b); } // CHECK-LABEL: define{{.*}} void @test1( -// CHECK: call void @noescapeFunc1({{.*}} captures(none) {{.*}}) -// CHECK: declare void @noescapeFunc1({{.*}} noundef captures(none)) +// CHECK: call void @noescapeFunc1({{.*}} captures(address) {{.*}}) +// CHECK: declare void @noescapeFunc1({{.*}} noundef captures(address)) void test1(int *i) { noescapeFunc1(i); } // CHECK-LABEL: define{{.*}} void @test2( -// CHECK: call void @noescapeFunc2({{.*}} captures(none) {{.*}}) -// CHECK: declare void @noescapeFunc2({{.*}} noundef captures(none)) +// CHECK: call void @noescapeFunc2({{.*}} captures(address) {{.*}}) +// CHECK: declare void @noescapeFunc2({{.*}} noundef captures(address)) void test2(id i) { noescapeFunc2(i); } // CHECK-LABEL: define{{.*}} void @test3( -// CHECK: call void @noescapeFunc3({{.*}} captures(none) {{.*}}) -// CHECK: declare void @noescapeFunc3({{.*}} captures(none)) +// CHECK: call void @noescapeFunc3({{.*}} captures(address) {{.*}}) +// CHECK: declare void @noescapeFunc3({{.*}} captures(address)) void test3(union U u) { noescapeFunc3(u); } -// CHECK: define internal void @"\01-[C0 m0:]"({{.*}}, {{.*}}, {{.*}} captures(none) {{.*}}) +// CHECK: define internal void @"\01-[C0 m0:]"({{.*}}, {{.*}}, {{.*}} captures(address) {{.*}}) // CHECK-LABEL: define{{.*}} void @test4( -// CHECK: call void @objc_msgSend(ptr {{.*}}, ptr {{.*}}, ptr noundef captures(none) {{.*}}) +// CHECK: call void @objc_msgSend(ptr {{.*}}, ptr {{.*}}, ptr noundef captures(address) {{.*}}) @interface C0 -(void) m0:(int*)__attribute__((noescape)) p0; @@ -72,9 +72,9 @@ void test4(C0 *c0, int *p) { } // CHECK-LABEL: define{{.*}} void @test5( -// CHECK: call void {{.*}}(ptr noundef @{{.*}}, ptr noundef captures(none) {{.*}}) -// CHECK: call void {{.*}}(ptr {{.*}}, ptr noundef captures(none) {{.*}}) -// CHECK: define internal void @{{.*}}(ptr {{.*}}, ptr noundef captures(none) {{.*}}) +// CHECK: call void {{.*}}(ptr noundef @{{.*}}, ptr noundef captures(address) {{.*}}) +// CHECK: call void {{.*}}(ptr {{.*}}, ptr noundef captures(address) {{.*}}) +// CHECK: define internal void @{{.*}}(ptr {{.*}}, ptr noundef captures(address) {{.*}}) typedef void (^BlockTy2)(__attribute__((noescape)) int *); _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
