Skip to content

Commit d47c126

Browse files
authored
[DirectX] Lower llvm.lifetime.* intrinsics to stores when DXIL version is lower than 1.6 (#147432)
Fixes #147394 References DXC for the implementation logic: https://github.com/microsoft/DirectXShaderCompiler/blob/d751c827ed3b61e87fdf57d0f424cb2d7af30cd0/lib/HLSL/DxilPreparePasses.cpp#L693-L699 If DXIL Version < 1.6 then replace lifetime intrinsics with stores - For validator version >= 1.6, store an undef - For validator version < 1.6, store zeros else keep the lifetime intrinsics in the DXIL. After this PR, the number of DML shaders failing validation due to #146974 is reduced from 157 to 50.
1 parent 03a2723 commit d47c126

File tree

3 files changed

+92
-16
lines changed

3 files changed

+92
-16
lines changed

llvm/lib/Target/DirectX/DXILOpLowering.cpp

Lines changed: 55 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,13 @@ class OpLowerer {
3939
DXILOpBuilder OpBuilder;
4040
DXILResourceMap &DRM;
4141
DXILResourceTypeMap &DRTM;
42+
const ModuleMetadataInfo &MMDI;
4243
SmallVector<CallInst *> CleanupCasts;
4344

4445
public:
45-
OpLowerer(Module &M, DXILResourceMap &DRM, DXILResourceTypeMap &DRTM)
46-
: M(M), OpBuilder(M), DRM(DRM), DRTM(DRTM) {}
46+
OpLowerer(Module &M, DXILResourceMap &DRM, DXILResourceTypeMap &DRTM,
47+
const ModuleMetadataInfo &MMDI)
48+
: M(M), OpBuilder(M), DRM(DRM), DRTM(DRTM), MMDI(MMDI) {}
4749

4850
/// Replace every call to \c F using \c ReplaceCall, and then erase \c F. If
4951
/// there is an error replacing a call, we emit a diagnostic and return true.
@@ -316,8 +318,7 @@ class OpLowerer {
316318
/// model and taking into account binding information from
317319
/// DXILResourceAnalysis.
318320
bool lowerHandleFromBinding(Function &F) {
319-
const Triple &TT = M.getTargetTriple();
320-
if (TT.getDXILVersion() < VersionTuple(1, 6))
321+
if (MMDI.DXILVersion < VersionTuple(1, 6))
321322
return lowerToCreateHandle(F);
322323
return lowerToBindAndAnnotateHandle(F);
323324
}
@@ -486,8 +487,6 @@ class OpLowerer {
486487
}
487488

488489
[[nodiscard]] bool lowerRawBufferLoad(Function &F) {
489-
const Triple &TT = M.getTargetTriple();
490-
VersionTuple DXILVersion = TT.getDXILVersion();
491490
const DataLayout &DL = F.getDataLayout();
492491
IRBuilder<> &IRB = OpBuilder.getIRB();
493492
Type *Int8Ty = IRB.getInt8Ty();
@@ -511,7 +510,7 @@ class OpLowerer {
511510
ConstantInt::get(Int32Ty, DL.getPrefTypeAlign(ScalarTy).value());
512511

513512
Expected<CallInst *> OpCall =
514-
DXILVersion >= VersionTuple(1, 2)
513+
MMDI.DXILVersion >= VersionTuple(1, 2)
515514
? OpBuilder.tryCreateOp(OpCode::RawBufferLoad,
516515
{Handle, Index0, Index1, Mask, Align},
517516
CI->getName(), NewRetTy)
@@ -586,8 +585,6 @@ class OpLowerer {
586585
}
587586

588587
[[nodiscard]] bool lowerBufferStore(Function &F, bool IsRaw) {
589-
const Triple &TT = M.getTargetTriple();
590-
VersionTuple DXILVersion = TT.getDXILVersion();
591588
const DataLayout &DL = F.getDataLayout();
592589
IRBuilder<> &IRB = OpBuilder.getIRB();
593590
Type *Int8Ty = IRB.getInt8Ty();
@@ -654,7 +651,7 @@ class OpLowerer {
654651
SmallVector<Value *, 9> Args{
655652
Handle, Index0, Index1, DataElements[0],
656653
DataElements[1], DataElements[2], DataElements[3], Mask};
657-
if (IsRaw && DXILVersion >= VersionTuple(1, 2)) {
654+
if (IsRaw && MMDI.DXILVersion >= VersionTuple(1, 2)) {
658655
Op = OpCode::RawBufferStore;
659656
// RawBufferStore requires the alignment
660657
Args.push_back(
@@ -745,6 +742,37 @@ class OpLowerer {
745742
});
746743
}
747744

745+
[[nodiscard]] bool lowerLifetimeIntrinsic(Function &F) {
746+
IRBuilder<> &IRB = OpBuilder.getIRB();
747+
return replaceFunction(F, [&](CallInst *CI) -> Error {
748+
IRB.SetInsertPoint(CI);
749+
Value *Ptr = CI->getArgOperand(1);
750+
assert(Ptr->getType()->isPointerTy() &&
751+
"Expected operand of lifetime intrinsic to be a pointer");
752+
753+
auto ZeroOrUndef = [&](Type *Ty) {
754+
return MMDI.ValidatorVersion < VersionTuple(1, 6)
755+
? Constant::getNullValue(Ty)
756+
: UndefValue::get(Ty);
757+
};
758+
759+
Value *Val = nullptr;
760+
if (auto *GV = dyn_cast<GlobalVariable>(Ptr)) {
761+
if (GV->hasInitializer() || GV->isExternallyInitialized())
762+
return Error::success();
763+
Val = ZeroOrUndef(GV->getValueType());
764+
} else if (auto *AI = dyn_cast<AllocaInst>(Ptr))
765+
Val = ZeroOrUndef(AI->getAllocatedType());
766+
767+
assert(Val && "Expected operand of lifetime intrinsic to be a global "
768+
"variable or alloca instruction");
769+
IRB.CreateStore(Val, Ptr, false);
770+
771+
CI->eraseFromParent();
772+
return Error::success();
773+
});
774+
}
775+
748776
[[nodiscard]] bool lowerIsFPClass(Function &F) {
749777
IRBuilder<> &IRB = OpBuilder.getIRB();
750778
Type *RetTy = IRB.getInt1Ty();
@@ -803,8 +831,6 @@ class OpLowerer {
803831
case Intrinsic::dx_resource_casthandle:
804832
// NOTE: llvm.dbg.value is supported as is in DXIL.
805833
case Intrinsic::dbg_value:
806-
case Intrinsic::lifetime_start:
807-
case Intrinsic::lifetime_end:
808834
case Intrinsic::not_intrinsic:
809835
if (F.use_empty())
810836
F.eraseFromParent();
@@ -855,6 +881,17 @@ class OpLowerer {
855881
case Intrinsic::ctpop:
856882
HasErrors |= lowerCtpopToCountBits(F);
857883
break;
884+
case Intrinsic::lifetime_start:
885+
case Intrinsic::lifetime_end:
886+
if (F.use_empty())
887+
F.eraseFromParent();
888+
else {
889+
if (MMDI.DXILVersion < VersionTuple(1, 6))
890+
HasErrors |= lowerLifetimeIntrinsic(F);
891+
else
892+
continue;
893+
}
894+
break;
858895
case Intrinsic::is_fpclass:
859896
HasErrors |= lowerIsFPClass(F);
860897
break;
@@ -872,8 +909,9 @@ class OpLowerer {
872909
PreservedAnalyses DXILOpLowering::run(Module &M, ModuleAnalysisManager &MAM) {
873910
DXILResourceMap &DRM = MAM.getResult<DXILResourceAnalysis>(M);
874911
DXILResourceTypeMap &DRTM = MAM.getResult<DXILResourceTypeAnalysis>(M);
912+
const ModuleMetadataInfo MMDI = MAM.getResult<DXILMetadataAnalysis>(M);
875913

876-
bool MadeChanges = OpLowerer(M, DRM, DRTM).lowerIntrinsics();
914+
const bool MadeChanges = OpLowerer(M, DRM, DRTM, MMDI).lowerIntrinsics();
877915
if (!MadeChanges)
878916
return PreservedAnalyses::all();
879917
PreservedAnalyses PA;
@@ -891,8 +929,10 @@ class DXILOpLoweringLegacy : public ModulePass {
891929
getAnalysis<DXILResourceWrapperPass>().getResourceMap();
892930
DXILResourceTypeMap &DRTM =
893931
getAnalysis<DXILResourceTypeWrapperPass>().getResourceTypeMap();
932+
const ModuleMetadataInfo MMDI =
933+
getAnalysis<DXILMetadataAnalysisWrapperPass>().getModuleMetadata();
894934

895-
return OpLowerer(M, DRM, DRTM).lowerIntrinsics();
935+
return OpLowerer(M, DRM, DRTM, MMDI).lowerIntrinsics();
896936
}
897937
StringRef getPassName() const override { return "DXIL Op Lowering"; }
898938
DXILOpLoweringLegacy() : ModulePass(ID) {}
@@ -901,6 +941,7 @@ class DXILOpLoweringLegacy : public ModulePass {
901941
void getAnalysisUsage(llvm::AnalysisUsage &AU) const override {
902942
AU.addRequired<DXILResourceTypeWrapperPass>();
903943
AU.addRequired<DXILResourceWrapperPass>();
944+
AU.addRequired<DXILMetadataAnalysisWrapperPass>();
904945
AU.addPreserved<DXILResourceWrapperPass>();
905946
AU.addPreserved<DXILMetadataAnalysisWrapperPass>();
906947
AU.addPreserved<ShaderFlagsAnalysisWrapper>();

llvm/test/CodeGen/DirectX/legalize-lifetimes.ll renamed to llvm/test/CodeGen/DirectX/legalize-lifetimes-valver-1.5.ll

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@
33
; CHECK-LABEL: define void @test_legal_lifetime() {
44
; CHECK-NEXT: [[ACCUM_I_FLAT:%.*]] = alloca [1 x i32], align 4
55
; CHECK-NEXT: [[GEP:%.*]] = getelementptr i32, ptr [[ACCUM_I_FLAT]], i32 0
6-
; CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 4, ptr nonnull [[ACCUM_I_FLAT]])
6+
; CHECK-NEXT: store [1 x i32] zeroinitializer, ptr [[ACCUM_I_FLAT]], align 4
77
; CHECK-NEXT: store i32 0, ptr [[GEP]], align 4
8-
; CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 4, ptr nonnull [[ACCUM_I_FLAT]])
8+
; CHECK-NEXT: store [1 x i32] zeroinitializer, ptr [[ACCUM_I_FLAT]], align 4
99
; CHECK-NEXT: ret void
1010
;
1111
define void @test_legal_lifetime() {
@@ -16,3 +16,7 @@ define void @test_legal_lifetime() {
1616
call void @llvm.lifetime.end.p0(i64 4, ptr nonnull %accum.i.flat)
1717
ret void
1818
}
19+
20+
; Set the validator version to 1.5
21+
!dx.valver = !{!0}
22+
!0 = !{i32 1, i32 5}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
; RUN: opt -S -passes='dxil-op-lower' -mtriple=dxil-pc-shadermodel6.3-library %s | FileCheck %s --check-prefixes=CHECK,CHECK-SM63
2+
; RUN: opt -S -passes='dxil-op-lower' -mtriple=dxil-pc-shadermodel6.6-library %s | FileCheck %s --check-prefixes=CHECK,CHECK-SM66
3+
4+
; CHECK-LABEL: define void @test_legal_lifetime() {
5+
;
6+
; CHECK-SM63-NEXT: [[ACCUM_I_FLAT:%.*]] = alloca [1 x i32], align 4
7+
; CHECK-SM63-NEXT: [[GEP:%.*]] = getelementptr i32, ptr [[ACCUM_I_FLAT]], i32 0
8+
; CHECK-SM63-NEXT: store [1 x i32] undef, ptr [[ACCUM_I_FLAT]], align 4
9+
; CHECK-SM63-NEXT: store i32 0, ptr [[GEP]], align 4
10+
; CHECK-SM63-NEXT: store [1 x i32] undef, ptr [[ACCUM_I_FLAT]], align 4
11+
;
12+
; CHECK-SM66-NEXT: [[ACCUM_I_FLAT:%.*]] = alloca [1 x i32], align 4
13+
; CHECK-SM66-NEXT: [[GEP:%.*]] = getelementptr i32, ptr [[ACCUM_I_FLAT]], i32 0
14+
; CHECK-SM66-NEXT: call void @llvm.lifetime.start.p0(i64 4, ptr nonnull [[ACCUM_I_FLAT]])
15+
; CHECK-SM66-NEXT: store i32 0, ptr [[GEP]], align 4
16+
; CHECK-SM66-NEXT: call void @llvm.lifetime.end.p0(i64 4, ptr nonnull [[ACCUM_I_FLAT]])
17+
;
18+
; CHECK-NEXT: ret void
19+
;
20+
define void @test_legal_lifetime() {
21+
%accum.i.flat = alloca [1 x i32], align 4
22+
%gep = getelementptr i32, ptr %accum.i.flat, i32 0
23+
call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %accum.i.flat)
24+
store i32 0, ptr %gep, align 4
25+
call void @llvm.lifetime.end.p0(i64 4, ptr nonnull %accum.i.flat)
26+
ret void
27+
}
28+
29+
; Set the validator version to 1.6
30+
!dx.valver = !{!0}
31+
!0 = !{i32 1, i32 6}

0 commit comments

Comments
 (0)