Skip to content

Commit fee27b3

Browse files
authored
[DirectX] Legalize lifetime intrinsics for DXIL (#148003)
Fixes #147395 This PR legalizes lifetime intrinsics for DXIL by - Adding a bitcast for the lifetime intrinsics' pointer operand in dxil-prepare to ensure it gets cast to an `i8*` when written to DXIL - Removing the memory attribute from lifetime intrinsics in dxil-prepare to match DXIL - Making the DXIL bitcode writer write the base/demangled name of lifetime intrinsics to the symbol table - Making lifetime intrinsics an exception to Int64Ops shader flag analysis (otherwise we get `error: Flags must match usage.` from the validator)
1 parent a999a1b commit fee27b3

File tree

6 files changed

+155
-5
lines changed

6 files changed

+155
-5
lines changed

llvm/lib/Target/DirectX/DXILPrepare.cpp

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "llvm/IR/AttributeMask.h"
2525
#include "llvm/IR/IRBuilder.h"
2626
#include "llvm/IR/Instruction.h"
27+
#include "llvm/IR/IntrinsicInst.h"
2728
#include "llvm/IR/Module.h"
2829
#include "llvm/InitializePasses.h"
2930
#include "llvm/Pass.h"
@@ -239,6 +240,11 @@ class DXILPrepareModule : public ModulePass {
239240
for (size_t Idx = 0, End = F.arg_size(); Idx < End; ++Idx)
240241
F.removeParamAttrs(Idx, AttrMask);
241242

243+
// Lifetime intrinsics in LLVM 3.7 do not have the memory FnAttr
244+
if (Intrinsic::ID IID = F.getIntrinsicID();
245+
IID == Intrinsic::lifetime_start || IID == Intrinsic::lifetime_end)
246+
F.removeFnAttr(Attribute::Memory);
247+
242248
for (auto &BB : F) {
243249
IRBuilder<> Builder(&BB);
244250
for (auto &I : make_early_inc_range(BB)) {
@@ -247,7 +253,7 @@ class DXILPrepareModule : public ModulePass {
247253

248254
// Emtting NoOp bitcast instructions allows the ValueEnumerator to be
249255
// unmodified as it reserves instruction IDs during contruction.
250-
if (auto LI = dyn_cast<LoadInst>(&I)) {
256+
if (auto *LI = dyn_cast<LoadInst>(&I)) {
251257
if (Value *NoOpBitcast = maybeGenerateBitcast(
252258
Builder, PointerTypes, I, LI->getPointerOperand(),
253259
LI->getType())) {
@@ -257,7 +263,7 @@ class DXILPrepareModule : public ModulePass {
257263
}
258264
continue;
259265
}
260-
if (auto SI = dyn_cast<StoreInst>(&I)) {
266+
if (auto *SI = dyn_cast<StoreInst>(&I)) {
261267
if (Value *NoOpBitcast = maybeGenerateBitcast(
262268
Builder, PointerTypes, I, SI->getPointerOperand(),
263269
SI->getValueOperand()->getType())) {
@@ -268,7 +274,7 @@ class DXILPrepareModule : public ModulePass {
268274
}
269275
continue;
270276
}
271-
if (auto GEP = dyn_cast<GetElementPtrInst>(&I)) {
277+
if (auto *GEP = dyn_cast<GetElementPtrInst>(&I)) {
272278
if (Value *NoOpBitcast = maybeGenerateBitcast(
273279
Builder, PointerTypes, I, GEP->getPointerOperand(),
274280
GEP->getSourceElementType()))
@@ -280,6 +286,17 @@ class DXILPrepareModule : public ModulePass {
280286
CB->removeRetAttrs(AttrMask);
281287
for (size_t Idx = 0, End = CB->arg_size(); Idx < End; ++Idx)
282288
CB->removeParamAttrs(Idx, AttrMask);
289+
// LLVM 3.7 Lifetime intrinics require an i8* pointer operand, so we
290+
// insert a bitcast here to ensure that is the case
291+
if (isa<LifetimeIntrinsic>(CB)) {
292+
Value *PtrOperand = CB->getArgOperand(1);
293+
Builder.SetInsertPoint(CB);
294+
PointerType *PtrTy = cast<PointerType>(PtrOperand->getType());
295+
Value *NoOpBitcast = Builder.Insert(
296+
CastInst::Create(Instruction::BitCast, PtrOperand,
297+
Builder.getPtrTy(PtrTy->getAddressSpace())));
298+
CB->setArgOperand(1, NoOpBitcast);
299+
}
283300
continue;
284301
}
285302
}

llvm/lib/Target/DirectX/DXILShaderFlags.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ void ModuleShaderFlags::updateFunctionFlags(ComputedShaderFlags &CSF,
152152
if (!CSF.Int64Ops)
153153
CSF.Int64Ops = I.getType()->isIntegerTy(64);
154154

155-
if (!CSF.Int64Ops) {
155+
if (!CSF.Int64Ops && !isa<LifetimeIntrinsic>(&I)) {
156156
for (const Value *Op : I.operands()) {
157157
if (Op->getType()->isIntegerTy(64)) {
158158
CSF.Int64Ops = true;

llvm/lib/Target/DirectX/DXILWriter/DXILBitcodeWriter.cpp

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2545,6 +2545,25 @@ void DXILBitcodeWriter::writeInstruction(const Instruction &I, unsigned InstID,
25452545
Vals.clear();
25462546
}
25472547

2548+
// HLSL Change
2549+
namespace {
2550+
struct ValueNameCreator {
2551+
MallocAllocator Allocator;
2552+
SmallVector<ValueName *, 2>
2553+
ValueNames; // SmallVector N = 2 because we currently only expect this
2554+
// to hold ValueNames for Lifetime intrinsics
2555+
~ValueNameCreator() {
2556+
for (auto *VN : ValueNames)
2557+
VN->Destroy(Allocator);
2558+
}
2559+
ValueName *create(StringRef Name, Value *V) {
2560+
ValueName *VN = ValueName::create(Name, Allocator, V);
2561+
ValueNames.push_back(VN);
2562+
return VN;
2563+
}
2564+
};
2565+
} // anonymous namespace
2566+
25482567
// Emit names for globals/functions etc.
25492568
void DXILBitcodeWriter::writeFunctionLevelValueSymbolTable(
25502569
const ValueSymbolTable &VST) {
@@ -2559,9 +2578,24 @@ void DXILBitcodeWriter::writeFunctionLevelValueSymbolTable(
25592578
// to ensure the binary is the same no matter what values ever existed.
25602579
SmallVector<const ValueName *, 16> SortedTable;
25612580

2581+
// HLSL Change
2582+
ValueNameCreator VNC;
25622583
for (auto &VI : VST) {
2563-
SortedTable.push_back(VI.second->getValueName());
2584+
ValueName *VN = VI.second->getValueName();
2585+
// Clang mangles lifetime intrinsic names by appending '.p0' to the end,
2586+
// making them invalid lifetime intrinsics in LLVM 3.7. We can't
2587+
// demangle in dxil-prepare because it would result in invalid IR.
2588+
// Therefore we have to do this in the bitcode writer while writing its
2589+
// name to the symbol table.
2590+
if (const Function *Fn = dyn_cast<Function>(VI.getValue());
2591+
Fn && Fn->isIntrinsic()) {
2592+
Intrinsic::ID IID = Fn->getIntrinsicID();
2593+
if (IID == Intrinsic::lifetime_start || IID == Intrinsic::lifetime_end)
2594+
VN = VNC.create(Intrinsic::getBaseName(IID), VI.second);
2595+
}
2596+
SortedTable.push_back(VN);
25642597
}
2598+
25652599
// The keys are unique, so there shouldn't be stability issues.
25662600
llvm::sort(SortedTable, [](const ValueName *A, const ValueName *B) {
25672601
return A->first() < B->first();
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
; RUN: opt -S --passes="print-dx-shader-flags" 2>&1 %s | FileCheck %s
2+
; RUN: llc %s --filetype=obj -o - | obj2yaml | FileCheck %s --check-prefix=DXC
3+
4+
target triple = "dxil-pc-shadermodel6.7-library"
5+
6+
; CHECK: ; Combined Shader Flags for Module
7+
; CHECK-NEXT: ; Shader Flags Value: 0x00000000
8+
; CHECK-NEXT: ;
9+
; CHECK-NOT: ; Note: shader requires additional functionality:
10+
; CHECK-NOT: ; 64-Bit integer
11+
; CHECK-NOT: ; Note: extra DXIL module flags:
12+
; CHECK-NOT: ;
13+
; CHECK-NEXT: ; Shader Flags for Module Functions
14+
; CHECK-NEXT: ; Function lifetimes : 0x00000000
15+
16+
define void @lifetimes() #0 {
17+
%a = alloca [4 x i32], align 8
18+
call void @llvm.lifetime.start.p0(i64 16, ptr nonnull %a)
19+
call void @llvm.lifetime.end.p0(i64 16, ptr nonnull %a)
20+
ret void
21+
}
22+
23+
; Function Attrs: nounwind memory(argmem: readwrite)
24+
declare void @llvm.lifetime.start.p0(i64, ptr) #1
25+
26+
; Function Attrs: nounwind memory(argmem: readwrite)
27+
declare void @llvm.lifetime.end.p0(i64, ptr) #1
28+
29+
attributes #0 = { convergent norecurse nounwind "hlsl.export"}
30+
attributes #1 = { nounwind memory(argmem: readwrite) }
31+
32+
; DXC: - Name: SFI0
33+
; DXC-NEXT: Size: 8
34+
; DXC-NOT: Flags:
35+
; DXC-NOT: Int64Ops: true
36+
; DXC: ...

llvm/test/CodeGen/DirectX/legalize-lifetimes-valver-1.6.ll

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
; RUN: opt -S -passes='dxil-op-lower' -mtriple=dxil-pc-shadermodel6.3-library %s | FileCheck %s --check-prefixes=CHECK,CHECK-SM63
22
; RUN: opt -S -passes='dxil-op-lower' -mtriple=dxil-pc-shadermodel6.6-library %s | FileCheck %s --check-prefixes=CHECK,CHECK-SM66
3+
; RUN: opt -S -dxil-op-lower -dxil-prepare -mtriple=dxil-pc-shadermodel6.6-library %s | FileCheck %s --check-prefixes=CHECK,CHECK-PREPARE
34

45
; CHECK-LABEL: define void @test_legal_lifetime() {
56
;
@@ -15,6 +16,14 @@
1516
; CHECK-SM66-NEXT: store i32 0, ptr [[GEP]], align 4
1617
; CHECK-SM66-NEXT: call void @llvm.lifetime.end.p0(i64 4, ptr nonnull [[ACCUM_I_FLAT]])
1718
;
19+
; CHECK-PREPARE-NEXT: [[ACCUM_I_FLAT:%.*]] = alloca [1 x i32], align 4
20+
; CHECK-PREPARE-NEXT: [[GEP:%.*]] = getelementptr i32, ptr [[ACCUM_I_FLAT]], i32 0
21+
; CHECK-PREPARE-NEXT: [[BITCAST:%.*]] = bitcast ptr [[ACCUM_I_FLAT]] to ptr
22+
; CHECK-PREPARE-NEXT: call void @llvm.lifetime.start.p0(i64 4, ptr nonnull [[BITCAST]])
23+
; CHECK-PREPARE-NEXT: store i32 0, ptr [[GEP]], align 4
24+
; CHECK-PREPARE-NEXT: [[BITCAST:%.*]] = bitcast ptr [[ACCUM_I_FLAT]] to ptr
25+
; CHECK-PREPARE-NEXT: call void @llvm.lifetime.end.p0(i64 4, ptr nonnull [[BITCAST]])
26+
;
1827
; CHECK-NEXT: ret void
1928
;
2029
define void @test_legal_lifetime() {
@@ -26,6 +35,22 @@ define void @test_legal_lifetime() {
2635
ret void
2736
}
2837

38+
; CHECK-PREPARE-DAG: attributes [[LIFETIME_ATTRS:#.*]] = { nounwind }
39+
40+
; CHECK-PREPARE-DAG: ; Function Attrs: nounwind
41+
; CHECK-PREPARE-DAG: declare void @llvm.lifetime.start.p0(i64, ptr) [[LIFETIME_ATTRS]]
42+
43+
; CHECK-PREPARE-DAG: ; Function Attrs: nounwind
44+
; CHECK-PREPARE-DAG: declare void @llvm.lifetime.end.p0(i64, ptr) [[LIFETIME_ATTRS]]
45+
46+
; Function Attrs: nounwind memory(argmem: readwrite)
47+
declare void @llvm.lifetime.end.p0(i64, ptr) #0
48+
49+
; Function Attrs: nounwind memory(argmem: readwrite)
50+
declare void @llvm.lifetime.start.p0(i64, ptr) #0
51+
52+
attributes #0 = { nounwind memory(argmem: readwrite) }
53+
2954
; Set the validator version to 1.6
3055
!dx.valver = !{!0}
3156
!0 = !{i32 1, i32 6}

llvm/test/tools/dxil-dis/lifetimes.ll

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
; RUN: llc --filetype=obj %s -o - | dxil-dis -o - | FileCheck %s
2+
target triple = "dxil-unknown-shadermodel6.7-library"
3+
4+
define void @test_lifetimes() {
5+
; CHECK-LABEL: test_lifetimes
6+
; CHECK-NEXT: [[ALLOCA:%.*]] = alloca [2 x i32], align 4
7+
; CHECK-NEXT: [[GEP:%.*]] = getelementptr [2 x i32], [2 x i32]* [[ALLOCA]], i32 0, i32 0
8+
; CHECK-NEXT: [[BITCAST:%.*]] = bitcast [2 x i32]* [[ALLOCA]] to i8*
9+
; CHECK-NEXT: call void @llvm.lifetime.start(i64 4, i8* nonnull [[BITCAST]])
10+
; CHECK-NEXT: store i32 0, i32* [[GEP]], align 4
11+
; CHECK-NEXT: [[BITCAST:%.*]] = bitcast [2 x i32]* [[ALLOCA]] to i8*
12+
; CHECK-NEXT: call void @llvm.lifetime.end(i64 4, i8* nonnull [[BITCAST]])
13+
; CHECK-NEXT: ret void
14+
;
15+
%a = alloca [2 x i32], align 4
16+
%gep = getelementptr [2 x i32], ptr %a, i32 0, i32 0
17+
call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %a)
18+
store i32 0, ptr %gep, align 4
19+
call void @llvm.lifetime.end.p0(i64 4, ptr nonnull %a)
20+
ret void
21+
}
22+
23+
; CHECK-DAG: attributes [[LIFETIME_ATTRS:#.*]] = { nounwind }
24+
25+
; CHECK-DAG: ; Function Attrs: nounwind
26+
; CHECK-DAG: declare void @llvm.lifetime.start(i64, i8* nocapture) [[LIFETIME_ATTRS]]
27+
28+
; CHECK-DAG: ; Function Attrs: nounwind
29+
; CHECK-DAG: declare void @llvm.lifetime.end(i64, i8* nocapture) [[LIFETIME_ATTRS]]
30+
31+
; Function Attrs: nounwind memory(argmem: readwrite)
32+
declare void @llvm.lifetime.end.p0(i64, ptr) #0
33+
34+
; Function Attrs: nounwind memory(argmem: readwrite)
35+
declare void @llvm.lifetime.start.p0(i64, ptr) #0
36+
37+
attributes #0 = { nounwind memory(argmem: readwrite) }
38+

0 commit comments

Comments
 (0)