Skip to content

Commit 14a94fb

Browse files
committed
Add support for cache_modifier::nc (non-coherent cache) via __ldg
1 parent f4e2d65 commit 14a94fb

5 files changed

Lines changed: 42 additions & 7 deletions

File tree

‎docs/guides/cache_modifiers.md‎

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ Kernel Float exposes these hints through the `kf::cache_modifier` enumeration.
66

77
## The `cache_modifier` enum
88

9-
`kf::cache_modifier` mirrors CUDA's `__ldca`/`__ldcg`/`__ldcs`/`__ldlu`/`__ldcv` load intrinsics and `__stwb`/`__stcg`/`__stcs`/`__stwt` store intrinsics:
9+
`kf::cache_modifier` mirrors CUDA's `__ldca`/`__ldcg`/`__ldcs`/`__ldlu`/`__ldcv`/`__ldg` load intrinsics and `__stwb`/`__stcg`/`__stcs`/`__stwt` store intrinsics:
1010

1111
| Value | Meaning | Load intrinsic | Store intrinsic |
1212
|-----------------------------|-------------------------------------------------------------|-----------------|------------------|
@@ -16,11 +16,12 @@ Kernel Float exposes these hints through the `kf::cache_modifier` enumeration.
1616
| `streaming` (alias `cs`) | Streaming access, data is likely accessed only once | `__ldcs` | `__stcs` |
1717
| `uncached` (alias `cv`/`wt`) | Bypass caching entirely and always go to memory | `__ldcv` | `__stwt` |
1818
| `last_use` (alias `lu`) | Last use; the cache line will not be reused (loads only) | `__ldlu` | - |
19+
| `read_only` (alias `nc`) | Load through the read-only/non-coherent cache (loads only, data must not be written during the kernel) | `__ldg` | - |
1920

20-
Note that you can use both the short PTX-derived name (`ca`, `cg`, `cs`, `cv`, `wt`, `lu`)
21-
or a more descriptive alias (`cache_all`, `cache_global`, `streaming`, `uncached`, `last_use`).
21+
Note that you can use both the short PTX-derived name (`ca`, `cg`, `cs`, `cv`, `wt`, `lu`, `nc`)
22+
or a more descriptive alias (`cache_all`, `cache_global`, `streaming`, `uncached`, `last_use`, `read_only`).
2223

23-
`last_use` has no store equivalent (there is no `__stlu` intrinsic), so using it with a store operation silently falls back to a plain, unmodified store.
24+
`last_use` and `read_only` have no store equivalent (there is no `__stlu` or corresponding store for `__ldg`), so using either with a store operation silently falls back to a plain, unmodified store.
2425

2526
## Using `read_aligned`/`write_aligned`
2627

@@ -64,7 +65,7 @@ Cache modifiers are a *hint*, not a guarantee. Kernel Float will attempt to emit
6465
but will fall back to regular load/store if not possible. There are several cases:
6566

6667
* The code is not being compiled for a CUDA device (e.g., host or HIP). The cache intrinsics are CUDA-specific.
67-
* The requested modifier is `normal` (or `last_use` for a store, since it has no store equivalent).
68+
* The requested modifier is `normal` (or `last_use`/`read_only` for a store, since neither has a store equivalent).
6869
* The data type being accessed has a size that does not match the supported cache intrinsics (1, 2, 4, 8, or 16 bytes).
6970

7071
In these cases, the cache hint is ignored and a regular load/store instruction is emitted.

‎include/kernel_float/memory.h‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,9 @@ enum struct cache_modifier {
136136

137137
last_use, // last use, the cache line will not be re-used afterwards (loads only, not store equivalent).
138138
lu = last_use, // Load only: last use, the cache line will not be re-used (`__ldlu`)
139+
140+
read_only, // load through the read-only/non-coherent cache (loads only, not store equivalent).
141+
nc = read_only // Load only: non-coherent, read-only cache load (`ld.global.nc`, i.e. `__ldg`).
139142
};
140143

