459459 s_mov_b32 \won , 0
460460.Lrt_done\@:
461461.endm
462+
463+ // --------------------------------------------------------------------------------------------
464+ // Phase - B (Task 5 ) watermark thresholds + LDS put - runtime helper + bail - time commit macros.
465+ // Watermark decision (SPEC ; mirrors coop CTRL_LOW/CTRL_HIGH, occ_dispatch DSWS_LOW/HIGH):
466+ // occ_X < CTRL_LOW - > compute STARVED for X - > shrink a compute wave into feed - X.
467+ // occ_X > CTRL_HIGH_X - > feed - X OVER - SERVING - > grow a feed - X wave into compute.
468+ // occ_A in [ 0 , G ], occ_B in [ 0 , FN ] (occ_sample bounds) , so the HIGH marks are per - ring - depth.
469+ // --------------------------------------------------------------------------------------------
470+ .ifndef CTRL_LOW
471+ .set CTRL_LOW , 1 // occ_X < 1 (== 0 , ring empty at consume) - > starved
472+ .endif
473+ .ifndef CTRL_HIGH_A
474+ .set CTRL_HIGH_A , (G - 1 ) // occ_A > G - 1 - > A - ring saturated - > A - feed over - serving
475+ .endif
476+ .ifndef CTRL_HIGH_B
477+ .set CTRL_HIGH_B , (FN - 1 ) // occ_B > FN - 1 - > B - ring saturated - > B - feed over - serving
478+ .endif
479+
480+ // lds_put_r: lane - 0 - of - wave write scalar \ssrc - > LDS [ \saddr ] (RUNTIME byte offset in a sreg). Mirrors
481+ // the coop lds_put_v idiom but takes a SCALAR address (symmetry with lds_get_r). Used by the claimer's
482+ // Step - 4 snapshot write into the runtime parity half of SNAP_BASE. Temps RP_A/RP_D are v11/v14 (<=v15 ,
483+ // pre - grow safe) ; s49 is the exec save (matches lds_put).
484+ .macro lds_put_r saddr , ssrc
485+ s_mov_b32 s49 , exec_lo
486+ v_cmp_eq_u32 vcc_lo , 0 , v2
487+ s_and_b32 exec_lo , exec_lo , vcc_lo
488+ s_cbranch_execz .Lputr_skip\@
489+ v_mov_b32 v [ RP_A ], \saddr
490+ v_mov_b32 v [ RP_D ], \ssrc
491+ ds_store_b32 v [ RP_A ], v [ RP_D ]
492+ s_wait_dscnt 0x0
493+ .Lputr_skip\@:
494+ s_mov_b32 exec_lo , s49
495+ .endm
496+
497+ // conv_dec_floor: floor - guarded ATOMIC decrement of a role slot -- \ok < - 1 iff it decremented \slot_off
498+ // (only when the current value was > 1 ) , else 0 (floor hit ; source role must keep >= 1 wave). A
499+ // ds_cmpstore_rtn_b32 CAS loop (re - reads on a lost race) , so two same - source converters in one epoch
500+ // (e.g. compute - >Afeed and compute - >Bfeed both dec NCOMP_SLOT) can never drive the slot below 1 .
501+ // Clob: s52 (read value) , s53 (new/CAS - return) , s65 (exec save) ; v5/v6/v7 (<=v15, pre-grow safe).
502+ .macro conv_dec_floor slot_off , ok
503+ s_mov_b32 \ok , 0
504+ .Lcdf_retry\@:
505+ lds_get s52 , \slot_off // s52 = current source - slot count
506+ s_cmp_le_u32 s52 , 1
507+ s_cbranch_scc1 .Lcdf_done\@ // <= 1 - > at floor , cannot convert away (ok stays 0 )
508+ s_sub_u32 s53 , s52 , 1 // new = old - 1
509+ s_mov_b32 s65 , exec_lo // lane0 - only CAS (one attempt per WAVE)
510+ v_cmp_eq_u32 vcc_lo , 0 , v2
511+ s_and_b32 exec_lo , exec_lo , vcc_lo
512+ s_cbranch_execz .Lcdf_restore\@
513+ v_mov_b32 v5 , \slot_off // vaddr = &slot
514+ v_mov_b32 v6 , s52 // v6 = expected old ( CMP )
515+ v_mov_b32 v7 , s53 // v7 = new value (NEW)
516+ ds_cmpstore_rtn_b32 v6 , v5 , v7 , v6 // slot = (slot==old)? new : slot ; v6 <- prior
517+ s_wait_dscnt 0x0
518+ .Lcdf_restore\@:
519+ s_mov_b32 exec_lo , s65
520+ v_readfirstlane_b32 s53 , v6 // s53 = prior (lane0 CAS result , broadcast)
521+ s_cmp_eq_u32 s53 , s52 // success iff prior == expected (we were the swapper)
522+ s_cbranch_scc0 .Lcdf_retry\@ // lost the race - > re - read and retry
523+ s_mov_b32 \ok , 1
524+ .Lcdf_done\@:
525+ .endm
526+
527+ // conv_apply: the bail - time role - conversion COMMIT (SPEC 3 . 4 Approach A). Precondition: s58 = s_win
528+ // ( 1 iff this wave won the (dir , epoch) ticket). Ordered strictly BEFORE the QUIESCE_CNT bump the
529+ // CALLER emits after this macro (the quiesce counter is the snapshot handshake).
530+ // ORDER: (a) floor - guarded dec of \src_slot - > (b) reserve the VGPR sum - envelope \delta (shrink
531+ // always ok ; grow may abort over BUDGET) -> (c) on ok: inc \dst_slot, flip private role reg (s59),
532+ // s_alloc_vgpr \alloc_sz (GROW=NFV feed - >compute / SHRINK= 32 compute - >feed) with SCC - retry - >
533+ // (d) on floor - fail or reserve - abort: cancel , remain current role (undo the source dec if a
534+ // reservation abort happened after the dec ).
535+ // PRE - GROW OOR WINDOW (SPEC 4 , # 1 brick risk): the wave is lean - 32 on entry ; every LDS/atomic temp
536+ // read before the s_alloc_vgpr GROW is <=v15 (occ_sample/try_gate v5/v6/v7 + v11/v14 ; conv_dec_floor
537+ // v5/v6/v7 ; lds_fetch_add v11/v14) and every carried scalar is <=s65. NO >v15 source before GROW.
538+ // Clob: s52 , s53 , s54 ( + conv_dec_floor / reserve_try scratch) ; s59 = new role slot id (record).
539+ .macro conv_apply src_slot , dst_slot , delta , alloc_sz
540+ s_cmp_eq_u32 s58 , 0
541+ s_cbranch_scc1 .Lca_skip\@ // lost the ticket - > no conversion this bail
542+ conv_dec_floor \src_slot , s54 // (a) floor - guarded atomic dec of source slot
543+ s_cmp_eq_u32 s54 , 0
544+ s_cbranch_scc1 .Lca_skip\@ // floor - fail (source at 1 ) - > cancel , remain current role
545+ reserve_try (\delta) , s53 // (b) reserve VGPR envelope (grow may abort ; shrink ok)
546+ s_cmp_eq_u32 s53 , 0
547+ s_cbranch_scc0 .Lca_commit\@
548+ lds_fetch_add s52 , \src_slot , 1 // (d) reserve aborted: UNDO the source dec , cancel
549+ s_branch .Lca_skip\@
550+ .Lca_commit\@:
551+ lds_fetch_add s52 , \dst_slot , 1 // (c) inc dest slot (unbounded - > plain atomic add )
552+ s_mov_b32 s59 , \dst_slot // flip private current - role reg (records new role slot id)
553+ // ---- s_alloc_vgpr resize: THE pre - grow OOR window closes here ; all reads above were <=v15 ----
554+ .Lca_alloc\@:
555+ s_alloc_vgpr \alloc_sz // GROW(NFV) / SHRINK( 32 ) ; SCC-retry (brick-class rule)
556+ s_cbranch_scc0 .Lca_alloc\@
557+ .Lca_skip\@:
558+ .endm
462559.endif
463560
464561// ============================================================================================
@@ -622,6 +719,24 @@ occ_kernel:
622719 lds_get s44 , EPOCH_OFF // ...then bump EPOCH LAST
623720 s_add_u32 s44 , s44 , 1
624721 s_mov_b32 s35 , s44
722+ .if DSWS2_CONV
723+ // ---- Step 4 (SPEC 3 . 4 decision 1 ): snapshot the LIVE role mix into the NEXT epoch's parity half of
724+ // SNAP_BASE , and reset QUIESCE_CNT , BEFORE the epoch bump below (which is published LAST). Followers
725+ // and the claimer's own wait - done for THIS super - tile then size their quiesce sentinels from
726+ // parity(newEpoch) -- so the quiesce counter is the snapshot handshake (Step 3 reads it). ----
727+ s_and_b32 s46 , s44 , 1 // parity(newEpoch)
728+ s_mul_i32 s46 , s46 , 12 // parity * 12 ( 3 - word half) -- matches SNAP_BASE comment
729+ s_add_u32 s46 , s46 , SNAP_BASE // s46 = byte offset of parity half
730+ lds_get s47 , NCOMP_SLOT
731+ lds_put_r s46 , s47 // snap.nC = live NCOMP_SLOT
732+ s_add_u32 s46 , s46 , 4
733+ lds_get s47 , NAFEED_SLOT
734+ lds_put_r s46 , s47 // snap.nA = live NAFEED_SLOT
735+ s_add_u32 s46 , s46 , 4
736+ lds_get s47 , NBFEED_SLOT
737+ lds_put_r s46 , s47 // snap.nB = live NBFEED_SLOT
738+ lds_put QUIESCE_CNT_OFF , 0 // reset the per - super - tile bail counter
739+ .endif
625740 lds_put EPOCH_OFF , s44
626741 BSTAGE // claimer helps stage B for this super - tile (s30 , s31)
627742 // A7 advance gate: free resident A/B only when ALL G rowblks are computed + flushed
@@ -662,6 +777,65 @@ occ_kernel:
662777 // decode/resident state (round - table finding # 1 ). Sentinels = threshold + #role - waves ( each does
663778 // exactly one terminal bail). NOTE: compile - time NCOMP/NAFEED/NBFEED is correct for STATIC roles ;
664779 // Phase - B conversion must switch these to live role counts / epoch - snapshot drained counters.
780+ .if DSWS2_CONV
781+ // ---- Step 3 (SPEC 3 . 4 decision 1 ): size the three claim - counter sentinels from THIS epoch's parity
782+ // snapshot (live role mix written at broadcast , Step 4 ) instead of compile - time NCOMP/NAFEED/NBFEED ,
783+ // so conversions re - tune the quiesce thresholds. A SEPARATE QUIESCE_CNT >= WAVES - 1 cross - check is the
784+ // DIAG safety net ; under DIAG a mismatch between the two is flagged to occ[29] (byte 116). ----
785+ lds_get s45 , EPOCH_OFF
786+ s_and_b32 s45 , s45 , 1 // parity(thisEpoch)
787+ s_mul_i32 s45 , s45 , 12 // parity * 12 ( 3 - word half)
788+ s_add_u32 s45 , s45 , SNAP_BASE
789+ lds_get_r s46 , s45 // snap.nC
790+ s_add_u32 s45 , s45 , 4
791+ lds_get_r s47 , s45 // snap.nA
792+ s_add_u32 s45 , s45 , 4
793+ lds_get_r s48 , s45 // snap.nB
794+ s_add_u32 s46 , s46 , G // thr_rowblk = G + snap.nC
795+ s_add_u32 s47 , s47 , G // thr_arow = G + snap.nA
796+ s_add_u32 s48 , s48 , FN // thr_bfrag = FN + snap.nB
797+ // sentinels_done (s50) = (ROWBLK_NEXT>=thr_rowblk) & (BFRAG_NEXT>=thr_bfrag) & (AROW_NEXT>=thr_arow)
798+ s_mov_b32 s50 , 1
799+ lds_get s44 , ROWBLK_NEXT_OFF
800+ s_cmp_lt_u32 s44 , s46
801+ s_cbranch_scc0 .Lqc_rb_ok
802+ s_mov_b32 s50 , 0
803+ .Lqc_rb_ok:
804+ lds_get s44 , BFRAG_NEXT_OFF
805+ s_cmp_lt_u32 s44 , s48
806+ s_cbranch_scc0 .Lqc_bf_ok
807+ s_mov_b32 s50 , 0
808+ .Lqc_bf_ok:
809+ lds_get s44 , AROW_NEXT_OFF
810+ s_cmp_lt_u32 s44 , s47
811+ s_cbranch_scc0 .Lqc_ar_ok
812+ s_mov_b32 s50 , 0
813+ .Lqc_ar_ok:
814+ // quiesce_done (s51) = QUIESCE_CNT >= WAVES - 1 ( each of the WAVES - 1 non - claimer waves bumped once)
815+ s_mov_b32 s51 , 1
816+ lds_get s44 , QUIESCE_CNT_OFF
817+ s_cmp_lt_u32 s44 , (WAVES - 1 )
818+ s_cbranch_scc0 .Lqc_q_ok
819+ s_mov_b32 s51 , 0
820+ .Lqc_q_ok:
821+ .if DIAG
822+ // DIAG cross - check: the snapshot sentinels and the QUIESCE handshake must AGREE. Publish their XOR
823+ // ( 1 = mismatch) to occ [ 29 ] (byte 116 ; clear of occ[26]/27=104/108 Task3, occ[28]=112 Task4).
824+ s_xor_b32 s52 , s50 , s51
825+ v_cmp_eq_u32 vcc_lo , 0 , v2
826+ s_mov_b32 s16 , exec_lo
827+ s_and_b32 exec_lo , exec_lo , vcc_lo
828+ s_cbranch_execz .Lqc_diag_skip
829+ v_mov_b32 v14 , s52 // v14 <= v15: pre - grow safe (claimer is lean - 32 anyway)
830+ global_store_b32 v4 , v14 , s [ 0 : 1 ] offset: 116 scope:SCOPE_DEV // occ [ 29 ] = quiesce/snapshot mismatch
831+ .Lqc_diag_skip:
832+ s_mov_b32 exec_lo , s16
833+ .endif
834+ // advance only when BOTH the snapshot sentinels AND the QUIESCE cross - check agree they are done
835+ s_and_b32 s50 , s50 , s51
836+ s_cmp_eq_u32 s50 , 0
837+ s_cbranch_scc1 .Lclaimer_wait_done
838+ .else
665839 lds_get s44 , ROWBLK_NEXT_OFF // G claims + NCOMP terminal bails
666840 s_cmp_lt_u32 s44 , (G + NCOMP)
667841 s_cbranch_scc1 .Lclaimer_wait_done
@@ -671,6 +845,7 @@ occ_kernel:
671845 lds_get s44 , AROW_NEXT_OFF // G claims + NAFEED terminal bails
672846 s_cmp_lt_u32 s44 , (G + NAFEED)
673847 s_cbranch_scc1 .Lclaimer_wait_done
848+ .endif
674849 s_branch .Lclaim_loop
675850.Lclaimer_terminal:
676851 lds_put STI_OFF , 0xFFFFFFFF // FIX 1 (e): publish SENTINEL ( not the raw over - claimed sti )...
@@ -714,6 +889,21 @@ occ_kernel:
714889 s_cbranch_scc1 .Lretire
715890 DECODE_STI // s30=tcol s31=ksi (mblk unused)
716891 BSTAGE
892+ .if DSWS2_CONV
893+ // ==== Phase - B decision (Step 1 ) + bail - time commit (Step 2 ) , B - feed wave ====
894+ // Lean - 32 - > PRE - GROW window (the feed - >compute GROW closes it inside conv_apply , post all <=v15
895+ // reads). If the B - ring is OVER - SERVED (occ_B>CTRL_HIGH_B) grow one B - feed - >compute (dir 3 ):
896+ // reserve delta + (NFV - VLEAN) (may abort over BUDGET - > stay B - feed).
897+ s_mov_b32 s58 , 0
898+ occ_sample s55 , s56 // s55=occ_A , s56=occ_B
899+ s_cmp_gt_u32 s56 , CTRL_HIGH_B
900+ s_cbranch_scc0 .Lbfeed_quiesce
901+ s_mov_b32 s57 , 3 // dir 3 : B - feed - > compute
902+ try_gate 3 , s58
903+ conv_apply NBFEED_SLOT , NCOMP_SLOT , + (NFV - VLEAN) , NFV
904+ .Lbfeed_quiesce:
905+ lds_fetch_add s61 , QUIESCE_CNT_OFF , 1 // commit - before - bump ordering (SPEC 3 . 4 decision 2 )
906+ .endif
717907 s_branch .Lbfeed_follow
718908
719909// ============================================================================================
@@ -742,6 +932,22 @@ occ_kernel:
742932 s_cbranch_scc1 .Lretire
743933 DECODE_STI // s19=mblk s31=ksi (tcol unused)
744934 ASTAGE
935+ .if DSWS2_CONV
936+ // ==== Phase - B decision (Step 1 ) + bail - time commit (Step 2 ) , A - feed wave ====
937+ // Wave is lean - 32 here (feeds never grow) , so this is a PRE - GROW window ; the conversion GROW
938+ // (feed - >compute , s_alloc_vgpr NFV) is the ONLY grow and closes the window inside conv_apply , after
939+ // all <=v15 LDS /atomic reads. If the A - ring is OVER - SERVED (occ_A>CTRL_HIGH_A) grow one A - feed - >
940+ // compute (dir 2 ): reserve delta + (NFV - VLEAN) (may abort over BUDGET - > stay A - feed).
941+ s_mov_b32 s58 , 0
942+ occ_sample s55 , s56 // s55=occ_A , s56=occ_B
943+ s_cmp_gt_u32 s55 , CTRL_HIGH_A
944+ s_cbranch_scc0 .Lafeed_quiesce
945+ s_mov_b32 s57 , 2 // dir 2 : A - feed - > compute
946+ try_gate 2 , s58
947+ conv_apply NAFEED_SLOT , NCOMP_SLOT , + (NFV - VLEAN) , NFV
948+ .Lafeed_quiesce:
949+ lds_fetch_add s61 , QUIESCE_CNT_OFF , 1 // commit - before - bump ordering (SPEC 3 . 4 decision 2 )
950+ .endif
745951 s_branch .Lafeed_follow
746952
747953// ============================================================================================
@@ -861,6 +1067,33 @@ occ_kernel:
8611067 lds_inc ROWBLK_DONE_OFF // rowblk r computed + flushed (frees the A7 advance gate)
8621068 s_branch .Lcompute_claim
8631069.Lcompute_drained:
1070+ .if DSWS2_CONV
1071+ // ==== Phase - B role - boundary decision (Step 1 ) + bail - time commit (Step 2 ) , compute wave ====
1072+ // Wave is lean - 32 here (the last claimed rowblk shrank to 32 ; a zero-claim drain never grew), so the
1073+ // whole decision + commit runs in the PRE - GROW window -- every temp <=v15 / scalar <=s65 (see conv_apply
1074+ // OOR note). If compute is STARVED for A (occ_A<CTRL_LOW) it converts one compute - >A - feed (dir 0 ) ;
1075+ // else if starved for B , compute - >B - feed (dir 1 ). Both are SHRINKs: reserve delta - (NFV - VLEAN) ,
1076+ // s_alloc_vgpr 32 (already lean - > no - op). Persistent: s57=dir , s58=s_win , s59=new - role (all outside
1077+ // s60..s65 so occ_sample/try_gate/reserve_try/conv_ * cannot clobber them while live).
1078+ s_mov_b32 s58 , 0 // s_win = 0 (default: raced no ticket)
1079+ occ_sample s55 , s56 // s55=occ_A in [ 0 , G ], s56=occ_B in [ 0 , FN ] (clob s60 , s61)
1080+ s_cmp_lt_u32 s55 , CTRL_LOW
1081+ s_cbranch_scc0 .Lcmp_try_b
1082+ s_mov_b32 s57 , 0 // dir 0 : compute - > A - feed
1083+ try_gate 0 , s58 // s58 = s_win (single - winner per (dir , epoch))
1084+ conv_apply NCOMP_SLOT , NAFEED_SLOT , - (NFV - VLEAN) , 32
1085+ s_branch .Lcmp_quiesce
1086+ .Lcmp_try_b:
1087+ s_cmp_lt_u32 s56 , CTRL_LOW
1088+ s_cbranch_scc0 .Lcmp_quiesce
1089+ s_mov_b32 s57 , 1 // dir 1 : compute - > B - feed
1090+ try_gate 1 , s58
1091+ conv_apply NCOMP_SLOT , NBFEED_SLOT , - (NFV - VLEAN) , 32
1092+ .Lcmp_quiesce:
1093+ // ORDERING (SPEC 3 . 4 decision 2 ): the commit above fully completed (role - slot CAS + reservation +
1094+ // s_alloc_vgpr) BEFORE this QUIESCE_CNT bump -- the bump is the snapshot handshake the claimer reads.
1095+ lds_fetch_add s61 , QUIESCE_CNT_OFF , 1 // exactly one bump per non - claimer wave / super - tile
1096+ .endif
8641097 s_branch .Lcompute_follow // this super - tile's compute drained - > re - check epoch/terminal
8651098
8661099// ---- A7 role - agnostic terminal (followers): retire. (Claimer retires via .Lclaimer_terminal.) ----
0 commit comments