Skip to content

Conversation

@oskarwirga
Copy link
Contributor

When building rustc std for arm64e, core fails to compile successfully with the error:

Constant ValueID not recognized.
UNREACHABLE executed at rust/src/llvm-project/llvm/lib/Transforms/Utils/FunctionComparator.cpp:523!

This is a result of function merging so I modified FunctionComparator.cpp as the ConstantPtrAuth value would go unchecked in the switch statement.

The test case is a reduction from the failure in core and fails on main with:

********************
FAIL: LLVM :: Transforms/MergeFunc/ptrauth-const-compare.ll (59809 of 59995)
******************** TEST 'LLVM :: Transforms/MergeFunc/ptrauth-const-compare.ll' FAILED ********************
Exit Code: 2

Command Output (stdout):
--
# RUN: at line 3
/Users/oskarwirga/llvm-project/build/bin/opt -S -passes=mergefunc < /Users/oskarwirga/llvm-project/llvm/test/Transforms/MergeFunc/ptrauth-const-compare.ll | /Users/oskarwirga/llvm-project/build/bin/FileCheck /Users/oskarwirga/llvm-project/llvm/test/Transforms/MergeFunc/ptrauth-const-compare.ll
# executed command: /Users/oskarwirga/llvm-project/build/bin/opt -S -passes=mergefunc
# .---command stderr------------
# | Constant ValueID not recognized.
# | UNREACHABLE executed at /Users/oskarwirga/llvm-project/llvm/lib/Transforms/Utils/FunctionComparator.cpp:523!
# | PLEASE submit a bug report to https://github.com/llvm/llvm-project/issues/ and include the crash backtrace and instructions to reproduce the bug.
# | Stack dump:
# | 0.	Program arguments: /Users/oskarwirga/llvm-project/build/bin/opt -S -passes=mergefunc
# | 1.	Running pass "mergefunc" on module "<stdin>"
# |  #0 0x0000000103335770 llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) (/Users/oskarwirga/llvm-project/build/bin/opt+0x102651770)
# |  #1 0x00000001033336bc llvm::sys::RunSignalHandlers() (/Users/oskarwirga/llvm-project/build/bin/opt+0x10264f6bc)
# |  #2 0x0000000103336218 SignalHandler(int, __siginfo*, void*) (/Users/oskarwirga/llvm-project/build/bin/opt+0x102652218)
# |  #3 0x000000018e6c16a4 (/usr/lib/system/libsystem_platform.dylib+0x1804ad6a4)
# |  #4 0x000000018e68788c (/usr/lib/system/libsystem_pthread.dylib+0x18047388c)
# |  #5 0x000000018e590a3c (/usr/lib/system/libsystem_c.dylib+0x18037ca3c)
# |  #6 0x00000001032a84bc llvm::install_out_of_memory_new_handler() (/Users/oskarwirga/llvm-project/build/bin/opt+0x1025c44bc)
# |  #7 0x00000001033b37c0 llvm::FunctionComparator::cmpMDNode(llvm::MDNode const*, llvm::MDNode const*) const (/Users/oskarwirga/llvm-project/build/bin/opt+0x1026cf7c0)
# |  #8 0x00000001033b4d90 llvm::FunctionComparator::cmpBasicBlocks(llvm::BasicBlock const*, llvm::BasicBlock const*) const (/Users/oskarwirga/llvm-project/build/bin/opt+0x1026d0d90)
# |  #9 0x00000001033b5234 llvm::FunctionComparator::compare() (/Users/oskarwirga/llvm-project/build/bin/opt+0x1026d1234)
# | #10 0x0000000102d6d868 (anonymous namespace)::MergeFunctions::insert(llvm::Function*) (/Users/oskarwirga/llvm-project/build/bin/opt+0x102089868)
# | #11 0x0000000102d6bc0c llvm::MergeFunctionsPass::runOnModule(llvm::Module&) (/Users/oskarwirga/llvm-project/build/bin/opt+0x102087c0c)
# | #12 0x0000000102d6b430 llvm::MergeFunctionsPass::run(llvm::Module&, llvm::AnalysisManager<llvm::Module>&) (/Users/oskarwirga/llvm-project/build/bin/opt+0x102087430)
# | #13 0x0000000102b90558 llvm::PassManager<llvm::Module, llvm::AnalysisManager<llvm::Module>>::run(llvm::Module&, llvm::AnalysisManager<llvm::Module>&) (/Users/oskarwirga/llvm-project/build/bin/opt+0x101eac558)
# | #14 0x0000000103734bc4 llvm::runPassPipeline(llvm::StringRef, llvm::Module&, llvm::TargetMachine*, llvm::TargetLibraryInfoImpl*, llvm::ToolOutputFile*, llvm::ToolOutputFile*, llvm::ToolOutputFile*, llvm::StringRef, llvm::ArrayRef<llvm::PassPlugin>, llvm::ArrayRef<std::__1::function<void (llvm::PassBuilder&)>>, llvm::opt_tool::OutputKind, llvm::opt_tool::VerifierKind, bool, bool, bool, bool, bool, bool, bool, bool) (/Users/oskarwirga/llvm-project/build/bin/opt+0x102a50bc4)
# | #15 0x000000010373cc28 optMain (/Users/oskarwirga/llvm-project/build/bin/opt+0x102a58c28)
# | #16 0x000000018e2e6b98
# `-----------------------------
# error: command failed with exit status: -6
# executed command: /Users/oskarwirga/llvm-project/build/bin/FileCheck /Users/oskarwirga/llvm-project/llvm/test/Transforms/MergeFunc/ptrauth-const-compare.ll
# .---command stderr------------
# | FileCheck error: '<stdin>' is empty.
# | FileCheck command line:  /Users/oskarwirga/llvm-project/build/bin/FileCheck /Users/oskarwirga/llvm-project/llvm/test/Transforms/MergeFunc/ptrauth-const-compare.ll
# `-----------------------------
# error: command failed with exit status: 2

