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]]
+;

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).

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.
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.

6 participants