Skip to content
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

[SPIR-V] Emit valid Lifestart/Lifestop instructions #98475

Merged
merged 2 commits into from
Aug 12, 2024

Conversation

VyacheslavLevytskyy
Copy link
Contributor

@VyacheslavLevytskyy VyacheslavLevytskyy commented Jul 11, 2024

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 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.

@VyacheslavLevytskyy VyacheslavLevytskyy marked this pull request as ready for review August 1, 2024 11:53
@llvmbot
Copy link
Collaborator

llvmbot commented Aug 1, 2024

@llvm/pr-subscribers-backend-spir-v

Author: Vyacheslav Levytskyy (VyacheslavLevytskyy)

Changes

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 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.


Full diff: https://github.com/llvm/llvm-project/pull/98475.diff

3 Files Affected:

  • (modified) llvm/lib/Target/SPIRV/SPIRVISelLowering.cpp (+29)
  • (modified) llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp (+1-3)
  • (modified) llvm/test/CodeGen/SPIRV/llvm-intrinsics/lifetime.ll (+48-9)
diff --git a/llvm/lib/Target/SPIRV/SPIRVISelLowering.cpp b/llvm/lib/Target/SPIRV/SPIRVISelLowering.cpp
index ae4e039744286..69d72a75a428a 100644
--- a/llvm/lib/Target/SPIRV/SPIRVISelLowering.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVISelLowering.cpp
@@ -203,6 +203,30 @@ static void validateGroupWaitEventsPtr(const SPIRVSubtarget &STI,
   doInsertBitcast(STI, MRI, GR, I, OpReg, OpIdx, NewPtrType);
 }
 
+static void validateLifetimeStart(const SPIRVSubtarget &STI,
+                                  MachineRegisterInfo *MRI,
+                                  SPIRVGlobalRegistry &GR, MachineInstr &I) {
+  Register PtrReg = I.getOperand(0).getReg();
+  MachineFunction *MF = I.getParent()->getParent();
+  Register PtrTypeReg = getTypeReg(MRI, PtrReg);
+  SPIRVType *PtrType = GR.getSPIRVTypeForVReg(PtrTypeReg, MF);
+  SPIRVType *PonteeElemType = PtrType ? GR.getPointeeType(PtrType) : nullptr;
+  if (!PonteeElemType || PonteeElemType->getOpcode() == SPIRV::OpTypeVoid ||
+      (PonteeElemType->getOpcode() == SPIRV::OpTypeInt &&
+       PonteeElemType->getOperand(1).getImm() == 8))
+    return;
+  // To keep the code valid a bitcast must be inserted
+  SPIRV::StorageClass::StorageClass SC =
+      static_cast<SPIRV::StorageClass::StorageClass>(
+          PtrType->getOperand(1).getImm());
+  MachineIRBuilder MIB(I);
+  LLVMContext &Context = MF->getFunction().getContext();
+  SPIRVType *ElemType =
+      GR.getOrCreateSPIRVType(IntegerType::getInt8Ty(Context), MIB);
+  SPIRVType *NewPtrType = GR.getOrCreateSPIRVPointerType(ElemType, MIB, SC);
+  doInsertBitcast(STI, MRI, GR, I, PtrReg, 0, NewPtrType);
+}
+
 static void validateGroupAsyncCopyPtr(const SPIRVSubtarget &STI,
                                       MachineRegisterInfo *MRI,
                                       SPIRVGlobalRegistry &GR, MachineInstr &I,
@@ -413,6 +437,11 @@ void SPIRVTargetLowering::finalizeLowering(MachineFunction &MF) const {
                                       SPIRV::OpTypeBool))
           MI.setDesc(STI.getInstrInfo()->get(SPIRV::OpLogicalNotEqual));
         break;
+      case SPIRV::OpLifetimeStart:
+      case SPIRV::OpLifetimeStop:
+        if (MI.getOperand(1).getImm() > 0)
+          validateLifetimeStart(STI, MRI, GR, MI);
+        break;
       case SPIRV::OpGroupAsyncCopy:
         validateGroupAsyncCopyPtr(STI, MRI, GR, MI, 3);
         validateGroupAsyncCopyPtr(STI, MRI, GR, MI, 4);
diff --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
index ed786bd33aa05..5b04cc6661e49 100644
--- a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
@@ -2088,9 +2088,7 @@ bool SPIRVInstructionSelector::selectIntrinsic(Register ResVReg,
                                                        : SPIRV::OpLifetimeStop;
     int64_t Size = I.getOperand(I.getNumExplicitDefs() + 1).getImm();
     Register PtrReg = I.getOperand(I.getNumExplicitDefs() + 2).getReg();
-    unsigned PonteeOpType = GR.getPointeeTypeOp(PtrReg);
-    bool IsNonvoidPtr = PonteeOpType != 0 && PonteeOpType != SPIRV::OpTypeVoid;
-    if (Size == -1 || IsNonvoidPtr)
+    if (Size == -1)
       Size = 0;
     BuildMI(BB, I, I.getDebugLoc(), TII.get(Op)).addUse(PtrReg).addImm(Size);
   } break;