@llvmbot
Copy link
Member

llvmbot commented Sep 17, 2025

@llvm/pr-subscribers-llvm-transforms

Author: Oskar Wirga (oskarwirga)

Changes

When building rustc std for arm64e, core fails to compile successfully with the error:

Constant ValueID not recognized.
UNREACHABLE executed at rust/src/llvm-project/llvm/lib/Transforms/Utils/FunctionComparator.cpp:523!

This is a result of function merging so I modified FunctionComparator.cpp as the ConstantPtrAuth value would go unchecked in the switch statement.

The test case is a reduction from the failure in core and fails on main with:

********************
FAIL: LLVM :: Transforms/MergeFunc/ptrauth-const-compare.ll (59809 of 59995)
******************** TEST 'LLVM :: Transforms/MergeFunc/ptrauth-const-compare.ll' FAILED ********************
Exit Code: 2

Command Output (stdout):
--
# RUN: at line 3
/Users/oskarwirga/llvm-project/build/bin/opt -S -passes=mergefunc &lt; /Users/oskarwirga/llvm-project/llvm/test/Transforms/MergeFunc/ptrauth-const-compare.ll | /Users/oskarwirga/llvm-project/build/bin/FileCheck /Users/oskarwirga/llvm-project/llvm/test/Transforms/MergeFunc/ptrauth-const-compare.ll
# executed command: /Users/oskarwirga/llvm-project/build/bin/opt -S -passes=mergefunc
# .---command stderr------------
# | Constant ValueID not recognized.
# | UNREACHABLE executed at /Users/oskarwirga/llvm-project/llvm/lib/Transforms/Utils/FunctionComparator.cpp:523!
# | PLEASE submit a bug report to https://github.com/llvm/llvm-project/issues/ and include the crash backtrace and instructions to reproduce the bug.
# | Stack dump:
# | 0.	Program arguments: /Users/oskarwirga/llvm-project/build/bin/opt -S -passes=mergefunc
# | 1.	Running pass "mergefunc" on module "&lt;stdin&gt;"
# |  #<!-- -->0 0x0000000103335770 llvm::sys::PrintStackTrace(llvm::raw_ostream&amp;, int) (/Users/oskarwirga/llvm-project/build/bin/opt+0x102651770)
# |  #<!-- -->1 0x00000001033336bc llvm::sys::RunSignalHandlers() (/Users/oskarwirga/llvm-project/build/bin/opt+0x10264f6bc)
# |  #<!-- -->2 0x0000000103336218 SignalHandler(int, __siginfo*, void*) (/Users/oskarwirga/llvm-project/build/bin/opt+0x102652218)
# |  #<!-- -->3 0x000000018e6c16a4 (/usr/lib/system/libsystem_platform.dylib+0x1804ad6a4)
# |  #<!-- -->4 0x000000018e68788c (/usr/lib/system/libsystem_pthread.dylib+0x18047388c)
# |  #<!-- -->5 0x000000018e590a3c (/usr/lib/system/libsystem_c.dylib+0x18037ca3c)
# |  #<!-- -->6 0x00000001032a84bc llvm::install_out_of_memory_new_handler() (/Users/oskarwirga/llvm-project/build/bin/opt+0x1025c44bc)
# |  #<!-- -->7 0x00000001033b37c0 llvm::FunctionComparator::cmpMDNode(llvm::MDNode const*, llvm::MDNode const*) const (/Users/oskarwirga/llvm-project/build/bin/opt+0x1026cf7c0)
# |  #<!-- -->8 0x00000001033b4d90 llvm::FunctionComparator::cmpBasicBlocks(llvm::BasicBlock const*, llvm::BasicBlock const*) const (/Users/oskarwirga/llvm-project/build/bin/opt+0x1026d0d90)
# |  #<!-- -->9 0x00000001033b5234 llvm::FunctionComparator::compare() (/Users/oskarwirga/llvm-project/build/bin/opt+0x1026d1234)
# | #<!-- -->10 0x0000000102d6d868 (anonymous namespace)::MergeFunctions::insert(llvm::Function*) (/Users/oskarwirga/llvm-project/build/bin/opt+0x102089868)
# | #<!-- -->11 0x0000000102d6bc0c llvm::MergeFunctionsPass::runOnModule(llvm::Module&amp;) (/Users/oskarwirga/llvm-project/build/bin/opt+0x102087c0c)
# | #<!-- -->12 0x0000000102d6b430 llvm::MergeFunctionsPass::run(llvm::Module&amp;, llvm::AnalysisManager&lt;llvm::Module&gt;&amp;) (/Users/oskarwirga/llvm-project/build/bin/opt+0x102087430)
# | #<!-- -->13 0x0000000102b90558 llvm::PassManager&lt;llvm::Module, llvm::AnalysisManager&lt;llvm::Module&gt;&gt;::run(llvm::Module&amp;, llvm::AnalysisManager&lt;llvm::Module&gt;&amp;) (/Users/oskarwirga/llvm-project/build/bin/opt+0x101eac558)
# | #<!-- -->14 0x0000000103734bc4 llvm::runPassPipeline(llvm::StringRef, llvm::Module&amp;, llvm::TargetMachine*, llvm::TargetLibraryInfoImpl*, llvm::ToolOutputFile*, llvm::ToolOutputFile*, llvm::ToolOutputFile*, llvm::StringRef, llvm::ArrayRef&lt;llvm::PassPlugin&gt;, llvm::ArrayRef&lt;std::__1::function&lt;void (llvm::PassBuilder&amp;)&gt;&gt;, llvm::opt_tool::OutputKind, llvm::opt_tool::VerifierKind, bool, bool, bool, bool, bool, bool, bool, bool) (/Users/oskarwirga/llvm-project/build/bin/opt+0x102a50bc4)
# | #<!-- -->15 0x000000010373cc28 optMain (/Users/oskarwirga/llvm-project/build/bin/opt+0x102a58c28)
# | #<!-- -->16 0x000000018e2e6b98
# `-----------------------------
# error: command failed with exit status: -6
# executed command: /Users/oskarwirga/llvm-project/build/bin/FileCheck /Users/oskarwirga/llvm-project/llvm/test/Transforms/MergeFunc/ptrauth-const-compare.ll
# .---command stderr------------
# | FileCheck error: '&lt;stdin&gt;' is empty.
# | FileCheck command line:  /Users/oskarwirga/llvm-project/build/bin/FileCheck /Users/oskarwirga/llvm-project/llvm/test/Transforms/MergeFunc/ptrauth-const-compare.ll
# `-----------------------------
# error: command failed with exit status: 2

