https://github.com/ahatanak updated https://github.com/llvm/llvm-project/pull/109056
>From eede4b2c2916a3016643fb56f87f7601dfaff69b Mon Sep 17 00:00:00 2001 From: Akira Hatanaka <ahata...@gmail.com> Date: Mon, 16 Sep 2024 17:12:13 -0700 Subject: [PATCH 1/4] [PAC] Re-sign a pointer to a noexcept member function when it is converted to a pointer to a member function without noexcept Fixes https://github.com/llvm/llvm-project/issues/106487. --- clang/lib/CodeGen/CGExprScalar.cpp | 9 +- clang/lib/CodeGen/ItaniumCXXABI.cpp | 28 ++++- .../ptrauth-member-function-pointer.cpp | 109 +++++++++++++++++- 3 files changed, 136 insertions(+), 10 deletions(-) diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp index 82caf65ac68d6b..76e2f047e84ae4 100644 --- a/clang/lib/CodeGen/CGExprScalar.cpp +++ b/clang/lib/CodeGen/CGExprScalar.cpp @@ -2419,8 +2419,13 @@ Value *ScalarExprEmitter::VisitCastExpr(CastExpr *CE) { return Visit(const_cast<Expr*>(E)); case CK_NoOp: { - return CE->changesVolatileQualification() ? EmitLoadOfLValue(CE) - : Visit(const_cast<Expr *>(E)); + if (CE->changesVolatileQualification()) + return EmitLoadOfLValue(CE); + auto V = Visit(const_cast<Expr *>(E)); + if (CGF.CGM.getCodeGenOpts().PointerAuth.CXXMemberFunctionPointers && + CE->getType()->isMemberFunctionPointerType()) + V = CGF.CGM.getCXXABI().EmitMemberPointerConversion(CGF, CE, V); + return V; } case CK_BaseToDerived: { diff --git a/clang/lib/CodeGen/ItaniumCXXABI.cpp b/clang/lib/CodeGen/ItaniumCXXABI.cpp index dcc35d5689831e..085ed84b5108b4 100644 --- a/clang/lib/CodeGen/ItaniumCXXABI.cpp +++ b/clang/lib/CodeGen/ItaniumCXXABI.cpp @@ -924,17 +924,20 @@ ItaniumCXXABI::EmitMemberPointerConversion(CodeGenFunction &CGF, if (isa<llvm::Constant>(src)) return EmitMemberPointerConversion(E, cast<llvm::Constant>(src)); + QualType DstType = E->getType(), SrcType = E->getSubExpr()->getType(); + assert(E->getCastKind() == CK_DerivedToBaseMemberPointer || E->getCastKind() == CK_BaseToDerivedMemberPointer || - E->getCastKind() == CK_ReinterpretMemberPointer); + E->getCastKind() == CK_ReinterpretMemberPointer || + (E->getCastKind() == CK_NoOp && + getContext().hasSameFunctionTypeIgnoringExceptionSpec( + DstType->getPointeeType(), SrcType->getPointeeType()))); CGBuilderTy &Builder = CGF.Builder; - QualType DstType = E->getType(); if (DstType->isMemberFunctionPointerType()) { if (const auto &NewAuthInfo = CGM.getMemberFunctionPointerAuthInfo(DstType)) { - QualType SrcType = E->getSubExpr()->getType(); assert(SrcType->isMemberFunctionPointerType()); const auto &CurAuthInfo = CGM.getMemberFunctionPointerAuthInfo(SrcType); llvm::Value *MemFnPtr = Builder.CreateExtractValue(src, 0, "memptr.ptr"); @@ -971,6 +974,11 @@ ItaniumCXXABI::EmitMemberPointerConversion(CodeGenFunction &CGF, } } + // Conversion from a pointer to a noexcept member function to a pointer to a + // member function without noexcept doesn't require any additional processing. + if (E->getCastKind() == CK_NoOp) + return src; + // Under Itanium, reinterprets don't require any additional processing. if (E->getCastKind() == CK_ReinterpretMemberPointer) return src; @@ -1045,16 +1053,24 @@ pointerAuthResignMemberFunctionPointer(llvm::Constant *Src, QualType DestType, llvm::Constant * ItaniumCXXABI::EmitMemberPointerConversion(const CastExpr *E, llvm::Constant *src) { + QualType DstType = E->getType(), SrcType = E->getSubExpr()->getType(); + assert(E->getCastKind() == CK_DerivedToBaseMemberPointer || E->getCastKind() == CK_BaseToDerivedMemberPointer || - E->getCastKind() == CK_ReinterpretMemberPointer); - - QualType DstType = E->getType(); + E->getCastKind() == CK_ReinterpretMemberPointer || + (E->getCastKind() == CK_NoOp && + getContext().hasSameFunctionTypeIgnoringExceptionSpec( + DstType->getPointeeType(), SrcType->getPointeeType()))); if (DstType->isMemberFunctionPointerType()) src = pointerAuthResignMemberFunctionPointer( src, DstType, E->getSubExpr()->getType(), CGM); + // Conversion from a pointer to a noexcept member function to a pointer to a + // member function without noexcept doesn't require any additional processing. + if (E->getCastKind() == CK_NoOp) + return src; + // Under Itanium, reinterprets don't require any additional processing. if (E->getCastKind() == CK_ReinterpretMemberPointer) return src; diff --git a/clang/test/CodeGenCXX/ptrauth-member-function-pointer.cpp b/clang/test/CodeGenCXX/ptrauth-member-function-pointer.cpp index 0a9ac3fa510f56..3408e7e18c3adc 100644 --- a/clang/test/CodeGenCXX/ptrauth-member-function-pointer.cpp +++ b/clang/test/CodeGenCXX/ptrauth-member-function-pointer.cpp @@ -1,10 +1,12 @@ -// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -emit-llvm -std=c++11 -O1 -disable-llvm-passes -o - %s | FileCheck -check-prefixes=CHECK,NODEBUG,DARWIN %s +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -emit-llvm -std=c++11 -O1 -disable-llvm-passes -o - %s | FileCheck -check-prefixes=CHECK,NODEBUG,DARWIN,CXX11 %s +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -emit-llvm -std=c++17 -O1 -disable-llvm-passes -o - %s | FileCheck -check-prefixes=CHECK,NODEBUG,DARWIN,CXX17 %s // RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -emit-llvm -std=c++11 -O1 -disable-llvm-passes -debug-info-kind=limited -o - %s | FileCheck -check-prefixes=CHECK,DARWIN %s // RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -emit-llvm -std=c++11 -O1 -disable-llvm-passes -stack-protector 1 -o - %s | FileCheck %s -check-prefix=STACK-PROT // RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -emit-llvm -std=c++11 -O1 -disable-llvm-passes -stack-protector 2 -o - %s | FileCheck %s -check-prefix=STACK-PROT // RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -emit-llvm -std=c++11 -O1 -disable-llvm-passes -stack-protector 3 -o - %s | FileCheck %s -check-prefix=STACK-PROT -// RUN: %clang_cc1 -triple aarch64-linux-gnu -fptrauth-calls -fptrauth-intrinsics -emit-llvm -std=c++11 -O1 -disable-llvm-passes -o - %s | FileCheck -check-prefixes=CHECK,NODEBUG,ELF %s +// RUN: %clang_cc1 -triple aarch64-linux-gnu -fptrauth-calls -fptrauth-intrinsics -emit-llvm -std=c++11 -O1 -disable-llvm-passes -o - %s | FileCheck -check-prefixes=CHECK,NODEBUG,ELF,CXX11 %s +// RUN: %clang_cc1 -triple aarch64-linux-gnu -fptrauth-calls -fptrauth-intrinsics -emit-llvm -std=c++17 -O1 -disable-llvm-passes -o - %s | FileCheck -check-prefixes=CHECK,NODEBUG,ELF,CXX17 %s // RUN: %clang_cc1 -triple aarch64-linux-gnu -fptrauth-calls -fptrauth-intrinsics -emit-llvm -std=c++11 -O1 -disable-llvm-passes -debug-info-kind=limited -o - %s | FileCheck -check-prefixes=CHECK,ELF %s // RUN: %clang_cc1 -triple aarch64-linux-gnu -fptrauth-calls -fptrauth-intrinsics -emit-llvm -std=c++11 -O1 -disable-llvm-passes -stack-protector 1 -o - %s | FileCheck %s -check-prefix=STACK-PROT // RUN: %clang_cc1 -triple aarch64-linux-gnu -fptrauth-calls -fptrauth-intrinsics -emit-llvm -std=c++11 -O1 -disable-llvm-passes -stack-protector 2 -o - %s | FileCheck %s -check-prefix=STACK-PROT @@ -20,6 +22,11 @@ // CHECK: @__const._Z13testArrayInitv.c0 = private unnamed_addr constant %struct.Class0 { { i64, i64 } { i64 ptrtoint (ptr ptrauth (ptr @_ZN5Base011nonvirtual0Ev, i32 0, i64 35591) to i64), i64 0 } }, align 8 // CHECK: @__const._Z13testArrayInitv.c1 = private unnamed_addr constant %struct.Class0 { { i64, i64 } { i64 ptrtoint (ptr ptrauth (ptr @_ZN5Base08virtual1Ev_vfpthunk_, i32 0, i64 35591) to i64), i64 0 } }, align 8 +// CHECK: @_ZN22testNoexceptConversion6mfptr1E = global { i64, i64 } { i64 ptrtoint (ptr ptrauth (ptr @_ZN22testNoexceptConversion1S19nonvirtual_noexceptEv, i32 0, i64 [[DISC_NO_NOEXCEPT:.*]]) to i64), i64 0 }, +// CHECK: @_ZN22testNoexceptConversion6mfptr2E = global { i64, i64 } { i64 ptrtoint (ptr ptrauth (ptr @_ZN22testNoexceptConversion1S16virtual_noexceptEv_vfpthunk_, i32 0, i64 [[DISC_NO_NOEXCEPT]]) to i64), i64 0 }, +// CXX11: @_ZN22testNoexceptConversion15mfptr3_noexceptE = global { i64, i64 } { i64 ptrtoint (ptr ptrauth (ptr @_ZN22testNoexceptConversion1S19nonvirtual_noexceptEv, i32 0, i64 [[DISC_NO_NOEXCEPT]]) to i64), i64 0 }, +// CXX17: @_ZN22testNoexceptConversion15mfptr3_noexceptE = global { i64, i64 } { i64 ptrtoint (ptr ptrauth (ptr @_ZN22testNoexceptConversion1S19nonvirtual_noexceptEv, i32 0, i64 [[DISC_NOEXCEPT:.*]]) to i64), i64 0 }, + // CHECK: @_ZTV5Base0 = unnamed_addr constant { [5 x ptr] } { [5 x ptr] [ptr null, ptr @_ZTI5Base0, // CHECK-SAME: ptr ptrauth (ptr @_ZN5Base08virtual1Ev, i32 0, i64 55600, ptr getelementptr inbounds ({ [5 x ptr] }, ptr @_ZTV5Base0, i32 0, i32 0, i32 2)), // CHECK-SAME: ptr ptrauth (ptr @_ZN5Base08virtual3Ev, i32 0, i64 53007, ptr getelementptr inbounds ({ [5 x ptr] }, ptr @_ZTV5Base0, i32 0, i32 0, i32 3)), @@ -438,3 +445,101 @@ void testArrayInit() { void testConvertNull() { VariadicMethodTy0 t = (VariadicMethodTy0)(MethodTy0{}); } + +namespace testNoexceptConversion { + +// CHECK-LABEL: define internal void @__cxx_global_var_init() +// CXX17: [[ENTRY:.*]]: +// CHECK: %[[V0:.*]] = load { i64, i64 }, ptr @_ZN22testNoexceptConversion15mfptr0_noexceptE, align 8 +// CXX17: %[[MEMPTR_PTR:.*]] = extractvalue { i64, i64 } %[[V0]], 0 +// CXX17: %[[MEMPTR_ADJ:.*]] = extractvalue { i64, i64 } %[[V0]], 1 +// CXX17: %[[V1:.*]] = and i64 %[[MEMPTR_ADJ]], 1 +// CXX17: %[[IS_VIRTUAL_OFFSET:.*]] = icmp ne i64 %[[V1]], 0 +// CXX17: br i1 %[[IS_VIRTUAL_OFFSET]], label %[[MERGE:.*]], label %[[RESIGN:.*]] + +// CXX17: [[RESIGN]]: +// CXX17: %[[V2:.*]] = inttoptr i64 %[[MEMPTR_PTR]] to ptr +// CXX17: %[[V3:.*]] = icmp ne ptr %[[V2]], null +// CXX17: br i1 %[[V3]], label %[[RESIGN_NONNULL:.*]], label %[[RESIGN_CONT:.*]] + +// CXX17: [[RESIGN_NONNULL]]: +// CXX17: %[[V4:.*]] = ptrtoint ptr %[[V2]] to i64 +// CXX17: %[[V5:.*]] = call i64 @llvm.ptrauth.resign(i64 %[[V4]], i32 0, i64 [[DISC_NOEXCEPT]], i32 0, i64 [[DISC_NO_NOEXCEPT]]) +// CXX17: %[[V6:.*]] = inttoptr i64 %[[V5]] to ptr +// CXX17: br label %[[RESIGN_CONT]] + +// CXX17: [[RESIGN_CONT]]: +// CXX17: %[[V7:.*]] = phi ptr [ null, %[[RESIGN]] ], [ %[[V6]], %[[RESIGN_NONNULL]] ] +// CXX17: %[[V8:.*]] = ptrtoint ptr %[[V7]] to i64 +// CXX17: %[[V9:.*]] = insertvalue { i64, i64 } %[[V0]], i64 %[[V8]], 0 +// CXX17: br label %[[MERGE]] + +// CXX17: [[MERGE]]: +// CXX17: %[[V10:.*]] = phi { i64, i64 } [ %[[V0]], %[[ENTRY]] ], [ %[[V9]], %[[RESIGN_CONT]] ] +// CXX17: store { i64, i64 } %[[V10]], ptr @_ZN22testNoexceptConversion6mfptr4E, align 8 +// CXX11: store { i64, i64 } %[[V0]], ptr @_ZN22testNoexceptConversion6mfptr4E, align 8 + +// CHECK: define {{.*}}void @_ZN22testNoexceptConversion5test0Ev() +// CHECK: %[[P0:.*]] = alloca { i64, i64 }, align 8 +// CHECK: store { i64, i64 } { i64 ptrtoint (ptr ptrauth (ptr @_ZN22testNoexceptConversion1S19nonvirtual_noexceptEv, i32 0, i64 [[DISC_NO_NOEXCEPT]]) to i64), i64 0 }, ptr %[[P0]], align 8, + +// CHECK: define {{.*}}void @_ZN22testNoexceptConversion5test1Ev() +// CHECK: %[[P0:.*]] = alloca { i64, i64 }, align 8 +// CHECK: store { i64, i64 } { i64 ptrtoint (ptr ptrauth (ptr @_ZN22testNoexceptConversion1S16virtual_noexceptEv_vfpthunk_, i32 0, i64 [[DISC_NO_NOEXCEPT]]) to i64), i64 0 }, ptr %[[P0]], align 8, + +// CHECK: define {{.*}}void @_ZN22testNoexceptConversion5test2Ev() +// CXX17: [[ENTRY:.*]]: +// CHECK: %[[P0:.*]] = alloca { i64, i64 }, align 8 +// CHECK: %[[V0:.*]] = load { i64, i64 }, ptr @_ZN22testNoexceptConversion15mfptr0_noexceptE, align 8 +// CXX17: %[[MEMPTR_PTR:.*]] = extractvalue { i64, i64 } %[[V0]], 0 +// CXX17: %[[MEMPTR_ADJ:.*]] = extractvalue { i64, i64 } %[[V0]], 1 +// CXX17: %[[V1:.*]] = and i64 %[[MEMPTR_ADJ]], 1 +// CXX17: %[[IS_VIRTUAL_OFFSET:.*]] = icmp ne i64 %[[V1]], 0 +// CXX17: br i1 %[[IS_VIRTUAL_OFFSET]], label %[[MERGE:.*]], label %[[RESIGN:.*]] + +// CXX17: [[RESIGN]]: +// CXX17: %[[V2:.*]] = inttoptr i64 %[[MEMPTR_PTR]] to ptr +// CXX17: %[[V3:.*]] = icmp ne ptr %[[V2]], null +// CXX17: br i1 %[[V3]], label %[[RESIGN_NONNULL:.*]], label %[[RESIGN_CONT:.*]] + +// CXX17: [[RESIGN_NONNULL]]: +// CXX17: %[[V4:.*]] = ptrtoint ptr %[[V2]] to i64 +// CXX17: %[[V5:.*]] = call i64 @llvm.ptrauth.resign(i64 %[[V4]], i32 0, i64 [[DISC_NOEXCEPT]], i32 0, i64 [[DISC_NO_NOEXCEPT]]) +// CXX17: %[[V6:.*]] = inttoptr i64 %[[V5]] to ptr +// CXX17: br label %[[RESIGN_CONT]] + +// CXX17: [[RESIGN_CONT]]: +// CXX17: %[[V7:.*]] = phi ptr [ null, %[[RESIGN]] ], [ %[[V6]], %[[RESIGN_NONNULL]] ] +// CXX17: %[[V8:.*]] = ptrtoint ptr %[[V7]] to i64 +// CXX17: %[[V9:.*]] = insertvalue { i64, i64 } %[[V0]], i64 %[[V8]], 0 +// CXX17: br label %[[MERGE]] + +// CXX17: [[MERGE]]: +// CXX17: %[[V10:.*]] = phi { i64, i64 } [ %[[V0]], %[[ENTRY]] ], [ %[[V9]], %[[RESIGN_CONT]] ] +// CXX11: store { i64, i64 } %[[V0]], ptr %[[P0]], align 8 +// CXX17: store { i64, i64 } %[[V10]], ptr %[[P0]], align 8 + +struct S { + void nonvirtual_noexcept() noexcept; + virtual void virtual_noexcept() noexcept; +}; + +void (S::*mfptr0_noexcept)() noexcept; +void (S::*mfptr1)() = &S::nonvirtual_noexcept; +void (S::*mfptr2)() = &S::virtual_noexcept; +void (S::*mfptr3_noexcept)() noexcept = &S::nonvirtual_noexcept; +void (S::*mfptr4)() = mfptr0_noexcept; + +void test0() { + void (S::*p0)() = &S::nonvirtual_noexcept; +} + +void test1() { + void (S::*p0)() = &S::virtual_noexcept; +} + +void test2() { + void (S::*p0)() = mfptr0_noexcept; +} + +} >From bb2051660628be74ae42bf86fc8d63581177dcee Mon Sep 17 00:00:00 2001 From: Akira Hatanaka <ahata...@gmail.com> Date: Tue, 14 Jan 2025 09:27:32 -0800 Subject: [PATCH 2/4] Ignore exception specification when computing discriminator of member function pointers --- clang/lib/AST/ASTContext.cpp | 7 ++ clang/lib/CodeGen/CGExprScalar.cpp | 9 +- clang/lib/CodeGen/ItaniumCXXABI.cpp | 28 ++---- .../ptrauth-member-function-pointer.cpp | 85 +++++-------------- 4 files changed, 36 insertions(+), 93 deletions(-) diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index 67841a30a571f3..311cf80eb6bff7 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -3442,6 +3442,13 @@ uint16_t ASTContext::getPointerAuthTypeDiscriminator(QualType T) { encodeTypeForFunctionPointerAuth(*this, Out, T); } else { T = T.getUnqualifiedType(); + // Drop exception specification from member function pointer type. + if (auto *MPT = T->getAs<MemberPointerType>()) + if (MPT->isMemberFunctionPointer()) { + QualType FT = + getFunctionTypeWithExceptionSpec(MPT->getPointeeType(), EST_None); + T = getMemberPointerType(FT, MPT->getClass()); + } std::unique_ptr<MangleContext> MC(createMangleContext()); MC->mangleCanonicalTypeName(T, Out); } diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp index 76e2f047e84ae4..82caf65ac68d6b 100644 --- a/clang/lib/CodeGen/CGExprScalar.cpp +++ b/clang/lib/CodeGen/CGExprScalar.cpp @@ -2419,13 +2419,8 @@ Value *ScalarExprEmitter::VisitCastExpr(CastExpr *CE) { return Visit(const_cast<Expr*>(E)); case CK_NoOp: { - if (CE->changesVolatileQualification()) - return EmitLoadOfLValue(CE); - auto V = Visit(const_cast<Expr *>(E)); - if (CGF.CGM.getCodeGenOpts().PointerAuth.CXXMemberFunctionPointers && - CE->getType()->isMemberFunctionPointerType()) - V = CGF.CGM.getCXXABI().EmitMemberPointerConversion(CGF, CE, V); - return V; + return CE->changesVolatileQualification() ? EmitLoadOfLValue(CE) + : Visit(const_cast<Expr *>(E)); } case CK_BaseToDerived: { diff --git a/clang/lib/CodeGen/ItaniumCXXABI.cpp b/clang/lib/CodeGen/ItaniumCXXABI.cpp index 085ed84b5108b4..dcc35d5689831e 100644 --- a/clang/lib/CodeGen/ItaniumCXXABI.cpp +++ b/clang/lib/CodeGen/ItaniumCXXABI.cpp @@ -924,20 +924,17 @@ ItaniumCXXABI::EmitMemberPointerConversion(CodeGenFunction &CGF, if (isa<llvm::Constant>(src)) return EmitMemberPointerConversion(E, cast<llvm::Constant>(src)); - QualType DstType = E->getType(), SrcType = E->getSubExpr()->getType(); - assert(E->getCastKind() == CK_DerivedToBaseMemberPointer || E->getCastKind() == CK_BaseToDerivedMemberPointer || - E->getCastKind() == CK_ReinterpretMemberPointer || - (E->getCastKind() == CK_NoOp && - getContext().hasSameFunctionTypeIgnoringExceptionSpec( - DstType->getPointeeType(), SrcType->getPointeeType()))); + E->getCastKind() == CK_ReinterpretMemberPointer); CGBuilderTy &Builder = CGF.Builder; + QualType DstType = E->getType(); if (DstType->isMemberFunctionPointerType()) { if (const auto &NewAuthInfo = CGM.getMemberFunctionPointerAuthInfo(DstType)) { + QualType SrcType = E->getSubExpr()->getType(); assert(SrcType->isMemberFunctionPointerType()); const auto &CurAuthInfo = CGM.getMemberFunctionPointerAuthInfo(SrcType); llvm::Value *MemFnPtr = Builder.CreateExtractValue(src, 0, "memptr.ptr"); @@ -974,11 +971,6 @@ ItaniumCXXABI::EmitMemberPointerConversion(CodeGenFunction &CGF, } } - // Conversion from a pointer to a noexcept member function to a pointer to a - // member function without noexcept doesn't require any additional processing. - if (E->getCastKind() == CK_NoOp) - return src; - // Under Itanium, reinterprets don't require any additional processing. if (E->getCastKind() == CK_ReinterpretMemberPointer) return src; @@ -1053,24 +1045,16 @@ pointerAuthResignMemberFunctionPointer(llvm::Constant *Src, QualType DestType, llvm::Constant * ItaniumCXXABI::EmitMemberPointerConversion(const CastExpr *E, llvm::Constant *src) { - QualType DstType = E->getType(), SrcType = E->getSubExpr()->getType(); - assert(E->getCastKind() == CK_DerivedToBaseMemberPointer || E->getCastKind() == CK_BaseToDerivedMemberPointer || - E->getCastKind() == CK_ReinterpretMemberPointer || - (E->getCastKind() == CK_NoOp && - getContext().hasSameFunctionTypeIgnoringExceptionSpec( - DstType->getPointeeType(), SrcType->getPointeeType()))); + E->getCastKind() == CK_ReinterpretMemberPointer); + + QualType DstType = E->getType(); if (DstType->isMemberFunctionPointerType()) src = pointerAuthResignMemberFunctionPointer( src, DstType, E->getSubExpr()->getType(), CGM); - // Conversion from a pointer to a noexcept member function to a pointer to a - // member function without noexcept doesn't require any additional processing. - if (E->getCastKind() == CK_NoOp) - return src; - // Under Itanium, reinterprets don't require any additional processing. if (E->getCastKind() == CK_ReinterpretMemberPointer) return src; diff --git a/clang/test/CodeGenCXX/ptrauth-member-function-pointer.cpp b/clang/test/CodeGenCXX/ptrauth-member-function-pointer.cpp index 3408e7e18c3adc..05223cfd4c99a6 100644 --- a/clang/test/CodeGenCXX/ptrauth-member-function-pointer.cpp +++ b/clang/test/CodeGenCXX/ptrauth-member-function-pointer.cpp @@ -1,11 +1,11 @@ -// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -emit-llvm -std=c++11 -O1 -disable-llvm-passes -o - %s | FileCheck -check-prefixes=CHECK,NODEBUG,DARWIN,CXX11 %s +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -emit-llvm -std=c++11 -O1 -disable-llvm-passes -o - %s | FileCheck -check-prefixes=CHECK,NODEBUG,DARWIN %s // RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -emit-llvm -std=c++17 -O1 -disable-llvm-passes -o - %s | FileCheck -check-prefixes=CHECK,NODEBUG,DARWIN,CXX17 %s // RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -emit-llvm -std=c++11 -O1 -disable-llvm-passes -debug-info-kind=limited -o - %s | FileCheck -check-prefixes=CHECK,DARWIN %s // RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -emit-llvm -std=c++11 -O1 -disable-llvm-passes -stack-protector 1 -o - %s | FileCheck %s -check-prefix=STACK-PROT // RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -emit-llvm -std=c++11 -O1 -disable-llvm-passes -stack-protector 2 -o - %s | FileCheck %s -check-prefix=STACK-PROT // RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -emit-llvm -std=c++11 -O1 -disable-llvm-passes -stack-protector 3 -o - %s | FileCheck %s -check-prefix=STACK-PROT -// RUN: %clang_cc1 -triple aarch64-linux-gnu -fptrauth-calls -fptrauth-intrinsics -emit-llvm -std=c++11 -O1 -disable-llvm-passes -o - %s | FileCheck -check-prefixes=CHECK,NODEBUG,ELF,CXX11 %s +// RUN: %clang_cc1 -triple aarch64-linux-gnu -fptrauth-calls -fptrauth-intrinsics -emit-llvm -std=c++11 -O1 -disable-llvm-passes -o - %s | FileCheck -check-prefixes=CHECK,NODEBUG,ELF %s // RUN: %clang_cc1 -triple aarch64-linux-gnu -fptrauth-calls -fptrauth-intrinsics -emit-llvm -std=c++17 -O1 -disable-llvm-passes -o - %s | FileCheck -check-prefixes=CHECK,NODEBUG,ELF,CXX17 %s // RUN: %clang_cc1 -triple aarch64-linux-gnu -fptrauth-calls -fptrauth-intrinsics -emit-llvm -std=c++11 -O1 -disable-llvm-passes -debug-info-kind=limited -o - %s | FileCheck -check-prefixes=CHECK,ELF %s // RUN: %clang_cc1 -triple aarch64-linux-gnu -fptrauth-calls -fptrauth-intrinsics -emit-llvm -std=c++11 -O1 -disable-llvm-passes -stack-protector 1 -o - %s | FileCheck %s -check-prefix=STACK-PROT @@ -22,10 +22,9 @@ // CHECK: @__const._Z13testArrayInitv.c0 = private unnamed_addr constant %struct.Class0 { { i64, i64 } { i64 ptrtoint (ptr ptrauth (ptr @_ZN5Base011nonvirtual0Ev, i32 0, i64 35591) to i64), i64 0 } }, align 8 // CHECK: @__const._Z13testArrayInitv.c1 = private unnamed_addr constant %struct.Class0 { { i64, i64 } { i64 ptrtoint (ptr ptrauth (ptr @_ZN5Base08virtual1Ev_vfpthunk_, i32 0, i64 35591) to i64), i64 0 } }, align 8 -// CHECK: @_ZN22testNoexceptConversion6mfptr1E = global { i64, i64 } { i64 ptrtoint (ptr ptrauth (ptr @_ZN22testNoexceptConversion1S19nonvirtual_noexceptEv, i32 0, i64 [[DISC_NO_NOEXCEPT:.*]]) to i64), i64 0 }, -// CHECK: @_ZN22testNoexceptConversion6mfptr2E = global { i64, i64 } { i64 ptrtoint (ptr ptrauth (ptr @_ZN22testNoexceptConversion1S16virtual_noexceptEv_vfpthunk_, i32 0, i64 [[DISC_NO_NOEXCEPT]]) to i64), i64 0 }, -// CXX11: @_ZN22testNoexceptConversion15mfptr3_noexceptE = global { i64, i64 } { i64 ptrtoint (ptr ptrauth (ptr @_ZN22testNoexceptConversion1S19nonvirtual_noexceptEv, i32 0, i64 [[DISC_NO_NOEXCEPT]]) to i64), i64 0 }, -// CXX17: @_ZN22testNoexceptConversion15mfptr3_noexceptE = global { i64, i64 } { i64 ptrtoint (ptr ptrauth (ptr @_ZN22testNoexceptConversion1S19nonvirtual_noexceptEv, i32 0, i64 [[DISC_NOEXCEPT:.*]]) to i64), i64 0 }, +// CHECK: @_ZN22testNoexceptConversion6mfptr1E = global { i64, i64 } { i64 ptrtoint (ptr ptrauth (ptr @_ZN22testNoexceptConversion1S19nonvirtual_noexceptEv, i32 0, i64 [[TYPEDISC3:.*]]) to i64), i64 0 }, +// CHECK: @_ZN22testNoexceptConversion6mfptr2E = global { i64, i64 } { i64 ptrtoint (ptr ptrauth (ptr @_ZN22testNoexceptConversion1S16virtual_noexceptEv_vfpthunk_, i32 0, i64 [[TYPEDISC3]]) to i64), i64 0 }, +// CHECK: @_ZN22testNoexceptConversion15mfptr3_noexceptE = global { i64, i64 } { i64 ptrtoint (ptr ptrauth (ptr @_ZN22testNoexceptConversion1S19nonvirtual_noexceptEv, i32 0, i64 [[TYPEDISC3]]) to i64), i64 0 }, // CHECK: @_ZTV5Base0 = unnamed_addr constant { [5 x ptr] } { [5 x ptr] [ptr null, ptr @_ZTI5Base0, // CHECK-SAME: ptr ptrauth (ptr @_ZN5Base08virtual1Ev, i32 0, i64 55600, ptr getelementptr inbounds ({ [5 x ptr] }, ptr @_ZTV5Base0, i32 0, i32 0, i32 2)), @@ -84,6 +83,9 @@ struct Derived1 : Base0, Base1 { }; typedef void (Base0::*MethodTy0)(); +#if __cplusplus >= 201703L +typedef void (Base0::*NoExceptMethodTy0)() noexcept; +#endif typedef void (Base0::*VariadicMethodTy0)(int, ...); typedef void (Derived0::*MethodTy1)(); @@ -300,6 +302,15 @@ void test1(Base0 *a0, MethodTy0 a1) { (a0->*a1)(); } +// CXX17: %[[V14:.*]] = phi ptr [ %{{.*}}, {{.*}} ], [ %{{.*}}, {{.*}} ] +// CXX17: %[[V15:.*]] = phi i64 [ 0, {{.*}} ], [ [[TYPEDISC0]], {{.*}} ] +// CXX17: call void %[[V14]](ptr noundef nonnull align {{[0-9]+}} dereferenceable(8) %[[V4]]) {{.*}}[ "ptrauth"(i32 0, i64 %[[V15]]) ] +#if __cplusplus >= 201703L +void test1_noexcept(Base0 *a0, NoExceptMethodTy0 a1) { + (a0->*a1)(); +} +#endif + // CHECK: define{{.*}} void @_Z15testConversion0M5Base0FvvEM8Derived0FvvE([2 x i64] %[[METHOD0_COERCE:.*]], [2 x i64] %[[METHOD1_COERCE:.*]]) // CHECK: %[[METHOD0:.*]] = alloca { i64, i64 }, align 8 // CHECK: %[[METHOD1:.*]] = alloca { i64, i64 }, align 8 @@ -449,75 +460,21 @@ void testConvertNull() { namespace testNoexceptConversion { // CHECK-LABEL: define internal void @__cxx_global_var_init() -// CXX17: [[ENTRY:.*]]: // CHECK: %[[V0:.*]] = load { i64, i64 }, ptr @_ZN22testNoexceptConversion15mfptr0_noexceptE, align 8 -// CXX17: %[[MEMPTR_PTR:.*]] = extractvalue { i64, i64 } %[[V0]], 0 -// CXX17: %[[MEMPTR_ADJ:.*]] = extractvalue { i64, i64 } %[[V0]], 1 -// CXX17: %[[V1:.*]] = and i64 %[[MEMPTR_ADJ]], 1 -// CXX17: %[[IS_VIRTUAL_OFFSET:.*]] = icmp ne i64 %[[V1]], 0 -// CXX17: br i1 %[[IS_VIRTUAL_OFFSET]], label %[[MERGE:.*]], label %[[RESIGN:.*]] - -// CXX17: [[RESIGN]]: -// CXX17: %[[V2:.*]] = inttoptr i64 %[[MEMPTR_PTR]] to ptr -// CXX17: %[[V3:.*]] = icmp ne ptr %[[V2]], null -// CXX17: br i1 %[[V3]], label %[[RESIGN_NONNULL:.*]], label %[[RESIGN_CONT:.*]] - -// CXX17: [[RESIGN_NONNULL]]: -// CXX17: %[[V4:.*]] = ptrtoint ptr %[[V2]] to i64 -// CXX17: %[[V5:.*]] = call i64 @llvm.ptrauth.resign(i64 %[[V4]], i32 0, i64 [[DISC_NOEXCEPT]], i32 0, i64 [[DISC_NO_NOEXCEPT]]) -// CXX17: %[[V6:.*]] = inttoptr i64 %[[V5]] to ptr -// CXX17: br label %[[RESIGN_CONT]] - -// CXX17: [[RESIGN_CONT]]: -// CXX17: %[[V7:.*]] = phi ptr [ null, %[[RESIGN]] ], [ %[[V6]], %[[RESIGN_NONNULL]] ] -// CXX17: %[[V8:.*]] = ptrtoint ptr %[[V7]] to i64 -// CXX17: %[[V9:.*]] = insertvalue { i64, i64 } %[[V0]], i64 %[[V8]], 0 -// CXX17: br label %[[MERGE]] - -// CXX17: [[MERGE]]: -// CXX17: %[[V10:.*]] = phi { i64, i64 } [ %[[V0]], %[[ENTRY]] ], [ %[[V9]], %[[RESIGN_CONT]] ] -// CXX17: store { i64, i64 } %[[V10]], ptr @_ZN22testNoexceptConversion6mfptr4E, align 8 -// CXX11: store { i64, i64 } %[[V0]], ptr @_ZN22testNoexceptConversion6mfptr4E, align 8 +// CHECK: store { i64, i64 } %[[V0]], ptr @_ZN22testNoexceptConversion6mfptr4E, align 8 // CHECK: define {{.*}}void @_ZN22testNoexceptConversion5test0Ev() // CHECK: %[[P0:.*]] = alloca { i64, i64 }, align 8 -// CHECK: store { i64, i64 } { i64 ptrtoint (ptr ptrauth (ptr @_ZN22testNoexceptConversion1S19nonvirtual_noexceptEv, i32 0, i64 [[DISC_NO_NOEXCEPT]]) to i64), i64 0 }, ptr %[[P0]], align 8, +// CHECK: store { i64, i64 } { i64 ptrtoint (ptr ptrauth (ptr @_ZN22testNoexceptConversion1S19nonvirtual_noexceptEv, i32 0, i64 [[TYPEDISC3]]) to i64), i64 0 }, ptr %[[P0]], align 8, // CHECK: define {{.*}}void @_ZN22testNoexceptConversion5test1Ev() // CHECK: %[[P0:.*]] = alloca { i64, i64 }, align 8 -// CHECK: store { i64, i64 } { i64 ptrtoint (ptr ptrauth (ptr @_ZN22testNoexceptConversion1S16virtual_noexceptEv_vfpthunk_, i32 0, i64 [[DISC_NO_NOEXCEPT]]) to i64), i64 0 }, ptr %[[P0]], align 8, +// CHECK: store { i64, i64 } { i64 ptrtoint (ptr ptrauth (ptr @_ZN22testNoexceptConversion1S16virtual_noexceptEv_vfpthunk_, i32 0, i64 [[TYPEDISC3]]) to i64), i64 0 }, ptr %[[P0]], align 8, // CHECK: define {{.*}}void @_ZN22testNoexceptConversion5test2Ev() -// CXX17: [[ENTRY:.*]]: // CHECK: %[[P0:.*]] = alloca { i64, i64 }, align 8 // CHECK: %[[V0:.*]] = load { i64, i64 }, ptr @_ZN22testNoexceptConversion15mfptr0_noexceptE, align 8 -// CXX17: %[[MEMPTR_PTR:.*]] = extractvalue { i64, i64 } %[[V0]], 0 -// CXX17: %[[MEMPTR_ADJ:.*]] = extractvalue { i64, i64 } %[[V0]], 1 -// CXX17: %[[V1:.*]] = and i64 %[[MEMPTR_ADJ]], 1 -// CXX17: %[[IS_VIRTUAL_OFFSET:.*]] = icmp ne i64 %[[V1]], 0 -// CXX17: br i1 %[[IS_VIRTUAL_OFFSET]], label %[[MERGE:.*]], label %[[RESIGN:.*]] - -// CXX17: [[RESIGN]]: -// CXX17: %[[V2:.*]] = inttoptr i64 %[[MEMPTR_PTR]] to ptr -// CXX17: %[[V3:.*]] = icmp ne ptr %[[V2]], null -// CXX17: br i1 %[[V3]], label %[[RESIGN_NONNULL:.*]], label %[[RESIGN_CONT:.*]] - -// CXX17: [[RESIGN_NONNULL]]: -// CXX17: %[[V4:.*]] = ptrtoint ptr %[[V2]] to i64 -// CXX17: %[[V5:.*]] = call i64 @llvm.ptrauth.resign(i64 %[[V4]], i32 0, i64 [[DISC_NOEXCEPT]], i32 0, i64 [[DISC_NO_NOEXCEPT]]) -// CXX17: %[[V6:.*]] = inttoptr i64 %[[V5]] to ptr -// CXX17: br label %[[RESIGN_CONT]] - -// CXX17: [[RESIGN_CONT]]: -// CXX17: %[[V7:.*]] = phi ptr [ null, %[[RESIGN]] ], [ %[[V6]], %[[RESIGN_NONNULL]] ] -// CXX17: %[[V8:.*]] = ptrtoint ptr %[[V7]] to i64 -// CXX17: %[[V9:.*]] = insertvalue { i64, i64 } %[[V0]], i64 %[[V8]], 0 -// CXX17: br label %[[MERGE]] - -// CXX17: [[MERGE]]: -// CXX17: %[[V10:.*]] = phi { i64, i64 } [ %[[V0]], %[[ENTRY]] ], [ %[[V9]], %[[RESIGN_CONT]] ] -// CXX11: store { i64, i64 } %[[V0]], ptr %[[P0]], align 8 -// CXX17: store { i64, i64 } %[[V10]], ptr %[[P0]], align 8 +// CHECK: store { i64, i64 } %[[V0]], ptr %[[P0]], align 8, struct S { void nonvirtual_noexcept() noexcept; >From 6a5d101e630fca43c02a918971800e7b17888e0e Mon Sep 17 00:00:00 2001 From: Akira Hatanaka <ahata...@gmail.com> Date: Thu, 16 Jan 2025 12:19:42 -0800 Subject: [PATCH 3/4] Improve comment and Avoid rebuilding the member pointer type if it already doesn't have an exception spec --- clang/lib/AST/ASTContext.cpp | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index 311cf80eb6bff7..c1409ce65b5355 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -3443,11 +3443,33 @@ uint16_t ASTContext::getPointerAuthTypeDiscriminator(QualType T) { } else { T = T.getUnqualifiedType(); // Drop exception specification from member function pointer type. + // Calls to member function pointers don't need to worry about + // language interop or the laxness of the C type compatibility rules. + // We just mangle the member pointer type directly, which is + // implicitly much stricter about type matching. However, we do + // strip any top-level exception specification before this mangling. + // C++23 requires calls to work when the function type is convertible + // to the pointer type by a function pointer conversion, which can + // change the exception specification. This does not technically + // require the exception specification to not affect representation, + // because the function pointer conversion is still always a direct + // value conversion and therefore an opportunity to resign the + // pointer. (This is in contrast to e.g. qualification conversions, + // which can be applied in nested pointer positions, effectively + // requiring qualified and unqualified representations to match.) + // However, it is pragmatic to ignore exception specifications + // because it allows a certain amount of `noexcept` mismatching + // to not become a visible ODR problem. This also leaves some + // room for the committee to add laxness to function pointer + // conversions in future standards. if (auto *MPT = T->getAs<MemberPointerType>()) if (MPT->isMemberFunctionPointer()) { - QualType FT = - getFunctionTypeWithExceptionSpec(MPT->getPointeeType(), EST_None); - T = getMemberPointerType(FT, MPT->getClass()); + QualType PointeeType = MPT->getPointeeType(); + if (PointeeType->castAs<FunctionProtoType>()->getExceptionSpecType() != + EST_None) { + QualType FT = getFunctionTypeWithExceptionSpec(PointeeType, EST_None); + T = getMemberPointerType(FT, MPT->getClass()); + } } std::unique_ptr<MangleContext> MC(createMangleContext()); MC->mangleCanonicalTypeName(T, Out); >From eb62db58fbe01c4ce1fb70a61080e043ef64b7e4 Mon Sep 17 00:00:00 2001 From: Akira Hatanaka <ahata...@gmail.com> Date: Thu, 16 Jan 2025 12:45:04 -0800 Subject: [PATCH 4/4] Fix test --- clang/test/CodeGenCXX/ptrauth-member-function-pointer.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/clang/test/CodeGenCXX/ptrauth-member-function-pointer.cpp b/clang/test/CodeGenCXX/ptrauth-member-function-pointer.cpp index 05223cfd4c99a6..e9436f11b5106e 100644 --- a/clang/test/CodeGenCXX/ptrauth-member-function-pointer.cpp +++ b/clang/test/CodeGenCXX/ptrauth-member-function-pointer.cpp @@ -302,9 +302,10 @@ void test1(Base0 *a0, MethodTy0 a1) { (a0->*a1)(); } +// CXX17: define{{.*}} void @_Z14test1_noexceptP5Base0MS_DoFvvE( // CXX17: %[[V14:.*]] = phi ptr [ %{{.*}}, {{.*}} ], [ %{{.*}}, {{.*}} ] // CXX17: %[[V15:.*]] = phi i64 [ 0, {{.*}} ], [ [[TYPEDISC0]], {{.*}} ] -// CXX17: call void %[[V14]](ptr noundef nonnull align {{[0-9]+}} dereferenceable(8) %[[V4]]) {{.*}}[ "ptrauth"(i32 0, i64 %[[V15]]) ] +// CXX17: call void %[[V14]](ptr noundef nonnull align {{[0-9]+}} dereferenceable(8) %{{.*}}) {{.*}}[ "ptrauth"(i32 0, i64 %[[V15]]) ] #if __cplusplus >= 201703L void test1_noexcept(Base0 *a0, NoExceptMethodTy0 a1) { (a0->*a1)(); _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits