Skip to content

[BUG] Cache policy from cute.copy is dropped when the copy atom is created with invariant=True #3726

Description

@kainzhong

Which component has the problem?

CuTe DSL

Bug Report

Describe the bug
When you create a copy atom with invariant=True and pass a L2 cache policy to cute.copy's cache_policy param, it will be silently dropped.

Steps/Code to reproduce bug

"""Minimal repro: CopyG2ROp(invariant=True) + cute.copy(cache_policy=...) drops the L2 policy.

Usage: python repro.py {inv,noinv}   (profile with ncu and read the SASS of `k`)
"""
import sys
import torch
import cutlass
from cutlass import cute, BFloat16, Int64, Float32
from cutlass.cute.runtime import from_dlpack

INVARIANT = sys.argv[1] == "inv"

@cute.kernel
def k(mX: cute.Tensor, mY: cute.Tensor):
    tidx, _, _ = cute.arch.thread_idx()
    policy = cute.arch.inline_ptx(
        "createpolicy.fractional.L2::evict_first.b64 {$w0}, {$r0};",
        write_only_types=[Int64], read_only_args=[Float32(1.0)])
    load = cute.make_copy_atom(cute.nvgpu.CopyG2ROp(), BFloat16, num_bits_per_copy=256,
                               invariant=INVARIANT)
    src = cute.make_tensor((mX.iterator + tidx * 16).align(32), cute.make_layout(16))
    r = cute.make_rmem_tensor((16,), BFloat16)
    cute.copy(load, src, r, cache_policy=policy)
    dst = cute.make_tensor(mY.iterator + tidx * 16, cute.make_layout(16))
    cute.autovec_copy(r, dst)

@cute.jit
def launch(mX: cute.Tensor, mY: cute.Tensor):
    k(mX, mY).launch(grid=[1, 1, 1], block=[32, 1, 1])

x = torch.randn(512, dtype=torch.bfloat16, device="cuda"); y = torch.empty_like(x)
launch(from_dlpack(x, assumed_align=32), from_dlpack(y, assumed_align=32))
torch.cuda.synchronize()
assert torch.equal(x, y)
print("ok", "invariant=" + str(INVARIANT))

Run CUTE_DSL_KEEP_PTX=1 python cutedsl_invariant_cache_policy_repro.py and examine the PTX, you can see ld.global.nc.v8.b32 doesn't have the cache hint despite we did pass one in CuTeDSL.

Run CUTE_DSL_KEEP_PTX=1 python cutedsl_invariant_cache_policy_repro.py --no-invariant which will use invariant=False, and examine the PTX, this time the cache hint presents: ld.global.L2::cache_hint.v4.b64

Expected behavior
I can use both cache policy and invariant hint

Environment details (please complete the following information):

  • Environment location: GB200, nvidia-cutlass-dsl==4.8.0

Additional context
Add any other context about the problem here.

Activity

  1. yunweili3 commented on Oct 10, 2026

    @yunweili3

    Reproduced on B200 with nvidia-cutlass-dsl 4.8.0, and the drop is below the Python layer.

    With CUTE_DSL_KEEP=ir-debug,ptx, the raw IR for the invariant=True case still carries the policy:

    %atom = cute.make_atom() : () -> !cute_nvgpu.atom.g2r<bf16 copy_bits = 256 invariant = true>
    %17 = cute_nvgpu.atom.set_value<cache_policy>(%atom, %4) : (!cute_nvgpu.atom.g2r<bf16 copy_bits = 256 invariant = true>, i64)
    cute.copy(%17, %grouped, %grouped_29)
    

    but the PTX comes out as ld.global.nc.v8.b32 with no L2::cache_hint, while the same copy with
    invariant=False lowers to ld.global.L2::cache_hint.v4.b64 {...}, [%rd7], %rd1. So the lowering of
    cute.copy for an invariant G2R atom takes the non-coherent-load path and never looks at the
    policy field.

    Two related observations:

    • Atom creation already refuses invariant=True together with any other non-default load
      attribute (l1c_evict_priority, load_cache_mode, ...): invariant_load is not allowed when the other non-default memory features (e.g., mem_order) are set to non-default values. The L2 policy
      escapes that check because it is a runtime value attached later through atom.set_value.
    • PTX does support the combination: ld.global.nc.L2::cache_hint.v8.b32 {...}, [addr], policy;
      assembles fine with ptxas -arch=sm_100a (CUDA 13.1), so the lowering could emit it.

    Until the lowering does, #3727 makes the Python side fail loudly instead of silently dropping
    the policy: cute.copy(..., cache_policy=...) with an invariant=True G2R atom raises a
    ValueError pointing at the two options. Workaround meanwhile: invariant=False with the policy
    (you lose the .nc path), or an inline-PTX ld.global.nc.L2::cache_hint for the hot loop.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions