[llvm] [LoopVersioningLICM] Do not let llvm.pseudoprobe block loop versioning (PR #209684)

chandan singh via llvm-commits llvm-commits at lists.llvm.org
Sun Jul 19 23:03:55 PDT 2026


https://github.com/chandankds updated https://github.com/llvm/llvm-project/pull/209684

>From 942118e6980c8ef3759404ed1b45588fb487b9e0 Mon Sep 17 00:00:00 2001
From: chandankds <chandankds at gmail.com>
Date: Thu, 16 Jul 2026 12:38:38 +0000
Subject: [PATCH] [LoopVersioningLICM] Do not let llvm.pseudoprobe block loop
 versioning

LoopVersioningLICM::instructionSafeForVersioning() rejects any call that is
not proven to not access memory. llvm.pseudoprobe is declared
IntrInaccessibleMemOnly (so the optimizer will not delete or sink it), so
AA->doesNotAccessMemory() returns false and the probe is treated as an unsafe
call site. This disables loop-versioning LICM for essentially every hot loop
in a sample-based / CSSPGO profile-guided build (in the presence of
-fpseudo-probe-for-profiling), since a pseudo probe is inserted on every basic block.

Pseudo probes are pure profiling placeholders with no observable effect on the
loop's memory accesses, so skip them in the call-safety check.

Also added a willreturn check to the call-safety test, so a call that may not return must not be versioned around.

RFC: https://discourse.llvm.org/t/csspgo-unblocking-pseudo-probe-safe-optimizations/90946
---
 .../Transforms/Scalar/LoopVersioningLICM.cpp  | 13 +++-
 .../LoopVersioningLICM/pseudoprobe.ll         | 74 +++++++++++++++++++
 2 files changed, 86 insertions(+), 1 deletion(-)
 create mode 100644 llvm/test/Transforms/LoopVersioningLICM/pseudoprobe.ll

diff --git a/llvm/lib/Transforms/Scalar/LoopVersioningLICM.cpp b/llvm/lib/Transforms/Scalar/LoopVersioningLICM.cpp
index 3aed643ee8065..8b0c0eb15818d 100644
--- a/llvm/lib/Transforms/Scalar/LoopVersioningLICM.cpp
+++ b/llvm/lib/Transforms/Scalar/LoopVersioningLICM.cpp
@@ -307,11 +307,22 @@ bool LoopVersioningLICM::instructionSafeForVersioning(Instruction *I) {
       LLVM_DEBUG(dbgs() << "    Convergent call site found.\n");
       return false;
     }
+    if (!Call->willReturn()) {
+      LLVM_DEBUG(dbgs() << "    Call site that may not return found.\n");
+      return false;
+    }
 
-    if (!AA->doesNotAccessMemory(Call)) {
+    // A call that only accesses inaccessible memory cannot alias any pointer
+    // accessed in the loop, so it has no observable effect on the loop's memory
+    // accesses and is safe for versioning. This covers llvm.pseudoprobe (used
+    // for sample-based profiling under -fpseudo-probe-for-profiling) as well as
+    // other inaccessible-memory intrinsics such as llvm.sideeffect.
+    if (Call->mayThrow() ||
+        !AA->getMemoryEffects(Call).onlyAccessesInaccessibleMem()) {
       LLVM_DEBUG(dbgs() << "    Unsafe call site found.\n");
       return false;
     }
+    return true;
   }
 
   // Avoid loops with possiblity of throw
diff --git a/llvm/test/Transforms/LoopVersioningLICM/pseudoprobe.ll b/llvm/test/Transforms/LoopVersioningLICM/pseudoprobe.ll
new file mode 100644
index 0000000000000..cb9e617f0e4d7
--- /dev/null
+++ b/llvm/test/Transforms/LoopVersioningLICM/pseudoprobe.ll
@@ -0,0 +1,74 @@
+; RUN: opt %s -passes='loop(loop-versioning-licm)' -S | FileCheck %s
+;
+; LoopVersioningLICM must not refuse to version a loop solely because of a call
+; that only accesses inaccessible memory. Such a call cannot alias any pointer
+; accessed in the loop, so it is safe for versioning. This notably covers
+; llvm.pseudoprobe, which is inserted on every block under sample-based
+; profiling (in the presence of -fpseudo-probe-for-profiling), as well as other
+; inaccessible-memory intrinsics such as llvm.sideeffect.
+
+; A loop containing an llvm.pseudoprobe intrinsic is still versioned.
+; CHECK-LABEL: @test_pseudoprobe_lvlicm(
+; CHECK: lver.check
+define double @test_pseudoprobe_lvlicm(ptr %x, ptr %y, i32 %n) {
+entry:
+  %cmp = icmp sgt i32 %n, 0
+  br i1 %cmp, label %ph, label %exit
+
+ph:                                               ; preds = %entry
+  br label %body
+
+body:                                             ; preds = %body, %ph
+  %i = phi i32 [ 0, %ph ], [ %inext, %body ]
+  %sum = phi double [ 0.000000e+00, %ph ], [ %sumnext, %body ]
+  %yidx = getelementptr inbounds double, ptr %y, i32 %i
+  %yv = load double, ptr %yidx, align 8
+  %add = fadd double %yv, 1.000000e+00
+  %xidx = getelementptr inbounds double, ptr %x, i32 %i
+  store double %add, ptr %xidx, align 8
+  %x0 = load double, ptr %x, align 8              ; loop-invariant load
+  %sumnext = fadd double %sum, %x0
+  call void @llvm.pseudoprobe(i64 1234, i64 1, i32 0, i64 -1)
+  %inext = add nuw nsw i32 %i, 1
+  %exitcond = icmp eq i32 %inext, %n
+  br i1 %exitcond, label %exit, label %body
+
+exit:                                             ; preds = %body, %entry
+  %sumlcssa = phi double [ 0.000000e+00, %entry ], [ %sumnext, %body ]
+  ret double %sumlcssa
+}
+
+; The same holds for any call that only accesses inaccessible memory, e.g.
+; llvm.sideeffect.
+; CHECK-LABEL: @test_sideeffect_lvlicm(
+; CHECK: lver.check
+define double @test_sideeffect_lvlicm(ptr %x, ptr %y, i32 %n) {
+entry:
+  %cmp = icmp sgt i32 %n, 0
+  br i1 %cmp, label %ph, label %exit
+
+ph:                                               ; preds = %entry
+  br label %body
+
+body:                                             ; preds = %body, %ph
+  %i = phi i32 [ 0, %ph ], [ %inext, %body ]
+  %sum = phi double [ 0.000000e+00, %ph ], [ %sumnext, %body ]
+  %yidx = getelementptr inbounds double, ptr %y, i32 %i
+  %yv = load double, ptr %yidx, align 8
+  %add = fadd double %yv, 1.000000e+00
+  %xidx = getelementptr inbounds double, ptr %x, i32 %i
+  store double %add, ptr %xidx, align 8
+  %x0 = load double, ptr %x, align 8              ; loop-invariant load
+  %sumnext = fadd double %sum, %x0
+  call void @llvm.sideeffect()
+  %inext = add nuw nsw i32 %i, 1
+  %exitcond = icmp eq i32 %inext, %n
+  br i1 %exitcond, label %exit, label %body
+
+exit:                                             ; preds = %body, %entry
+  %sumlcssa = phi double [ 0.000000e+00, %entry ], [ %sumnext, %body ]
+  ret double %sumlcssa
+}
+
+declare void @llvm.pseudoprobe(i64, i64, i32, i64)
+declare void @llvm.sideeffect()



More information about the llvm-commits mailing list