diff --git a/llvm/test/CodeGen/SPIRV/llvm-intrinsics/lifetime.ll b/llvm/test/CodeGen/SPIRV/llvm-intrinsics/lifetime.ll
index 710a1581f760c..7fae8759c1f7d 100644
--- a/llvm/test/CodeGen/SPIRV/llvm-intrinsics/lifetime.ll
+++ b/llvm/test/CodeGen/SPIRV/llvm-intrinsics/lifetime.ll
@@ -1,22 +1,61 @@
+; RUN: llc -O0 -mtriple=spirv64-unknown-unknown %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
+
 ; RUN: llc -O0 -mtriple=spirv32-unknown-unknown %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv32-unknown-unknown %s -o - -filetype=obj | spirv-val %}
 
-; CHECK: OpFunction
-; CHECK: %[[FooArg:.*]] = OpVariable
-; CHECK: OpLifetimeStart %[[FooArg]], 0
-; CHECK: OpCopyMemorySized
-; CHECK: OpBitcast
-; CHECK: OpInBoundsPtrAccessChain
-; CHECK: OpLifetimeStop %[[FooArg]], 0
+; CHECK-DAG: %[[#Char:]] = OpTypeInt 8 0
+; CHECK-DAG: %[[#PtrChar:]] = OpTypePointer Function %[[#Char]]
 
 %tprange = type { %tparray }
 %tparray = type { [2 x i64] }
 
+; CHECK: OpFunction
+; CHECK: %[[#FooVar:]] = OpVariable
+; CHECK: %[[#Casted1:]] = OpBitcast %[[#PtrChar]] %[[#FooVar]]
+; CHECK: OpLifetimeStart %[[#Casted1]], 72
+; CHECK: OpCopyMemorySized
+; CHECK: OpBitcast
+; CHECK: OpInBoundsPtrAccessChain
+; CHECK: %[[#Casted2:]] = OpBitcast %[[#PtrChar]] %[[#FooVar]]
+; CHECK: OpLifetimeStop %[[#Casted2]], 72
 define spir_func void @foo(ptr noundef byval(%tprange) align 8 %_arg_UserRange) {
   %RoundedRangeKernel = alloca %tprange, align 8
-  call void @llvm.lifetime.start.p0(i64 72, ptr nonnull %RoundedRangeKernel) #7
+  call void @llvm.lifetime.start.p0(i64 72, ptr nonnull %RoundedRangeKernel)
   call void @llvm.memcpy.p0.p0.i64(ptr align 8 %RoundedRangeKernel, ptr align 8 %_arg_UserRange, i64 16, i1 false)
   %KernelFunc = getelementptr inbounds i8, ptr %RoundedRangeKernel, i64 16
-  call void @llvm.lifetime.end.p0(i64 72, ptr nonnull %RoundedRangeKernel) #7
+  call void @llvm.lifetime.end.p0(i64 72, ptr nonnull %RoundedRangeKernel)
+  ret void
+}
+
+; CHECK: OpFunction
+; CHECK: %[[#BarVar:]] = OpVariable
+; CHECK: OpLifetimeStart %[[#BarVar]], 0
+; CHECK: OpCopyMemorySized
+; CHECK: OpBitcast
+; CHECK: OpInBoundsPtrAccessChain
+; CHECK: OpLifetimeStop %[[#BarVar]], 0
+define spir_func void @bar(ptr noundef byval(%tprange) align 8 %_arg_UserRange) {
+  %RoundedRangeKernel = alloca %tprange, align 8
+  call void @llvm.lifetime.start.p0(i64 -1, ptr nonnull %RoundedRangeKernel)
+  call void @llvm.memcpy.p0.p0.i64(ptr align 8 %RoundedRangeKernel, ptr align 8 %_arg_UserRange, i64 16, i1 false)
+  %KernelFunc = getelementptr inbounds i8, ptr %RoundedRangeKernel, i64 16
+  call void @llvm.lifetime.end.p0(i64 -1, ptr nonnull %RoundedRangeKernel)
+  ret void
+}
+
+; CHECK: OpFunction
+; CHECK: %[[#TestVar:]] = OpVariable
+; CHECK: OpLifetimeStart %[[#TestVar]], 1
+; CHECK: OpCopyMemorySized
+; CHECK: OpInBoundsPtrAccessChain
+; CHECK: OpLifetimeStop %[[#TestVar]], 1
+define spir_func void @test(ptr noundef align 8 %_arg) {
+  %var = alloca i8, align 8
+  call void @llvm.lifetime.start.p0(i64 1, ptr nonnull %var)
+  call void @llvm.memcpy.p0.p0.i64(ptr align 8 %var, ptr align 8 %_arg, i64 1, i1 false)
+  %KernelFunc = getelementptr inbounds i8, ptr %var, i64 0
+  call void @llvm.lifetime.end.p0(i64 1, ptr nonnull %var)
   ret void
 }
 

@VyacheslavLevytskyy VyacheslavLevytskyy merged commit 281f59f into llvm:main Aug 12, 2024
10 of 12 checks passed
bwendling pushed a commit to bwendling/llvm-project that referenced this pull request Aug 15, 2024
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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants