Skip to content

Commit 281f59f

Browse files
[SPIR-V] Emit valid Lifestart/Lifestop instructions (llvm#98475)
This PR fixes emission of valid OpLifestart/OpLifestop instructions. According to https://registry.khronos.org/SPIR-V/specs/unified1/SPIRV.html#OpLifetimeStart: "Size must be 0 if Pointer is a pointer to a non-void type or the Addresses [capability](https://registry.khronos.org/SPIR-V/specs/unified1/SPIRV.html#Capability) is not declared.". The `Size` argument is set the corresponding intrinsics arguments, so Size is not zero we must ensure that Pointer has the required type by inserting a bitcast if needed.
1 parent cd08fad commit 281f59f

File tree

3 files changed

+78
-12
lines changed

3 files changed

+78
-12
lines changed

llvm/lib/Target/SPIRV/SPIRVISelLowering.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,30 @@ static void validateGroupWaitEventsPtr(const SPIRVSubtarget &STI,
203203
doInsertBitcast(STI, MRI, GR, I, OpReg, OpIdx, NewPtrType);
204204
}
205205

206+
static void validateLifetimeStart(const SPIRVSubtarget &STI,
207+
MachineRegisterInfo *MRI,
208+
SPIRVGlobalRegistry &GR, MachineInstr &I) {
209+
Register PtrReg = I.getOperand(0).getReg();
210+
MachineFunction *MF = I.getParent()->getParent();
211+
Register PtrTypeReg = getTypeReg(MRI, PtrReg);
212+
SPIRVType *PtrType = GR.getSPIRVTypeForVReg(PtrTypeReg, MF);
213+
SPIRVType *PonteeElemType = PtrType ? GR.getPointeeType(PtrType) : nullptr;
214+
if (!PonteeElemType || PonteeElemType->getOpcode() == SPIRV::OpTypeVoid ||
215+
(PonteeElemType->getOpcode() == SPIRV::OpTypeInt &&
216+
PonteeElemType->getOperand(1).getImm() == 8))
217+
return;
218+
// To keep the code valid a bitcast must be inserted
219+
SPIRV::StorageClass::StorageClass SC =
220+
static_cast<SPIRV::StorageClass::StorageClass>(
221+
PtrType->getOperand(1).getImm());
222+
MachineIRBuilder MIB(I);
223+
LLVMContext &Context = MF->getFunction().getContext();
224+
SPIRVType *ElemType =
225+
GR.getOrCreateSPIRVType(IntegerType::getInt8Ty(Context), MIB);
226+
SPIRVType *NewPtrType = GR.getOrCreateSPIRVPointerType(ElemType, MIB, SC);
227+
doInsertBitcast(STI, MRI, GR, I, PtrReg, 0, NewPtrType);
228+
}
229+
206230
static void validateGroupAsyncCopyPtr(const SPIRVSubtarget &STI,
207231
MachineRegisterInfo *MRI,
208232
SPIRVGlobalRegistry &GR, MachineInstr &I,
@@ -413,6 +437,11 @@ void SPIRVTargetLowering::finalizeLowering(MachineFunction &MF) const {
413437
SPIRV::OpTypeBool))
414438
MI.setDesc(STI.getInstrInfo()->get(SPIRV::OpLogicalNotEqual));
415439
break;
440+
case SPIRV::OpLifetimeStart:
441+
case SPIRV::OpLifetimeStop:
442+
if (MI.getOperand(1).getImm() > 0)
443+
validateLifetimeStart(STI, MRI, GR, MI);
444+
break;
416445
case SPIRV::OpGroupAsyncCopy:
417446
validateGroupAsyncCopyPtr(STI, MRI, GR, MI, 3);
418447
validateGroupAsyncCopyPtr(STI, MRI, GR, MI, 4);

llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2088,9 +2088,7 @@ bool SPIRVInstructionSelector::selectIntrinsic(Register ResVReg,
20882088
: SPIRV::OpLifetimeStop;
20892089
int64_t Size = I.getOperand(I.getNumExplicitDefs() + 1).getImm();
20902090
Register PtrReg = I.getOperand(I.getNumExplicitDefs() + 2).getReg();
2091-
unsigned PonteeOpType = GR.getPointeeTypeOp(PtrReg);
2092-
bool IsNonvoidPtr = PonteeOpType != 0 && PonteeOpType != SPIRV::OpTypeVoid;
2093-
if (Size == -1 || IsNonvoidPtr)
2091+
if (Size == -1)
20942092
Size = 0;
20952093
BuildMI(BB, I, I.getDebugLoc(), TII.get(Op)).addUse(PtrReg).addImm(Size);
20962094
} break;

llvm/test/CodeGen/SPIRV/llvm-intrinsics/lifetime.ll

Lines changed: 48 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,61 @@
1+
; RUN: llc -O0 -mtriple=spirv64-unknown-unknown %s -o - | FileCheck %s
2+
; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
3+
14
; RUN: llc -O0 -mtriple=spirv32-unknown-unknown %s -o - | FileCheck %s
5+
; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv32-unknown-unknown %s -o - -filetype=obj | spirv-val %}
26

