[llvm] [PtrAuth] Add ConstantPtrAuth comparator to FunctionComparator.cpp (PR #159480)

Oskar Wirga via llvm-commits llvm-commits at lists.llvm.org
Mon Dec 22 11:26:24 PST 2025


https://github.com/oskarwirga updated https://github.com/llvm/llvm-project/pull/159480

>From 72d8ad7dca5ddc289cf3d0497f8ffff8a9bfe22c Mon Sep 17 00:00:00 2001
From: Oskar Wirga <oskar.wirga at gmail.com>
Date: Thu, 11 Sep 2025 19:16:40 -0700
Subject: [PATCH] [PtrAuth] Add ConstantPtrAuth comparator to
 FunctionComparator.cpp

When building rustc std for arm64e, the optimizations result in devirtualization of indirect calls.

This can happen during function merging so I modified FunctionComparator.cpp as the ConstantPtrAuth value would go unchecked in the switch statement.
---
 .../Transforms/Utils/FunctionComparator.cpp   |  12 ++
 .../MergeFunc/ptrauth-const-compare.ll        | 130 ++++++++++++++++++
 2 files changed, 142 insertions(+)
 create mode 100644 llvm/test/Transforms/MergeFunc/ptrauth-const-compare.ll

diff --git a/llvm/lib/Transforms/Utils/FunctionComparator.cpp b/llvm/lib/Transforms/Utils/FunctionComparator.cpp
index 7cf19177db545..6928de881f797 100644
--- a/llvm/lib/Transforms/Utils/FunctionComparator.cpp
+++ b/llvm/lib/Transforms/Utils/FunctionComparator.cpp
@@ -518,6 +518,18 @@ int FunctionComparator::cmpConstants(const Constant *L,
     const auto *REquiv = cast<DSOLocalEquivalent>(R);
     return cmpGlobalValues(LEquiv->getGlobalValue(), REquiv->getGlobalValue());
   }
+  case Value::ConstantPtrAuthVal: {
+    // Handle authenticated pointer constants produced by ConstantPtrAuth::get.
+    const ConstantPtrAuth *LPA = cast<ConstantPtrAuth>(L);
+    const ConstantPtrAuth *RPA = cast<ConstantPtrAuth>(R);
+    if (int Res = cmpConstants(LPA->getPointer(), RPA->getPointer()))
+      return Res;
+    if (int Res = cmpConstants(LPA->getKey(), RPA->getKey()))
+      return Res;
+    if (int Res = cmpConstants(LPA->getDiscriminator(), RPA->getDiscriminator()))
+      return Res;
+    return cmpConstants(LPA->getAddrDiscriminator(), RPA->getAddrDiscriminator());
+  }
   default: // Unknown constant, abort.
     LLVM_DEBUG(dbgs() << "Looking at valueID " << L->getValueID() << "\n");
     llvm_unreachable("Constant ValueID not recognized.");
diff --git a/llvm/test/Transforms/MergeFunc/ptrauth-const-compare.ll b/llvm/test/Transforms/MergeFunc/ptrauth-const-compare.ll
new file mode 100644
index 0000000000000..931e64e3bc733
--- /dev/null
+++ b/llvm/test/Transforms/MergeFunc/ptrauth-const-compare.ll
@@ -0,0 +1,130 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt -passes=mergefunc -S < %s | FileCheck %s
+; Ensure MergeFunc handles ConstantPtrAuth correctly and does not
+; merge when any ptrauth operand differs (ptr, key, int disc, addr disc).
+
+target triple = "arm64e-apple-ios14.0.0"
+
+declare void @baz()
+ at ADDR = external global i8
+
+declare void @callee(ptr)
+
+; different base pointer (null vs @baz)
+
+define i32 @f_ptr_null() {
+; CHECK-LABEL: define i32 @f_ptr_null() {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    call void @callee(ptr ptrauth (ptr null, i32 0))
+; CHECK-NEXT:    ret i32 1
+;
+entry:
+  call void @callee(ptr ptrauth (ptr null, i32 0))
+  ret i32 1
+}
+
+define i32 @g_ptr_baz() {
+; CHECK-LABEL: define i32 @g_ptr_baz() {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    call void @callee(ptr ptrauth (ptr @baz, i32 0))
+; CHECK-NEXT:    ret i32 2
+;
+entry:
+  call void @callee(ptr ptrauth (ptr @baz, i32 0))
+  ret i32 2
+}
+
+; different key (i32 0 vs i32 1)
+
+define i32 @f_key0() {
+; CHECK-LABEL: define i32 @f_key0() {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    call void @callee(ptr ptrauth (ptr @baz, i32 0))
+; CHECK-NEXT:    ret i32 3
+;
+entry:
+  call void @callee(ptr ptrauth (ptr @baz, i32 0))
+  ret i32 3
+}
+
+define i32 @g_key1() {
+; CHECK-LABEL: define i32 @g_key1() {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    call void @callee(ptr ptrauth (ptr @baz, i32 1))
+; CHECK-NEXT:    ret i32 4
+;
+entry:
+  call void @callee(ptr ptrauth (ptr @baz, i32 1))
+  ret i32 4
+}
+
+; different integer disc (i64 0 vs i64 7)
+
+define i32 @f_disc0() {
+; CHECK-LABEL: define i32 @f_disc0() {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    call void @callee(ptr ptrauth (ptr @baz, i32 0))
+; CHECK-NEXT:    ret i32 5
+;
+entry:
+  call void @callee(ptr ptrauth (ptr @baz, i32 0))
+  ret i32 5
+}
+
+define i32 @g_disc7() {
+; CHECK-LABEL: define i32 @g_disc7() {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    call void @callee(ptr ptrauth (ptr @baz, i32 0, i64 7))
+; CHECK-NEXT:    ret i32 6
+;
+entry:
+  call void @callee(ptr ptrauth (ptr @baz, i32 0, i64 7))
+  ret i32 6
+}
+
+; different addr disc (ptr null vs @ADDR)
+
+define i32 @f_addr_null() {
+; CHECK-LABEL: define i32 @f_addr_null() {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    call void @callee(ptr ptrauth (ptr @baz, i32 0))
+; CHECK-NEXT:    ret i32 7
+;
+entry:
+  call void @callee(ptr ptrauth (ptr @baz, i32 0))
+  ret i32 7
+}
+
+define i32 @g_addr_ADDR() {
+; CHECK-LABEL: define i32 @g_addr_ADDR() {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    call void @callee(ptr ptrauth (ptr @baz, i32 0, i64 0, ptr @ADDR))
+; CHECK-NEXT:    ret i32 8
+;
+entry:
+  call void @callee(ptr ptrauth (ptr @baz, i32 0, i64 0, ptr @ADDR))
+  ret i32 8
+}
+
+; positive test: identical ptrauth operands, should be merged
+
+define void @merge_ptrauth_a() {
+; CHECK-LABEL: define void @merge_ptrauth_a() {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    call void @callee(ptr ptrauth (ptr @baz, i32 0, i64 0, ptr @ADDR))
+; CHECK-NEXT:    ret void
+;
+entry:
+  call void @callee(ptr ptrauth (ptr @baz, i32 0, i64 0, ptr @ADDR))
+  ret void
+}
+
+define void @merge_ptrauth_b() {
+; CHECK-LABEL: define void @merge_ptrauth_b() {
+; CHECK-NEXT:    tail call void @merge_ptrauth_a()
+; CHECK-NEXT:    ret void
+;
+entry:
+  call void @callee(ptr ptrauth (ptr @baz, i32 0, i64 0, ptr @ADDR))
+  ret void
+}



More information about the llvm-commits mailing list