[clang] [clang][PAC] Add __builtin_get_vtable_pointer (PR #139790)

via cfe-commits cfe-commits at lists.llvm.org
Tue May 13 13:39:19 PDT 2025


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang-codegen

Author: Oliver Hunt (ojhunt)

<details>
<summary>Changes</summary>

With pointer authentication it becomes non-trivial to correctly load the vtable pointer of a polymorphic object.

__builtin_get_vtable_pointer is a function that performs the load and performs the appropriate authentication operations if necessary.

---

Patch is 30.42 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/139790.diff


8 Files Affected:

- (modified) clang/docs/LanguageExtensions.rst (+33) 
- (modified) clang/docs/ReleaseNotes.rst (+2) 
- (modified) clang/include/clang/Basic/Builtins.td (+6) 
- (modified) clang/include/clang/Basic/DiagnosticSemaKinds.td (+8) 
- (modified) clang/lib/CodeGen/CGBuiltin.cpp (+13) 
- (modified) clang/lib/Sema/SemaChecking.cpp (+37) 
- (added) clang/test/CodeGenCXX/builtin-get-vtable-pointer.cpp (+304) 
- (added) clang/test/SemaCXX/builtin-get-vtable-pointer.cpp (+123) 


``````````diff
diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst
index f56f2a640bb36..5561bfb944713 100644
--- a/clang/docs/LanguageExtensions.rst
+++ b/clang/docs/LanguageExtensions.rst
@@ -3062,6 +3062,39 @@ 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_call_with_static_chain``
 ------------------------------------
 
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index bc13d02e2d20b..342771ca83608 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -291,6 +291,8 @@ Non-comprehensive list of changes in this release
   different than before.
 - Fixed a crash when a VLA with an invalid size expression was used within a
   ``sizeof`` or ``typeof`` expression. (#GH138444)
+- Added `__builtin_get_vtable_pointer` to directly load the primary vtable pointer from a
+  polymorphic object.
 
 New Compiler Flags
 ------------------
diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td
index 11b1e247237a7..52c0515d4e1b5 100644
--- a/clang/include/clang/Basic/Builtins.td
+++ b/clang/include/clang/Basic/Builtins.td
@@ -970,6 +970,12 @@ 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*)";
+}
+
 // 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 3efe9593b8633..a54eb924ad5bf 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -12729,6 +12729,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 45e0f69c46902..019c70726c267 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"
@@ -5365,6 +5366,18 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
     return RValue::get(Result);
   }
 
+  case Builtin::BI__builtin_get_vtable_pointer: {
+    const Expr *Target = E->getArg(0);
+    QualType TargetType = Target->getType();
+    const CXXRecordDecl *Decl = TargetType->getPointeeCXXRecordDecl();
+    assert(Decl);
+    auto ThisAddress = EmitPointerWithAlignment(Target);
+    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 55121b90fa167..39c4fc3fb08e4 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -1825,6 +1825,39 @@ static ExprResult PointerAuthStringDiscriminator(Sema &S, CallExpr *Call) {
   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();
+  const CXXRecordDecl *SubjectRecord = SubjectType->getPointeeCXXRecordDecl();
+  if (!SubjectType->isPointerType() || !SubjectRecord) {
+    S.Diag(Subject->getBeginLoc(),
+           diag::err_get_vtable_pointer_incorrect_type)
+        << 0 << SubjectType;
+    return ExprError();
+  }
+  if (S.RequireCompleteType(
+    Subject->getBeginLoc(), SubjectType->getPointeeType(),
+          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 << SubjectRecord;
+    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();
@@ -2719,6 +2752,10 @@ 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);
+
   // 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..5577e01a09f6f
--- /dev/null
+++ b/clang/test/CodeGenCXX/builtin-get-vtable-pointer.cpp
@@ -0,0 +1,304 @@
+// 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-N...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/139790


More information about the cfe-commits mailing list