141144
namespace detail {
@@ -210,6 +213,8 @@ KERNEL_FLOAT_INLINE S cache_load(const S* ptr) {
210213
result = __ldlu(raw_ptr);
211214
} else if constexpr (Modifier == cache_modifier::cv) {
212215
result = __ldcv(raw_ptr);
216+
} else if constexpr (Modifier == cache_modifier::nc) {
217+
result = __ldg(raw_ptr);
213218
} else {
214219
return *ptr;
215220
}

‎single_include/kernel_float.h‎

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@
1616

1717
//================================================================================
1818
// this file has been auto-generated, do not modify its contents!
19-
// date: 2026-07-23 12:03:44.219200
20-
// git hash: 91cc6e374e07f8c2222c6ded1755cb774b6fb1af
19+
// date: 2026-08-25 17:05:35.335482
20+
// git hash: f4e2d65be6e0f28156781eb766bae0fbcbe48b42
2121
//================================================================================
2222

2323
#ifndef KERNEL_FLOAT_MACROS_H
@@ -2756,6 +2756,9 @@ enum struct cache_modifier {
27562756

27572757
last_use, // last use, the cache line will not be re-used afterwards (loads only, not store equivalent).
27582758
lu = last_use, // Load only: last use, the cache line will not be re-used (`__ldlu`)
2759+
2760+
read_only, // load through the read-only/non-coherent cache (loads only, not store equivalent).
2761+
nc = read_only // Load only: non-coherent, read-only cache load (`ld.global.nc`, i.e. `__ldg`).
27592762
};
27602763

27612764
namespace detail {
@@ -2830,6 +2833,8 @@ KERNEL_FLOAT_INLINE S cache_load(const S* ptr) {
28302833
result = __ldlu(raw_ptr);
28312834
} else if constexpr (Modifier == cache_modifier::cv) {
28322835
result = __ldcv(raw_ptr);
2836+
} else if constexpr (Modifier == cache_modifier::nc) {
2837+
result = __ldg(raw_ptr);
28332838
} else {
28342839
return *ptr;
28352840
}

‎tests/memory.cu‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,10 @@ struct cache_modifier_test {
175175
auto v = kf::read_aligned<N, kf::cache_modifier::cv>(input.data);
176176
ASSERT_EQ_ALL(v[I], T(double(I)));
177177
}
178+
{
179+
auto v = kf::read_aligned<N, kf::cache_modifier::nc>(input.data);
180+
ASSERT_EQ_ALL(v[I], T(double(I)));
181+
}
178182

179183
auto v = kf::read_aligned<N>(input.data);
180184

@@ -210,6 +214,12 @@ struct cache_modifier_test {
210214
kf::write_aligned<N, kf::cache_modifier::lu>(output.data, v);
211215
ASSERT_EQ_ALL(output.data[I], T(double(I)));
212216
}
217+
{
218+
// `nc` has no store equivalent; write_aligned should fall back to a plain store.
219+
storage_type output = {T(double(I * 0))...};
220+
kf::write_aligned<N, kf::cache_modifier::nc>(output.data, v);
221+
ASSERT_EQ_ALL(output.data[I], T(double(I)));
222+
}
213223
}
214224
};
215225

‎tests_codegen/vectorized_load.cu‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,5 +87,19 @@ __global__ void vector4_load_cv(const float *input, float *output) {
8787
output[0] = kf::sum(kf::read_aligned<4, kf::cache_modifier::uncached>(input));
8888
}
8989

90+
// aligned read of 2 elements through the read-only/non-coherent cache, packed
91+
// into a single scalar 64-bit load (no native .vN form for cache-modified loads)
92+
// CHECK: ld.global.nc.u64
93+
__global__ void vector2_load_nc(const float *input, float *output) {
94+
output[0] = kf::sum(kf::read_aligned<2, kf::cache_modifier::read_only>(input));
95+
}
96+
97+
// aligned read of 4 elements through the read-only/non-coherent cache,
98+
// vectorized as a v2.u64 load
99+
// CHECK: ld.global.nc.v2.u64
100+
__global__ void vector4_load_nc(const float *input, float *output) {
101+
output[0] = kf::sum(kf::read_aligned<4, kf::cache_modifier::read_only>(input));
102+
}
103+
90104

91105
}

0 commit comments

Comments
 (0)