@@ -307,13 +307,19 @@ class SPIRVInstructionSelector : public InstructionSelector {
307
307
bool selectHandleFromBinding (Register &ResVReg, const SPIRVType *ResType,
308
308
MachineInstr &I) const ;
309
309
310
+ bool selectCounterHandleFromBinding (Register &ResVReg,
311
+ const SPIRVType *ResType,
312
+ MachineInstr &I) const ;
313
+
310
314
bool selectReadImageIntrinsic (Register &ResVReg, const SPIRVType *ResType,
311
315
MachineInstr &I) const ;
312
316
bool selectImageWriteIntrinsic (MachineInstr &I) const ;
313
317
bool selectResourceGetPointer (Register &ResVReg, const SPIRVType *ResType,
314
318
MachineInstr &I) const ;
315
319
bool selectModf (Register ResVReg, const SPIRVType *ResType,
316
320
MachineInstr &I) const ;
321
+ bool selectUpdateCounter (Register &ResVReg, const SPIRVType *ResType,
322
+ MachineInstr &I) const ;
317
323
bool selectFrexp (Register ResVReg, const SPIRVType *ResType,
318
324
MachineInstr &I) const ;
319
325
// Utilities
@@ -3443,6 +3449,10 @@ bool SPIRVInstructionSelector::selectIntrinsic(Register ResVReg,
3443
3449
case Intrinsic::spv_resource_handlefrombinding: {
3444
3450
return selectHandleFromBinding (ResVReg, ResType, I);
3445
3451
}
3452
+ case Intrinsic::spv_resource_counterhandlefrombinding:
3453
+ return selectCounterHandleFromBinding (ResVReg, ResType, I);
3454
+ case Intrinsic::spv_resource_updatecounter:
3455
+ return selectUpdateCounter (ResVReg, ResType, I);
3446
3456
case Intrinsic::spv_resource_store_typedbuffer: {
3447
3457
return selectImageWriteIntrinsic (I);
3448
3458
}
@@ -3478,6 +3488,130 @@ bool SPIRVInstructionSelector::selectHandleFromBinding(Register &ResVReg,
3478
3488
*cast<GIntrinsic>(&I), I);
3479
3489
}
3480
3490
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
+ }
3481
3615
bool SPIRVInstructionSelector::selectReadImageIntrinsic (
3482
3616
Register &ResVReg, const SPIRVType *ResType, MachineInstr &I) const {
3483
3617
0 commit comments