From 1607a76c4fd2bd7f0c4e834d2935668d51daf55f Mon Sep 17 00:00:00 2001 From: Oliver Hunt Date: Sun, 13 Apr 2025 00:47:18 -0700 Subject: [PATCH] [clang] Add builtin_get_vtable_pointer and virtual_member_address These are a pair of builtins to support particularly weird edge case operations while correctly handling the non-trivial implicit pointer authentication schemas applied to polymorphic members. Co-authored-by: Tim Northover --- clang/docs/LanguageExtensions.rst | 66 ++++ clang/docs/ReleaseNotes.rst | 3 + clang/include/clang/Basic/Builtins.td | 12 + .../clang/Basic/DiagnosticSemaKinds.td | 19 + clang/lib/CodeGen/CGBuiltin.cpp | 35 ++ clang/lib/Sema/SemaChecking.cpp | 92 +++++ .../CodeGenCXX/builtin-get-vtable-pointer.cpp | 350 ++++++++++++++++++ .../builtin_virtual_member_address.cpp | 68 ++++ .../SemaCXX/builtin-get-vtable-pointer.cpp | 124 +++++++ .../builtin_virtual_member_address.cpp | 60 +++ 10 files changed, 829 insertions(+) create mode 100644 clang/test/CodeGenCXX/builtin-get-vtable-pointer.cpp create mode 100644 clang/test/CodeGenCXX/builtin_virtual_member_address.cpp create mode 100644 clang/test/SemaCXX/builtin-get-vtable-pointer.cpp create mode 100644 clang/test/SemaCXX/builtin_virtual_member_address.cpp diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst index 3b8a9cac6587a..62e3002407631 100644 --- a/clang/docs/LanguageExtensions.rst +++ b/clang/docs/LanguageExtensions.rst @@ -3031,6 +3031,72 @@ following way: Query for this feature with ``__has_builtin(__builtin_offsetof)``. +``__builtin_get_vtable_pointer`` +-------------------------------- + +``__builtin_get_vtable_pointer`` loads and authenticates the primary vtable +pointer from an instance of a polymorphic C++ class. + +**Syntax**: + +.. code-block:: c++ + + __builtin_get_vtable_pointer(PolymorphicClass*) + +**Example of Use**: + +.. code-block:: c++ + + struct PolymorphicClass { + virtual ~PolymorphicClass(); + }; + + PolymorphicClass anInstance; + const void* vtablePointer = __builtin_get_vtable_pointer(&anInstance); + +**Description**: + +The ``__builtin_get_vtable_pointer`` builtin loads the primary vtable +pointer from a polymorphic C++ type. If the target platform authenticates +vtable pointers, this builtin will perform the authentication and produce +the underlying raw pointer. The object being queried must be polymorphic, +and so must also be a complete type. + +Query for this feature with ``__has_builtin(__builtin_get_vtable_pointer)``. + +``__builtin_virtual_member_address`` +------------------------------------ + +``__builtin_virtual_member_address`` loads the function pointer that would +be called by a virtual method. + +**Syntax**: + +.. code-block:: c++ + + __builtin_virtual_member_address(PolymorphicClass&, Member function pointer) + +**Exampe of Use** + +.. code-block:: c++ + + struct PolymorphicClass { + virtual ~PolymorphicClass(); + virtual void SomeMethod(); + }; + + PolymorphicClass anInstance; + const void* MethodAddress = + __builtin_virtual_member_address(anInstance, &PolymorphicClass::SomeMethod); + +**Description** + +This builtin returns the dynamic target for virtual dispatch of the requested virtual +method. If the target platform supports pointer authentication, it emits the code to +authenticates the vtable pointer and the virtual function pointer being loaded. The returned +value is an untyped pointer as it cannot reasonably be proved that any given use of the returned +function pointer is correct, so we want to discourage any attempt to do such. + ``__builtin_call_with_static_chain`` ------------------------------------ diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 11f62bc881b03..16091ba152d70 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -191,6 +191,9 @@ Non-comprehensive list of changes in this release - Support parsing the `cc` operand modifier and alias it to the `c` modifier (#GH127719). - Added `__builtin_elementwise_exp10`. - For AMDPGU targets, added `__builtin_v_cvt_off_f32_i4` that maps to the `v_cvt_off_f32_i4` instruction. +- Added `__builtin_get_vtable_pointer` to directly load the primary vtable pointer from a + polymorphic object and `__builtin_virtual_member_address` to load the real function pointer of a + virtual method. New Compiler Flags ------------------ diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td index 868e5b92acdc9..a55f411343c2d 100644 --- a/clang/include/clang/Basic/Builtins.td +++ b/clang/include/clang/Basic/Builtins.td @@ -970,6 +970,18 @@ def IsWithinLifetime : LangBuiltin<"CXX_LANG"> { let Prototype = "bool(void*)"; } +def GetVtablePointer : LangBuiltin<"CXX_LANG"> { + let Spellings = ["__builtin_get_vtable_pointer"]; + let Attributes = [CustomTypeChecking, NoThrow, Const]; + let Prototype = "void*(void*)"; +} + +def VirtualMemberAddress : Builtin { + let Spellings = ["__builtin_virtual_member_address"]; + let Attributes = [CustomTypeChecking, NoThrow, Const]; + let Prototype = "void*(void*,void*)"; +} + // GCC exception builtins def EHReturn : Builtin { let Spellings = ["__builtin_eh_return"]; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 180ca39bc07e9..e6b0584df35b4 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -1013,6 +1013,17 @@ def err_ptrauth_indirect_goto_addrlabel_arithmetic : Error< "%select{subtraction|addition}0 of address-of-label expressions is not " "supported with ptrauth indirect gotos">; +def err_virtual_member_lhs_cxxrec + : Error<"first argument to __builtin_virtual_member_address must have C++ " + "class type">; +def err_virtual_member_addrof + : Error< + "second argument to __builtin_virtual_member_address must be the " + "address of a virtual C++ member function: for example '&Foo::func'">; +def err_virtual_member_inherit + : Error<"first argument to __builtin_virtual_member_address must have a " + "type deriving from class where second argument was defined">; + /// main() // static main() is not an error in C, just in C++. def warn_static_main : Warning<"'main' should not be declared static">, @@ -12547,6 +12558,14 @@ def err_bit_cast_non_trivially_copyable : Error< def err_bit_cast_type_size_mismatch : Error< "size of '__builtin_bit_cast' source type %0 does not match destination type %1 (%2 vs %3 bytes)">; +def err_get_vtable_pointer_incorrect_type + : Error<"__builtin_get_vtable_pointer requires an argument of%select{| " + "polymorphic}0 class pointer type" + ", but %1 %select{was provided|has no virtual methods}0">; +def err_get_vtable_pointer_requires_complete_type + : Error<"__builtin_get_vtable_pointer requires an argument with a complete " + "type, but %0 is incomplete">; + // SYCL-specific diagnostics def warn_sycl_kernel_num_of_template_params : Warning< "'sycl_kernel' attribute only applies to a function template with at least" diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp index fe55dfffc1cbe..9402dfa40b1ba 100644 --- a/clang/lib/CodeGen/CGBuiltin.cpp +++ b/clang/lib/CodeGen/CGBuiltin.cpp @@ -17,6 +17,7 @@ #include "CGDebugInfo.h" #include "CGObjCRuntime.h" #include "CGOpenCLRuntime.h" +#include "CGPointerAuthInfo.h" #include "CGRecordLayout.h" #include "CGValue.h" #include "CodeGenFunction.h" @@ -5349,6 +5350,40 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID, return RValue::get(Result); } + case Builtin::BI__builtin_virtual_member_address: { + Address This = EmitLValue(E->getArg(0)).getAddress(); + APValue ConstMemFun; + E->getArg(1)->isCXX11ConstantExpr(getContext(), &ConstMemFun, nullptr); + const CXXMethodDecl *CXXMethod = + cast(ConstMemFun.getMemberPointerDecl()); + const CGFunctionInfo &FInfo = + CGM.getTypes().arrangeCXXMethodDeclaration(CXXMethod); + llvm::FunctionType *Ty = CGM.getTypes().GetFunctionType(FInfo); + CGCallee VCallee = CGM.getCXXABI().getVirtualFunctionPointer( + *this, CXXMethod, This, Ty, E->getBeginLoc()); + llvm::Value *Callee = VCallee.getFunctionPointer(); + if (const CGPointerAuthInfo &Schema = VCallee.getPointerAuthInfo()) + Callee = EmitPointerAuthAuth(Schema, Callee); + return RValue::get(Callee); + } + + case Builtin::BI__builtin_get_vtable_pointer: { + const Expr *Target = E->getArg(0); + QualType TargetType = Target->getType(); + QualType RecordType = TargetType; + if (RecordType->isPointerOrReferenceType()) + RecordType = RecordType->getPointeeType(); + const CXXRecordDecl *Decl = RecordType->getAsCXXRecordDecl(); + assert(Decl); + auto ThisAddress = TargetType->isPointerType() + ? EmitPointerWithAlignment(Target) + : EmitLValue(Target).getAddress(); + assert(ThisAddress.isValid()); + llvm::Value *VTablePointer = + GetVTablePtr(ThisAddress, Int8PtrTy, Decl, VTableAuthMode::MustTrap); + return RValue::get(VTablePointer); + } + case Builtin::BI__exception_code: case Builtin::BI_exception_code: return RValue::get(EmitSEHExceptionCode()); diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index bffd0dd461d3d..dffc569b3cf49 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -1782,6 +1782,92 @@ static ExprResult PointerAuthStringDiscriminator(Sema &S, CallExpr *Call) { return Call; } +static ExprResult VirtualMemberAddress(Sema &S, CallExpr *Call) { + if (S.checkArgCount(Call, 2)) + return ExprError(); + + for (int i = 0; i < 2; ++i) { + ExprResult ArgRValue = + S.DefaultFunctionArrayLvalueConversion(Call->getArg(1)); + if (ArgRValue.isInvalid()) + return ExprError(); + Call->setArg(1, ArgRValue.get()); + } + + if (Call->getArg(0)->isTypeDependent() || Call->getArg(1)->isValueDependent()) + return Call; + + const Expr *ThisArg = Call->getArg(0); + QualType ThisTy = ThisArg->getType(); + if (ThisTy->isPointerOrReferenceType()) + ThisTy = ThisTy->getPointeeType(); + if (!ThisTy->getAsCXXRecordDecl()) { + S.Diag(ThisArg->getExprLoc(), diag::err_virtual_member_lhs_cxxrec); + return ExprError(); + } + + const Expr *MemFunArg = Call->getArg(1); + APValue Result; + if (!MemFunArg->isCXX11ConstantExpr(S.getASTContext(), &Result, nullptr)) { + S.Diag(MemFunArg->getExprLoc(), diag::err_virtual_member_addrof); + return ExprError(); + } + + if (!Result.isMemberPointer() || + !isa(Result.getMemberPointerDecl())) { + S.Diag(MemFunArg->getExprLoc(), diag::err_virtual_member_addrof); + return ExprError(); + } + + const CXXMethodDecl *CXXMethod = + cast(Result.getMemberPointerDecl()); + if (!CXXMethod->isVirtual()) { + S.Diag(MemFunArg->getExprLoc(), diag::err_virtual_member_addrof); + return ExprError(); + } + + if (ThisTy->getAsCXXRecordDecl() != CXXMethod->getParent() && + !S.IsDerivedFrom(Call->getBeginLoc(), ThisTy, + CXXMethod->getFunctionObjectParameterType())) { + S.Diag(ThisArg->getExprLoc(), diag::err_virtual_member_inherit); + return ExprError(); + } + return Call; +} + +static ExprResult GetVTablePointer(Sema &S, CallExpr *Call) { + if (S.checkArgCount(Call, 1)) + return ExprError(); + ExprResult ThisArg = S.DefaultFunctionArrayLvalueConversion(Call->getArg(0)); + if (ThisArg.isInvalid()) + return ExprError(); + Call->setArg(0, ThisArg.get()); + const Expr *Subject = Call->getArg(0); + QualType SubjectType = Subject->getType(); + if (SubjectType->isPointerOrReferenceType()) + SubjectType = SubjectType->getPointeeType(); + const CXXRecordDecl *SubjectRecord = SubjectType->getAsCXXRecordDecl(); + if (!SubjectRecord) { + S.Diag(Subject->getBeginLoc(), diag::err_get_vtable_pointer_incorrect_type) + << 0 << Subject->getType(); + return ExprError(); + } + if (S.RequireCompleteType( + Subject->getBeginLoc(), SubjectType, + diag::err_get_vtable_pointer_requires_complete_type)) { + return ExprError(); + } + + if (!SubjectRecord->isPolymorphic()) { + S.Diag(Subject->getBeginLoc(), diag::err_get_vtable_pointer_incorrect_type) + << 1 << SubjectType; + return ExprError(); + } + QualType ReturnType = S.Context.getPointerType(S.Context.VoidTy.withConst()); + Call->setType(ReturnType); + return Call; +} + static ExprResult BuiltinLaunder(Sema &S, CallExpr *TheCall) { if (S.checkArgCount(TheCall, 1)) return ExprError(); @@ -2625,6 +2711,12 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID, return PointerAuthAuthAndResign(*this, TheCall); case Builtin::BI__builtin_ptrauth_string_discriminator: return PointerAuthStringDiscriminator(*this, TheCall); + + case Builtin::BI__builtin_get_vtable_pointer: + return GetVTablePointer(*this, TheCall); + case Builtin::BI__builtin_virtual_member_address: + return VirtualMemberAddress(*this, TheCall); + // OpenCL v2.0, s6.13.16 - Pipe functions case Builtin::BIread_pipe: case Builtin::BIwrite_pipe: diff --git a/clang/test/CodeGenCXX/builtin-get-vtable-pointer.cpp b/clang/test/CodeGenCXX/builtin-get-vtable-pointer.cpp new file mode 100644 index 0000000000000..f0ee13152ddf7 --- /dev/null +++ b/clang/test/CodeGenCXX/builtin-get-vtable-pointer.cpp @@ -0,0 +1,350 @@ +// RUN: %clang_cc1 %s -x c++ -std=c++11 -triple x86_64-apple-darwin10 -emit-llvm -O1 -disable-llvm-passes -no-enable-noundef-analysis -o - | FileCheck --check-prefix=CHECK-NOAUTH %s +// RUN: %clang_cc1 %s -x c++ -std=c++11 -triple arm64-apple-ios -fptrauth-calls -fptrauth-vtable-pointer-type-discrimination -emit-llvm -O1 -disable-llvm-passes -no-enable-noundef-analysis -o - | FileCheck --check-prefix=CHECK-TYPEAUTH %s +// RUN: %clang_cc1 %s -x c++ -std=c++11 -triple arm64-apple-ios -fptrauth-calls -fptrauth-vtable-pointer-address-discrimination -emit-llvm -O1 -disable-llvm-passes -no-enable-noundef-analysis -o - | FileCheck --check-prefix=CHECK-ADDRESSAUTH %s +// RUN: %clang_cc1 %s -x c++ -std=c++11 -triple arm64-apple-ios -fptrauth-calls -fptrauth-vtable-pointer-type-discrimination -fptrauth-vtable-pointer-address-discrimination -emit-llvm -O1 -disable-llvm-passes -no-enable-noundef-analysis -o - | FileCheck --check-prefix=CHECK-BOTHAUTH %s +// FIXME: Assume load should not require -fstrict-vtable-pointers + +namespace test1 { +struct A { + A(); + virtual void bar(); +}; + +struct B : A { + B(); + virtual void foo(); +}; + +struct Z : A {}; +struct C : Z, B { + C(); + virtual void wibble(); +}; + +struct D : virtual A { +}; + +struct E : D, B { +}; + +const void *a(A *o) { + // CHECK-NOAUTH: define ptr @_ZN5test11aEPNS_1AE(ptr %o) #0 { + // CHECK-TYPEAUTH: define ptr @_ZN5test11aEPNS_1AE(ptr %o) #0 { + return __builtin_get_vtable_pointer(o); + // CHECK-NOAUTH: %vtable = load ptr, ptr %0, align 8 + // CHECK-TYPEAUTH: %0 = load ptr, ptr %o.addr, align 8 + // CHECK-TYPEAUTH: %vtable = load ptr, ptr %0, align 8 + // CHECK-TYPEAUTH: %1 = ptrtoint ptr %vtable to i64 + // CHECK-TYPEAUTH: %2 = call i64 @llvm.ptrauth.auth(i64 %1, i32 2, i64 48388) + // CHECK-TYPEAUTH: %3 = inttoptr i64 %2 to ptr + // CHECK-TYPEAUTH: %4 = load volatile i8, ptr %3, align 8 + // CHECK-ADDRESSAUTH: %2 = ptrtoint ptr %vtable to i64 + // CHECK-ADDRESSAUTH: %3 = call i64 @llvm.ptrauth.auth(i64 %2, i32 2, i64 %1) + // CHECK-ADDRESSAUTH: %4 = inttoptr i64 %3 to ptr + // CHECK-ADDRESSAUTH: %5 = load volatile i8, ptr %4, align 8 + // CHECK-BOTHAUTH: [[T1:%.*]] = ptrtoint ptr %0 to i64 + // CHECK-BOTHAUTH: [[T2:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T1]], i64 48388) + // CHECK-BOTHAUTH: [[T3:%.*]] = ptrtoint ptr %vtable to i64 + // CHECK-BOTHAUTH: [[T4:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T3]], i32 2, i64 [[T2]]) + // CHECK-BOTHAUTH: [[T5:%.*]] = inttoptr i64 [[T4]] to ptr + // CHECK-BOTHAUTH: [[T6:%.*]] = load volatile i8, ptr [[T5]], align 8 +} + +const void *b(B *o) { + // CHECK-TYPEAUTH: define ptr @_ZN5test11bEPNS_1BE(ptr %o) #0 { + // CHECK-NOAUTH: define ptr @_ZN5test11bEPNS_1BE(ptr %o) #0 { + return __builtin_get_vtable_pointer(o); + // CHECK-NOAUTH: %vtable = load ptr, ptr %0, align 8 + // CHECK-TYPEAUTH: %vtable = load ptr, ptr %0, align 8 + // CHECK-TYPEAUTH: %1 = ptrtoint ptr %vtable to i64 + // CHECK-TYPEAUTH: %2 = call i64 @llvm.ptrauth.auth(i64 %1, i32 2, i64 48388) + // CHECK-TYPEAUTH: %3 = inttoptr i64 %2 to ptr + // CHECK-TYPEAUTH: %4 = load volatile i8, ptr %3, align 8 + // CHECK-ADDRESSAUTH: %2 = ptrtoint ptr %vtable to i64 + // CHECK-ADDRESSAUTH: %3 = call i64 @llvm.ptrauth.auth(i64 %2, i32 2, i64 %1) + // CHECK-ADDRESSAUTH: %4 = inttoptr i64 %3 to ptr + // CHECK-ADDRESSAUTH: %5 = load volatile i8, ptr %4, align 8 + // CHECK-BOTHAUTH: [[T2:%.*]] = call i64 @llvm.ptrauth.blend(i64 %1, i64 48388) + // CHECK-BOTHAUTH: [[T3:%.*]] = ptrtoint ptr %vtable to i64 + // CHECK-BOTHAUTH: [[T4:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T3]], i32 2, i64 [[T2]]) + // CHECK-BOTHAUTH: [[T5:%.*]] = inttoptr i64 [[T4]] to ptr + // CHECK-BOTHAUTH: [[T6:%.*]] = load volatile i8, ptr [[T5]], align 8 +} + +const void *b_as_A(B *o) { + // CHECK-NOAUTH: define ptr @_ZN5test16b_as_AEPNS_1BE(ptr %o) #0 { + return __builtin_get_vtable_pointer((A *)o); + // CHECK-NOAUTH: %vtable = load ptr, ptr %0, align 8 + // CHECK-TYPEAUTH: %vtable = load ptr, ptr %0, align 8 + // CHECK-TYPEAUTH: %1 = ptrtoint ptr %vtable to i64 + // CHECK-TYPEAUTH: %2 = call i64 @llvm.ptrauth.auth(i64 %1, i32 2, i64 48388) + // CHECK-TYPEAUTH: %3 = inttoptr i64 %2 to ptr + // CHECK-TYPEAUTH: %4 = load volatile i8, ptr %3, align 8 + // CHECK-ADDRESSAUTH: %2 = ptrtoint ptr %vtable to i64 + // CHECK-ADDRESSAUTH: %3 = call i64 @llvm.ptrauth.auth(i64 %2, i32 2, i64 %1) + // CHECK-ADDRESSAUTH: %4 = inttoptr i64 %3 to ptr + // CHECK-ADDRESSAUTH: %5 = load volatile i8, ptr %4, align 8 + // CHECK-BOTHAUTH: [[T2:%.*]] = call i64 @llvm.ptrauth.blend(i64 %1, i64 48388) + // CHECK-BOTHAUTH: [[T3:%.*]] = ptrtoint ptr %vtable to i64 + // CHECK-BOTHAUTH: [[T4:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T3]], i32 2, i64 [[T2]]) + // CHECK-BOTHAUTH: [[T5:%.*]] = inttoptr i64 [[T4]] to ptr + // CHECK-BOTHAUTH: [[T6:%.*]] = load volatile i8, ptr [[T5]], align 8 +} + +const void *c(C *o) { + // CHECK-NOAUTH: define ptr @_ZN5test11cEPNS_1CE(ptr %o) #0 { + return __builtin_get_vtable_pointer(o); + // CHECK-NOAUTH: %vtable = load ptr, ptr %0, align 8 + // CHECK-TYPEAUTH: %vtable = load ptr, ptr %0, align 8 + // CHECK-TYPEAUTH: %1 = ptrtoint ptr %vtable to i64 + // CHECK-TYPEAUTH: %2 = call i64 @llvm.ptrauth.auth(i64 %1, i32 2, i64 48388) + // CHECK-TYPEAUTH: %3 = inttoptr i64 %2 to ptr + // CHECK-TYPEAUTH: %4 = load volatile i8, ptr %3, align 8 + // CHECK-ADDRESSAUTH: %2 = ptrtoint ptr %vtable to i64 + // CHECK-ADDRESSAUTH: %3 = call i64 @llvm.ptrauth.auth(i64 %2, i32 2, i64 %1) + // CHECK-ADDRESSAUTH: %4 = inttoptr i64 %3 to ptr + // CHECK-ADDRESSAUTH: %5 = load volatile i8, ptr %4, align 8 + // CHECK-BOTHAUTH: [[T2:%.*]] = call i64 @llvm.ptrauth.blend(i64 %1, i64 48388) + // CHECK-BOTHAUTH: [[T3:%.*]] = ptrtoint ptr %vtable to i64 + // CHECK-BOTHAUTH: [[T4:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T3]], i32 2, i64 [[T2]]) + // CHECK-BOTHAUTH: [[T5:%.*]] = inttoptr i64 [[T4]] to ptr + // CHECK-BOTHAUTH: [[T6:%.*]] = load volatile i8, ptr [[T5]], align 8 +} + +const void *c_as_Z(C *o) { + // CHECK-NOAUTH: define ptr @_ZN5test16c_as_ZEPNS_1CE(ptr %o) #0 { + return __builtin_get_vtable_pointer((Z *)o); + // CHECK-NOAUTH: %0 = load ptr, ptr %o.addr, align 8 + // CHECK-NOAUTH: %vtable = load ptr, ptr %0, align 8 + // CHECK-TYPEAUTH: %vtable = load ptr, ptr %0, align 8 + // CHECK-TYPEAUTH: %1 = ptrtoint ptr %vtable to i64 + // CHECK-TYPEAUTH: %2 = call i64 @llvm.ptrauth.auth(i64 %1, i32 2, i64 48388) + // CHECK-TYPEAUTH: %3 = inttoptr i64 %2 to ptr + // CHECK-TYPEAUTH: %4 = load volatile i8, ptr %3, align 8 + // CHECK-ADDRESSAUTH: %2 = ptrtoint ptr %vtable to i64 + // CHECK-ADDRESSAUTH: %3 = call i64 @llvm.ptrauth.auth(i64 %2, i32 2, i64 %1) + // CHECK-ADDRESSAUTH: %4 = inttoptr i64 %3 to ptr + // CHECK-ADDRESSAUTH: %5 = load volatile i8, ptr %4, align 8 + // CHECK-BOTHAUTH: [[T2:%.*]] = call i64 @llvm.ptrauth.blend(i64 %1, i64 48388) + // CHECK-BOTHAUTH: [[T3:%.*]] = ptrtoint ptr %vtable to i64 + // CHECK-BOTHAUTH: [[T4:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T3]], i32 2, i64 [[T2]]) + // CHECK-BOTHAUTH: [[T5:%.*]] = inttoptr i64 [[T4]] to ptr + // CHECK-BOTHAUTH: [[T6:%.*]] = load volatile i8, ptr [[T5]], align 8 +} + +const void *c_as_B(C *o) { + // CHECK-NOAUTH: define ptr @_ZN5test16c_as_BEPNS_1CE(ptr %o) #0 { + return __builtin_get_vtable_pointer((B *)o); + // CHECK-NOAUTH: %add.ptr = getelementptr inbounds i8, ptr %0, i64 8 + // CHECK-NOAUTH: br label %cast.end + // CHECK-NOAUTH: %cast.result = phi ptr [ %add.ptr, %cast.notnull ], [ null, %entry ] + // CHECK-NOAUTH: %vtable = load ptr, ptr %cast.result, align 8 + // CHECK-TYPEAUTH: %cast.result = phi ptr [ %add.ptr, %cast.notnull ], [ null, %entry ] + // CHECK-TYPEAUTH: %vtable = load ptr, ptr %cast.result, align 8 + // CHECK-TYPEAUTH: %2 = ptrtoint ptr %vtable to i64 + // CHECK-TYPEAUTH: %3 = call i64 @llvm.ptrauth.auth(i64 %2, i32 2, i64 48388) + // CHECK-TYPEAUTH: %4 = inttoptr i64 %3 to ptr + // CHECK-TYPEAUTH: %5 = load volatile i8, ptr %4, align 8 + // CHECK-ADDRESSAUTH: %2 = ptrtoint ptr %cast.result to i64 + // CHECK-ADDRESSAUTH: %3 = ptrtoint ptr %vtable to i64 + // CHECK-ADDRESSAUTH: %4 = call i64 @llvm.ptrauth.auth(i64 %3, i32 2, i64 %2) + // CHECK-ADDRESSAUTH: %5 = inttoptr i64 %4 to ptr + // CHECK-ADDRESSAUTH: %6 = load volatile i8, ptr %5, align 8 + // CHECK-BOTHAUTH: [[T2:%.*]] = call i64 @llvm.ptrauth.blend(i64 %2, i64 48388) + // CHECK-BOTHAUTH: [[T3:%.*]] = ptrtoint ptr %vtable to i64 + // CHECK-BOTHAUTH: [[T4:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T3]], i32 2, i64 [[T2]]) + // CHECK-BOTHAUTH: [[T5:%.*]] = inttoptr i64 [[T4]] to ptr + // CHECK-BOTHAUTH: [[T6:%.*]] = load volatile i8, ptr [[T5]], align 8 +} + +const void *d(D *o) { + // CHECK-NOAUTH: define ptr @_ZN5test11dEPNS_1DE(ptr %o) #0 { + return __builtin_get_vtable_pointer(o); + // CHECK-NOAUTH: %vtable = load ptr, ptr %0, align 8 + // CHECK-TYPEAUTH: %vtable = load ptr, ptr %0, align 8 + // CHECK-TYPEAUTH: %1 = ptrtoint ptr %vtable to i64 + // CHECK-TYPEAUTH: %2 = call i64 @llvm.ptrauth.auth(i64 %1, i32 2, i64 48388) + // CHECK-TYPEAUTH: %3 = inttoptr i64 %2 to ptr + // CHECK-TYPEAUTH: %4 = load volatile i8, ptr %3, align 8 + // CHECK-ADDRESSAUTH: %1 = ptrtoint ptr %0 to i64 + // CHECK-ADDRESSAUTH: %2 = ptrtoint ptr %vtable to i64 + // CHECK-ADDRESSAUTH: %3 = call i64 @llvm.ptrauth.auth(i64 %2, i32 2, i64 %1) + // CHECK-ADDRESSAUTH: %4 = inttoptr i64 %3 to ptr + // CHECK-ADDRESSAUTH: %5 = load volatile i8, ptr %4, align 8 + // CHECK-BOTHAUTH: [[T1:%.*]] = ptrtoint ptr %0 to i64 + // CHECK-BOTHAUTH: [[T2:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T1]], i64 48388) + // CHECK-BOTHAUTH: [[T3:%.*]] = ptrtoint ptr %vtable to i64 + // CHECK-BOTHAUTH: [[T4:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T3]], i32 2, i64 [[T2]]) + // CHECK-BOTHAUTH: [[T5:%.*]] = inttoptr i64 [[T4]] to ptr + // CHECK-BOTHAUTH: [[T6:%.*]] = load volatile i8, ptr [[T5]], align 8 +} + +const void *d_as_A(D *o) { + // CHECK-NOAUTH: define ptr @_ZN5test16d_as_AEPNS_1DE(ptr %o) #0 { + return __builtin_get_vtable_pointer((A *)o); + // CHECK-NOAUTH: %vtable = load ptr, ptr %0, align 8 + // CHECK-NOAUTH: %vbase.offset.ptr = getelementptr i8, ptr %vtable, i64 -32 + // CHECK-NOAUTH: %vbase.offset = load i64, ptr %vbase.offset.ptr, align 8 + // CHECK-NOAUTH: %add.ptr = getelementptr inbounds i8, ptr %0, i64 %vbase.offset + // CHECK-NOAUTH: %cast.result = phi ptr [ %add.ptr, %cast.notnull ], [ null, %entry ] + // CHECK-NOAUTH: %vtable1 = load ptr, ptr %cast.result, align 8 + // CHECK-TYPEAUTH: %vtable1 = load ptr, ptr %cast.result, align 8 + // CHECK-TYPEAUTH: %5 = ptrtoint ptr %vtable1 to i64 + // CHECK-TYPEAUTH: %6 = call i64 @llvm.ptrauth.auth(i64 %5, i32 2, i64 48388) + // CHECK-TYPEAUTH: %7 = inttoptr i64 %6 to ptr + // CHECK-TYPEAUTH: %8 = load volatile i8, ptr %7, align 8 + // CHECK-ADDRESSAUTH: %6 = ptrtoint ptr %cast.result to i64 + // CHECK-ADDRESSAUTH: %7 = ptrtoint ptr %vtable1 to i64 + // CHECK-ADDRESSAUTH: %8 = call i64 @llvm.ptrauth.auth(i64 %7, i32 2, i64 %6) + // CHECK-ADDRESSAUTH: %9 = inttoptr i64 %8 to ptr + // CHECK-ADDRESSAUTH: %10 = load volatile i8, ptr %9, align 8 + // CHECK-BOTHAUTH: [[T1:%.*]] = ptrtoint ptr %cast.result to i64 + // CHECK-BOTHAUTH: [[T2:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T1]], i64 48388) + // CHECK-BOTHAUTH: [[T3:%.*]] = ptrtoint ptr %vtable1 to i64 + // CHECK-BOTHAUTH: [[T4:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T3]], i32 2, i64 [[T2]]) + // CHECK-BOTHAUTH: [[T5:%.*]] = inttoptr i64 [[T4]] to ptr + // CHECK-BOTHAUTH: [[T6:%.*]] = load volatile i8, ptr [[T5]], align 8 +} + +const void *e(E *o) { + // CHECK-NOAUTH: define ptr @_ZN5test11eEPNS_1EE(ptr %o) #0 { + return __builtin_get_vtable_pointer(o); + // CHECK-NOAUTH: %vtable = load ptr, ptr %0, align 8 + // CHECK-TYPEAUTH: %vtable = load ptr, ptr %0, align 8 + // CHECK-TYPEAUTH: %1 = ptrtoint ptr %vtable to i64 + // CHECK-TYPEAUTH: %2 = call i64 @llvm.ptrauth.auth(i64 %1, i32 2, i64 48388) + // CHECK-TYPEAUTH: %3 = inttoptr i64 %2 to ptr + // CHECK-TYPEAUTH: %4 = load volatile i8, ptr %3, align 8 + // CHECK-ADDRESSAUTH: [[T1:%.*]] = ptrtoint ptr %0 to i64 + // CHECK-ADDRESSAUTH: [[T2:%.*]] = ptrtoint ptr %vtable to i64 + // CHECK-ADDRESSAUTH: [[T3:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T2]], i32 2, i64 [[T1]]) + // CHECK-ADDRESSAUTH: [[T4:%.*]] = inttoptr i64 [[T3]] to ptr + // CHECK-ADDRESSAUTH: [[T5:%.*]] = load volatile i8, ptr [[T4]], align 8 + // CHECK-BOTHAUTH: [[T1:%.*]] = ptrtoint ptr %0 to i64 + // CHECK-BOTHAUTH: [[T2:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T1]], i64 48388) + // CHECK-BOTHAUTH: [[T3:%.*]] = ptrtoint ptr %vtable to i64 + // CHECK-BOTHAUTH: [[T4:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T3]], i32 2, i64 [[T2]]) + // CHECK-BOTHAUTH: [[T5:%.*]] = inttoptr i64 [[T4]] to ptr + // CHECK-BOTHAUTH: [[T6:%.*]] = load volatile i8, ptr [[T5]], align 8 +} + +const void *e_as_B(E *o) { + // CHECK-NOAUTH: define ptr @_ZN5test16e_as_BEPNS_1EE(ptr %o) #0 { + return __builtin_get_vtable_pointer((B *)o); + // CHECK-NOAUTH: %add.ptr = getelementptr inbounds i8, ptr %0, i64 8 + // CHECK-NOAUTH: %cast.result = phi ptr [ %add.ptr, %cast.notnull ], [ null, %entry ] + // CHECK-NOAUTH: %vtable = load ptr, ptr %cast.result, align 8 + // CHECK-TYPEAUTH: %vtable = load ptr, ptr %cast.result, align 8 + // CHECK-TYPEAUTH: %2 = ptrtoint ptr %vtable to i64 + // CHECK-TYPEAUTH: %3 = call i64 @llvm.ptrauth.auth(i64 %2, i32 2, i64 48388) + // CHECK-TYPEAUTH: %4 = inttoptr i64 %3 to ptr + // CHECK-TYPEAUTH: %5 = load volatile i8, ptr %4, align 8 + // CHECK-ADDRESSAUTH: [[T1:%.*]] = ptrtoint ptr %cast.result to i64 + // CHECK-ADDRESSAUTH: [[T2:%.*]] = ptrtoint ptr %vtable to i64 + // CHECK-ADDRESSAUTH: [[T3:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T2]], i32 2, i64 [[T1]]) + // CHECK-ADDRESSAUTH: [[T4:%.*]] = inttoptr i64 [[T3]] to ptr + // CHECK-ADDRESSAUTH: [[T5:%.*]] = load volatile i8, ptr [[T4]], align 8 + // CHECK-BOTHAUTH: [[T1:%.*]] = ptrtoint ptr %cast.result to i64 + // CHECK-BOTHAUTH: [[T2:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T1]], i64 48388) + // CHECK-BOTHAUTH: [[T3:%.*]] = ptrtoint ptr %vtable to i64 + // CHECK-BOTHAUTH: [[T4:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T3]], i32 2, i64 [[T2]]) + // CHECK-BOTHAUTH: [[T5:%.*]] = inttoptr i64 [[T4]] to ptr + // CHECK-BOTHAUTH: [[T6:%.*]] = load volatile i8, ptr [[T5]], align 8 +} + +const void *e_as_B_ref(E &o) { + // CHECK-NOAUTH: define ptr @_ZN5test110e_as_B_refERNS_1EE + return __builtin_get_vtable_pointer((B &)o); + // CHECK-NOAUTH: [[THIS_ADDR:%.*]] = getelementptr inbounds i8, ptr %0 + // CHECK-NOAUTH: %vtable = load ptr, ptr [[THIS_ADDR]] + // CHECK-TYPEAUTH: [[THIS_ADDR:%.*]] = getelementptr inbounds i8, ptr %0 + // CHECK-TYPEAUTH: [[VTABLE:%.*]] = load ptr, ptr [[THIS_ADDR]] + // CHECK-TYPEAUTH: [[VTABLE_I64:%.*]] = ptrtoint ptr [[VTABLE]] to i64 + // CHECK-TYPEAUTH: [[VTABLE_I64_AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLE_I64]], i32 2, i64 48388) + // CHECK-ADDRESSAUTH: [[THIS_ADDR:%.*]] = getelementptr inbounds i8, ptr %0 + // CHECK-ADDRESSAUTH: [[VTABLE:%.*]] = load ptr, ptr [[THIS_ADDR]] + // CHECK-ADDRESSAUTH: [[THIS_ADDR_I64:%.*]] = ptrtoint ptr [[THIS_ADDR]] to i64 + // CHECK-ADDRESSAUTH: [[VTABLE_I64:%.*]] = ptrtoint ptr [[VTABLE]] to i64 + // CHECK-ADDRESSAUTH: [[VTABLE_I64_AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLE_I64]], i32 2, i64 [[THIS_ADDR_I64]]) + // CHECK-BOTHAUTH: [[THIS_ADDR:%.*]] = getelementptr inbounds i8, ptr %0 + // CHECK-BOTHAUTH: [[VTABLE:%.*]] = load ptr, ptr [[THIS_ADDR]] + // CHECK-BOTHAUTH: [[THIS_ADDR_I64:%.*]] = ptrtoint ptr [[THIS_ADDR]] to i64 + // CHECK-BOTHAUTH: [[DISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[THIS_ADDR_I64]], i64 48388) + // CHECK-BOTHAUTH: [[VTABLE_I64:%.*]] = ptrtoint ptr [[VTABLE]] to i64 + // CHECK-BOTHAUTH: [[VTABLE_I64_AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLE_I64]], i32 2, i64 [[DISC]]) + // CHECK-BOTHAUTH: [[VTABLE_AUTHED:%.*]] = inttoptr i64 [[VTABLE_I64_AUTHED]] to ptr +} + +const void *e_as_D(E *o) { + // CHECK-NOAUTH: define ptr @_ZN5test16e_as_DEPNS_1EE(ptr %o) #0 { + return __builtin_get_vtable_pointer((D *)o); + // CHECK-NOAUTH: %vtable = load ptr, ptr %0, align 8 + // CHECK-TYPEAUTH: %vtable = load ptr, ptr %0, align 8 + // CHECK-TYPEAUTH: %1 = ptrtoint ptr %vtable to i64 + // CHECK-TYPEAUTH: %2 = call i64 @llvm.ptrauth.auth(i64 %1, i32 2, i64 48388) + // CHECK-TYPEAUTH: %3 = inttoptr i64 %2 to ptr + // CHECK-TYPEAUTH: %4 = load volatile i8, ptr %3, align 8 + // CHECK-ADDRESSAUTH: [[T1:%.*]] = ptrtoint ptr %0 to i64 + // CHECK-ADDRESSAUTH: [[T2:%.*]] = ptrtoint ptr %vtable to i64 + // CHECK-ADDRESSAUTH: [[T3:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T2]], i32 2, i64 [[T1]]) + // CHECK-ADDRESSAUTH: [[T4:%.*]] = inttoptr i64 [[T3]] to ptr + // CHECK-ADDRESSAUTH: [[T5:%.*]] = load volatile i8, ptr [[T4]], align 8 + // CHECK-BOTHAUTH: [[T1:%.*]] = ptrtoint ptr %0 to i64 + // CHECK-BOTHAUTH: [[T2:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T1]], i64 48388) + // CHECK-BOTHAUTH: [[T3:%.*]] = ptrtoint ptr %vtable to i64 + // CHECK-BOTHAUTH: [[T4:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T3]], i32 2, i64 [[T2]]) + // CHECK-BOTHAUTH: [[T5:%.*]] = inttoptr i64 [[T4]] to ptr + // CHECK-BOTHAUTH: [[T6:%.*]] = load volatile i8, ptr [[T5]], align 8 +} +const void *e_as_D_ref(E &o) { + // CHECK-NOAUTH: define ptr @_ZN5test110e_as_D_refERNS_1EE( + return __builtin_get_vtable_pointer((D &)o); + // CHECK-NOAUTH: %vtable = load ptr, ptr %0, align 8 + // CHECK-TYPEAUTH: %vtable = load ptr, ptr %0, align 8 + // CHECK-TYPEAUTH: %1 = ptrtoint ptr %vtable to i64 + // CHECK-TYPEAUTH: %2 = call i64 @llvm.ptrauth.auth(i64 %1, i32 2, i64 48388) + // CHECK-TYPEAUTH: %3 = inttoptr i64 %2 to ptr + // CHECK-TYPEAUTH: %4 = load volatile i8, ptr %3, align 8 + // CHECK-ADDRESSAUTH: [[T1:%.*]] = ptrtoint ptr %0 to i64 + // CHECK-ADDRESSAUTH: [[T2:%.*]] = ptrtoint ptr %vtable to i64 + // CHECK-ADDRESSAUTH: [[T3:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T2]], i32 2, i64 [[T1]]) + // CHECK-ADDRESSAUTH: [[T4:%.*]] = inttoptr i64 [[T3]] to ptr + // CHECK-ADDRESSAUTH: [[T5:%.*]] = load volatile i8, ptr [[T4]], align 8 + // CHECK-BOTHAUTH: [[T1:%.*]] = ptrtoint ptr %0 to i64 + // CHECK-BOTHAUTH: [[T2:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T1]], i64 48388) + // CHECK-BOTHAUTH: [[T3:%.*]] = ptrtoint ptr %vtable to i64 + // CHECK-BOTHAUTH: [[T4:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T3]], i32 2, i64 [[T2]]) + // CHECK-BOTHAUTH: [[T5:%.*]] = inttoptr i64 [[T4]] to ptr + // CHECK-BOTHAUTH: [[T6:%.*]] = load volatile i8, ptr [[T5]], align 8 +} + +void test() { + A aInstance; + B bInstance; + C cInstance; + D dInstance; + E eInstance; + a(&aInstance); + a(&bInstance); + a((B *)&cInstance); + a(&dInstance); + a((D *)&eInstance); + a((B *)&eInstance); + b(&bInstance); + b(&cInstance); + b(&eInstance); + b_as_A(&bInstance); + c(&cInstance); + c_as_Z(&cInstance); + c_as_B(&cInstance); + d(&dInstance); + d(&eInstance); + d_as_A(&dInstance); + d_as_A(&eInstance); + e(&eInstance); + e_as_B(&eInstance); + e_as_B_ref(eInstance); + e_as_D(&eInstance); + e_as_D_ref(eInstance); +} +} // namespace test1 diff --git a/clang/test/CodeGenCXX/builtin_virtual_member_address.cpp b/clang/test/CodeGenCXX/builtin_virtual_member_address.cpp new file mode 100644 index 0000000000000..ba3b65a330106 --- /dev/null +++ b/clang/test/CodeGenCXX/builtin_virtual_member_address.cpp @@ -0,0 +1,68 @@ +// RUN: %clang_cc1 -triple arm64e-apple-ios -fptrauth-calls -emit-llvm -no-enable-noundef-analysis -o - %s | FileCheck %s --check-prefixes CHECK,CHECK-AUTH +// RUN: %clang_cc1 -triple arm64-apple-ios -emit-llvm -no-enable-noundef-analysis -o - %s | FileCheck %s --check-prefixes CHECK,CHECK-NOAUTH + +struct Base { + virtual void func1(); + virtual void func2(); + void nonvirt(); +}; + +struct Derived : Base { + virtual void func1(); +}; + +// CHECK-LABEL: define ptr @_Z7simple1R4Base(ptr{{.*}}%b) + +// CHECK-AUTH: [[BLOC:%.*]] = alloca ptr +// CHECK-AUTH: store ptr %b, ptr [[BLOC]] +// CHECK-AUTH: [[B:%.*]] = load ptr, ptr [[BLOC]] +// CHECK-AUTH: [[VTABLE:%.*]] = load ptr, ptr [[B]] +// CHECK-AUTH: [[VTABLE_AUTH_IN:%.*]] = ptrtoint ptr [[VTABLE]] to i64 +// CHECK-AUTH: [[VTABLE_AUTH_OUT:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLE_AUTH_IN]], i32 2, i64 0) +// CHECK-AUTH: [[VTABLE:%.*]] = inttoptr i64 [[VTABLE_AUTH_OUT]] to ptr +// CHECK-AUTH: [[FUNC_ADDR:%.*]] = getelementptr inbounds ptr, ptr [[VTABLE]], i64 1 +// CHECK-AUTH: [[FUNC:%.*]] = load ptr, ptr [[FUNC_ADDR]] +// CHECK-AUTH: [[FUNC_ADDR_I64:%.*]] = ptrtoint ptr [[FUNC_ADDR]] to i64 +// CHECK-AUTH: [[DISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[FUNC_ADDR_I64]], i64 25637) +// CHECK-AUTH: [[FUNC_I64:%.*]] = ptrtoint ptr [[FUNC]] to i64 +// CHECK-AUTH: [[FUNC_AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[FUNC_I64]], i32 0, i64 [[DISC]]) +// CHECK-AUTH: [[FUNC:%.*]] = inttoptr i64 [[FUNC_AUTHED]] to ptr +// CHECK-AUTH: ret ptr [[FUNC]] + +// CHECK-NOAUTH: [[BLOC:%.*]] = alloca ptr +// CHECK-NOAUTH: store ptr %b, ptr [[BLOC]] +// CHECK-NOAUTH: [[B:%.*]] = load ptr, ptr [[BLOC]] +// CHECK-NOAUTH: [[VTABLE:%.*]] = load ptr, ptr [[B]] +// CHECK-NOAUTH: [[FUNC_ADDR:%.*]] = getelementptr inbounds ptr, ptr [[VTABLE]], i64 1 +// CHECK-NOAUTH: [[FUNC:%.*]] = load ptr, ptr [[FUNC_ADDR]] +// CHECK-NOAUTH: ret ptr [[FUNC]] +void *simple1(Base &b) { + return __builtin_virtual_member_address(b, &Base::func2); +} + +// CHECK-LABEL: define ptr @_Z7simple2P4Base(ptr{{.*}}%b) + +// CHECK-AUTH: [[B_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-AUTH: store ptr %b, ptr [[B_ADDR]] +// CHECK-AUTH: [[VTABLE:%.*]] = load ptr, ptr [[B_ADDR]] +// CHECK-AUTH: [[VTABLE_I64:%.*]] = ptrtoint ptr [[VTABLE]] to i64 +// CHECK-AUTH: [[VTABLE_I64_AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VTABLE_I64]], i32 2, i64 0) +// CHECK-AUTH: [[VTABLE_AUTHED:%.*]] = inttoptr i64 [[VTABLE_I64_AUTHED]] to ptr +// CHECK-AUTH: [[VFUNCTION_ADDR:%.*]] = getelementptr inbounds ptr, ptr [[VTABLE_AUTHED]], i64 1 +// CHECK-AUTH: [[VFUNCTION_PTR:%.*]] = load ptr, ptr [[VFUNCTION_ADDR]] +// CHECK-AUTH: [[VFUNCTION_ADDR_I64:%.*]] = ptrtoint ptr [[VFUNCTION_ADDR]] to i64 +// CHECK-AUTH: [[DISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[VFUNCTION_ADDR_I64]], i64 25637) +// CHECK-AUTH: [[VFUNCTION_I64:%.*]] = ptrtoint ptr [[VFUNCTION_PTR]] to i64 +// CHECK-AUTH: [[AUTHED_FPTR_I64:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VFUNCTION_I64]], i32 0, i64 %5) +// CHECK-AUTH: [[AUTHED_FPTR:%.*]] = inttoptr i64 [[AUTHED_FPTR_I64]] to ptr +// CHECK-AUTH: ret ptr [[AUTHED_FPTR]] + +// CHECK-NOAUTH: [[B_ADDR:%.*]] = alloca ptr +// CHECK-NOAUTH: store ptr %b, ptr [[B_ADDR]] +// CHECK-NOAUTH: [[VTABLE:%.*]] = load ptr, ptr [[B_ADDR]] +// CHECK-NOAUTH: [[FUNC_ADDR:%.*]] = getelementptr inbounds ptr, ptr [[VTABLE]], i64 1 +// CHECK-NOAUTH: [[FUNC:%.*]] = load ptr, ptr [[FUNC_ADDR]] +// CHECK-NOAUTH: ret ptr [[FUNC]] +void *simple2(Base *b) { + return __builtin_virtual_member_address(b, &Base::func2); +} diff --git a/clang/test/SemaCXX/builtin-get-vtable-pointer.cpp b/clang/test/SemaCXX/builtin-get-vtable-pointer.cpp new file mode 100644 index 0000000000000..9818ab5b00f5f --- /dev/null +++ b/clang/test/SemaCXX/builtin-get-vtable-pointer.cpp @@ -0,0 +1,124 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -std=c++2a %s + +namespace basic { +struct ForwardDeclaration; // expected-note{{forward declaration of 'basic::ForwardDeclaration'}} + // expected-note@-1{{forward declaration of 'basic::ForwardDeclaration'}} +struct NonPolymorphic {}; +struct Polymorphic { + virtual ~Polymorphic(); +}; + +template +struct Foo { + virtual ~Foo(); +}; + +template <> +struct Foo { +}; + +template +struct Bar { + using SubType = typename T::SubType; + SubType *ty() const; +}; + +struct Thing1 { + using SubType = Thing1; +}; + +struct Thing2 { + using SubType = Thing2; + virtual ~Thing2(); +}; + +struct Thing3 { + using SubType = int; +}; + +struct Thing4 { + using SubType = Polymorphic; +}; + +struct Thing5 { + using SubType = NonPolymorphic; +}; + +struct Thing6 { + using SubType = ForwardDeclaration; +}; + +template +const void *getThing(const Bar *b = nullptr) { + return __builtin_get_vtable_pointer(b->ty()); // expected-error{{__builtin_get_vtable_pointer requires an argument of class pointer type, but 'SubType *' (aka 'int *') was provided}} + // expected-error@-1{{__builtin_get_vtable_pointer requires an argument of polymorphic class pointer type, but 'SubType' (aka 'basic::Thing1') has no virtual methods}} + // expected-error@-2{{__builtin_get_vtable_pointer requires an argument of polymorphic class pointer type, but 'SubType' (aka 'basic::NonPolymorphic') has no virtual methods}} + // expected-error@-3{{__builtin_get_vtable_pointer requires an argument with a complete type, but 'SubType' (aka 'basic::ForwardDeclaration') is incomplete}} +} +template +struct IncompleteTemplate; // expected-note{{template is declared here}} +template +struct MonomorphicTemplate { +}; +template +struct PolymorphicTemplate { + virtual ~PolymorphicTemplate(); +}; + +void test_function(int); // expected-note{{possible target for call}} + // expected-note@-1{{possible target for call}} +void test_function(double); // expected-note{{possible target for call}} + // expected-note@-1{{possible target for call}} + +void getVTablePointer() { + ForwardDeclaration *fd = nullptr; + NonPolymorphic np; + Polymorphic p; + NonPolymorphic np_array[1]; + Polymorphic p_array[1]; + __builtin_get_vtable_pointer(0); // expected-error{{__builtin_get_vtable_pointer requires an argument of class pointer type, but 'int' was provided}} + __builtin_get_vtable_pointer(nullptr); // expected-error{{__builtin_get_vtable_pointer requires an argument of class pointer type, but 'std::nullptr_t' was provided}} + __builtin_get_vtable_pointer(0.5); // expected-error{{__builtin_get_vtable_pointer requires an argument of class pointer type, but 'double' was provided}} + __builtin_get_vtable_pointer(fd); // expected-error{{__builtin_get_vtable_pointer requires an argument with a complete type, but 'ForwardDeclaration' is incomplete}} + __builtin_get_vtable_pointer(np); // expected-error{{__builtin_get_vtable_pointer requires an argument of polymorphic class pointer type, but 'NonPolymorphic' has no virtual methods}} + __builtin_get_vtable_pointer(&np); // expected-error{{__builtin_get_vtable_pointer requires an argument of polymorphic class pointer type, but 'NonPolymorphic' has no virtual methods}} + (void)__builtin_get_vtable_pointer(p); + __builtin_get_vtable_pointer(&p); // expected-warning{{ignoring return value of function declared with const attribute}} + __builtin_get_vtable_pointer(p_array); // expected-warning{{ignoring return value of function declared with const attribute}} + __builtin_get_vtable_pointer(&p_array); // expected-error{{__builtin_get_vtable_pointer requires an argument of class pointer type, but 'Polymorphic (*)[1]' was provided}} + __builtin_get_vtable_pointer(np_array); // expected-error{{__builtin_get_vtable_pointer requires an argument of polymorphic class pointer type, but 'NonPolymorphic' has no virtual methods}} + __builtin_get_vtable_pointer(&np_array); // expected-error{{__builtin_get_vtable_pointer requires an argument of class pointer type, but 'NonPolymorphic (*)[1]' was provided}} + __builtin_get_vtable_pointer(test_function); // expected-error{{reference to overloaded function could not be resolved; did you mean to call it?}} + // expected-error@-1{{reference to overloaded function could not be resolved; did you mean to call it?}} + Foo Food; + Foo Fooi; + (void)__builtin_get_vtable_pointer(Food); + (void)__builtin_get_vtable_pointer(&Food); + __builtin_get_vtable_pointer(Fooi); // expected-error{{__builtin_get_vtable_pointer requires an argument of polymorphic class pointer type, but 'Foo' has no virtual methods}} + __builtin_get_vtable_pointer(&Fooi); // expected-error{{__builtin_get_vtable_pointer requires an argument of polymorphic class pointer type, but 'Foo' has no virtual methods}} + + IncompleteTemplate *incomplete = nullptr; + (void)__builtin_get_vtable_pointer(incomplete); // expected-error{{implicit instantiation of undefined template 'basic::IncompleteTemplate'}} + PolymorphicTemplate *ptb = nullptr; + MonomorphicTemplate *mtb = nullptr; + PolymorphicTemplate pti; + MonomorphicTemplate mti; + PolymorphicTemplate ptf; + MonomorphicTemplate mtf; + (void)__builtin_get_vtable_pointer(ptb); + (void)__builtin_get_vtable_pointer(*ptb); + __builtin_get_vtable_pointer(mtb); // expected-error{{__builtin_get_vtable_pointer requires an argument of polymorphic class pointer type, but 'MonomorphicTemplate' has no virtual methods}} + (void)__builtin_get_vtable_pointer(pti); + __builtin_get_vtable_pointer(mti); // expected-error{{__builtin_get_vtable_pointer requires an argument of polymorphic class pointer type, but 'MonomorphicTemplate' has no virtual methods}} + (void)__builtin_get_vtable_pointer(&ptf); + __builtin_get_vtable_pointer(&mtf); // expected-error{{__builtin_get_vtable_pointer requires an argument of polymorphic class pointer type, but 'MonomorphicTemplate' has no virtual methods}} + + getThing(); // expected-note{{in instantiation of function template specialization 'basic::getThing' requested here}} + getThing(); + getThing(); // expected-note{{in instantiation of function template specialization 'basic::getThing' requested here}} + getThing(); + getThing(); // expected-note{{in instantiation of function template specialization 'basic::getThing' requested here}} + getThing(); // expected-note{{in instantiation of function template specialization 'basic::getThing' requested here}} +} + +} // namespace basic diff --git a/clang/test/SemaCXX/builtin_virtual_member_address.cpp b/clang/test/SemaCXX/builtin_virtual_member_address.cpp new file mode 100644 index 0000000000000..5ffe0f053ca4d --- /dev/null +++ b/clang/test/SemaCXX/builtin_virtual_member_address.cpp @@ -0,0 +1,60 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +struct Base { + virtual void func1(); + virtual void func2(); + + virtual void overloaded(); + virtual void overloaded() const; + + void nonvirt(); +}; + +struct Derived : Base { + virtual void func1(); +}; + +struct Unrelated { + virtual void func1(); +}; + +void *simple(Base &b) { + return __builtin_virtual_member_address(b, &Base::func1); +} + +void test(Base &b, Derived &d, Unrelated &u) { + __builtin_virtual_member_address(42, &Base::func1); // expected-error {{first argument to __builtin_virtual_member_address must have C++ class type}} + __builtin_virtual_member_address(u, &Base::func1); // expected-error {{first argument to __builtin_virtual_member_address must have a type deriving from class where second argument was defined}} + + __builtin_virtual_member_address(b, &Derived::func1); // expected-error {{first argument to __builtin_virtual_member_address must have a type deriving from class where second argument was defined}} + __builtin_virtual_member_address(b, &Unrelated::func1); // expected-error {{first argument to __builtin_virtual_member_address must have a type deriving from class where second argument was defined}} + + __builtin_virtual_member_address(b, 42); // expected-error {{second argument to __builtin_virtual_member_address must be the address of a virtual C++ member function: for example '&Foo::func'}} + __builtin_virtual_member_address(b, &Base::nonvirt); // expected-error {{second argument to __builtin_virtual_member_address must be the address of a virtual C++ member function: for example '&Foo::func'}} + + (void)__builtin_virtual_member_address(d, &Base::overloaded); // expected-error {{reference to overloaded function could not be resolved; did you mean to call it?}} expected-error {{reference to overloaded function could not be resolved; did you mean to call it?}} + (void)__builtin_virtual_member_address(d,(void (Base::*)() const) &Base::overloaded); + + (void)__builtin_virtual_member_address(d, &Base::func1); + (void)__builtin_virtual_member_address(Base(), &Base::func1); +} + +template void test(T &t, Base &b, Derived &d) { + (void)__builtin_virtual_member_address(&b, &Base::func1); + (void)__builtin_virtual_member_address(t, &Base::func1); + (void)__builtin_virtual_member_address(&t, &Base::func1); + (void)__builtin_virtual_member_address(t, &T::func1); + (void)__builtin_virtual_member_address(&t, &T::func1); + (void)__builtin_virtual_member_address(d, &T::func1); + (void)__builtin_virtual_member_address(&d, &T::func1); + (void)__builtin_virtual_member_address(t, &Unrelated::func1); // expected-error {{first argument to __builtin_virtual_member_address must have a type deriving from class where second argument was defined}} expected-error {{first argument to __builtin_virtual_member_address must have a type deriving from class where second argument was defined}} + +} + +void foo() { + Base b; + Derived d; + + test(b, b, d); // expected-note {{in instantiation of function template specialization 'test' requested here}} + test(d, b, d); // expected-note {{in instantiation of function template specialization 'test' requested here}} +}