Full diff: https://github.com/llvm/llvm-project/pull/159480.diff

2 Files Affected:

  • (modified) llvm/lib/Transforms/Utils/FunctionComparator.cpp (+13)
  • (added) llvm/test/Transforms/MergeFunc/ptrauth-const-compare.ll (+67)
diff --git a/llvm/lib/Transforms/Utils/FunctionComparator.cpp b/llvm/lib/Transforms/Utils/FunctionComparator.cpp
index 6d4026e8209de..4148ac9d4442d 100644
--- a/llvm/lib/Transforms/Utils/FunctionComparator.cpp
+++ b/llvm/lib/Transforms/Utils/FunctionComparator.cpp
@@ -353,6 +353,19 @@ int FunctionComparator::cmpConstants(const Constant *L,
     return 1;
   if (!L->isNullValue() && R->isNullValue())
     return -1;
+  
+  // Handle authenticated pointer constants produced by ConstantPtrAuth::get.
+  if (auto *PA1 = dyn_cast<ConstantPtrAuth>(L)) {
+    auto *PA2 = dyn_cast<ConstantPtrAuth>(R);
+    if (!PA2)
+      return cmpNumbers(L->getValueID(), R->getValueID());
+
+    if (int Res = cmpConstants(PA1->getPointer(), PA2->getPointer()))
+      return Res;
+    if (int Res = cmpConstants(PA1->getKey(), PA2->getKey()))
+      return Res;
+    return cmpConstants(PA1->getDiscriminator(), PA2->getDiscriminator());
+  }
 
   auto GlobalValueL = const_cast<GlobalValue *>(dyn_cast<GlobalValue>(L));
   auto GlobalValueR = const_cast<GlobalValue *>(dyn_cast<GlobalValue>(R));
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..75ebb834c6219
--- /dev/null
+++ b/llvm/test/Transforms/MergeFunc/ptrauth-const-compare.ll
@@ -0,0 +1,67 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --include-generated-funcs --version 2
+;; Check the case for ConstantPtrAuth to be compared properly in FunctionComparator.cpp
+; RUN: opt -S -passes=mergefunc < %s | FileCheck %s
+target triple = "arm64e-apple-ios14.0.0"
+
+define { ptr, i64 } @"foo"() {
+start:
+  %func = alloca [8 x i8], align 8
+  br i1 false, label %bb5, label %bb9
+
+bb9:                                              ; preds = %bb2, %start
+  %self = load i8, ptr null, align 1
+  br i1 false, label %bb2, label %bb5
+
+bb5:                                              ; preds = %bb9, %start
+  ret { ptr, i64 } zeroinitializer
+
+bb2:                                              ; preds = %bb9
+  call void ptrauth (ptr null, i32 0)(ptr null, i8 0)
+  br label %bb9
+}
+
+define { ptr, i64 } @"bar"() {
+start:
+  %func = alloca [8 x i8], align 8
+  br i1 false, label %bb5, label %bb9
+
+bb9:                                              ; preds = %bb2, %start
+  %self = load i8, ptr null, align 1
+  br i1 false, label %bb2, label %bb5
+
+bb5:                                              ; preds = %bb9, %start
+  ret { ptr, i64 } zeroinitializer
+
+bb2:                                              ; preds = %bb9
+  call void ptrauth (ptr @"baz", i32 0)(ptr null, i8 0)
+  br label %bb9
+}
+
+declare void @"baz"()
+; CHECK-LABEL: define { ptr, i64 } @foo() {
+; CHECK-NEXT:  start:
+; CHECK-NEXT:    [[FUNC:%.*]] = alloca [8 x i8], align 8
+; CHECK-NEXT:    br i1 false, label [[BB5:%.*]], label [[BB9:%.*]]
+; CHECK:       bb9:
+; CHECK-NEXT:    [[SELF:%.*]] = load i8, ptr null, align 1
+; CHECK-NEXT:    br i1 false, label [[BB2:%.*]], label [[BB5]]
+; CHECK:       bb5:
+; CHECK-NEXT:    ret { ptr, i64 } zeroinitializer
+; CHECK:       bb2:
+; CHECK-NEXT:    call void ptrauth (ptr null, i32 0)(ptr null, i8 0)
+; CHECK-NEXT:    br label [[BB9]]
+;
+;
+; CHECK-LABEL: define { ptr, i64 } @bar() {
+; CHECK-NEXT:  start:
+; CHECK-NEXT:    [[FUNC:%.*]] = alloca [8 x i8], align 8
+; CHECK-NEXT:    br i1 false, label [[BB5:%.*]], label [[BB9:%.*]]
+; CHECK:       bb9:
+; CHECK-NEXT:    [[SELF:%.*]] = load i8, ptr null, align 1
+; CHECK-NEXT:    br i1 false, label [[BB2:%.*]], label [[BB5]]
+; CHECK:       bb5:
+; CHECK-NEXT:    ret { ptr, i64 } zeroinitializer
+; CHECK:       bb2:
+; CHECK-NEXT:    call void ptrauth (ptr @baz, i32 0)(ptr null, i8 0)
+; CHECK-NEXT:    br label [[BB9]]
+;

@github-actions
Copy link

github-actions bot commented Sep 17, 2025

✅ With the latest revision this PR passed the C/C++ code formatter.

@ojhunt
Copy link
Contributor

ojhunt commented Oct 2, 2025

@ahmedbougacha I think you're better looking at this one

@atrosinenko
Copy link
Contributor

In a call instruction like this

  call void ptrauth (ptr @baz, i32 0)(ptr null)

a signed pointer to @baz is constructed and then an indirect call is performed via this pointer without "undoing" signing operation first with @llvm.ptrauth.auth or by passing a "ptrauth" call operand bundle. IIRC it is not an UB, but calling a signed-but-neither-authenticated-nor-stripped function pointer is intended to crash by design.

This is probably acceptable in this kind of tests (except for that I'm not sure if calling signed null pointer in f_ptr_null is not an UB due to the original pointer being null), but a simple transformation seems to make this IR look much more commonplace:

  call void @callee(ptr ptrauth (ptr @baz, i32 0))

This way, ptrauth constant provides an argument for the @callee function (which is called directly). Furthermore, since @callee is free to use its argument however it wants, there should not be an UB due to signed null pointer anymore. I'm not familiar with FunctionComparator, but brief testing on my side suggests the tests still behave as expected (opt crashes without the patch and the functions get merged as expected with the patch applied).

@asl asl requested a review from atrosinenko October 21, 2025 21:46
Copy link
Contributor

@atrosinenko atrosinenko left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for all the updates!

The PR description seems to be out of sync with the latest patch, as ptrauth-const-compare.ll has many synthetic test cases now.

@kovdan01
Copy link
Contributor

@oskarwirga Could you please clarify if you want to make some enhancements to your PR prior to merging or if you are happy with merging this "as is"? I think it would be very nice to get this merged relatively soon in order to have the fix included in the upcoming llvm release.

@atrosinenko As far as I can see, your unresolved comments are "feel free to ignore". Is my understanding correct or is there something you find crucial to fix before merging?

@jchlanda
Copy link
Contributor

I've hit the same unreachable when working with rust and pauth calls. While it's not a blocker, as rust targets can opt out of merge functions (merge_functions: MergeFunctions::Disabled), it feels like this PR provides a valid solution.

@oskarwirga
Copy link
Contributor Author

Sorry about the delay everyone! @atrosinenko thank you for your reviews, I've addressed those comments and feel good about where this diff is now.

@kovdan01 Now, I'm ready to merge this :)

@jchlanda

I've hit the same unreachable when working with rust and pauth calls.

I made progress on this earlier this year but very heavy draft (see this discussion for more details: rust-lang/rust#73628 (comment)). If you have something to share, I'd be happy to test/take a look/admire.

Copy link
Contributor

@atrosinenko atrosinenko left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@oskarwirga Thank you for the update! All my comments are addressed now, except for getting rid of numbered @callees (this basically means my original suggestion of returning obscure pairwise-equal numbers turned out not to be as reliably as it should be).

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.
@oskarwirga oskarwirga merged commit 7d2d4f6 into llvm:main Dec 23, 2025
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants