Skip to content

Conversation

@peterwaller-arm
Copy link
Member

CHR builds the merged hot-path predicate with IRBuilder::CreateLogicalAnd. That helper is implemented as a select and can constant-fold to a non- Instruction (e.g. i1 true). The pass then attempted to mark the merged condition as having explicitly unknown branch weights when profile data is present, but it unconditionally did cast(MergedCondition), which can crash in release builds.

Guard the metadata update with dyn_cast and pass the containing Function explicitly to avoid calling Instruction::getFunction when the value is not attached yet.

Add a regression test that exercises the constant-folding case.

Crashing stack:

  2.      Running pass "chr" on function "repro_crash"
  #0 0x0000000003be00a4 llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) (bin/opt+0x3be00a4)
  #1 0x0000000003bdd9e8 llvm::sys::RunSignalHandlers() (bin/opt+0x3bdd9e8)
  #2 0x0000000003be1300 SignalHandler(int, siginfo_t*, void*) Signals.cpp:0:0
  #3 0x0000ffffa8e1d840 (linux-vdso.so.1+0x840)
  #4 0x0000000003c815e0 llvm::Instruction::getFunction() const (bin/opt+0x3c815e0)
  #5 0x0000000003dcd35c llvm::setExplicitlyUnknownBranchWeightsIfProfiled(llvm::Instruction&, llvm::StringRef, llvm::Function const*) (bin/opt+0x3dcd35c)
  #6 0x0000000004fb3670 (anonymous namespace)::CHR::addToMergedCondition(bool, llvm::Value*, llvm::Instruction*, (anonymous namespace)::CHRScope*, llvm::IRBuilder<llvm::ConstantFolder, llvm::IRBuilderDefaultInserter>&, llvm::Value*&) ControlHeightReduction.cpp:0:0
  #7 0x0000000004fa7d88 (anonymous namespace)::CHR::run() ControlHeightReduction.cpp:0:0
  #8 0x0000000004fa3618 llvm::ControlHeightReductionPass::run(llvm::Function&, llvm::AnalysisManager<llvm::Function>&) (bin/opt+0x4fa3618)

Tests: opt < llvm/test/Transforms/PGOProfile/chr-unknown-profdata-crash.ll -passes='require,function(chr)' -force-chr -chr-merge-threshold=1 -disable-output

@llvmbot llvmbot added PGO Profile Guided Optimizations llvm:transforms labels Dec 29, 2025
@llvmbot
Copy link
Member

llvmbot commented Dec 29, 2025

@llvm/pr-subscribers-pgo

@llvm/pr-subscribers-llvm-transforms

Author: Peter Waller (peterwaller-arm)

Changes

CHR builds the merged hot-path predicate with IRBuilder::CreateLogicalAnd. That helper is implemented as a select and can constant-fold to a non- Instruction (e.g. i1 true). The pass then attempted to mark the merged condition as having explicitly unknown branch weights when profile data is present, but it unconditionally did cast<Instruction>(MergedCondition), which can crash in release builds.

Guard the metadata update with dyn_cast<Instruction> and pass the containing Function explicitly to avoid calling Instruction::getFunction when the value is not attached yet.

Add a regression test that exercises the constant-folding case.

Crashing stack:

  2.      Running pass "chr" on function "repro_crash"
  #<!-- -->0 0x0000000003be00a4 llvm::sys::PrintStackTrace(llvm::raw_ostream&amp;, int) (bin/opt+0x3be00a4)
  #<!-- -->1 0x0000000003bdd9e8 llvm::sys::RunSignalHandlers() (bin/opt+0x3bdd9e8)
  #<!-- -->2 0x0000000003be1300 SignalHandler(int, siginfo_t*, void*) Signals.cpp:0:0
  #<!-- -->3 0x0000ffffa8e1d840 (linux-vdso.so.1+0x840)
  #<!-- -->4 0x0000000003c815e0 llvm::Instruction::getFunction() const (bin/opt+0x3c815e0)
  #<!-- -->5 0x0000000003dcd35c llvm::setExplicitlyUnknownBranchWeightsIfProfiled(llvm::Instruction&amp;, llvm::StringRef, llvm::Function const*) (bin/opt+0x3dcd35c)
  #<!-- -->6 0x0000000004fb3670 (anonymous namespace)::CHR::addToMergedCondition(bool, llvm::Value*, llvm::Instruction*, (anonymous namespace)::CHRScope*, llvm::IRBuilder&lt;llvm::ConstantFolder, llvm::IRBuilderDefaultInserter&gt;&amp;, llvm::Value*&amp;) ControlHeightReduction.cpp:0:0
  #<!-- -->7 0x0000000004fa7d88 (anonymous namespace)::CHR::run() ControlHeightReduction.cpp:0:0
  #<!-- -->8 0x0000000004fa3618 llvm::ControlHeightReductionPass::run(llvm::Function&amp;, llvm::AnalysisManager&lt;llvm::Function&gt;&amp;) (bin/opt+0x4fa3618)

Tests: opt < llvm/test/Transforms/PGOProfile/chr-unknown-profdata-crash.ll -passes='require<profile-summary>,function(chr)' -force-chr -chr-merge-threshold=1 -disable-output


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

2 Files Affected:

  • (modified) llvm/lib/Transforms/Instrumentation/ControlHeightReduction.cpp (+2-2)
  • (added) llvm/test/Transforms/PGOProfile/chr-unknown-profdata-crash.ll (+47)
diff --git a/llvm/lib/Transforms/Instrumentation/ControlHeightReduction.cpp b/llvm/lib/Transforms/Instrumentation/ControlHeightReduction.cpp
index 726d94b27a7f2..c7b941319f8b9 100644
--- a/llvm/lib/Transforms/Instrumentation/ControlHeightReduction.cpp
+++ b/llvm/lib/Transforms/Instrumentation/ControlHeightReduction.cpp
@@ -1992,8 +1992,8 @@ void CHR::addToMergedCondition(bool IsTrueBiased, Value *Cond,
 
   // Use logical and to avoid propagating poison from later conditions.
   MergedCondition = IRB.CreateLogicalAnd(MergedCondition, Cond);
-  setExplicitlyUnknownBranchWeightsIfProfiled(
-      *cast<Instruction>(MergedCondition), DEBUG_TYPE);
+  if (auto *MergedInst = dyn_cast<Instruction>(MergedCondition))
+    setExplicitlyUnknownBranchWeightsIfProfiled(*MergedInst, DEBUG_TYPE, &F);
 }
 
 void CHR::transformScopes(SmallVectorImpl<CHRScope *> &CHRScopes) {
diff --git a/llvm/test/Transforms/PGOProfile/chr-unknown-profdata-crash.ll b/llvm/test/Transforms/PGOProfile/chr-unknown-profdata-crash.ll
new file mode 100644
index 0000000000000..a28dc0c5287c3
--- /dev/null
+++ b/llvm/test/Transforms/PGOProfile/chr-unknown-profdata-crash.ll
@@ -0,0 +1,47 @@
+; RUN: opt < %s -passes='require<profile-summary>,function(chr)' -force-chr -chr-merge-threshold=1 -disable-output
+
+declare void @foo()
+declare void @bar()
+
+; Regression test for a crash in CHR when setting unknown profdata on the
+; merged condition. IRBuilder::CreateLogicalAnd is implemented as a select and
+; can constant-fold to a non-Instruction value (e.g. `i1 true`). The buggy code
+; assumed it always produced an Instruction and did `cast<Instruction>(V)`,
+; which can segfault in release builds.
+define void @repro_crash() !prof !14 {
+entry:
+  br i1 true, label %then1, label %cont1, !prof !15
+
+then1:
+  call void @foo()
+  br label %cont1
+
+cont1:
+  br i1 true, label %then2, label %exit, !prof !15
+
+then2:
+  call void @bar()
+  br label %exit
+
+exit:
+  ret void
+}
+
+!llvm.module.flags = !{!0}
+!0 = !{i32 1, !"ProfileSummary", !1}
+!1 = !{!2, !3, !4, !5, !6, !7, !8, !9}
+!2 = !{!"ProfileFormat", !"InstrProf"}
+!3 = !{!"TotalCount", i64 10000}
+!4 = !{!"MaxCount", i64 10}
+!5 = !{!"MaxInternalCount", i64 1}
+!6 = !{!"MaxFunctionCount", i64 1000}
+!7 = !{!"NumCounts", i64 1}
+!8 = !{!"NumFunctions", i64 1}
+!9 = !{!"DetailedSummary", !10}
+!10 = !{!11, !12, !13}
+!11 = !{i32 10000, i64 100, i32 1}
+!12 = !{i32 999000, i64 100, i32 1}
+!13 = !{i32 999999, i64 1, i32 2}
+
+!14 = !{!"function_entry_count", i64 100}
+!15 = !{!"branch_weights", i32 1000000, i32 1}

CHR builds the merged hot-path predicate with IRBuilder::CreateLogicalAnd.
That helper is implemented as a select and can constant-fold to a non-
Instruction (e.g. i1 true). The pass then attempted to mark the merged
condition as having explicitly unknown branch weights when profile data is
present, but it unconditionally did cast<Instruction>(MergedCondition),
which can crash in release builds.

Guard the metadata update with dyn_cast<Instruction> and pass the
containing Function explicitly to avoid calling Instruction::getFunction
when the value is not attached yet.

Add a regression test that exercises the constant-folding case.

Crashing stack:

```
  2.      Running pass "chr" on function "repro_crash"
  #0 0x0000000003be00a4 llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) (bin/opt+0x3be00a4)
  llvm#1 0x0000000003bdd9e8 llvm::sys::RunSignalHandlers() (bin/opt+0x3bdd9e8)
  llvm#2 0x0000000003be1300 SignalHandler(int, siginfo_t*, void*) Signals.cpp:0:0
  llvm#3 0x0000ffffa8e1d840 (linux-vdso.so.1+0x840)
  llvm#4 0x0000000003c815e0 llvm::Instruction::getFunction() const (bin/opt+0x3c815e0)
  llvm#5 0x0000000003dcd35c llvm::setExplicitlyUnknownBranchWeightsIfProfiled(llvm::Instruction&, llvm::StringRef, llvm::Function const*) (bin/opt+0x3dcd35c)
  llvm#6 0x0000000004fb3670 (anonymous namespace)::CHR::addToMergedCondition(bool, llvm::Value*, llvm::Instruction*, (anonymous namespace)::CHRScope*, llvm::IRBuilder<llvm::ConstantFolder, llvm::IRBuilderDefaultInserter>&, llvm::Value*&) ControlHeightReduction.cpp:0:0
  llvm#7 0x0000000004fa7d88 (anonymous namespace)::CHR::run() ControlHeightReduction.cpp:0:0
  llvm#8 0x0000000004fa3618 llvm::ControlHeightReductionPass::run(llvm::Function&, llvm::AnalysisManager<llvm::Function>&) (bin/opt+0x4fa3618)
```

Tests: opt < llvm/test/Transforms/PGOProfile/chr-unknown-profdata-crash.ll -passes='require<profile-summary>,function(chr)' -force-chr -chr-merge-threshold=1 -disable-output
@peterwaller-arm
Copy link
Member Author

Force pushed to shrink the test case a little further, confirmed crashing prior to the code update and fixed after.

Copy link
Contributor

@boomanaiden154 boomanaiden154 left a comment

Choose a reason for hiding this comment

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

LGTM. Thanks for the fix. I'm pretty surprised we did not hit this anywhere on our own workloads.

ret void
}

