unlock unlimited code runs for $79, limited time →
NOSSLETTER.techLIVE
back to OSSpedia
memory-managementerror-handling

[LoopVectorize] Clear stale CycleAnalysis after vectorizing a loop

Project: llvm/llvm-project | Language: C++ | Concepts: memory-management, error-handling | Difficulty: senior
Link: https://github.com/llvm/llvm-project/pull/215237

The problem

After vectorizing a loop in LLVM's LoopVectorizePass, the cached CycleAnalysis can hold pointers to blocks that have been deleted. If BlockFrequencyInfo (BFI) is requested for a subsequent loop, it may use this stale CycleInfo and crash. This happens especially when another pass (like Dead Store Elimination, DSE) has previously cached CycleAnalysis.

Why it's non-trivial

A naive fix might just clear or ignore the CycleAnalysis cache globally, but this could be inefficient or cause unnecessary recomputation. The solution needs to ensure correctness (no stale pointers) without causing undue compile-time overhead or breaking analysis dependencies.

The approach

1. Store the FunctionAnalysisManager for later use:

+  FunctionAnalysisManager *FAM = nullptr;

This adds a member to keep a pointer to the FunctionAnalysisManager, so it can be accessed after vectorizing a loop.

2. Include CycleAnalysis header so we can reference it:

+#include "llvm/Analysis/CycleAnalysis.h"

This allows the code to refer to CycleAnalysis directly.

3. After vectorizing a loop, clear the cached CycleAnalysis if it exists:

+      // If CycleAnalysis was cached by a prior pass (e.g. DSE), it now holds
+      // stale pointers to blocks that may have been deleted during
+      // vectorization. Clear it so that BlockFrequencyAnalysis (if requested
+      // for a later loop) recomputes it fresh.
+      if (FAM->getCachedResult<CycleAnalysis>(F))
+        FAM->clearAnalysis<CycleAnalysis>(F);

This checks if CycleAnalysis is cached for the current function and, if so, clears it. This prevents later requests for BFI from using stale analysis results.

4. Store the FunctionAnalysisManager pointer at the start of the pass:

+  FAM = &AM;

This sets the FAM member so it can be used later in the pass.

5. Add a regression test: A new test file (bfi-stale-crash.ll) is added to ensure that vectorizing multiple loops no longer crashes when BFI is requested after vectorization and CycleAnalysis has been cached by a prior pass.

From the review thread

aengelke: Can we alternatively explicitly invalidate CycleAnalysis in such cases?

kartcq: I have tried this approach in latest patchset
Please let me know your thoughts on the patch
Pushed the same to my other perf branch for compile time tracking as well.

CC - @fhahn @aengelke


fhahn: ```suggestion if (FAM->getCachedResult<CycleAnalysis>(F))


Can FAM ever be nullptr?

kartcq: I am not sure. But will apply this change to be safe.
Thanks for the review @fhahn

Takeaways

  • When a transformation pass modifies the IR, any cached analysis that holds pointers to IR objects may become invalid and must be explicitly invalidated or cleared.
  • It's important to check for and clear stale analysis results after mutating passes to prevent use-after-free bugs or crashes in subsequent passes.
  • Always consider the dependencies and side effects of analysis caching when designing or maintaining compiler passes, especially in multi-pass pipelines.