@@ -5018,6 +5018,20 @@ def _materialize_atomic_add_owner_outputs(*outputs: mx.array) -> None:
50185018 mx .synchronize ()
50195019
50205020
5021+ def _sync_owner_output_scalars (* items : tuple [mx .array , tuple [int , ...]]) -> None :
5022+ # Keep MLX owner-output buffers live across custom-VJP boundaries without
5023+ # staging, copying, or host-readback of the tensor.
5024+ scalars = []
5025+ for output , index in items :
5026+ try :
5027+ scalars .append (output [index ])
5028+ except IndexError :
5029+ pass
5030+ if scalars :
5031+ mx .eval (* scalars )
5032+ mx .synchronize ()
5033+
5034+
50215035@mx .custom_function
50225036def m2rnn_apply_packed_with_state_path_c (
50235037 conv_input : mx .array ,
@@ -5282,6 +5296,12 @@ def _apply_vjp(
52825296 )
52835297 return _match_primal_gradient_dtypes (grads , primals )
52845298 dy_recurrent , dconv_post , dD , dprojected = post_grads
5299+ _sync_owner_output_scalars (
5300+ (dy_recurrent , (0 , 0 , 0 , 0 )),
5301+ (dconv_post , (0 , 0 , 0 )),
5302+ (dD , (0 , 0 )),
5303+ (dprojected , (0 , 0 , 0 )),
5304+ )
52855305 dconv_recurrent , dW , dxf , dh0 = _m2rnn_mapped_packed_bwd_path_b_reduced (
52865306 dy_recurrent ,
52875307 conv_input ,
@@ -5392,14 +5412,7 @@ def _apply_vjp(
53925412 v_heads = v_heads ,
53935413 g_heads = g_heads ,
53945414 )
5395- # MLX can otherwise drop the first TileLang owner-output from the
5396- # custom-VJP return graph under pytest capture. A scalar readback keeps
5397- # the dy buffer live at the autograd boundary without staging or
5398- # copying the tensor.
5399- try :
5400- _ = grads [0 ][0 , 0 , 0 , 0 ].item ()
5401- except IndexError :
5402- pass
5415+ _sync_owner_output_scalars ((grads [0 ], (0 , 0 , 0 , 0 )))
54035416 return _match_primal_gradient_dtypes (grads , primals )
54045417
54055418 return _apply
0 commit comments