Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
89 commits
Select commit Hold shift + click to select a range
d521341
Bound Metal buffer COUNT, not just bytes, in MetalAllocator
anupsv Jun 15, 2026
008c51c
Merge pull request #1 from Layr-Labs/fix/metal-resource-count-trim
anupsv Jun 15, 2026
aa480bd
Bound Metal buffer COUNT, not just bytes, in MetalAllocator
anupsv Jun 15, 2026
921d1ba
Merge darkbloom-base: its resource-count trim is already included on …
Gajesh2007 Jun 25, 2026
d5a2404
Merge pull request #3 from Layr-Labs/darkbloom/mlx-0.32.0-nax
Gajesh2007 Jun 25, 2026
a4b2b4c
perf(mlx): opt-in Gemma 4 expert-QMM tile kernel with parallel descri…
Gajesh2007 Aug 10, 2026
d3c82db
perf(metal): E=256 expert-tile route + trust + gpu::eval UAF fix — da…
Gajesh2007 Aug 13, 2026
9b6575c
Return tuple in meshgrid (#4229)
JasonHonKL Aug 13, 2026
1d717bd
Add endpoint parameter to linspace (#4184)
AKnassa Aug 14, 2026
306bdcd
Fix vmap of partition/argpartition dropping the kth argument (#4116)
Adityaj0 Aug 14, 2026
d9ad465
Fix nan_to_num replacing inf with 0 for float16 and bfloat16 (#4222)
codeAnqiang-ma Aug 14, 2026
bd5c3a2
Fix einsum not broadcasting batch dimensions in batched tensordot (#4…
Adityaj0 Aug 14, 2026
56e026d
Dequantize in float32 (#4241)
RohanGautam Aug 14, 2026
6ba2d44
chore: Reject complex in erf and erfinv (#4243)
ayaangazali Aug 14, 2026
3d23f7d
Fix cpu compilation failure of abs with uint (#4240)
ayaangazali Aug 14, 2026
4947e3b
Fix quantize matrix multiplication floor issue (#4251)
JasonHonKL Aug 14, 2026
adf21de
Only use MPI backend for world size > 1 (#4210)
erwinzhang7 Aug 14, 2026
140faa8
chore: Reject complex in expm1, sigmoid and arctan2 (#4257)
ayaangazali Aug 15, 2026
9ab977b
Decompose small kernel-depth 3D convs into 2D convs (#3785)
katlun-lgtm Aug 15, 2026
052d428
Fix Metal sort of a view with a negative stride (#4252)
robertomeroni Aug 16, 2026
b34545d
Mirror the depth axis in the decomposed 3D conv when flipped (#4277)
ayaangazali Aug 16, 2026
a1e0e0b
Fix Metal row reductions on negative-stride views (#4267)
FU-max-boop Aug 16, 2026
9f5f793
[CUDA] Fix custom kernel cache collision for same name, different sou…
haoxu0 Aug 16, 2026
2542e08
Fix ops rejecting integers larger than INT32_MAX (#4255)
FeliGame Aug 16, 2026
4b342c0
Fix var/std for complex numbers (#4260)
ayaangazali Aug 16, 2026
3973edd
Fix int32 overflow in conv padded input and pad shapes (#4258)
eyupcanakman Aug 16, 2026
bac07b3
chore: Reject complex in remainder (#4270)
ayaangazali Aug 16, 2026
c2bcf47
chore: Compare the macOS SDK version as a version when gating JACCL (…
erwinzhang7 Aug 16, 2026
a44fc8c
Clamp ring socket transfers so a payload of 2 GiB or more can be sent…
erwinzhang7 Aug 16, 2026
bbebc8f
chore: Use normalize_axis_index in split/unstack/partition/topk (#4288)
Adityaj0 Aug 16, 2026
61a4867
Remove grouped output in CI (#4195)
zcbenz Aug 17, 2026
0bb8121
[CUDA] Fix finding cuda 13 headers in JIT compilation (#3995)
zcbenz Aug 17, 2026
d9e2b0d
Refactor wheel building script (#3818)
zcbenz Aug 17, 2026
8e00a2d
Make mx.compile cache erasing thread safe (#4248)
zcbenz Aug 17, 2026
9a79573
Add builds for free-threaded python (#3812)
zcbenz Aug 17, 2026
98a188c
Fix int32 overflow in concatenate/repeat/kron (#4303)
ayaangazali Aug 17, 2026
e0fe540
python: Widen list elements that do not fit in int32 to int64 (#4305)
ayaangazali Aug 17, 2026
06f154b
Propagate CPU errors to events (#3742)
zcbenz Aug 17, 2026
d331598
Fix mx.arange dtype inference overflow regression (#4324)
shoutoutuoadi325 Aug 18, 2026
cee24b3
Add workflow to update pull request limit bypass list (#4320)
zcbenz Aug 18, 2026
3a62199
Support head dimension 72 in Metal full attention (#4330)
dhiltgen Aug 18, 2026
2900658
Patch bump to 0.32.2 (#4333)
zcbenz Aug 18, 2026
f889b46
Preserve subnormal float values when casting to bool (#4224)
reckylurker Aug 18, 2026
6172852
python: Support assigning through a bare Ellipsis index (#4314)
Adityaj0 Aug 18, 2026
d5841be
Fix divmod truncating the quotient for floats (#4108)
ayaangazali Aug 18, 2026
772bc8a
Add force_fused option to scaled_dot_product_attention (#4185)
hojin12312 Aug 18, 2026
7f062dd
chore: Reject negative eps in the normalization layers (#4312)
ayaangazali Aug 18, 2026
a4a2c1e
Bound GGUF metadata string/array values against the file mapping (#4212)
x14ngch3n Aug 18, 2026
fa0d446
Read each K/V byte once in gqa-8 decode attention (#4077)
dudududukim Aug 18, 2026
3e8113c
Fix fft vmap and jvp for transforms over a subset of axes (#4138)
kapellirohith Aug 19, 2026
db935c0
Fix median dropping NaN (#4146)
devteamaegis Aug 19, 2026
eb38e25
Fix the CPU scan over a size one axis with a padded stride (#4139)
kapellirohith Aug 19, 2026
8a81722
chore: Validate the optimizer betas at construction (#4310)
ayaangazali Aug 19, 2026
6c0f02a
`RMSNormVJP` backward writes a full `{n_rows, D}` `gw_temp` intermedi…
JasonHonKL Aug 19, 2026
3a98589
[Bug]: add default none value to axis parameter of the take_along_axi…
aaishwarymishra Aug 19, 2026
714a7ef
Add a fused full-attention path for head_dim 256 on NAX devices (#3842)
wyanzhao Aug 19, 2026
e2c1e28
Update nanobind to 2.15.0 (#4337)
XXXXRT666 Aug 20, 2026
c7ff35d
Skip unnecessary simdgroup computations for quantised MOE matmuls on …
RohanGautam Aug 20, 2026
057cdc9
Add AI usage policy (#4331)
zcbenz Aug 20, 2026
994d9d5
Raise cpu stream errors from synchronize (#4338)
robertomeroni Aug 20, 2026
b3f1e1e
chore: Validate eps in Adam at construction (#4361)
vraj00222 Aug 20, 2026
27fec90
Bound winograd conv2d working set by tiling the batch (#4102)
Gusanidas Aug 20, 2026
a082cb9
Use a 32-row block in qmm_t_nax when one block covers all of M (#4171)
dwijenpatel Aug 21, 2026
dcf4b2a
chore: Deduplicate fftshift and ifftshift (#4318)
Adityaj0 Aug 21, 2026
0a725e3
perf(metal): port small-batch qmv_wide (#9)
Gajesh2007 Aug 21, 2026
18672fb
Fix Log and Equal is_equivalent ignoring primitive state (#4266)
kapellirohith Aug 22, 2026
9b95475
Stabilize reduced-precision InstanceNorm (#4230)
ternaus Aug 22, 2026
846d176
chore: Normalize negative axes in sort and argsort (#4332)
deBrian07 Aug 22, 2026
1038679
Clean up main thread compile cache before python interpreter shuts do…
zcbenz Aug 22, 2026
c7a185a
chore: Check malformed jaccl hostfile that miss rdma in pairs (#4284)
erwinzhang7 Aug 22, 2026
02adf7b
Round mxfp8 block scales up to avoid saturation (#4353)
dhiltgen Aug 23, 2026
7789905
Add support for the __array_namespace_info__ (#4334)
aaishwarymishra Aug 23, 2026
ab3ef95
Stop a failed CUDA graph commit from poisoning the encoder (#4356)
strayberry Aug 23, 2026
7408e68
Fix quantized kernels in JIT build (#4372)
dwijenpatel Aug 23, 2026
d9077d8
Avoid zero work in stride-2 ConvTranspose3d (#4343)
ternaus Aug 23, 2026
451dc87
[CUDA] Ce fused kernel (#3947)
nastya236 Aug 23, 2026
29b61c0
Fix cpu exclusive scan for complex numbers (#4272)
ayaangazali Aug 24, 2026
9d173ff
Support Relocatable CUDA DLLs on Windows (#4382)
dhiltgen Aug 24, 2026
c793734
Use cast_to for fused AsType in compiled Metal kernels (#4351)
katlun-lgtm Aug 24, 2026
9d16475
python: Declare DLPackCompatible protocol members as methods (#4384)
Adityaj0 Aug 24, 2026
a9eed5a
Fix quantizing sliced arrays (#4381)
zcbenz Aug 24, 2026
43d2f06
Fix einsum dropping a trailing empty subscript (#4299)
Adityaj0 Aug 24, 2026
17a9316
Merge remote-tracking branch 'upstream/main' into codex/merge/mlx-ups…
glg2672 Aug 24, 2026
768b4c5
fix: resolve upstream fp qvm merge
glg2672 Aug 24, 2026
1e93028
fix: remove duplicate qmv wide merge
glg2672 Aug 24, 2026
bc42b3d
style: format merged MLX changes
glg2672 Aug 25, 2026
6d12ff2
test: synchronize shared-buffer teardown
glg2672 Aug 25, 2026
2c000e5
Merge origin/main into codex/merge/mlx-upstream
glg2672 Aug 26, 2026
f647d24
fix: remove duplicate expert QMM device accessors
glg2672 Aug 26, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 7 additions & 8 deletions mlx/backend/common/gemma4_expert_qmm.h
Original file line number Diff line number Diff line change
Expand Up @@ -119,11 +119,11 @@ inline Gemma4ExpertQMMRoute classify_gemma4_expert_qmm(
return Gemma4ExpertQMMRoute::fallback_nax;
}
if (!input.affine || !input.transpose || !input.has_bias ||
!input.indices_uint32 || !input.indices_contiguous ||
!input.x_bfloat16 || !input.x_contiguous || !input.w_uint32 ||
!input.w_contiguous || !input.scales_bfloat16 ||
!input.scales_contiguous || !input.biases_bfloat16 ||
!input.biases_contiguous || input.group_size != 64 || input.bits != 4) {
!input.indices_uint32 || !input.indices_contiguous || !input.x_bfloat16 ||
!input.x_contiguous || !input.w_uint32 || !input.w_contiguous ||
!input.scales_bfloat16 || !input.scales_contiguous ||
!input.biases_bfloat16 || !input.biases_contiguous ||
input.group_size != 64 || input.bits != 4) {
return Gemma4ExpertQMMRoute::fallback_quantization;
}
const bool gemma4 = input.expert_count == 128;
Expand Down Expand Up @@ -183,9 +183,8 @@ struct Gemma4ExpertQMMCounterSnapshot {
bool armed{false};

uint64_t attempts() const {
return hits + fallback_nax + fallback_outer_route +
fallback_quantization + fallback_topology +
fallback_assignment_count + fallback_geometry +
return hits + fallback_nax + fallback_outer_route + fallback_quantization +
fallback_topology + fallback_assignment_count + fallback_geometry +
fallback_metallib_unavailable + fallback_sortedness_retracted;
}
};
Expand Down
38 changes: 20 additions & 18 deletions mlx/backend/metal/allocator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@

#include <mach/vm_page_size.h>
#include <unistd.h>
#include <cassert>
#include <algorithm>
#include <cassert>
#include <cerrno>
#include <cstdlib>
#include <limits>
Expand Down Expand Up @@ -70,13 +70,13 @@ MetalAllocator::MetalAllocator(Device& d)
// crash. The value may only LOWER the ceiling (it is clamped to the OS limit)
// — raising it above what the hardware/OS reports would invite the very crash
// this guards against. Strictly validated: a plain unsigned decimal that
// consumes the whole string, is non-zero, and does not overflow; anything else
// (empty, sign, junk, range error) is ignored and the OS limit stands.
// consumes the whole string, is non-zero, and does not overflow; anything
// else (empty, sign, junk, range error) is ignored and the OS limit stands.
if (const char* rl = std::getenv("MLX_RESOURCE_LIMIT")) {
while (*rl == ' ' || *rl == '\t') {
++rl;
}
if (*rl >= '0' && *rl <= '9') { // unsigned decimal only (reject sign/junk)
if (*rl >= '0' && *rl <= '9') { // unsigned decimal only (reject sign/junk)
errno = 0;
char* end = nullptr;
unsigned long long v = std::strtoull(rl, &end, 10);
Expand Down Expand Up @@ -158,11 +158,13 @@ Buffer MetalAllocator::malloc(size_t size) {
if (!buf) {
size_t mem_required = get_active_memory() + get_cache_memory() + size;

auto pool = metal::new_scoped_memory_pool();

// If we have a lot of memory pressure try to reclaim memory from the cache.
// NOTE: release_cached_buffers takes a BYTES-to-free target; when the buffers
// are tiny this frees only a few entries even though the COUNT is the binding
// constraint, so the byte path alone cannot bound num_resources_ (see the
// count-aware reclaim below).
// NOTE: release_cached_buffers takes a BYTES-to-free target; when the
// buffers are tiny this frees only a few entries even though the COUNT is
// the binding constraint, so the byte path alone cannot bound
// num_resources_ (see the count-aware reclaim below).
if (mem_required >= gc_limit_ || num_resources_ >= resource_limit_) {
num_resources_ -=
buffer_cache_.release_cached_buffers(mem_required - gc_limit_);
Expand All @@ -171,18 +173,18 @@ Buffer MetalAllocator::malloc(size_t size) {
// Count-aware reclaim (Darkbloom): the Metal resource COUNT limit
// (resource_limit_, ~iogpu.rsrc_limit/499000) is independent of byte usage.
// Under churn with many distinct buffer shapes (varied prompt lengths,
// growing KV caches, multiple co-resident models) freed buffers are recycled
// into the size-keyed cache and never reused at that exact size, so the cache
// ENTRY COUNT creeps toward the limit while byte usage stays modest — the
// byte-driven trim above never fires (its threshold is ~physical RAM). Once
// the count crosses a high-water mark, proactively clear the cache (pure
// reuse pool — clearing only costs re-allocation, never correctness) so the
// count drops back to the live working set. This makes the count limit
// unreachable by any request mix / batching method, while the existing byte
// limits keep total memory below physical RAM.
// growing KV caches, multiple co-resident models) freed buffers are
// recycled into the size-keyed cache and never reused at that exact size,
// so the cache ENTRY COUNT creeps toward the limit while byte usage stays
// modest — the byte-driven trim above never fires (its threshold is
// ~physical RAM). Once the count crosses a high-water mark, proactively
// clear the cache (pure reuse pool — clearing only costs re-allocation,
// never correctness) so the count drops back to the live working set. This
// makes the count limit unreachable by any request mix / batching method,
// while the existing byte limits keep total memory below physical RAM.
if (resource_limit_ > 0 &&
num_resources_ >= (resource_limit_ * resource_high_water_num_) /
resource_high_water_den_) {
resource_high_water_den_) {
num_resources_ -= buffer_cache_.clear();
}

Expand Down
15 changes: 5 additions & 10 deletions mlx/backend/metal/device.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -589,8 +589,7 @@ Device::Device() : device_(load_device()), residency_sets_(device_.get()) {
auto pool = new_scoped_memory_pool();
default_library_ = NS::TransferPtr(load_default_library(device_.get()));

std::string expert_qmm_env =
env::get_var("MLX_GATHER_QMM_EXPERT_SLICES", "");
std::string expert_qmm_env = env::get_var("MLX_GATHER_QMM_EXPERT_SLICES", "");
std::transform(
expert_qmm_env.begin(),
expert_qmm_env.end(),
Expand All @@ -615,16 +614,14 @@ Device::Device() : device_(load_device()), residency_sets_(device_.get()) {
"alN_true_bm_32_bn_32_bk_32";
auto has_default_function = [this](const char* name) {
auto ns_name = NS::String::string(name, NS::ASCIIStringEncoding);
auto function =
NS::TransferPtr(default_library_->newFunction(ns_name));
auto function = NS::TransferPtr(default_library_->newFunction(ns_name));
return function.get() != nullptr;
};
// All expert-tile symbols ship from one source-matched metallib
// (scripts/fetch-metallib.sh completeness contract), so availability is
// all-or-nothing: a metallib missing any of them predates this revision
// and must fail the whole route closed.
gemma4_expert_qmm_aot_available_ =
has_default_function(descriptor_kernel) &&
gemma4_expert_qmm_aot_available_ = has_default_function(descriptor_kernel) &&
has_default_function(descriptor_kernel_e256) &&
has_default_function(tile_kernel);
if (gemma4_expert_qmm_requested_ && gemma4_expert_qmm_aot_available_) {
Expand Down Expand Up @@ -1043,8 +1040,7 @@ void gemma4_expert_qmm_diagnostics_snapshot(
diagnostics->fallback_outer_route = counters.fallback_outer_route;
diagnostics->fallback_quantization = counters.fallback_quantization;
diagnostics->fallback_topology = counters.fallback_topology;
diagnostics->fallback_assignment_count =
counters.fallback_assignment_count;
diagnostics->fallback_assignment_count = counters.fallback_assignment_count;
diagnostics->fallback_geometry = counters.fallback_geometry;
diagnostics->fallback_metallib_unavailable =
counters.fallback_metallib_unavailable;
Expand Down Expand Up @@ -1080,8 +1076,7 @@ extern "C" void mlx_metal_gemma4_expert_qmm_diagnostics_clear_and_arm(void) {
}
}

extern "C" void
mlx_metal_gemma4_expert_qmm_diagnostics_snapshot_and_disarm(
extern "C" void mlx_metal_gemma4_expert_qmm_diagnostics_snapshot_and_disarm(
mlx_metal_gemma4_expert_qmm_diagnostics* diagnostics) {
gemma4_expert_qmm_diagnostics_snapshot(diagnostics, true);
}
Expand Down
9 changes: 5 additions & 4 deletions mlx/backend/metal/device.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include <unordered_set>

#include "mlx/array.h"
#include "mlx/backend/common/gemma4_expert_qmm.h"
#include "mlx/backend/common/metal_kernel.h"
#include "mlx/backend/common/gemma4_expert_qmm.h"
#include "mlx/backend/metal/resident.h"
Expand Down Expand Up @@ -196,10 +197,6 @@ class MLX_API Device {
const MTLFCList& func_consts = {},
const std::vector<MTL::Function*>& linked_functions = {});

ResidencySets& residency_sets() {
return residency_sets_;
}

bool gemma4_expert_qmm_requested() const {
return gemma4_expert_qmm_requested_;
}
Expand Down Expand Up @@ -240,6 +237,10 @@ class MLX_API Device {
gemma4_expert_qmm_counters_.clear_and_arm();
}

ResidencySets& residency_sets() {
return residency_sets_;
}

private:
NS::SharedPtr<MTL::Library> build_library_(
const std::string& source_string,
Expand Down
18 changes: 6 additions & 12 deletions mlx/backend/metal/kernels/quantized.h
Original file line number Diff line number Diff line change
Expand Up @@ -2615,8 +2615,7 @@ template <int NE>
for (int i = int(lid) + 1; i < M; i += int(expert_count)) {
adjacent_ok = adjacent_ok && indices[i - 1] <= indices[i];
}
const uint violation_vote =
simd_or((boundary_ok && adjacent_ok) ? 0u : 1u);
const uint violation_vote = simd_or((boundary_ok && adjacent_ok) ? 0u : 1u);
if (simd_lid == 0) {
violation_votes[simd_gid] = violation_vote;
}
Expand Down Expand Up @@ -2650,8 +2649,7 @@ template <int NE>
threadgroup_barrier(mem_flags::mem_threadgroup);
}

const uint descriptor_count =
inclusive_tile_offsets[expert_count - 1];
const uint descriptor_count = inclusive_tile_offsets[expert_count - 1];
if (lid == expert_count - 1) {
// A retracted count keeps the tile kernel's capacity check memory-safe
// (every threadgroup early-returns) and unambiguously signals the host:
Expand All @@ -2666,8 +2664,7 @@ template <int NE>
uint expert_lower = 0;
uint expert_upper = expert_count;
while (expert_lower < expert_upper) {
const uint midpoint =
expert_lower + (expert_upper - expert_lower) / 2;
const uint midpoint = expert_lower + (expert_upper - expert_lower) / 2;
if (inclusive_tile_offsets[midpoint] <= slot) {
expert_lower = midpoint + 1;
} else {
Expand All @@ -2677,10 +2674,8 @@ template <int NE>
const uint expert = expert_lower;
const uint expert_tile_begin =
expert == 0 ? 0 : inclusive_tile_offsets[expert - 1];
const uint row =
segment_starts[expert] + (slot - expert_tile_begin) * BM;
const uint row_count =
min(BM, segment_starts[expert + 1] - row);
const uint row = segment_starts[expert] + (slot - expert_tile_begin) * BM;
const uint row_count = min(BM, segment_starts[expert + 1] - row);
descriptors[slot] = uint4(row, row_count, expert, 0);
}
}
Expand Down Expand Up @@ -2737,8 +2732,7 @@ template <
x += row_start * size_t(K);
y += row_start * size_t(N);
const device uint8_t* expert_w =
reinterpret_cast<const device uint8_t*>(w) +
expert * expert_w_stride;
reinterpret_cast<const device uint8_t*>(w) + expert * expert_w_stride;
scales += expert * expert_sb_stride;
biases += expert * expert_sb_stride;

Expand Down
21 changes: 7 additions & 14 deletions mlx/backend/metal/quantized.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@

namespace mlx::core {

using metal::classify_gemma4_expert_qmm;
using metal::Gemma4ExpertQMMRoute;
using metal::Gemma4ExpertQMMRouteInput;
using metal::classify_gemma4_expert_qmm;

namespace {

Expand Down Expand Up @@ -1691,8 +1691,7 @@ Gemma4ExpertQMMRoute try_gemma4_expert_qmm(
compute_encoder.set_bytes(N, c++);

compute_encoder.dispatch_threadgroups(
MTL::Size((N + bn - 1) / bn, max_tile_count, 1),
MTL::Size(32, wn, wm));
MTL::Size((N + bn - 1) / bn, max_tile_count, 1), MTL::Size(32, wn, wm));
return Gemma4ExpertQMMRoute::hit;
}

Expand Down Expand Up @@ -1721,8 +1720,7 @@ void gather_qmm_rhs(
route_input.requested = true;
route_input.outer_route = true;
route_input.nax_available = true;
d.record_armed_gemma4_expert_qmm(
classify_gemma4_expert_qmm(route_input));
d.record_armed_gemma4_expert_qmm(classify_gemma4_expert_qmm(route_input));
}
return gather_qmm_rhs_nax(
/* const array& x_ = */ x_,
Expand Down Expand Up @@ -1798,8 +1796,7 @@ void gather_qmm_rhs(
route_input.biases_contiguous = biases_ && biases_->flags().row_contiguous;
route_input.group_size = group_size;
route_input.bits = bits;
route_input.expert_count =
w.size() / w.shape(-1) / w.shape(-2);
route_input.expert_count = w.size() / w.shape(-1) / w.shape(-2);
route_input.assignments = M;
route_input.index_count = indices.size();
route_input.k = K;
Expand Down Expand Up @@ -2078,11 +2075,8 @@ void QuantizedMatmul::eval_gpu(const std::vector<array>& inputs, array& out) {
// record below and the dispatch decision evaluate this one predicate so a
// future tuning change cannot desynchronize them.
// TODO: Tune 16 and 4 here a bit better.
static constexpr bool takes_sorted_rhs_route(
int M,
int B,
int E,
bool right_sorted) {
static constexpr bool
takes_sorted_rhs_route(int M, int B, int E, bool right_sorted) {
return M == 1 && B >= 16 && right_sorted && B / E >= 4;
}

Expand Down Expand Up @@ -2116,8 +2110,7 @@ void GatherQMM::eval_gpu(const std::vector<array>& inputs, array& out) {
Gemma4ExpertQMMRouteInput route_input;
route_input.requested = true;
route_input.outer_route = false;
d.record_armed_gemma4_expert_qmm(
classify_gemma4_expert_qmm(route_input));
d.record_armed_gemma4_expert_qmm(classify_gemma4_expert_qmm(route_input));
}

// We are walking x in order and w is also in order so we can batch up the
Expand Down
1 change: 1 addition & 0 deletions tests/array_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -601,6 +601,7 @@ TEST_CASE("test array shared buffer") {
array b = array(buf_b, shape, float32, deleter);

eval(a + b);
synchronize();
}

TEST_CASE("test make empty array") {
Expand Down
Loading