!llvm.module.flags = !{!0}
Copy link
Contributor

Choose a reason for hiding this comment

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

A lot of this metadata looks unnecessary for the reproducer?

Copy link
Member Author

Choose a reason for hiding this comment

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

I have tried to delete various unnecessary-looking pieces but did not quickly discover more I can remove and still preserve the crash on the bad version. llvm-reduce doesn't remove anything, either. If you have ideas of specific things to remove which preserve the crash, please let me know.

@llvm-ci
Copy link
Collaborator

llvm-ci commented Jan 2, 2026

LLVM Buildbot has detected a new failure on builder lldb-aarch64-windows running on linaro-armv8-windows-msvc-05 while building llvm at step 6 "test".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/141/builds/14122

Here is the relevant piece of the build log for the reference
Step 6 (test) failure: build (failure)
...
UNSUPPORTED: lldb-api :: commands/platform/process/list/TestProcessList.py (206 of 2387)
UNSUPPORTED: lldb-api :: commands/platform/sdk/TestPlatformSDK.py (207 of 2387)
PASS: lldb-api :: commands/plugin/TestPlugin.py (208 of 2387)
UNSUPPORTED: lldb-api :: commands/process/attach-resume/TestAttachResume.py (209 of 2387)
PASS: lldb-api :: commands/process/attach/TestProcessAttach.py (210 of 2387)
UNSUPPORTED: lldb-api :: commands/process/attach/attach_denied/TestAttachDenied.py (211 of 2387)
PASS: lldb-api :: commands/process/continue_to_bkpt/TestContinueToBkpts.py (212 of 2387)
XFAIL: lldb-api :: commands/platform/connect/TestPlatformConnect.py (213 of 2387)
UNSUPPORTED: lldb-api :: commands/process/handle/TestProcessHandle.py (214 of 2387)
XFAIL: lldb-api :: commands/process/launch-with-shellexpand/TestLaunchWithShellExpand.py (215 of 2387)
FAIL: lldb-api :: commands/process/detach-resumes/TestDetachResumes.py (216 of 2387)
******************** TEST 'lldb-api :: commands/process/detach-resumes/TestDetachResumes.py' FAILED ********************
Script:
--
C:/Users/tcwg/scoop/apps/python/current/python.exe C:/Users/tcwg/llvm-worker/lldb-aarch64-windows/llvm-project/lldb\test\API\dotest.py -u CXXFLAGS -u CFLAGS --env LLVM_LIBS_DIR=C:/Users/tcwg/llvm-worker/lldb-aarch64-windows/build/./lib --env LLVM_INCLUDE_DIR=C:/Users/tcwg/llvm-worker/lldb-aarch64-windows/build/include --env LLVM_TOOLS_DIR=C:/Users/tcwg/llvm-worker/lldb-aarch64-windows/build/./bin --arch aarch64 --build-dir C:/Users/tcwg/llvm-worker/lldb-aarch64-windows/build/lldb-test-build.noindex --lldb-module-cache-dir C:/Users/tcwg/llvm-worker/lldb-aarch64-windows/build/lldb-test-build.noindex/module-cache-lldb\lldb-api --clang-module-cache-dir C:/Users/tcwg/llvm-worker/lldb-aarch64-windows/build/lldb-test-build.noindex/module-cache-clang\lldb-api --executable C:/Users/tcwg/llvm-worker/lldb-aarch64-windows/build/./bin/lldb.exe --compiler C:/Users/tcwg/llvm-worker/lldb-aarch64-windows/build/./bin/clang.exe --dsymutil C:/Users/tcwg/llvm-worker/lldb-aarch64-windows/build/./bin/dsymutil.exe --make C:/Users/tcwg/scoop/shims/make.exe --llvm-tools-dir C:/Users/tcwg/llvm-worker/lldb-aarch64-windows/build/./bin --lldb-obj-root C:/Users/tcwg/llvm-worker/lldb-aarch64-windows/build/tools/lldb --lldb-libs-dir C:/Users/tcwg/llvm-worker/lldb-aarch64-windows/build/./lib --cmake-build-type Release --skip-category=watchpoint C:\Users\tcwg\llvm-worker\lldb-aarch64-windows\llvm-project\lldb\test\API\commands\process\detach-resumes -p TestDetachResumes.py
--
Exit Code: 1

Command Output (stdout):
--
lldb version 22.0.0git (https://github.com/llvm/llvm-project.git revision 14c98bc5a2c1d0b05a3cc27cf40c9c2a89503893)
  clang revision 14c98bc5a2c1d0b05a3cc27cf40c9c2a89503893
  llvm revision 14c98bc5a2c1d0b05a3cc27cf40c9c2a89503893
Skipping the following test categories: watchpoint, libc++, libstdcxx, dwo, dsym, gmodules, debugserver, objc, fork, pexpect


--
Command Output (stderr):
--
ls: cannot access 'C:\Users\tcwg\llvm-worker\lldb-aarch64-windows\build\lldb-test-build.noindex\commands\process\detach-resumes\TestDetachResumes.test_detach_resumes\exit_file_1767351688': No such file or directory
ls: cannot access 'C:\Users\tcwg\llvm-worker\lldb-aarch64-windows\build\lldb-test-build.noindex\commands\process\detach-resumes\TestDetachResumes.test_detach_resumes\exit_file_1767351688': No such file or directory
ls: cannot access 'C:\Users\tcwg\llvm-worker\lldb-aarch64-windows\build\lldb-test-build.noindex\commands\process\detach-resumes\TestDetachResumes.test_detach_resumes\exit_file_1767351688': No such file or directory
ls: cannot access 'C:\Users\tcwg\llvm-worker\lldb-aarch64-windows\build\lldb-test-build.noindex\commands\process\detach-resumes\TestDetachResumes.test_detach_resumes\exit_file_1767351688': No such file or directory
ls: cannot access 'C:\Users\tcwg\llvm-worker\lldb-aarch64-windows\build\lldb-test-build.noindex\commands\process\detach-resumes\TestDetachResumes.test_detach_resumes\exit_file_1767351688': No such file or directory
ls: cannot access 'C:\Users\tcwg\llvm-worker\lldb-aarch64-windows\build\lldb-test-build.noindex\commands\process\detach-resumes\TestDetachResumes.test_detach_resumes\exit_file_1767351688': No such file or directory
FAIL: LLDB (C:\Users\tcwg\llvm-worker\lldb-aarch64-windows\build\bin\clang.exe-aarch64) :: test_detach_resumes (TestDetachResumes.DetachResumesTestCase.test_detach_resumes)

======================================================================

FAIL: test_detach_resumes (TestDetachResumes.DetachResumesTestCase.test_detach_resumes)

----------------------------------------------------------------------

Traceback (most recent call last):

  File "C:\Users\tcwg\llvm-worker\lldb-aarch64-windows\llvm-project\lldb\packages\Python\lldbsuite\test\decorators.py", line 156, in wrapper

    return func(*args, **kwargs)


@peterwaller-arm peterwaller-arm deleted the chr-fix branch January 5, 2026 10:50
mahesh-attarde pushed a commit to mahesh-attarde/llvm-project that referenced this pull request Jan 6, 2026
CHR builds the merged hot-path predicate with
IRBuilder::CreateLogicalAnd. That helper is implemented as a select and
can constant-fold to a non- Instruction (e.g. i1 true). The pass then
attempted to mark the merged condition as having explicitly unknown
branch weights when profile data is present, but it unconditionally did
cast<Instruction>(MergedCondition), which can crash in release builds.

Guard the metadata update with dyn_cast<Instruction> and pass the
containing Function explicitly to avoid calling Instruction::getFunction
when the value is not attached yet.

Add a regression test that exercises the constant-folding case.

Crashing stack:

```
  2.      Running pass "chr" on function "repro_crash"
  #0 0x0000000003be00a4 llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) (bin/opt+0x3be00a4)
  #1 0x0000000003bdd9e8 llvm::sys::RunSignalHandlers() (bin/opt+0x3bdd9e8)
  #2 0x0000000003be1300 SignalHandler(int, siginfo_t*, void*) Signals.cpp:0:0
  #3 0x0000ffffa8e1d840 (linux-vdso.so.1+0x840)
  #4 0x0000000003c815e0 llvm::Instruction::getFunction() const (bin/opt+0x3c815e0)
  llvm#5 0x0000000003dcd35c llvm::setExplicitlyUnknownBranchWeightsIfProfiled(llvm::Instruction&, llvm::StringRef, llvm::Function const*) (bin/opt+0x3dcd35c)
  llvm#6 0x0000000004fb3670 (anonymous namespace)::CHR::addToMergedCondition(bool, llvm::Value*, llvm::Instruction*, (anonymous namespace)::CHRScope*, llvm::IRBuilder<llvm::ConstantFolder, llvm::IRBuilderDefaultInserter>&, llvm::Value*&) ControlHeightReduction.cpp:0:0
  llvm#7 0x0000000004fa7d88 (anonymous namespace)::CHR::run() ControlHeightReduction.cpp:0:0
  llvm#8 0x0000000004fa3618 llvm::ControlHeightReductionPass::run(llvm::Function&, llvm::AnalysisManager<llvm::Function>&) (bin/opt+0x4fa3618)
```

Tests: opt <
llvm/test/Transforms/PGOProfile/chr-unknown-profdata-crash.ll
-passes='require<profile-summary>,function(chr)' -force-chr
-chr-merge-threshold=1 -disable-output
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

llvm:transforms PGO Profile Guided Optimizations

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[clang] Crash at -O3 with PGO: Assertion `isa<To>(Val) && "cast<Ty>() argument of incompatible type!"' failed

4 participants