[llvm] Preserve AA metadata on coerced GVN loads (PR #190394)

via llvm-commits llvm-commits at lists.llvm.org
Fri Apr 3 14:39:54 PDT 2026


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-llvm-transforms

Author: Pppp1116 (Pppp1116)

<details>
<summary>Changes</summary>

## Summary

This change preserves scoped alias-analysis metadata when GVN forwards a narrower load from an existing wider load.

## What changed

- merge `!alias.scope`, `!noalias`, and `!noalias_addrspace` metadata from the replaced load into the surviving widened load in GVN's coerced-load path
- keep that AA metadata when dropping other non-type-stable metadata after materializing the replacement value
- add a regression test that checks both the preserved metadata after `gvn` and the dead-store deletion that becomes possible after `gvn,dse`

## Why

GVN already knows how to fold a smaller load into a truncation of a wider dominating load, but in that typed coercion path it dropped scoped AA metadata from the surviving load. That made later default-pipeline passes lose alias facts they could otherwise use.

## Impact

Preserving the scoped AA metadata lets later passes such as DSE prove more memory independence on real code without adding a new flag or pipeline stage.

## Validation

- `build-clangd/bin/opt -S -passes=gvn < llvm/test/Transforms/GVN/coerced-load-aa-metadata.ll | build-clangd/bin/FileCheck llvm/test/Transforms/GVN/coerced-load-aa-metadata.ll --check-prefix=GVN`
- `build-clangd/bin/opt -S -passes='gvn,dse' < llvm/test/Transforms/GVN/coerced-load-aa-metadata.ll | build-clangd/bin/FileCheck llvm/test/Transforms/GVN/coerced-load-aa-metadata.ll --check-prefix=DSE`
- `build-clangd/bin/llvm-lit -sv llvm/test/Transforms/GVN/coerced-load-aa-metadata.ll`


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


2 Files Affected:

- (modified) llvm/lib/Transforms/Scalar/GVN.cpp (+4-3) 
- (added) llvm/test/Transforms/GVN/coerced-load-aa-metadata.ll (+38) 


``````````diff
diff --git a/llvm/lib/Transforms/Scalar/GVN.cpp b/llvm/lib/Transforms/Scalar/GVN.cpp
index 7cab4be169123..7f0ed8cae485b 100644
--- a/llvm/lib/Transforms/Scalar/GVN.cpp
+++ b/llvm/lib/Transforms/Scalar/GVN.cpp
@@ -1165,6 +1165,7 @@ Value *AvailableValue::MaterializeAdjustedValue(LoadInst *Load,
     } else {
       Res = getValueForLoad(CoercedLoad, Offset, LoadTy, InsertPt,
                             Load->getFunction());
+      combineAAMetadata(CoercedLoad, Load);
       // We are adding a new user for this load, for which the original
       // metadata may not hold. Additionally, the new load may have a different
       // size and type, so their metadata cannot be combined in any
@@ -1172,13 +1173,13 @@ Value *AvailableValue::MaterializeAdjustedValue(LoadInst *Load,
       // Drop all metadata that is not known to cause immediate UB on violation,
       // unless the load has !noundef, in which case all metadata violations
       // will be promoted to UB.
-      // TODO: We can combine noalias/alias.scope metadata here, because it is
-      // independent of the load type.
       if (!CoercedLoad->hasMetadata(LLVMContext::MD_noundef))
         CoercedLoad->dropUnknownNonDebugMetadata(
             {LLVMContext::MD_dereferenceable,
              LLVMContext::MD_dereferenceable_or_null,
-             LLVMContext::MD_invariant_load, LLVMContext::MD_invariant_group});
+             LLVMContext::MD_invariant_load, LLVMContext::MD_invariant_group,
+             LLVMContext::MD_alias_scope, LLVMContext::MD_noalias,
+             LLVMContext::MD_noalias_addrspace});
       LLVM_DEBUG(dbgs() << "GVN COERCED NONLOCAL LOAD:\nOffset: " << Offset
                         << "  " << *getCoercedLoadValue() << '\n'
                         << *Res << '\n'
diff --git a/llvm/test/Transforms/GVN/coerced-load-aa-metadata.ll b/llvm/test/Transforms/GVN/coerced-load-aa-metadata.ll
new file mode 100644
index 0000000000000..39976ee8fe4a4
--- /dev/null
+++ b/llvm/test/Transforms/GVN/coerced-load-aa-metadata.ll
@@ -0,0 +1,38 @@
+; RUN: opt -S -passes=gvn < %s | FileCheck %s --check-prefix=GVN
+; RUN: opt -S -passes='gvn,dse' < %s | FileCheck %s --check-prefix=DSE
+
+define i16 @test(ptr %p1, ptr %p2) {
+; GVN-LABEL: define i16 @test(
+; GVN-SAME: ptr [[P1:%.*]], ptr [[P2:%.*]]) {
+; GVN:       entry:
+; GVN-NEXT:    store i8 0, ptr [[P1]], align 1, !noalias [[SCOPE:![0-9]+]]
+; GVN-NEXT:    [[L16:%.*]] = load i16, ptr [[P2]], align 2, !alias.scope [[SCOPE]]
+; GVN-NEXT:    [[L8:%.*]] = trunc i16 [[L16]] to i8
+; GVN-NEXT:    [[COND:%.*]] = icmp eq i8 [[L8]], 7
+; GVN-NEXT:    store i8 1, ptr [[P1]], align 1
+; GVN-NEXT:    [[R:%.*]] = select i1 [[COND]], i16 [[L16]], i16 0
+; GVN-NEXT:    ret i16 [[R]]
+;
+; DSE-LABEL: define i16 @test(
+; DSE-SAME: ptr [[P1:%.*]], ptr [[P2:%.*]]) {
+; DSE:       entry:
+; DSE-NEXT:    [[L16:%.*]] = load i16, ptr [[P2]], align 2, !alias.scope [[SCOPE:![0-9]+]]
+; DSE-NEXT:    [[L8:%.*]] = trunc i16 [[L16]] to i8
+; DSE-NEXT:    [[COND:%.*]] = icmp eq i8 [[L8]], 7
+; DSE-NEXT:    store i8 1, ptr [[P1]], align 1
+; DSE-NEXT:    [[R:%.*]] = select i1 [[COND]], i16 [[L16]], i16 0
+; DSE-NEXT:    ret i16 [[R]]
+;
+entry:
+  store i8 0, ptr %p1, align 1, !noalias !2
+  %l16 = load i16, ptr %p2, align 2, !alias.scope !2
+  %l8 = load i8, ptr %p2, align 1, !alias.scope !2
+  %cond = icmp eq i8 %l8, 7
+  store i8 1, ptr %p1, align 1
+  %r = select i1 %cond, i16 %l16, i16 0
+  ret i16 %r
+}
+
+!0 = !{!0}
+!1 = !{!1, !0}
+!2 = !{!1}

``````````

</details>


https://github.com/llvm/llvm-project/pull/190394


More information about the llvm-commits mailing list