Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
9 changes: 5 additions & 4 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,14 +60,15 @@ All services are plain classes (no dependency injection container):

A local SPM package (the app's only package dependency) that wraps the vendored whisper.cpp C/C++ library — including Whisper and Parakeet — for use from Swift:

- `LocalWhisper/vendor/` — Git submodule pinned to a whisper.cpp release tag (currently v1.9.1), containing the ggml + whisper + parakeet source.
- `LocalWhisper/vendor/` — Git submodule pinned to a whisper.cpp release tag (currently v1.9.4), containing the ggml + whisper + parakeet source.
- `LocalWhisper/include/` — Copies of the public headers (`whisper.h`, `parakeet.h`, ggml headers) and `module.modulemap`. These are physical copies, not symlinks.
- `LocalWhisper/MetalObjC/` — The ggml Metal backend: the `.metal` shader + its headers (raw copies of the vendor sources) and thin `*-mrc.m` wrappers compiled without ARC.
- `LocalWhisper/Package.swift` — Hand-curated source list compiling only Apple-relevant sources (ARM NEON, Accelerate, Metal, CoreML encoder). Excludes CUDA, Vulkan, x86, and other non-Apple backends. Because there is no CMake, version macros (`WHISPER_VERSION`, `PARAKEET_VERSION`, etc.) must be defined here manually.
- `LocalWhisper/MetalObjC/` — The ggml Metal backend: `kernels/` (per-kind `.metal` sources + `common.h`/`quantize.h`/`dequantize.h`), `ggml-common.h`, `ggml-metal-impl.h` (all raw copies of the vendor sources), and thin `*-mrc.m` wrappers compiled without ARC. The kernels and headers are bundled as resources with their layout preserved; ggml compiles them at runtime and resolves their `#include`s relative to `kernels/` and its parent.
- `LocalWhisper/generated/ggml-version.h` — Hand-maintained stand-in for the CMake-generated header (`GGML_VERSION`, `GGML_COMMIT`).
- `LocalWhisper/Package.swift` — Hand-curated source list compiling only Apple-relevant sources (ARM NEON, Accelerate, Metal, CoreML encoder). Excludes CUDA, Vulkan, x86, and other non-Apple backends. Because there is no CMake, version macros (`WHISPER_VERSION`, `PARAKEET_VERSION`) must be defined here manually.
- Linked frameworks: Accelerate, CoreML, Metal, MetalKit
- Key defines: `GGML_USE_ACCELERATE`, `GGML_USE_CPU`, `GGML_USE_METAL`, `WHISPER_USE_COREML`, `WHISPER_COREML_ALLOW_FALLBACK`

Bumping the vendored whisper.cpp version is a manual multi-step sync (submodule checkout + re-copy headers/Metal sources + update `Package.swift`), not just a submodule update.
Bumping the vendored whisper.cpp version is a manual multi-step sync (submodule checkout + re-copy headers/Metal sources + update `Package.swift` sources/excludes/version macros + update `generated/ggml-version.h`), not just a submodule update. Check `ggml/src/ggml-metal/CMakeLists.txt` for new kernel files and the vendor diff for new `.cpp` files or generated `.h.in` headers.

## Key Patterns

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>SchemeUserState</key>
<dict>
<key>LocalWhisper.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>1</integer>
</dict>
</dict>
</dict>
</plist>
17 changes: 14 additions & 3 deletions LocalWhisper/MetalObjC/ggml-common.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,9 @@ typedef sycl::half2 ggml_half2;
#define QI1_0 (QK1_0 / 32)
#define QR1_0 1

#define QI2_0 (QK2_0 / 32)
#define QR2_0 1


#define QI4_0 (QK4_0 / (4 * QR4_0))
#define QR4_0 2
Expand Down Expand Up @@ -181,6 +184,13 @@ typedef struct {
} block_q1_0;
static_assert(sizeof(block_q1_0) == sizeof(ggml_half) + QK1_0 / 8, "wrong q1_0 block size/padding");

#define QK2_0 64
typedef struct {
ggml_half d; // delta (scale)
uint8_t qs[QK2_0 / 4]; // 2 bits per element
} block_q2_0;
static_assert(sizeof(block_q2_0) == sizeof(ggml_half) + QK2_0 / 4, "wrong q2_0 block size/padding");

#define QK4_0 32
typedef struct {
ggml_half d; // delta
Expand Down Expand Up @@ -1111,16 +1121,17 @@ GGML_TABLE_BEGIN(int8_t, kvalues_iq4nl, 16)
-127, -104, -83, -65, -49, -35, -22, -10, 1, 13, 25, 38, 53, 69, 89, 113,
GGML_TABLE_END()

// e2m1 values (doubled)
// e2m1 values (doubled), shared by MXFP4 and NVFP4
// ref: https://www.opencompute.org/documents/ocp-microscaling-formats-mx-v1-0-spec-final-pdf
GGML_TABLE_BEGIN(int8_t, kvalues_mxfp4, 16)
GGML_TABLE_BEGIN(int8_t, kvalues_fp4, 16)
0, 1, 2, 3, 4, 6, 8, 12, 0, -1, -2, -3, -4, -6, -8, -12,
GGML_TABLE_END()
#define kvalues_mxfp4 kvalues_fp4

#define NGRID_IQ1S 2048
#define IQ1S_DELTA 0.125f
#define IQ1M_DELTA 0.125f
#if defined(GGML_COMMON_IMPL_C)
#if defined(GGML_COMMON_IMPL_C) || defined(GGML_COMMON_IMPL_CPP)
GGML_TABLE_BEGIN(uint64_t, iq1s_grid, NGRID_IQ1S)
0xffffffffffffffff, 0xffffffffffffff01, 0xffffffffffff0000, 0xffffffffffff01ff,
0xffffffffffff0101, 0xffffffffff00ff00, 0xffffffffff000000, 0xffffffffff01ffff,
Expand Down
170 changes: 170 additions & 0 deletions LocalWhisper/MetalObjC/ggml-metal-impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@
#define N_R0_Q1_0 8
#define N_SG_Q1_0 2

#define N_R0_Q2_0 8
#define N_SG_Q2_0 2

#define N_R0_Q4_0 4
#define N_SG_Q4_0 2

Expand Down Expand Up @@ -84,6 +87,9 @@
#define N_R0_IQ4_XS 2
#define N_SG_IQ4_XS 2

#define N_R0_TQ2_0 4
#define N_SG_TQ2_0 2

// function constants offsets
#define FC_FLASH_ATTN_EXT_PAD 100
#define FC_FLASH_ATTN_EXT_BLK 200
Expand All @@ -109,6 +115,13 @@
#define OP_FLASH_ATTN_EXT_VEC_NQPSG 1
#define OP_FLASH_ATTN_EXT_VEC_NCPSG 32

#define OP_LIGHTNING_INDEXER_DK 128
#define OP_LIGHTNING_INDEXER_NH 64
#define OP_LIGHTNING_INDEXER_NHPTG 8
#define OP_LIGHTNING_INDEXER_NKPSG 8
#define OP_LIGHTNING_INDEXER_NSG 8
#define OP_LIGHTNING_INDEXER_NBPTG 8

#define OP_UNARY_NUM_SCALE 10
#define OP_UNARY_NUM_FILL 11
#define OP_UNARY_NUM_CLAMP 12
Expand Down Expand Up @@ -145,6 +158,10 @@
#define OP_SUM_ROWS_NUM_SUM_ROWS 10
#define OP_SUM_ROWS_NUM_MEAN 11

#define OP_SSM_SCAN_SSD_CS 64 // Metal-specific; Chunk Size; 64 is largest multiple of 8 (simdgroup tile) fitting into 32 KiB Metal threadgroup mem limit (~26.75 KiB shared mem; see smem layout comment in kernel_ssm_scan_ssd_mma_f32)
#define OP_SSM_SCAN_SSD_HD 64 // Metal-specific; Head Dim the MMA kernel is specialized for (Mamba-2); use_mma gates on d_inner == this
#define OP_SSM_SCAN_SSD_NSG 4 // Metal-specific; Number of SimdGroups per threadgroup; NSG*32 == threads dispatched per threadgroup

// kernel argument structs
//
// - element counters (e.g. ne00) typically use int32_t to reduce register usage
Expand Down Expand Up @@ -316,6 +333,7 @@ typedef struct {
uint64_t nb3;
int32_t n_past;
int32_t n_dims;
int32_t n_offs;
int32_t n_ctx_orig;
float freq_base;
float freq_scale;
Expand All @@ -328,8 +346,21 @@ typedef struct {
int32_t sect_2;
int32_t sect_3;
bool src2;
bool inplace;
} ggml_metal_kargs_rope;

typedef struct {
int32_t ne0;
int32_t ne1;
int32_t ne2;
int32_t ne3;
uint64_t nb0;
uint64_t nb1;
uint64_t nb2;
uint64_t nb3;
int32_t nblocks;
} ggml_metal_kargs_flash_attn_ext_kv_f16;

typedef struct {
int32_t ne11;
int32_t ne_12_2; // assume K and V are same shape
Expand Down Expand Up @@ -427,8 +458,21 @@ typedef struct {
float m1;
int32_t n_head_log2;
float logit_softcap;
int32_t n_kv_max_padded;
} ggml_metal_kargs_flash_attn_ext_vec;

typedef struct {
int32_t ne30;
int32_t ne31;
int32_t ne32;
int32_t ne33;
uint64_t nb31;
uint64_t nb32;
uint64_t nb33;
int32_t n_kv_max;
int32_t n_kv_max_padded;
} ggml_metal_kargs_flash_attn_ext_vec_idx;

typedef struct {
int32_t nrows;
} ggml_metal_kargs_flash_attn_ext_vec_reduce;
Expand Down Expand Up @@ -603,6 +647,21 @@ typedef struct {
uint64_t nb1;
} ggml_metal_kargs_conv_transpose_1d;

typedef struct {
int32_t T_in;
int32_t T_out;
int32_t OC;
int32_t K;
int32_t K_OC;
int32_t s0;
int32_t p0;
} ggml_metal_kargs_col2im_1d;

typedef struct {
int32_t T;
int32_t C;
} ggml_metal_kargs_snake;

typedef struct {
int32_t IC;
int32_t IH;
Expand All @@ -614,6 +673,7 @@ typedef struct {
uint64_t nb0;
uint64_t nb1;
uint64_t nb2;
uint64_t nb3;
} ggml_metal_kargs_conv_transpose_2d;

typedef struct {
Expand Down Expand Up @@ -646,6 +706,34 @@ typedef struct {
int32_t d1;
} ggml_metal_kargs_conv_2d;

typedef struct {
uint64_t nb00; // kernel strides
uint64_t nb01;
uint64_t nb02;
uint64_t nb10; // input strides
uint64_t nb11;
uint64_t nb12;
uint64_t nb13;
uint64_t nb0; // output strides
uint64_t nb1;
uint64_t nb2;
uint64_t nb3;
int32_t IW; // input width
int32_t IH; // input height
int32_t KW; // kernel width
int32_t KH; // kernel height
int32_t C; // channels (IC == OC for depthwise)
int32_t OW; // output width
int32_t OH; // output height
int32_t N; // batch size
int32_t s0; // stride x
int32_t s1; // stride y
int32_t p0; // padding x
int32_t p1; // padding y
int32_t d0; // dilation x
int32_t d1; // dilation y
} ggml_metal_kargs_conv_2d_dw;

typedef struct {
uint64_t ofs0;
uint64_t ofs1;
Expand Down Expand Up @@ -823,7 +911,10 @@ typedef struct {
int64_t n_head;
int64_t n_group;
int64_t n_seq_tokens;
int64_t n_seq_tokens_total;
int64_t token_offset;
int64_t n_seqs;
int64_t K;
uint64_t s_off;
uint64_t nb00;
uint64_t nb01;
Expand Down Expand Up @@ -1111,6 +1202,21 @@ typedef struct {
int32_t len;
} ggml_metal_kargs_argsort_merge;

typedef struct {
int32_t ne00; // number of columns (elements per row)
int32_t ne01; // rows
int32_t ne02;
int32_t ne03;
uint64_t nb01; // row stride in src0
uint64_t nb02;
uint64_t nb03;
int32_t top_k; // k
} ggml_metal_kargs_top_k;

typedef struct {
int32_t nrows;
} ggml_metal_kargs_fwht;

typedef struct {
int64_t ne0;
float start;
Expand All @@ -1121,6 +1227,66 @@ typedef struct {
int64_t val;
} ggml_metal_kargs_memset;

typedef struct {
int32_t n_kv;
int32_t n_batch;
int32_t mask_ne3;
uint64_t nb1;
uint64_t nb3;
uint64_t nbq1;
uint64_t nbq2;
uint64_t nbq3;
uint64_t nbk2;
uint64_t nbk3;
uint64_t nbw1;
uint64_t nbw3;
uint64_t nbm1;
uint64_t nbm3;
} ggml_metal_kargs_lightning_indexer;

typedef struct {
int32_t n_tokens;
int32_t n_iter;
uint64_t nb_m0;
uint64_t nb_m1;
uint64_t nb_s0;
uint64_t nb_b0;
uint64_t nb_d0;
uint64_t nb_d1;
uint64_t nb_d2;
float eps;
} ggml_metal_kargs_dsv4_hc_comb;

typedef struct {
int32_t n_embd;
int32_t n_tokens;
uint64_t nb_x0;
uint64_t nb_x1;
uint64_t nb_x2;
uint64_t nb_w0;
uint64_t nb_w1;
uint64_t nb_d0;
uint64_t nb_d1;
} ggml_metal_kargs_dsv4_hc_pre;

typedef struct {
int32_t n_embd;
int32_t n_tokens;
uint64_t nb_x0;
uint64_t nb_x1;
uint64_t nb_r0;
uint64_t nb_r1;
uint64_t nb_r2;
uint64_t nb_p0;
uint64_t nb_p1;
uint64_t nb_c0;
uint64_t nb_c1;
uint64_t nb_c2;
uint64_t nb_d0;
uint64_t nb_d1;
uint64_t nb_d2;
} ggml_metal_kargs_dsv4_hc_post;

typedef struct {
int32_t ne00;
int32_t ne01;
Expand Down Expand Up @@ -1172,4 +1338,8 @@ typedef struct {
int64_t np;
} ggml_metal_kargs_opt_step_sgd;

typedef struct {
int64_t ne;
} ggml_metal_kargs_silu_back;

#endif // GGML_METAL_IMPL
Loading
Loading