[llvm] 52cf94a - [AMDGPU] Drop !noundef when widening sub-DWORD constant loads (#201085)
via llvm-commits
llvm-commits at lists.llvm.org
Wed Jun 3 06:20:29 PDT 2026
Author: Arseniy Obolenskiy
Date: 2026-06-03T15:20:23+02:00
New Revision: 52cf94a82eabfe70d417c17a3ab38736ded4938b
URL: https://github.com/llvm/llvm-project/commit/52cf94a82eabfe70d417c17a3ab38736ded4938b
DIFF: https://github.com/llvm/llvm-project/commit/52cf94a82eabfe70d417c17a3ab38736ded4938b.diff
LOG: [AMDGPU] Drop !noundef when widening sub-DWORD constant loads (#201085)
The widened i32 load reads bytes outside the original sub-DWORD load, so
new op cannot claim !noundef
Added:
Modified:
llvm/lib/Target/AMDGPU/AMDGPUCodeGenPrepare.cpp
llvm/lib/Target/AMDGPU/AMDGPULateCodeGenPrepare.cpp
llvm/lib/Target/AMDGPU/AMDGPUMemoryUtils.cpp
llvm/lib/Target/AMDGPU/AMDGPUMemoryUtils.h
llvm/test/CodeGen/AMDGPU/amdgpu-late-codegenprepare.ll
llvm/test/CodeGen/AMDGPU/widen_extending_scalar_loads.ll
Removed:
################################################################################
diff --git a/llvm/lib/Target/AMDGPU/AMDGPUCodeGenPrepare.cpp b/llvm/lib/Target/AMDGPU/AMDGPUCodeGenPrepare.cpp
index 1bcfc1da3b84e..4995cd2d1956b 100644
--- a/llvm/lib/Target/AMDGPU/AMDGPUCodeGenPrepare.cpp
+++ b/llvm/lib/Target/AMDGPU/AMDGPUCodeGenPrepare.cpp
@@ -13,6 +13,7 @@
//===----------------------------------------------------------------------===//
#include "AMDGPU.h"
+#include "AMDGPUMemoryUtils.h"
#include "AMDGPUTargetMachine.h"
#include "SIModeRegisterDefaults.h"
#include "llvm/ADT/SetVector.h"
@@ -1561,17 +1562,15 @@ bool AMDGPUCodeGenPrepareImpl::visitLoadInst(LoadInst &I) {
Type *I32Ty = Builder.getInt32Ty();
LoadInst *WidenLoad = Builder.CreateLoad(I32Ty, I.getPointerOperand());
- WidenLoad->copyMetadata(I);
+ AMDGPU::copyMetadataForWidenedLoad(*WidenLoad, I);
- // If we have range metadata, we need to convert the type, and not make
+ // The widened load reads the original bytes in the low bits, so a !range
+ // lower bound still holds. Convert it to the new type and don't make
// assumptions about the high bits.
- if (auto *Range = WidenLoad->getMetadata(LLVMContext::MD_range)) {
- ConstantInt *Lower =
- mdconst::extract<ConstantInt>(Range->getOperand(0));
+ if (auto *Range = I.getMetadata(LLVMContext::MD_range)) {
+ ConstantInt *Lower = mdconst::extract<ConstantInt>(Range->getOperand(0));
- if (Lower->isNullValue()) {
- WidenLoad->setMetadata(LLVMContext::MD_range, nullptr);
- } else {
+ if (!Lower->isNullValue()) {
Metadata *LowAndHigh[] = {
ConstantAsMetadata::get(ConstantInt::get(I32Ty, Lower->getValue().zext(32))),
// Don't make assumptions about the high bits.
diff --git a/llvm/lib/Target/AMDGPU/AMDGPULateCodeGenPrepare.cpp b/llvm/lib/Target/AMDGPU/AMDGPULateCodeGenPrepare.cpp
index 3844e68be8e8e..b5c2b366c8e7b 100644
--- a/llvm/lib/Target/AMDGPU/AMDGPULateCodeGenPrepare.cpp
+++ b/llvm/lib/Target/AMDGPU/AMDGPULateCodeGenPrepare.cpp
@@ -13,6 +13,7 @@
//===----------------------------------------------------------------------===//
#include "AMDGPU.h"
+#include "AMDGPUMemoryUtils.h"
#include "AMDGPUTargetMachine.h"
#include "llvm/Analysis/AssumptionCache.h"
#include "llvm/Analysis/UniformityAnalysis.h"
@@ -536,8 +537,7 @@ bool AMDGPULateCodeGenPrepare::visitLoadInst(LoadInst &LI) {
Offset - Adjust);
LoadInst *NewLd = IRB.CreateAlignedLoad(IRB.getInt32Ty(), NewPtr, Align(4));
- NewLd->copyMetadata(LI);
- NewLd->setMetadata(LLVMContext::MD_range, nullptr);
+ AMDGPU::copyMetadataForWidenedLoad(*NewLd, LI);
unsigned ShAmt = Adjust * 8;
Value *NewVal = IRB.CreateBitCast(
diff --git a/llvm/lib/Target/AMDGPU/AMDGPUMemoryUtils.cpp b/llvm/lib/Target/AMDGPU/AMDGPUMemoryUtils.cpp
index 9fbb19df1ba53..14592298c907f 100644
--- a/llvm/lib/Target/AMDGPU/AMDGPUMemoryUtils.cpp
+++ b/llvm/lib/Target/AMDGPU/AMDGPUMemoryUtils.cpp
@@ -17,6 +17,7 @@
#include "llvm/IR/Instructions.h"
#include "llvm/IR/IntrinsicInst.h"
#include "llvm/IR/IntrinsicsAMDGPU.h"
+#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/ReplaceConstant.h"
#define DEBUG_TYPE "amdgpu-memory-utils"
@@ -30,6 +31,22 @@ Align getAlign(const DataLayout &DL, const GlobalVariable *GV) {
GV->getValueType());
}
+void copyMetadataForWidenedLoad(LoadInst &Dest, const LoadInst &Source) {
+ SmallVector<std::pair<unsigned, MDNode *>, 8> MD;
+ Source.getAllMetadata(MD);
+ for (const auto [ID, N] : MD) {
+ switch (ID) {
+ case LLVMContext::MD_dbg:
+ case LLVMContext::MD_invariant_load:
+ case LLVMContext::MD_nontemporal:
+ Dest.setMetadata(ID, N);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
// Returns the target extension type of a global variable,
// which can only be a TargetExtType, an array or single-element struct of it,
// or their nesting combination.
diff --git a/llvm/lib/Target/AMDGPU/AMDGPUMemoryUtils.h b/llvm/lib/Target/AMDGPU/AMDGPUMemoryUtils.h
index 8868b93440768..518f9066e5164 100644
--- a/llvm/lib/Target/AMDGPU/AMDGPUMemoryUtils.h
+++ b/llvm/lib/Target/AMDGPU/AMDGPUMemoryUtils.h
@@ -35,6 +35,11 @@ using VariableFunctionMap = DenseMap<GlobalVariable *, DenseSet<Function *>>;
Align getAlign(const DataLayout &DL, const GlobalVariable *GV);
+// Copy metadata onto a load widened to read a superset of Source's bytes. Only
+// value-independent metadata is copied; metadata describing the loaded value
+// (!range, !noundef, !nofpclass, !tbaa, ...) is dropped.
+void copyMetadataForWidenedLoad(LoadInst &Dest, const LoadInst &Source);
+
// If GV is a named-barrier return its type. Otherwise return nullptr.
TargetExtType *isNamedBarrier(const GlobalVariable &GV);
diff --git a/llvm/test/CodeGen/AMDGPU/amdgpu-late-codegenprepare.ll b/llvm/test/CodeGen/AMDGPU/amdgpu-late-codegenprepare.ll
index 3e232bb1914f8..e816492ea381e 100644
--- a/llvm/test/CodeGen/AMDGPU/amdgpu-late-codegenprepare.ll
+++ b/llvm/test/CodeGen/AMDGPU/amdgpu-late-codegenprepare.ll
@@ -149,3 +149,62 @@ bb7:
%i8 = phi <4 x i8> [ zeroinitializer, %bb5 ], [ zeroinitializer, %bb3 ]
br label %bb1
}
+
+; The widened i32 load reads bytes outside the original i8 load, so !noundef
+; must not be carried over for GFX9.
+define amdgpu_kernel void @no_widen_noundef(ptr addrspace(4) align 4 %p, ptr addrspace(1) %out) {
+; GFX9-LABEL: @no_widen_noundef(
+; GFX9-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr addrspace(4) [[P:%.*]], i64 0
+; GFX9-NEXT: [[TMP2:%.*]] = load i32, ptr addrspace(4) [[TMP1]], align 4
+; GFX9-NEXT: [[TMP3:%.*]] = lshr i32 [[TMP2]], 8
+; GFX9-NEXT: [[TMP4:%.*]] = trunc i32 [[TMP3]] to i8
+; GFX9-NEXT: [[VZ:%.*]] = zext i8 [[TMP4]] to i32
+; GFX9-NEXT: store i32 [[VZ]], ptr addrspace(1) [[OUT:%.*]], align 4
+; GFX9-NEXT: ret void
+;
+; GFX12-LABEL: @no_widen_noundef(
+; GFX12-NEXT: [[P1:%.*]] = getelementptr inbounds i8, ptr addrspace(4) [[P:%.*]], i64 1
+; GFX12-NEXT: [[V:%.*]] = load i8, ptr addrspace(4) [[P1]], align 1, !noundef [[META0:![0-9]+]]
+; GFX12-NEXT: [[VZ:%.*]] = zext i8 [[V]] to i32
+; GFX12-NEXT: store i32 [[VZ]], ptr addrspace(1) [[OUT:%.*]], align 4
+; GFX12-NEXT: ret void
+;
+ %p1 = getelementptr inbounds i8, ptr addrspace(4) %p, i64 1
+ %v = load i8, ptr addrspace(4) %p1, align 1, !noundef !0
+ %vz = zext i8 %v to i32
+ store i32 %vz, ptr addrspace(1) %out, align 4
+ ret void
+}
+
+; The widened i32 load reads bytes outside the original f16 load, so !nofpclass
+; must not be carried over for GFX9.
+define amdgpu_kernel void @no_widen_nofpclass(ptr addrspace(4) align 4 %p, ptr addrspace(1) %out) {
+; GFX9-LABEL: @no_widen_nofpclass(
+; GFX9-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr addrspace(4) [[P:%.*]], i64 0
+; GFX9-NEXT: [[TMP2:%.*]] = load i32, ptr addrspace(4) [[TMP1]], align 4
+; GFX9-NEXT: [[TMP3:%.*]] = lshr i32 [[TMP2]], 16
+; GFX9-NEXT: [[TMP4:%.*]] = trunc i32 [[TMP3]] to i16
+; GFX9-NEXT: [[TMP5:%.*]] = bitcast i16 [[TMP4]] to half
+; GFX9-NEXT: [[VB:%.*]] = bitcast half [[TMP5]] to i16
+; GFX9-NEXT: [[VZ:%.*]] = zext i16 [[VB]] to i32
+; GFX9-NEXT: store i32 [[VZ]], ptr addrspace(1) [[OUT:%.*]], align 4
+; GFX9-NEXT: ret void
+;
+; GFX12-LABEL: @no_widen_nofpclass(
+; GFX12-NEXT: [[P1:%.*]] = getelementptr inbounds i8, ptr addrspace(4) [[P:%.*]], i64 2
+; GFX12-NEXT: [[V:%.*]] = load half, ptr addrspace(4) [[P1]], align 2, !nofpclass [[META1:![0-9]+]]
+; GFX12-NEXT: [[VB:%.*]] = bitcast half [[V]] to i16
+; GFX12-NEXT: [[VZ:%.*]] = zext i16 [[VB]] to i32
+; GFX12-NEXT: store i32 [[VZ]], ptr addrspace(1) [[OUT:%.*]], align 4
+; GFX12-NEXT: ret void
+;
+ %p1 = getelementptr inbounds i8, ptr addrspace(4) %p, i64 2
+ %v = load half, ptr addrspace(4) %p1, align 2, !nofpclass !1
+ %vb = bitcast half %v to i16
+ %vz = zext i16 %vb to i32
+ store i32 %vz, ptr addrspace(1) %out, align 4
+ ret void
+}
+
+!0 = !{}
+!1 = !{i32 3}
diff --git a/llvm/test/CodeGen/AMDGPU/widen_extending_scalar_loads.ll b/llvm/test/CodeGen/AMDGPU/widen_extending_scalar_loads.ll
index 24c1875159f67..95b6a1a31871d 100644
--- a/llvm/test/CodeGen/AMDGPU/widen_extending_scalar_loads.ll
+++ b/llvm/test/CodeGen/AMDGPU/widen_extending_scalar_loads.ll
@@ -317,6 +317,22 @@ define amdgpu_kernel void @constant_load_i16_align4_invariant(ptr addrspace(1) %
ret void
}
+; The widened i32 load reads bytes outside the original i16 load, so !noundef
+; must not be carried over.
+define amdgpu_kernel void @constant_load_i16_align4_noundef(ptr addrspace(1) %out, ptr addrspace(4) %in) #0 {
+; OPT-LABEL: @constant_load_i16_align4_noundef(
+; OPT-NEXT: [[TMP1:%.*]] = load i32, ptr addrspace(4) [[IN:%.*]], align 4
+; OPT-NEXT: [[TMP2:%.*]] = trunc i32 [[TMP1]] to i16
+; OPT-NEXT: [[EXT:%.*]] = sext i16 [[TMP2]] to i32
+; OPT-NEXT: store i32 [[EXT]], ptr addrspace(1) [[OUT:%.*]], align 4
+; OPT-NEXT: ret void
+;
+ %ld = load i16, ptr addrspace(4) %in, align 4, !noundef !6
+ %ext = sext i16 %ld to i32
+ store i32 %ext, ptr addrspace(1) %out
+ ret void
+}
+
attributes #0 = { nounwind }
; OPT: !0 = !{i32 5, i32 0}
More information about the llvm-commits
mailing list