3-
; CHECK: OpFunction
4-
; CHECK: %[[FooArg:.*]] = OpVariable
5-
; CHECK: OpLifetimeStart %[[FooArg]], 0
6-
; CHECK: OpCopyMemorySized
7-
; CHECK: OpBitcast
8-
; CHECK: OpInBoundsPtrAccessChain
9-
; CHECK: OpLifetimeStop %[[FooArg]], 0
7+
; CHECK-DAG: %[[#Char:]] = OpTypeInt 8 0
8+
; CHECK-DAG: %[[#PtrChar:]] = OpTypePointer Function %[[#Char]]
109

1110
%tprange = type { %tparray }
1211
%tparray = type { [2 x i64] }
1312

13+
; CHECK: OpFunction
14+
; CHECK: %[[#FooVar:]] = OpVariable
15+
; CHECK: %[[#Casted1:]] = OpBitcast %[[#PtrChar]] %[[#FooVar]]
16+
; CHECK: OpLifetimeStart %[[#Casted1]], 72
17+
; CHECK: OpCopyMemorySized
18+
; CHECK: OpBitcast
19+
; CHECK: OpInBoundsPtrAccessChain
20+
; CHECK: %[[#Casted2:]] = OpBitcast %[[#PtrChar]] %[[#FooVar]]
21+
; CHECK: OpLifetimeStop %[[#Casted2]], 72
1422
define spir_func void @foo(ptr noundef byval(%tprange) align 8 %_arg_UserRange) {
1523
%RoundedRangeKernel = alloca %tprange, align 8
16-
call void @llvm.lifetime.start.p0(i64 72, ptr nonnull %RoundedRangeKernel) #7
24+
call void @llvm.lifetime.start.p0(i64 72, ptr nonnull %RoundedRangeKernel)
1725
call void @llvm.memcpy.p0.p0.i64(ptr align 8 %RoundedRangeKernel, ptr align 8 %_arg_UserRange, i64 16, i1 false)
1826
%KernelFunc = getelementptr inbounds i8, ptr %RoundedRangeKernel, i64 16
19-
call void @llvm.lifetime.end.p0(i64 72, ptr nonnull %RoundedRangeKernel) #7
27+
call void @llvm.lifetime.end.p0(i64 72, ptr nonnull %RoundedRangeKernel)
28+
ret void
29+
}
30+
31+
; CHECK: OpFunction
32+
; CHECK: %[[#BarVar:]] = OpVariable
33+
; CHECK: OpLifetimeStart %[[#BarVar]], 0
34+
; CHECK: OpCopyMemorySized
35+
; CHECK: OpBitcast
36+
; CHECK: OpInBoundsPtrAccessChain
37+
; CHECK: OpLifetimeStop %[[#BarVar]], 0
38+
define spir_func void @bar(ptr noundef byval(%tprange) align 8 %_arg_UserRange) {
39+
%RoundedRangeKernel = alloca %tprange, align 8
40+
call void @llvm.lifetime.start.p0(i64 -1, ptr nonnull %RoundedRangeKernel)
41+
call void @llvm.memcpy.p0.p0.i64(ptr align 8 %RoundedRangeKernel, ptr align 8 %_arg_UserRange, i64 16, i1 false)
42+
%KernelFunc = getelementptr inbounds i8, ptr %RoundedRangeKernel, i64 16
43+
call void @llvm.lifetime.end.p0(i64 -1, ptr nonnull %RoundedRangeKernel)
44+
ret void
45+
}
46+
47+
; CHECK: OpFunction
48+
; CHECK: %[[#TestVar:]] = OpVariable
49+
; CHECK: OpLifetimeStart %[[#TestVar]], 1
50+
; CHECK: OpCopyMemorySized
51+
; CHECK: OpInBoundsPtrAccessChain
52+
; CHECK: OpLifetimeStop %[[#TestVar]], 1
53+
define spir_func void @test(ptr noundef align 8 %_arg) {
54+
%var = alloca i8, align 8
55+
call void @llvm.lifetime.start.p0(i64 1, ptr nonnull %var)
56+
call void @llvm.memcpy.p0.p0.i64(ptr align 8 %var, ptr align 8 %_arg, i64 1, i1 false)
57+
%KernelFunc = getelementptr inbounds i8, ptr %var, i64 0
58+
call void @llvm.lifetime.end.p0(i64 1, ptr nonnull %var)
2059
ret void
2160
}
2261

0 commit comments

Comments
 (0)