[llvm] [SPIRV] Fix enqueue empty kernel (PR #187671)

Dmitry Sidorov via llvm-commits llvm-commits at lists.llvm.org
Fri Apr 17 05:17:34 PDT 2026


================
@@ -2797,85 +2794,136 @@ getOrCreateSPIRVDeviceEventPointer(MachineIRBuilder &MIRBuilder,
 static bool buildEnqueueKernel(const SPIRV::IncomingCall *Call,
                                MachineIRBuilder &MIRBuilder,
                                SPIRVGlobalRegistry *GR) {
+  // In this function there are three stages:
+  //   1. prepare call indexes in order we expect them.
+  //   2. process all arguments which requered preparation.
+  //   3. create a SPIRV operator with arguments.
+
   MachineRegisterInfo *MRI = MIRBuilder.getMRI();
   const DataLayout &DL = MIRBuilder.getDataLayout();
-  bool IsSpirvOp = Call->isSpirvOp();
-  bool HasEvents = Call->Builtin->Name.contains("events") || IsSpirvOp;
   const SPIRVTypeInst Int32Ty = GR->getOrCreateSPIRVIntegerType(32, MIRBuilder);
 
-  // Make vararg instructions before OpEnqueueKernel.
-  // Local sizes arguments: Sizes of block invoke arguments. Clang generates
-  // local size operands as an array, so we need to unpack them.
+  // 1. prepare call indexes in order we expect them.
+  // Based on clang sources, clang/lib/CodeGen/CGBuiltin.cpp, BIenqueue_kernel,
+  // We expect 4 different layouts of call arguments:
+  //   1) No events, no vargs: {Queue, Flags, Range, Kernel, Block};
+  //   2) No events, varargs: {Queue, Flags, Range, Kernel, Block, NumElem,
+  //      ElemPtr};
+  //   3) events, no varargs: {Queue, Flags, Range, NumEvents,
+  //      EventWaitList, EventRet, Kernel, Block};
+  //   4) events, varargs: {Queue,
+  //      Flags, Range, NumEvents, EventWaitList, EventRet, Kernel, Block,
+  //      NumElem, ElemPtr};
+  //
+  // We also may expect __spirv_EnqueueKernel
+
+  bool IsSpirvOp = Call->isSpirvOp();
+  bool HasEvents = Call->Builtin->Name.contains("_events") || IsSpirvOp;
+  bool HasVarArgs = Call->Builtin->Name.contains("_varargs") || IsSpirvOp;
+
+  const unsigned NumArgs = Call->Arguments.size();
+  const unsigned BaseArgIdx = 0;
+  const unsigned IncorrectIdx = NumArgs + 1;
+
+  const unsigned QueueIdx = BaseArgIdx;
+  const unsigned FlagsIdx = BaseArgIdx + 1;
+  const unsigned NDRangeIdx = BaseArgIdx + 2;
+  const unsigned NumEventsIdx = HasEvents ? BaseArgIdx + 3 : IncorrectIdx;
+  const unsigned WaitEventsIdx = HasEvents ? BaseArgIdx + 4 : IncorrectIdx;
+  const unsigned RetEventIdx = HasEvents ? BaseArgIdx + 5 : IncorrectIdx;
+  const unsigned InvokeIdx = BaseArgIdx + 3 + (HasEvents ? 3 : 0);
+  const unsigned ParamIdx = BaseArgIdx + 4 + (HasEvents ? 3 : 0);
+  const unsigned LocalSizeNumElemIdx =
+      HasVarArgs ? (BaseArgIdx + 5 + (HasEvents ? 3 : 0)) : IncorrectIdx;
+  const unsigned LocalSizeElemPtrIdx =
+      HasVarArgs ? (BaseArgIdx + 6 + (HasEvents ? 3 : 0)) : IncorrectIdx;
+
+  const unsigned LastArgIdx =
+      (BaseArgIdx + 4 + (HasEvents ? 3 : 0) + (HasVarArgs ? 2 : 0));
+  assert(LastArgIdx < NumArgs && "Incorrect number arguments");
+
+  // 2. Process all arguments which requered preparation.
+  // 2.1 Events - use Call arguments, or use dummy nulls in case of absence of
+  // events
+  Register NumEventsReg;
+  Register WaitEventsReg;
+  Register RetEventReg;
+  if (HasEvents) {
+    NumEventsReg = Call->Arguments[NumEventsIdx];
+    WaitEventsReg = Call->Arguments[WaitEventsIdx];
+    RetEventReg = Call->Arguments[RetEventIdx];
+  } else {
+    NumEventsReg = buildConstantIntReg32(0, MIRBuilder, GR);
+    Register NullPtr = GR->getOrCreateConstNullPtr(
+        MIRBuilder, getOrCreateSPIRVDeviceEventPointer(MIRBuilder, GR));
+    WaitEventsReg = NullPtr;
+    RetEventReg = NullPtr;
+  }
+
+  // 2.2 Invoke (Kernel)
+  assert(getBlockStructInstr(Call->Arguments[InvokeIdx], MRI)->getOpcode() ==
+         TargetOpcode::G_GLOBAL_VALUE);
+
+  // 2.3 Param, Param Size, Param Align
+  Register BlockLiteralReg = Call->Arguments[ParamIdx];
+  const SPIRVTypeInst Int8Ty = GR->getOrCreateSPIRVIntegerType(8, MIRBuilder);
+  const SPIRVTypeInst Int8PtrGen = GR->getOrCreateSPIRVPointerType(
+      Int8Ty, MIRBuilder, SPIRV::StorageClass::Generic);
+  Type *PType = const_cast<Type *>(getBlockStructType(BlockLiteralReg, MRI));
+
+  Register ParamReg = createVirtualRegister(Int8PtrGen, GR, MIRBuilder);
+  MIRBuilder.buildInstr(SPIRV::OpBitcast)
+      .addDef(ParamReg)
+      .addUse(GR->getSPIRVTypeID(Int8PtrGen))
+      .addUse(BlockLiteralReg);
+  // TODO: these numbers should be obtained from block literal structure.
+  Register ParamSizeReg =
+      buildConstantIntReg32(DL.getTypeStoreSize(PType), MIRBuilder, GR);
+  Register ParamAlignReg =
+      buildConstantIntReg32(DL.getPrefTypeAlign(PType).value(), MIRBuilder, GR);
+
+  // 2.4 Local Size Array
   SmallVector<Register, 16> LocalSizes;
-  if (Call->Builtin->Name.contains("_varargs") || IsSpirvOp) {
-    const unsigned LocalSizeArrayIdx = HasEvents ? 9 : 6;
-    Register GepReg = Call->Arguments[LocalSizeArrayIdx];
-    MachineInstr *GepMI = MRI->getUniqueVRegDef(GepReg);
-    assert(isSpvIntrinsic(*GepMI, Intrinsic::spv_gep) &&
-           GepMI->getOperand(3).isReg());
-    Register ArrayReg = GepMI->getOperand(3).getReg();
-    MachineInstr *ArrayMI = MRI->getUniqueVRegDef(ArrayReg);
-    const Type *LocalSizeTy = getMachineInstrType(ArrayMI);
-    assert(LocalSizeTy && "Local size type is expected");
-    const uint64_t LocalSizeNum =
-        cast<ArrayType>(LocalSizeTy)->getNumElements();
-    unsigned SC = storageClassToAddressSpace(SPIRV::StorageClass::Generic);
-    const LLT LLType = LLT::pointer(SC, GR->getPointerSize());
-    const SPIRVTypeInst PointerSizeTy = GR->getOrCreateSPIRVPointerType(
-        Int32Ty, MIRBuilder, SPIRV::StorageClass::Function);
-    for (unsigned I = 0; I < LocalSizeNum; ++I) {
+  if (HasVarArgs) {
+    Register LocalSizeNumElem = Call->Arguments[LocalSizeNumElemIdx];
+    MachineInstr *LocalSizeNumElemMI = MRI->getUniqueVRegDef(LocalSizeNumElem);
+    const MachineOperand &ConstOp = LocalSizeNumElemMI->getOperand(1);
+    assert(LocalSizeNumElemMI->getOpcode() == TargetOpcode::G_CONSTANT &&
+           ConstOp.isCImm() && "Expected constant immediate");
+    uint64_t NumElem = ConstOp.getCImm()->getValue().getZExtValue();
+
+    Register LocalSizeArrayReg = Call->Arguments[LocalSizeElemPtrIdx];
+
+    for (unsigned i = 0; i < NumElem; ++i) {
       Register Reg = MRI->createVirtualRegister(&SPIRV::pIDRegClass);
-      MRI->setType(Reg, LLType);
-      GR->assignSPIRVTypeToVReg(PointerSizeTy, Reg, MIRBuilder.getMF());
----------------
MrSidims wrote:

Why these are removed?

https://github.com/llvm/llvm-project/pull/187671


More information about the llvm-commits mailing list