-
Notifications
You must be signed in to change notification settings - Fork 14.6k
[DirectX] Legalize lifetime intrinsics for DXIL #148003
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
This commit legalizes lifetime intrinsics for DXIL by - Making lifetime intrinsics an exception to Int64Ops shader flag analysis - Adding a bitcast for the lifetime intrinsics' pointer operand in dxil-prepare to ensure it gets cast to an i8* - Making the DXIL bitcode writer write the base/demangled name of lifetime intrinsics to the symbol table
@llvm/pr-subscribers-backend-directx Author: Deric C. (Icohedron) ChangesFixes #147395 This PR legalizes lifetime intrinsics for DXIL by
Full diff: https://github.com/llvm/llvm-project/pull/148003.diff 6 Files Affected:
diff --git a/llvm/lib/Target/DirectX/DXILPrepare.cpp b/llvm/lib/Target/DirectX/DXILPrepare.cpp
index c8866bfefdfc5..d3a8c0bb995c9 100644
--- a/llvm/lib/Target/DirectX/DXILPrepare.cpp
+++ b/llvm/lib/Target/DirectX/DXILPrepare.cpp
@@ -239,6 +239,13 @@ class DXILPrepareModule : public ModulePass {
for (size_t Idx = 0, End = F.arg_size(); Idx < End; ++Idx)
F.removeParamAttrs(Idx, AttrMask);
+ // Match FnAttrs of lifetime intrinsics in LLVM 3.7
+ if (F.isIntrinsic())
+ switch (F.getIntrinsicID())
+ case Intrinsic::lifetime_start:
+ case Intrinsic::lifetime_end:
+ F.removeFnAttr(Attribute::Memory);
+
for (auto &BB : F) {
IRBuilder<> Builder(&BB);
for (auto &I : make_early_inc_range(BB)) {
@@ -247,7 +254,7 @@ class DXILPrepareModule : public ModulePass {
// Emtting NoOp bitcast instructions allows the ValueEnumerator to be
// unmodified as it reserves instruction IDs during contruction.
- if (auto LI = dyn_cast<LoadInst>(&I)) {
+ if (auto *LI = dyn_cast<LoadInst>(&I)) {
if (Value *NoOpBitcast = maybeGenerateBitcast(
Builder, PointerTypes, I, LI->getPointerOperand(),
LI->getType())) {
@@ -257,7 +264,7 @@ class DXILPrepareModule : public ModulePass {
}
continue;
}
- if (auto SI = dyn_cast<StoreInst>(&I)) {
+ if (auto *SI = dyn_cast<StoreInst>(&I)) {
if (Value *NoOpBitcast = maybeGenerateBitcast(
Builder, PointerTypes, I, SI->getPointerOperand(),
SI->getValueOperand()->getType())) {
@@ -268,7 +275,7 @@ class DXILPrepareModule : public ModulePass {
}
continue;
}
- if (auto GEP = dyn_cast<GetElementPtrInst>(&I)) {
+ if (auto *GEP = dyn_cast<GetElementPtrInst>(&I)) {
if (Value *NoOpBitcast = maybeGenerateBitcast(
Builder, PointerTypes, I, GEP->getPointerOperand(),
GEP->getSourceElementType()))
@@ -280,6 +287,17 @@ class DXILPrepareModule : public ModulePass {
CB->removeRetAttrs(AttrMask);
for (size_t Idx = 0, End = CB->arg_size(); Idx < End; ++Idx)
CB->removeParamAttrs(Idx, AttrMask);
+ // LLVM 3.7 Lifetime intrinics require an i8* pointer operand, so we
+ // insert a bitcast here to ensure that is the case
+ if (isa<LifetimeIntrinsic>(CB)) {
+ Value *PtrOperand = CB->getArgOperand(1);
+ Builder.SetInsertPoint(CB);
+ PointerType *PtrTy = cast<PointerType>(PtrOperand->getType());
+ Value *NoOpBitcast = Builder.Insert(
+ CastInst::Create(Instruction::BitCast, PtrOperand,
+ Builder.getPtrTy(PtrTy->getAddressSpace())));
+ CB->setArgOperand(1, NoOpBitcast);
+ }
continue;
}
}
diff --git a/llvm/lib/Target/DirectX/DXILShaderFlags.cpp b/llvm/lib/Target/DirectX/DXILShaderFlags.cpp
index bd3349d2e18c5..eb4adfea5aed6 100644
--- a/llvm/lib/Target/DirectX/DXILShaderFlags.cpp
+++ b/llvm/lib/Target/DirectX/DXILShaderFlags.cpp
@@ -152,7 +152,7 @@ void ModuleShaderFlags::updateFunctionFlags(ComputedShaderFlags &CSF,
if (!CSF.Int64Ops)
CSF.Int64Ops = I.getType()->isIntegerTy(64);
- if (!CSF.Int64Ops) {
+ if (!CSF.Int64Ops && !isa<LifetimeIntrinsic>(&I)) {
for (const Value *Op : I.operands()) {
if (Op->getType()->isIntegerTy(64)) {
CSF.Int64Ops = true;
diff --git a/llvm/lib/Target/DirectX/DXILWriter/DXILBitcodeWriter.cpp b/llvm/lib/Target/DirectX/DXILWriter/DXILBitcodeWriter.cpp
index 1d79c3018439e..22736bbd559f0 100644
--- a/llvm/lib/Target/DirectX/DXILWriter/DXILBitcodeWriter.cpp
+++ b/llvm/lib/Target/DirectX/DXILWriter/DXILBitcodeWriter.cpp
@@ -2559,8 +2559,22 @@ void DXILBitcodeWriter::writeFunctionLevelValueSymbolTable(
// to ensure the binary is the same no matter what values ever existed.
SmallVector<const ValueName *, 16> SortedTable;
+ MallocAllocator Allocator;
for (auto &VI : VST) {
- SortedTable.push_back(VI.second->getValueName());
+ ValueName *VN = VI.second->getValueName();
+ // Clang mangles lifetime intrinsic names by appending '.p0' to the end,
+ // making them invalid lifetime intrinsics in LLVM 3.7. We can't
+ // demangle in dxil-prepare because it would result in invalid IR.
+ // Therefore we have to do this in the bitcode writer while writing its
+ // name to the symbol table.
+ if (const Function *Fn = dyn_cast<Function>(VI.getValue());
+ Fn && Fn->isIntrinsic()) {
+ Intrinsic::ID IID = Fn->getIntrinsicID();
+ if (IID == Intrinsic::lifetime_start || IID == Intrinsic::lifetime_end)
+ VN = ValueName::create(Intrinsic::getBaseName(IID), Allocator,
+ VI.second);
+ }
+ SortedTable.push_back(VN);
}
// The keys are unique, so there shouldn't be stability issues.
llvm::sort(SortedTable, [](const ValueName *A, const ValueName *B) {
diff --git a/llvm/test/CodeGen/DirectX/ShaderFlags/lifetimes-noint64op.ll b/llvm/test/CodeGen/DirectX/ShaderFlags/lifetimes-noint64op.ll
new file mode 100644
index 0000000000000..736c86ebb1299
--- /dev/null
+++ b/llvm/test/CodeGen/DirectX/ShaderFlags/lifetimes-noint64op.ll
@@ -0,0 +1,36 @@
+; RUN: opt -S --passes="print-dx-shader-flags" 2>&1 %s | FileCheck %s
+; RUN: llc %s --filetype=obj -o - | obj2yaml | FileCheck %s --check-prefix=DXC
+
+target triple = "dxil-pc-shadermodel6.7-library"
+
+; CHECK: ; Combined Shader Flags for Module
+; CHECK-NEXT: ; Shader Flags Value: 0x00000000
+; CHECK-NEXT: ;
+; CHECK-NOT: ; Note: shader requires additional functionality:
+; CHECK-NOT: ; 64-Bit integer
+; CHECK-NOT: ; Note: extra DXIL module flags:
+; CHECK-NOT: ;
+; CHECK-NEXT: ; Shader Flags for Module Functions
+; CHECK-NEXT: ; Function lifetimes : 0x00000000
+
+define void @lifetimes() #0 {
+ %a = alloca [4 x i32], align 8
+ call void @llvm.lifetime.start.p0(i64 16, ptr nonnull %a)
+ call void @llvm.lifetime.end.p0(i64 16, ptr nonnull %a)
+ ret void
+}
+
+; Function Attrs: nounwind memory(argmem: readwrite)
+declare void @llvm.lifetime.start.p0(i64, ptr) #1
+
+; Function Attrs: nounwind memory(argmem: readwrite)
+declare void @llvm.lifetime.end.p0(i64, ptr) #1
+
+attributes #0 = { convergent norecurse nounwind "hlsl.export"}
+attributes #1 = { nounwind memory(argmem: readwrite) }
+
+; DXC: - Name: SFI0
+; DXC-NEXT: Size: 8
+; DXC-NOT: Flags:
+; DXC-NOT: Int64Ops: true
+; DXC: ...
diff --git a/llvm/test/CodeGen/DirectX/legalize-lifetimes-valver-1.6.ll b/llvm/test/CodeGen/DirectX/legalize-lifetimes-valver-1.6.ll
index 6552ccddddab4..f77df2d812dfe 100644
--- a/llvm/test/CodeGen/DirectX/legalize-lifetimes-valver-1.6.ll
+++ b/llvm/test/CodeGen/DirectX/legalize-lifetimes-valver-1.6.ll
@@ -1,5 +1,6 @@
; RUN: opt -S -passes='dxil-op-lower' -mtriple=dxil-pc-shadermodel6.3-library %s | FileCheck %s --check-prefixes=CHECK,CHECK-SM63
; RUN: opt -S -passes='dxil-op-lower' -mtriple=dxil-pc-shadermodel6.6-library %s | FileCheck %s --check-prefixes=CHECK,CHECK-SM66
+; RUN: opt -S -dxil-op-lower -dxil-prepare -mtriple=dxil-pc-shadermodel6.6-library %s | FileCheck %s --check-prefixes=CHECK,CHECK-PREPARE
; CHECK-LABEL: define void @test_legal_lifetime() {
;
@@ -15,6 +16,14 @@
; CHECK-SM66-NEXT: store i32 0, ptr [[GEP]], align 4
; CHECK-SM66-NEXT: call void @llvm.lifetime.end.p0(i64 4, ptr nonnull [[ACCUM_I_FLAT]])
;
+; CHECK-PREPARE-NEXT: [[ACCUM_I_FLAT:%.*]] = alloca [1 x i32], align 4
+; CHECK-PREPARE-NEXT: [[GEP:%.*]] = getelementptr i32, ptr [[ACCUM_I_FLAT]], i32 0
+; CHECK-PREPARE-NEXT: [[BITCAST:%.*]] = bitcast ptr [[ACCUM_I_FLAT]] to ptr
+; CHECK-PREPARE-NEXT: call void @llvm.lifetime.start.p0(i64 4, ptr nonnull [[BITCAST]])
+; CHECK-PREPARE-NEXT: store i32 0, ptr [[GEP]], align 4
+; CHECK-PREPARE-NEXT: [[BITCAST:%.*]] = bitcast ptr [[ACCUM_I_FLAT]] to ptr
+; CHECK-PREPARE-NEXT: call void @llvm.lifetime.end.p0(i64 4, ptr nonnull [[BITCAST]])
+;
; CHECK-NEXT: ret void
;
define void @test_legal_lifetime() {
@@ -26,6 +35,22 @@ define void @test_legal_lifetime() {
ret void
}
+; CHECK-PREPARE-DAG: attributes [[LIFETIME_ATTRS:#.*]] = { nounwind }
+
+; CHECK-PREPARE-DAG: ; Function Attrs: nounwind
+; CHECK-PREPARE-DAG: declare void @llvm.lifetime.start.p0(i64, ptr) [[LIFETIME_ATTRS]]
+
+; CHECK-PREPARE-DAG: ; Function Attrs: nounwind
+; CHECK-PREPARE-DAG: declare void @llvm.lifetime.end.p0(i64, ptr) [[LIFETIME_ATTRS]]
+
+; Function Attrs: nounwind memory(argmem: readwrite)
+declare void @llvm.lifetime.end.p0(i64, ptr) #0
+
+; Function Attrs: nounwind memory(argmem: readwrite)
+declare void @llvm.lifetime.start.p0(i64, ptr) #0
+
+attributes #0 = { nounwind memory(argmem: readwrite) }
+
; Set the validator version to 1.6
!dx.valver = !{!0}
!0 = !{i32 1, i32 6}
diff --git a/llvm/test/tools/dxil-dis/lifetimes.ll b/llvm/test/tools/dxil-dis/lifetimes.ll
new file mode 100644
index 0000000000000..6be523f50895c
--- /dev/null
+++ b/llvm/test/tools/dxil-dis/lifetimes.ll
@@ -0,0 +1,38 @@
+; RUN: llc --filetype=obj %s -o - | dxil-dis -o - | FileCheck %s
+target triple = "dxil-unknown-shadermodel6.7-library"
+
+define void @test_lifetimes() {
+; CHECK-LABEL: test_lifetimes
+; CHECK-NEXT: [[ALLOCA:%.*]] = alloca [2 x i32], align 4
+; CHECK-NEXT: [[GEP:%.*]] = getelementptr [2 x i32], [2 x i32]* [[ALLOCA]], i32 0, i32 0
+; CHECK-NEXT: [[BITCAST:%.*]] = bitcast [2 x i32]* [[ALLOCA]] to i8*
+; CHECK-NEXT: call void @llvm.lifetime.start(i64 4, i8* nonnull [[BITCAST]])
+; CHECK-NEXT: store i32 0, i32* [[GEP]], align 4
+; CHECK-NEXT: [[BITCAST:%.*]] = bitcast [2 x i32]* [[ALLOCA]] to i8*
+; CHECK-NEXT: call void @llvm.lifetime.end(i64 4, i8* nonnull [[BITCAST]])
+; CHECK-NEXT: ret void
+;
+ %a = alloca [2 x i32], align 4
+ %gep = getelementptr i32, ptr %a, i32 0
+ call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %a)
+ store i32 0, ptr %gep, align 4
+ call void @llvm.lifetime.end.p0(i64 4, ptr nonnull %a)
+ ret void
+}
+
+; CHECK-DAG: attributes [[LIFETIME_ATTRS:#.*]] = { nounwind }
+
+; CHECK-DAG: ; Function Attrs: nounwind
+; CHECK-DAG: declare void @llvm.lifetime.start(i64, i8* nocapture) [[LIFETIME_ATTRS]]
+
+; CHECK-DAG: ; Function Attrs: nounwind
+; CHECK-DAG: declare void @llvm.lifetime.end(i64, i8* nocapture) [[LIFETIME_ATTRS]]
+
+; Function Attrs: nounwind memory(argmem: readwrite)
+declare void @llvm.lifetime.end.p0(i64, ptr) #0
+
+; Function Attrs: nounwind memory(argmem: readwrite)
+declare void @llvm.lifetime.start.p0(i64, ptr) #0
+
+attributes #0 = { nounwind memory(argmem: readwrite) }
+
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM implementation wise. Would like a second reviewer based on how this fits into the bigger picture.
; CHECK-PREPARE-NEXT: [[BITCAST:%.*]] = bitcast ptr [[ACCUM_I_FLAT]] to ptr | ||
; CHECK-PREPARE-NEXT: call void @llvm.lifetime.start.p0(i64 4, ptr nonnull [[BITCAST]]) | ||
; CHECK-PREPARE-NEXT: store i32 0, ptr [[GEP]], align 4 | ||
; CHECK-PREPARE-NEXT: [[BITCAST:%.*]] = bitcast ptr [[ACCUM_I_FLAT]] to ptr |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Without adding too much complexity to the code, is it possible to re-use this for both lifetimes?
You might be able to just iterate the users of PtrOperand
and detect a bitcast to set as the operand.
Happy to just be told no. But wanted to make we thought of it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
dxil-prepare inserts bitcasts for other instructions too, so I think this should be a suggestion for dxil-prepare as a whole and not just bitcasts for lifetimes intrinsics only.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
generally bitcasts are "free" in that they don't have any runtime implementation cost, and for lifetime markers which also have no runtime implementation cost, they're usually not a big deal. They do have some tiny cost on binary size, but I'd say this probably isn't worth fixing.
This reverts commit fee27b3.
…#149883) Reverts llvm/llvm-project#148003 to fix a DirectX backend build breakage due to #149310
Reverts llvm#148003 to fix a DirectX backend build breakage due to llvm#149310
Fixes #147395
This PR legalizes lifetime intrinsics for DXIL by
i8*
when written to DXILerror: Flags must match usage.
from the validator)