Skip to content

Make DxilValueCache a FunctionPass analysis - #9094

Open
Steven Perron (s-perron) wants to merge 4 commits into
microsoft:mainfrom
s-perron:fix-value-cache-stale-entries
Open

Steven Perron (s-perron) wants to merge 4 commits into
microsoft:mainfrom
s-perron:fix-value-cache-stale-entries

Conversation

@s-perron

Copy link
Copy Markdown
Collaborator

As an ImmutablePass, DxilValueCache was never invalidated. This is not
the intended use of ImmutablePass, since the DxilValueCache analysis can
change as the code changes. The motivating example is Reassociate
rewriting an expression in place:

%b = %a + 1 // %a is provably a constant, so %b is as well.
%c = %d + %b

==>

%b = %d + 1 // %b is no longer a constant, but DxilValueCache still
            // thinks it is.
%c = %b + %a

The stale data in DxilValueCache can lead other transformations to make
mistakes; here DxilRemoveDeadBlocks deleted a live loop exit.

The solution is to change DxilValueCache from an ImmutablePass to a
FunctionPass, modeled after LazyValueInfo and its LazyValueInfoCache.
That way the pass manager invalidates it after any pass that does not
claim to preserve it. Module passes that use it now query it per
function.

ScalarEvolution is modified to use a local DxilValueCache instead. The
loop passes preserve ScalarEvolution but not DxilValueCache, and the
legacy pass manager does not invalidate an analysis when one it depends
on is invalidated. Because ScalarEvolution queries the cache lazily,
after those passes have run, a shared instance could still hold values
computed before they rewrote the function. A cache that lives only for
one exhaustive trip count evaluation avoids that.

As an ImmutablePass, DxilValueCache was never invalidated. This is not
the intended use of ImmutablePass, since the DxilValueCache analysis can
change as the code changes. The motivating example is Reassociate
rewriting an expression in place:

```
%b = %a + 1 // %a is provably a constant, so %b is as well.
%c = %d + %b

==>

%b = %d + 1 // %b is no longer a constant, but DxilValueCache still
            // thinks it is.
%c = %b + %a
```

The stale data in DxilValueCache can lead other transformations to make
mistakes; here DxilRemoveDeadBlocks deleted a live loop exit.

The solution is to change DxilValueCache from an ImmutablePass to a
FunctionPass, modeled after LazyValueInfo and its LazyValueInfoCache.
That way the pass manager invalidates it after any pass that does not
claim to preserve it. Module passes that use it now query it per
function.

ScalarEvolution is modified to use a local DxilValueCache instead. The
loop passes preserve ScalarEvolution but not DxilValueCache, and the
legacy pass manager does not invalidate an analysis when one it depends
on is invalidated. Because ScalarEvolution queries the cache lazily,
after those passes have run, a shared instance could still hold values
computed before they rewrote the function. A cache that lives only for
one exhaustive trip count evaluation avoids that.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

Repeated DxilValueCache requests clear reusable analysis and can cause quadratic work, while the regression does not independently exercise pass-managed cache invalidation.

3 open issues
What changed in this PR

This PR makes DxilValueCache a function-level analysis so transformations invalidate stale results, addressing a miscompile that could remove live loop exits.

Changes:

  • Add cache reset hooks and update module-pass consumers to request analysis per function.
  • Give ScalarEvolution a private cache for each exhaustive trip-count evaluation.
  • Add a regression test for stale values after reassociation.
File Description
tools/​clang/​test/​DXC/​Passes/​DxilRemoveDeadBlocks/​stale-value-cache.ll Tests preservation of live loop exits after reassociation.
lib/​HLSL/​DxilNoOptLegalize.cpp Requests the cache per nonempty function.
lib/​HLSL/​DxilCondenseResources.cpp Requests function-scoped analysis during resource legalization.
lib/​Analysis/​ScalarEvolution.cpp Uses a private cache for exhaustive trip-count evaluation.
lib/​Analysis/​DxilValueCache.cpp Implements function-pass initialization and cache reset behavior.
include/​llvm/​Analysis/​DxilValueCache.h Changes the base class and declares lifecycle hooks.

Review effort: Balanced

Comment thread lib/HLSL/DxilCondenseResources.cpp Resolved
Comment thread lib/Analysis/DxilValueCache.cpp Outdated
Comment on lines +478 to +480
bool DxilValueCache::runOnFunction(Function &F) {
// Values are computed lazily by GetValue, so just drop the previous entries.
ResetAll();

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Chris B (@llvm-beanz) Let me know if I need one.

…ectly

The printer computes the constant value of every instruction, so a test can
build the cache, run a pass that rewrites an instruction in place, and check
that the stale value is not reported afterwards.
Every getAnalysis(F) call from a module pass reruns the on-the-fly pass
manager and empties the cache, so legalize each function's resource arrays
and PHIs with a single query instead of one per alloca and per PHI.
Printing no longer computes values, so the test seeds the cache through
dxil-elim-vector before reassociate rewrites %X in place.
Copilot AI balanced review requested due to automatic review settings November 3, 2026 20:59

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟢 Approval recommended

No active review issues are identified for the function-scoped value-cache change.

1 open issue
2 resolved since last review

Review effort: Balanced

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: New

Development

Successfully merging this pull request may close these issues.

2 participants