Skip to content
This repository was archived by the owner on Aug 16, 2026. It is now read-only.
This repository was archived by the owner on Aug 16, 2026. It is now read-only.

@simd has no kernel for elementwise copy, integer sum, or float convert #4

Description

@suidvandiewereld

What happens

@simd and @simd! reject loop shapes that ought to vectorize. A plain unit-stride copy fails:

fn cp(d: int32*, s: int32*, n: int32) {
  var k: int32 = 0;
  @simd while (k < n) { d[k] = s[k]; k = k + 1; }
}
warning: @simd loop was not vectorized: no vectorizer recognized this loop's shape
  (e.g. a non-unit stride, a loop-carried dependence, or a reduction/operation no kernel covers)

I hit this while adding @simd! to the hot loops of a 39,000 line graphics and simulation program. Every loop I marked failed. Some had earned it: they gather through an index buffer, or write one value every 27 slots. Others were plain copies.

What the vectorizer accepts

I wrote one loop per shape per element type and read --simd-report. A tick means it vectorized and names the kernel. A cross means it did not.

loop body int8 int16 int32 int64 float32 float64
d[k] = s[k] affine_map_f32 affine_map_f64
d[k] = a[k] + b[k] vloop_i32 vloop_f64
acc = acc + s[k] sum_f32
d[k] = <constant> simd_fill
d[k] = (float32)s[k]
int8 dot product from the docs simd_dot_i8

Two of these read oddly next to each other. Adding two int32 arrays works. Copying one does not. And no convert has a kernel at any width, in either direction.

int16 and int64 report a clear cause and point at a way out. The rest fall through to the catch-all.

The test that pins it down

Same two pointers, same stride, same trip count. Only the operation changes.

d[k] = s[k]          not vectorized
d[k] = s[k] + 0      not vectorized
d[k] = s[k] * 1      not vectorized
d[k] = s[k] + s[k]   vectorized (simd_vloop_i32)

The folder turns + 0 and * 1 back into a copy, and copy has no kernel. So the loop that does less work fails, and the loop that does more work passes. This rules out aliasing, dependence and stride, because all four loops share them. What is missing is a kernel, not an analysis.

Why it matters

@simd! is worth more than a hint because it breaks the build rather than letting speed drop in silence. But a contract you cannot meet on ordinary code is a contract nobody writes. That is what happened here: 39,000 lines, and not one @simd! survived.

The gaps are also the common shapes. Copies, integer sums and float64 to float32 converts make up a great deal of real loop bodies.

Suggested fix

Two parts. The first matters more.

  1. Add the missing kernels: elementwise copy at every width the vectorizer already loads and stores, integer sum, and converts between float64, float32 and int32.

  2. Say more when a loop falls through. The width failures already name the cause and offer a way out, and they are useful. The catch-all names nothing and carries no fix: line, so it reads as "your loop is wrong" when the truth is "no kernel covers this yet". Naming the missing kernel would turn a dead end into a decision.

Worth noting that --explain on the same compiler is much better at this. On the same program it grouped eight failing fill loops under one cause, gave one mechanical fix, and marked it verified by simulation. --simd-report could borrow that voice.

How to reproduce

import "std/io";

fn copy_plain  (d: int32*, s: int32*, n: int32) { var k: int32 = 0; @simd while (k < n) { d[k] = s[k];         k = k + 1; } }
fn copy_add0   (d: int32*, s: int32*, n: int32) { var k: int32 = 0; @simd while (k < n) { d[k] = s[k] + 0;     k = k + 1; } }
fn copy_mul1   (d: int32*, s: int32*, n: int32) { var k: int32 = 0; @simd while (k < n) { d[k] = s[k] * 1;     k = k + 1; } }
fn copy_selfadd(d: int32*, s: int32*, n: int32) { var k: int32 = 0; @simd while (k < n) { d[k] = s[k] + s[k]; k = k + 1; } }

fn main() -> int32 { println(cstr("ok")); return 0; }
mettle --build --release -s probe.mettle -o probe.exe --simd-report

The first three warn. The fourth vectorizes.

Version

mettle v0.14.0
host: x86_64
target: x86_64-windows (COFF)
METTLE_NO_SIMD=0

Metadata

Metadata

Labels

bugSomething isn't working

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions