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

chandan singh via llvm-commits llvm-commits at lists.llvm.org
Thu Jul 16 05:41:49 PDT 2026


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

>From 3efa14315ca0d9926c98b0de7a229341392b2809 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 (i.e. anything that is not readnone).
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.

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. Relax the call-safety check to allow such calls (that
return and do not throw) rather than requiring them to be fully readnone. This
covers llvm.pseudoprobe as well as other inaccessible-memory intrinsics such as
llvm.sideeffect.

While here, also add the missing willreturn check to the call-safety test:
versioning clones the loop and relies on every instruction being executed the
same number of times, 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  | 14 ++++
 .../LoopVersioningLICM/pseudoprobe.ll         | 74 +++++++++++++++++++
 2 files changed, 88 insertions(+)
 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..2fb2982604912 100644
--- a/llvm/lib/Transforms/Scalar/LoopVersioningLICM.cpp
+++ b/llvm/lib/Transforms/Scalar/LoopVersioningLICM.cpp
@@ -308,6 +308,20 @@ bool LoopVersioningLICM::instructionSafeForVersioning(Instruction *I) {
       return false;
     }
 
+    // A call that may not return is not
+    // safe to version around.
+    if (!Call->willReturn()) {
+      LLVM_DEBUG(dbgs() << "    Call site that may not return found.\n");
+      return false;
+    }
+
+    // Calls that only access inaccessible memory cannot alias loop memory, so
+    // they require no runtime check. This also covers llvm.pseudoprobe, which
+    // is used for sample-based profiling.
+    if (!I->mayThrow() &&
+        AA->getMemoryEffects(Call).onlyAccessesInaccessibleMem())
+      return true;
+
     if (!AA->doesNotAccessMemory(Call)) {
       LLVM_DEBUG(dbgs() << "    Unsafe call site found.\n");
       return false;
diff --git a/llvm/test/Transforms/LoopVersioningLICM/pseudoprobe.ll b/llvm/test/Transforms/LoopVersioningLICM/pseudoprobe.ll
new file mode 100644
index 0000000000000..b6da10cc7ecbf
--- /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 (-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(
+; CHECK: lver.check
+define double @test_pseudoprobe(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(
+; CHECK: lver.check
+define double @test_sideeffect(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