From c641da16ed556d4e6ceea7363715b3135bdd23c4 Mon Sep 17 00:00:00 2001 From: Icohedron Date: Thu, 10 Jul 2025 19:02:48 +0000 Subject: [PATCH 1/5] Add a GEP to load/stores on array allocas --- llvm/lib/Target/DirectX/DXILLegalizePass.cpp | 43 +++++++++++++++++++ .../legalize-load-store-array-alloca.ll | 23 ++++++++++ 2 files changed, 66 insertions(+) create mode 100644 llvm/test/CodeGen/DirectX/legalize-load-store-array-alloca.ll diff --git a/llvm/lib/Target/DirectX/DXILLegalizePass.cpp b/llvm/lib/Target/DirectX/DXILLegalizePass.cpp index 76a46c7a2b760..724ee57c05a39 100644 --- a/llvm/lib/Target/DirectX/DXILLegalizePass.cpp +++ b/llvm/lib/Target/DirectX/DXILLegalizePass.cpp @@ -562,6 +562,48 @@ legalizeGetHighLowi64Bytes(Instruction &I, } } +static void legalizeLoadStoreOnArrayAllocas( + Instruction &I, SmallVectorImpl &ToRemove, + DenseMap &) { + + Value *PtrOp; + [[maybe_unused]] Type *LoadStoreTy; + if (auto *LI = dyn_cast(&I)) { + PtrOp = LI->getPointerOperand(); + LoadStoreTy = LI->getType(); + } else if (auto *SI = dyn_cast(&I)) { + PtrOp = SI->getPointerOperand(); + LoadStoreTy = SI->getValueOperand()->getType(); + } else + return; + + assert(LoadStoreTy->isSingleValueType() && + "Expected load/store type to be a single-valued type"); + + auto *AllocaPtrOp = dyn_cast(PtrOp); + if (!AllocaPtrOp) + return; + + Type *Ty = AllocaPtrOp->getAllocatedType(); + if (!isa(Ty)) return; + assert(!isa(Ty->getArrayElementType()) && + "Expected allocated type of AllocaInst to be a flat ArrayType"); + + IRBuilder<> Builder(&I); + Value *Zero = Builder.getInt32(0); + Value *GEP = Builder.CreateInBoundsGEP(Ty, AllocaPtrOp, {Zero, Zero}); + + Value *NewLoadStore = nullptr; + if (auto *LI = dyn_cast(&I)) + NewLoadStore = Builder.CreateLoad(LI->getType(), GEP, LI->getName()); + else if (auto *SI = dyn_cast(&I)) + NewLoadStore = + Builder.CreateStore(SI->getValueOperand(), GEP, SI->isVolatile()); + + ToRemove.push_back(&I); + I.replaceAllUsesWith(NewLoadStore); +} + namespace { class DXILLegalizationPipeline { @@ -605,6 +647,7 @@ class DXILLegalizationPipeline { LegalizationPipeline[Stage1].push_back(legalizeMemCpy); LegalizationPipeline[Stage1].push_back(removeMemSet); LegalizationPipeline[Stage1].push_back(updateFnegToFsub); + LegalizationPipeline[Stage1].push_back(legalizeLoadStoreOnArrayAllocas); // Note: legalizeGetHighLowi64Bytes and // downcastI64toI32InsertExtractElements both modify extractelement, so they // must run staggered stages. legalizeGetHighLowi64Bytes runs first b\c it diff --git a/llvm/test/CodeGen/DirectX/legalize-load-store-array-alloca.ll b/llvm/test/CodeGen/DirectX/legalize-load-store-array-alloca.ll new file mode 100644 index 0000000000000..703f569f4dfab --- /dev/null +++ b/llvm/test/CodeGen/DirectX/legalize-load-store-array-alloca.ll @@ -0,0 +1,23 @@ +; RUN: opt -S -passes='dxil-legalize' -mtriple=dxil-pc-shadermodel6.3-library %s | FileCheck %s + +define float @load() { +; CHECK-LABEL: define float @load +; CHECK-NEXT: [[ALLOCA:%.*]] = alloca [2 x float], align 4 +; CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds [2 x float], ptr [[ALLOCA]], i32 0, i32 0 +; CHECK-NEXT: [[LOAD:%.*]] = load float, ptr [[GEP]], align 4 +; CHECK-NEXT: ret float [[LOAD]] + %a = alloca [2 x float], align 4 + %b = load float, ptr %a, align 4 + ret float %b +} + +define void @store() { +; CHECK-LABEL: define void @store +; CHECK-NEXT: [[ALLOCA:%.*]] = alloca [3 x i32], align 4 +; CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds [3 x i32], ptr [[ALLOCA]], i32 0, i32 0 +; CHECK-NEXT: store i32 0, ptr [[GEP]], align 4 +; CHECK-NEXT: ret void + %a = alloca [3 x i32], align 4 + store i32 0, ptr %a, align 4 + ret void +} From 7e7900a23ca5c6172f6359e5e309fa11ab3f6272 Mon Sep 17 00:00:00 2001 From: Icohedron Date: Thu, 10 Jul 2025 21:35:50 +0000 Subject: [PATCH 2/5] Apply clang-format --- llvm/lib/Target/DirectX/DXILLegalizePass.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/llvm/lib/Target/DirectX/DXILLegalizePass.cpp b/llvm/lib/Target/DirectX/DXILLegalizePass.cpp index 724ee57c05a39..0d55ac1230d8f 100644 --- a/llvm/lib/Target/DirectX/DXILLegalizePass.cpp +++ b/llvm/lib/Target/DirectX/DXILLegalizePass.cpp @@ -562,9 +562,10 @@ legalizeGetHighLowi64Bytes(Instruction &I, } } -static void legalizeLoadStoreOnArrayAllocas( - Instruction &I, SmallVectorImpl &ToRemove, - DenseMap &) { +static void +legalizeLoadStoreOnArrayAllocas(Instruction &I, + SmallVectorImpl &ToRemove, + DenseMap &) { Value *PtrOp; [[maybe_unused]] Type *LoadStoreTy; @@ -585,7 +586,8 @@ static void legalizeLoadStoreOnArrayAllocas( return; Type *Ty = AllocaPtrOp->getAllocatedType(); - if (!isa(Ty)) return; + if (!isa(Ty)) + return; assert(!isa(Ty->getArrayElementType()) && "Expected allocated type of AllocaInst to be a flat ArrayType"); From 76d88298fb6bbb472023adc892d9e88391a76718 Mon Sep 17 00:00:00 2001 From: Icohedron Date: Thu, 10 Jul 2025 22:28:24 +0000 Subject: [PATCH 3/5] Move pass to stage 2 to not conflict with i8 legalization --- llvm/lib/Target/DirectX/DXILLegalizePass.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/llvm/lib/Target/DirectX/DXILLegalizePass.cpp b/llvm/lib/Target/DirectX/DXILLegalizePass.cpp index 0d55ac1230d8f..eb2b118af17da 100644 --- a/llvm/lib/Target/DirectX/DXILLegalizePass.cpp +++ b/llvm/lib/Target/DirectX/DXILLegalizePass.cpp @@ -649,7 +649,6 @@ class DXILLegalizationPipeline { LegalizationPipeline[Stage1].push_back(legalizeMemCpy); LegalizationPipeline[Stage1].push_back(removeMemSet); LegalizationPipeline[Stage1].push_back(updateFnegToFsub); - LegalizationPipeline[Stage1].push_back(legalizeLoadStoreOnArrayAllocas); // Note: legalizeGetHighLowi64Bytes and // downcastI64toI32InsertExtractElements both modify extractelement, so they // must run staggered stages. legalizeGetHighLowi64Bytes runs first b\c it @@ -657,6 +656,7 @@ class DXILLegalizationPipeline { // downcastI64toI32InsertExtractElements needs to handle. LegalizationPipeline[Stage2].push_back( downcastI64toI32InsertExtractElements); + LegalizationPipeline[Stage2].push_back(legalizeLoadStoreOnArrayAllocas); } }; From 64bb53341ef40925e96da34c2dde5d79d6afe2dc Mon Sep 17 00:00:00 2001 From: Icohedron Date: Thu, 10 Jul 2025 22:29:06 +0000 Subject: [PATCH 4/5] Add tests with i8, and add nuw flag to GEP --- llvm/lib/Target/DirectX/DXILLegalizePass.cpp | 7 ++++--- llvm/test/CodeGen/DirectX/legalize-i8.ll | 10 ++++++---- .../DirectX/legalize-load-store-array-alloca.ll | 4 ++-- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/llvm/lib/Target/DirectX/DXILLegalizePass.cpp b/llvm/lib/Target/DirectX/DXILLegalizePass.cpp index eb2b118af17da..7e08eec1411d5 100644 --- a/llvm/lib/Target/DirectX/DXILLegalizePass.cpp +++ b/llvm/lib/Target/DirectX/DXILLegalizePass.cpp @@ -98,9 +98,9 @@ static void fixI8UseChain(Instruction &I, ElementType = AI->getAllocatedType(); if (auto *GEP = dyn_cast(NewOperands[0])) { ElementType = GEP->getSourceElementType(); - if (ElementType->isArrayTy()) - ElementType = ElementType->getArrayElementType(); } + if (ElementType->isArrayTy()) + ElementType = ElementType->getArrayElementType(); LoadInst *NewLoad = Builder.CreateLoad(ElementType, NewOperands[0]); ReplacedValues[Load] = NewLoad; ToRemove.push_back(Load); @@ -593,7 +593,8 @@ legalizeLoadStoreOnArrayAllocas(Instruction &I, IRBuilder<> Builder(&I); Value *Zero = Builder.getInt32(0); - Value *GEP = Builder.CreateInBoundsGEP(Ty, AllocaPtrOp, {Zero, Zero}); + Value *GEP = Builder.CreateGEP(Ty, AllocaPtrOp, {Zero, Zero}, "", + GEPNoWrapFlags::all()); Value *NewLoadStore = nullptr; if (auto *LI = dyn_cast(&I)) diff --git a/llvm/test/CodeGen/DirectX/legalize-i8.ll b/llvm/test/CodeGen/DirectX/legalize-i8.ll index f8aa2c5ecd932..7eb47ba661f4c 100644 --- a/llvm/test/CodeGen/DirectX/legalize-i8.ll +++ b/llvm/test/CodeGen/DirectX/legalize-i8.ll @@ -127,10 +127,9 @@ define i32 @i8_geps_index0() { ; CHECK: [[LOAD:%.*]] = load i32, ptr [[GEP]], align 4 ; CHECK-NEXT: ret i32 [[LOAD]] %1 = alloca [2 x i32], align 8 - %2 = getelementptr inbounds nuw i8, ptr %1, i32 0 - %3 = load i8, ptr %2 - %4 = sext i8 %3 to i32 - ret i32 %4 + %2 = load i8, ptr %1 + %3 = sext i8 %2 to i32 + ret i32 %3 } define i32 @i8_geps_index1() { @@ -149,11 +148,14 @@ define i32 @i8_geps_index1() { define i32 @i8_gep_store() { ; CHECK-LABEL: define i32 @i8_gep_store( ; CHECK-NEXT: [[ALLOCA:%.*]] = alloca [2 x i32], align 8 + ; CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds nuw [2 x i32], ptr [[ALLOCA]], i32 0, i32 0 + ; CHECK-NEXT: store i32 0, ptr [[GEP]], align 4 ; CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds nuw [2 x i32], ptr [[ALLOCA]], i32 0, i32 1 ; CHECK-NEXT: store i32 1, ptr [[GEP]], align 4 ; CHECK: [[LOAD:%.*]] = load i32, ptr [[GEP]], align 4 ; CHECK-NEXT: ret i32 [[LOAD]] %1 = alloca [2 x i32], align 8 + store i8 0, ptr %1 %2 = getelementptr inbounds nuw i8, ptr %1, i32 4 store i8 1, ptr %2 %3 = load i8, ptr %2 diff --git a/llvm/test/CodeGen/DirectX/legalize-load-store-array-alloca.ll b/llvm/test/CodeGen/DirectX/legalize-load-store-array-alloca.ll index 703f569f4dfab..b25b3de901d91 100644 --- a/llvm/test/CodeGen/DirectX/legalize-load-store-array-alloca.ll +++ b/llvm/test/CodeGen/DirectX/legalize-load-store-array-alloca.ll @@ -3,7 +3,7 @@ define float @load() { ; CHECK-LABEL: define float @load ; CHECK-NEXT: [[ALLOCA:%.*]] = alloca [2 x float], align 4 -; CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds [2 x float], ptr [[ALLOCA]], i32 0, i32 0 +; CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds nuw [2 x float], ptr [[ALLOCA]], i32 0, i32 0 ; CHECK-NEXT: [[LOAD:%.*]] = load float, ptr [[GEP]], align 4 ; CHECK-NEXT: ret float [[LOAD]] %a = alloca [2 x float], align 4 @@ -14,7 +14,7 @@ define float @load() { define void @store() { ; CHECK-LABEL: define void @store ; CHECK-NEXT: [[ALLOCA:%.*]] = alloca [3 x i32], align 4 -; CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds [3 x i32], ptr [[ALLOCA]], i32 0, i32 0 +; CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds nuw [3 x i32], ptr [[ALLOCA]], i32 0, i32 0 ; CHECK-NEXT: store i32 0, ptr [[GEP]], align 4 ; CHECK-NEXT: ret void %a = alloca [3 x i32], align 4 From 9ae1593fb06519e3b22df14462400dc7a3b8b805 Mon Sep 17 00:00:00 2001 From: Deric Cheung Date: Mon, 14 Jul 2025 23:20:28 +0000 Subject: [PATCH 5/5] Set operand instead of replacing instruction --- llvm/lib/Target/DirectX/DXILLegalizePass.cpp | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/llvm/lib/Target/DirectX/DXILLegalizePass.cpp b/llvm/lib/Target/DirectX/DXILLegalizePass.cpp index 7e08eec1411d5..6d7647f9abb56 100644 --- a/llvm/lib/Target/DirectX/DXILLegalizePass.cpp +++ b/llvm/lib/Target/DirectX/DXILLegalizePass.cpp @@ -568,12 +568,15 @@ legalizeLoadStoreOnArrayAllocas(Instruction &I, DenseMap &) { Value *PtrOp; + unsigned PtrOpIndex; [[maybe_unused]] Type *LoadStoreTy; if (auto *LI = dyn_cast(&I)) { PtrOp = LI->getPointerOperand(); + PtrOpIndex = LI->getPointerOperandIndex(); LoadStoreTy = LI->getType(); } else if (auto *SI = dyn_cast(&I)) { PtrOp = SI->getPointerOperand(); + PtrOpIndex = SI->getPointerOperandIndex(); LoadStoreTy = SI->getValueOperand()->getType(); } else return; @@ -595,16 +598,7 @@ legalizeLoadStoreOnArrayAllocas(Instruction &I, Value *Zero = Builder.getInt32(0); Value *GEP = Builder.CreateGEP(Ty, AllocaPtrOp, {Zero, Zero}, "", GEPNoWrapFlags::all()); - - Value *NewLoadStore = nullptr; - if (auto *LI = dyn_cast(&I)) - NewLoadStore = Builder.CreateLoad(LI->getType(), GEP, LI->getName()); - else if (auto *SI = dyn_cast(&I)) - NewLoadStore = - Builder.CreateStore(SI->getValueOperand(), GEP, SI->isVolatile()); - - ToRemove.push_back(&I); - I.replaceAllUsesWith(NewLoadStore); + I.setOperand(PtrOpIndex, GEP); } namespace {