@@ -307,13 +307,19 @@ class SPIRVInstructionSelector : public InstructionSelector {
307307 bool selectHandleFromBinding (Register &ResVReg, const SPIRVType *ResType,
308308 MachineInstr &I) const ;
309309
310+ bool selectCounterHandleFromBinding (Register &ResVReg,
311+ const SPIRVType *ResType,
312+ MachineInstr &I) const ;
313+
310314 bool selectReadImageIntrinsic (Register &ResVReg, const SPIRVType *ResType,
311315 MachineInstr &I) const ;
312316 bool selectImageWriteIntrinsic (MachineInstr &I) const ;
313317 bool selectResourceGetPointer (Register &ResVReg, const SPIRVType *ResType,
314318 MachineInstr &I) const ;
315319 bool selectModf (Register ResVReg, const SPIRVType *ResType,
316320 MachineInstr &I) const ;
321+ bool selectUpdateCounter (Register &ResVReg, const SPIRVType *ResType,
322+ MachineInstr &I) const ;
317323 bool selectFrexp (Register ResVReg, const SPIRVType *ResType,
318324 MachineInstr &I) const ;
319325 // Utilities
@@ -3443,6 +3449,10 @@ bool SPIRVInstructionSelector::selectIntrinsic(Register ResVReg,
34433449 case Intrinsic::spv_resource_handlefrombinding: {
34443450 return selectHandleFromBinding (ResVReg, ResType, I);
34453451 }
3452+ case Intrinsic::spv_resource_counterhandlefrombinding:
3453+ return selectCounterHandleFromBinding (ResVReg, ResType, I);
3454+ case Intrinsic::spv_resource_updatecounter:
3455+ return selectUpdateCounter (ResVReg, ResType, I);
34463456 case Intrinsic::spv_resource_store_typedbuffer: {
34473457 return selectImageWriteIntrinsic (I);
34483458 }
@@ -3478,6 +3488,130 @@ bool SPIRVInstructionSelector::selectHandleFromBinding(Register &ResVReg,
34783488 *cast<GIntrinsic>(&I), I);
34793489}
34803490
3491+ bool SPIRVInstructionSelector::selectCounterHandleFromBinding (
3492+ Register &ResVReg, const SPIRVType *ResType, MachineInstr &I) const {
3493+ auto &Intr = cast<GIntrinsic>(I);
3494+ assert (Intr.getIntrinsicID () ==
3495+ Intrinsic::spv_resource_counterhandlefrombinding);
3496+
3497+ // Extract information from the intrinsic call.
3498+ Register MainHandleReg = Intr.getOperand (2 ).getReg ();
3499+ auto *MainHandleDef = cast<GIntrinsic>(getVRegDef (*MRI, MainHandleReg));
3500+ assert (MainHandleDef->getIntrinsicID () ==
3501+ Intrinsic::spv_resource_handlefrombinding);
3502+
3503+ uint32_t Set = getIConstVal (Intr.getOperand (4 ).getReg (), MRI);
3504+ uint32_t Binding = getIConstVal (Intr.getOperand (3 ).getReg (), MRI);
3505+ uint32_t ArraySize = getIConstVal (MainHandleDef->getOperand (4 ).getReg (), MRI);
3506+ Register IndexReg = MainHandleDef->getOperand (5 ).getReg ();
3507+ const bool IsNonUniform = false ;
3508+ std::string CounterName =
3509+ getStringValueFromReg (MainHandleDef->getOperand (6 ).getReg (), *MRI) +
3510+ " .counter" ;
3511+
3512+ // Create the counter variable.
3513+ MachineIRBuilder MIRBuilder (I);
3514+ Register CounterVarReg = buildPointerToResource (
3515+ GR.getPointeeType (ResType), GR.getPointerStorageClass (ResType), Set,
3516+ Binding, ArraySize, IndexReg, IsNonUniform, CounterName, MIRBuilder);
3517+
3518+ return BuildCOPY (ResVReg, CounterVarReg, I);
3519+ }
3520+
3521+ bool SPIRVInstructionSelector::selectUpdateCounter (Register &ResVReg,
3522+ const SPIRVType *ResType,
3523+ MachineInstr &I) const {
3524+ auto &Intr = cast<GIntrinsic>(I);
3525+ assert (Intr.getIntrinsicID () == Intrinsic::spv_resource_updatecounter);
3526+
3527+ Register CounterHandleReg = Intr.getOperand (2 ).getReg ();
3528+ Register IncrReg = Intr.getOperand (3 ).getReg ();
3529+
3530+ // The counter handle is a pointer to the counter variable (which is a struct
3531+ // containing an i32). We need to get a pointer to that i32 member to do the
3532+ // atomic operation.
3533+ #ifndef NDEBUG
3534+ SPIRVType *CounterVarType = GR.getSPIRVTypeForVReg (CounterHandleReg);
3535+ SPIRVType *CounterVarPointeeType = GR.getPointeeType (CounterVarType);
3536+ assert (CounterVarPointeeType &&
3537+ CounterVarPointeeType->getOpcode () == SPIRV::OpTypeStruct &&
3538+ " Counter variable must be a struct" );
3539+ assert (GR.getPointerStorageClass (CounterVarType) ==
3540+ SPIRV::StorageClass::StorageBuffer &&
3541+ " Counter variable must be in the storage buffer storage class" );
3542+ assert (CounterVarPointeeType->getNumOperands () == 2 &&
3543+ " Counter variable must have exactly 1 member in the struct" );
3544+ const SPIRVType *MemberType =
3545+ GR.getSPIRVTypeForVReg (CounterVarPointeeType->getOperand (1 ).getReg ());
3546+ assert (MemberType->getOpcode () == SPIRV::OpTypeInt &&
3547+ " Counter variable struct must have a single i32 member" );
3548+ #endif
3549+
3550+ // The struct has a single i32 member.
3551+ MachineIRBuilder MIRBuilder (I);
3552+ const Type *LLVMIntType =
3553+ Type::getInt32Ty (I.getMF ()->getFunction ().getContext ());
3554+
3555+ SPIRVType *IntPtrType = GR.getOrCreateSPIRVPointerType (
3556+ LLVMIntType, MIRBuilder, SPIRV::StorageClass::StorageBuffer);
3557+
3558+ auto Zero = buildI32Constant (0 , I);
3559+ if (!Zero.second )
3560+ return false ;
3561+
3562+ Register PtrToCounter =
3563+ MRI->createVirtualRegister (GR.getRegClass (IntPtrType));
3564+ if (!BuildMI (*I.getParent (), I, I.getDebugLoc (),
3565+ TII.get (SPIRV::OpAccessChain))
3566+ .addDef (PtrToCounter)
3567+ .addUse (GR.getSPIRVTypeID (IntPtrType))
3568+ .addUse (CounterHandleReg)
3569+ .addUse (Zero.first )
3570+ .constrainAllUses (TII, TRI, RBI)) {
3571+ return false ;
3572+ }
3573+
3574+ // For UAV/SSBO counters, the scope is Device. The counter variable is not
3575+ // used as a flag. So the memory semantics can be None.
3576+ auto Scope = buildI32Constant (SPIRV::Scope::Device, I);
3577+ if (!Scope.second )
3578+ return false ;
3579+ auto Semantics = buildI32Constant (SPIRV::MemorySemantics::None, I);
3580+ if (!Semantics.second )
3581+ return false ;
3582+
3583+ int64_t IncrVal = getIConstValSext (IncrReg, MRI);
3584+ auto Incr = buildI32Constant (static_cast <uint32_t >(IncrVal), I);
3585+ if (!Incr.second )
3586+ return false ;
3587+
3588+ Register AtomicRes = MRI->createVirtualRegister (GR.getRegClass (ResType));
3589+ if (!BuildMI (*I.getParent (), I, I.getDebugLoc (), TII.get (SPIRV::OpAtomicIAdd))
3590+ .addDef (AtomicRes)
3591+ .addUse (GR.getSPIRVTypeID (ResType))
3592+ .addUse (PtrToCounter)
3593+ .addUse (Scope.first )
3594+ .addUse (Semantics.first )
3595+ .addUse (Incr.first )
3596+ .constrainAllUses (TII, TRI, RBI)) {
3597+ return false ;
3598+ }
3599+ if (IncrVal >= 0 ) {
3600+ return BuildCOPY (ResVReg, AtomicRes, I);
3601+ }
3602+
3603+ // In HLSL, IncrementCounter returns the value *before* the increment, while
3604+ // DecrementCounter returns the value *after* the decrement. Both are lowered
3605+ // to the same atomic intrinsic which returns the value *before* the
3606+ // operation. So for decrements (negative IncrVal), we must subtract the
3607+ // increment value from the result to get the post-decrement value.
3608+ return BuildMI (*I.getParent (), I, I.getDebugLoc (), TII.get (SPIRV::OpIAddS))
3609+ .addDef (ResVReg)
3610+ .addUse (GR.getSPIRVTypeID (ResType))
3611+ .addUse (AtomicRes)
3612+ .addUse (Incr.first )
3613+ .constrainAllUses (TII, TRI, RBI);
3614+ }
34813615bool SPIRVInstructionSelector::selectReadImageIntrinsic (
34823616 Register &ResVReg, const SPIRVType *ResType, MachineInstr &I) const {
34833617
0 commit comments