ddn.columnar.vector
The packed-bit vector kernels of ddn.df: dense core.simd loops that turn comparisons into bitmap words and arithmetic into raw value buffers, so kernels never pay per-element Nullable or per-element bitmap-append costs.
Every kernel computes every slot and leaves null handling to the caller's validity bitmaps (D14): the value and guard bits produced here are meaningful at valid slots only, and the packed bits are exactly the scalar semantics of the same op on the same operands (IEEE on floats, D's wrap-around on integers).
Comparisons pack one bit per element into ulong words, LSB-first, which is the layout Bitmap uses. Lanes wider than four bytes take the core.simd path (one vector compare per 32-byte block, mask lanes packed with shifts); narrower elements take the branchless scalar path, which is already bandwidth-bound at that width. Arithmetic writes raw physical elements with the same split: core.simd for `+`, `-`, `*` and floating `/`, the guarded scalar loop for the integer `/` and `%`, whose per-slot divisors must be inspected before dividing (no traps on null slots, D14; integer `/` is true division yielding FLOAT64).
Compiler and target matrix: the vector path requires the 32-byte * Implements ADR-2026-10-18-18 (amending ADR-2026-10-18-11's phase-1 "no core.simd" rule).
core.simd vector types to exist on the target, so baseline x86-64 builds of DMD and GDC (pre-AVX) compile every kernel onto the branchless scalar packing through simdCapable, LDC accepts the types at baseline and lowers them to SSE pairs, and -mcpu=avx/-mavx builds take the vector path wherever the compiler accepts the specific vector form: DMD rejects vector `==`/`!=` and the integer arithmetic forms, which vectorCompareOk and vectorArithOk route to the branchless scalar packing there (the floating arithmetic and ordered comparisons stay vectorised).
Copyright
Functions 23
size_t packedWords(size_t n) @safe pure nothrow @nogcThe number of packed words `n` elements need.size_t packCompare(T, string op)(const(T)[] a, const(T)[] b,
ulong[] outWords) if (isNumeric!T)Packs `a[i] op b[i]` for every element into bitmap words, LSB-first. The `core.simd` path compares 32-byte blocks; the scalar tail and the narrow element types use the branchless loop. Unwritten ta...size_t packCompareScalar(T, string op)(const(T)[] a, T scalar,
ulong[] outWords) if (isNumeric!T)Packs `a[i] op scalar` for every element into bitmap words, LSB-first. Unwritten tail bits of the last word are cleared.size_t packCompareString(string op)(const(string)[] a,
const(string)[] b, ulong[] outWords)Packs `a[i] op b[i]` for STRING operands into bitmap words, LSB-first. Strings compare lexicographically as byte slices, like `string`'s ordering. Unwritten tail bits of the last word are cleared.void arithInto(T, string op)(const(T)[] a, const(T)[] b,
T[] outValues) if (isNumeric!T && isOp!op)Computes `outValues[i] = a[i] op b[i]` densely, with D's arithmetic semantics (`+`, `-`, `*` wrap on integers, IEEE on floats; `/` is floating division). Null slots hold arbitrary bits on both side...void arithScalarInto(T, string op)(const(T)[] a, T scalar,
bool scalarOnLeft, T[] outValues) if (isNumeric!T && isOp!op)Computes `outValues[i]` for `a[i] op scalar` (or `scalar op a[i]`), densely, with D's arithmetic semantics.size_t guardedDivInto(T, R, string op)(const(T)[] a,
const(T)[] b, R[] outValues, ulong[] outGuards) if (isIntegral!T && (op == "/" || op == "%"))Computes the guarded integer division or modulo into `outValues`, writing a zero at guarded-out slots (the caller nulls those slots from the guard words). Every slot is inspected before dividing, s...size_t guardedDivScalarInto(T, R, string op)(const(T)[] a,
T scalar, bool scalarOnLeft, R[] outValues, ulong[] outGuards) if (isIntegral!T && (op == "/" || op == "%"))The scalar-operand counterpart of `guardedDivInto`: `a[i] op scalar`, or `scalar op a[i]`, with the guard on the divisor of that operand order.void negateInto(T)(const(T)[] a, T[] outValues) if (isNumeric!T)Computes `outValues[i] = -a[i]` densely (wrap-around on integers).R apply(T, R, string op)(T x, T y) @safe pure nothrowOne guarded application: true division for `/`, wrapping `%` otherwise.void maskTail(ulong[] words, size_t n) @safe pure nothrow @nogcClears the packed bits past element `n` in the last word.void clearWords(ulong[] words, size_t from, size_t to) @safe pure nothrow @nogcZeroes the packed words `[from, to)`.auto splat(T)(T value) @trustedBuilds a `core.simd` vector with every lane set to `value`.auto loadVec(T)(const(T)[] src) @trusted pure nothrowLoads one `core.simd` vector from a possibly unaligned element position through an aligned staging buffer (a plain `cast(V*)` dereference promises the vector's natural alignment, which element offs...void storeVec(T)(T[] dst, VecOf!T v) @trusted pure nothrowStores one `core.simd` vector to a possibly unaligned element position through an aligned staging buffer.size_t packCompareSimd(T, string op)(const(T)[] a, const(T)[] b,
ulong[] outWords) @trustedCompares 32-byte blocks of two arrays with `core.simd`, packing one mask bit per element; the scalar tail is left to the caller.size_t packCompareScalarSimd(T, string op)(const(T)[] a, T scalar,
ulong[] outWords) @trustedCompares 32-byte blocks against a broadcast scalar with `core.simd`, packing one mask bit per element; the scalar tail is left to the caller.void packTail(T, string op)(const(T)[] a, const(T)[] b,
ulong[] outWords, size_t bitBase)The branchless scalar packing of one run of comparisons, starting at bit `bitBase` of the destination.void packTailScalar(T, string op)(const(T)[] a, T scalar,
ulong[] outWords, size_t bitBase)The branchless scalar packing of one run of scalar comparisons, starting at bit `bitBase` of the destination.void arithLoop(T, string op)(const(T)[] a, const(T)[] b,
T[] outValues) @safe pure nothrowThe scalar fallback loop of `arithInto`: D's arithmetic semantics, element by element, into the destination. Used whole on targets whose `simdCapable` is false, and for the tail after the SIMD bloc...void arithScalarLoop(T, string op)(const(T)[] a, T scalar,
bool scalarOnLeft, T[] outValues) @safe pure nothrowThe scalar fallback loop of `arithScalarInto`: D's arithmetic semantics against a broadcast scalar, element by element.size_t arithIntoSimd(T, string op)(const(T)[] a, const(T)[] b,
T[] outValues) @trustedApplies `op` to two arrays in 32-byte `core.simd` blocks; the scalar tail is left to the caller.size_t arithScalarIntoSimd(T, string op)(const(T)[] a, T scalar,
bool scalarOnLeft, T[] outValues) @trustedApplies `op` with a broadcast scalar in 32-byte `core.simd` blocks; the scalar tail is left to the caller.Variables 2
PACK_BITS = 64The number of bits one packed ulong word holds.
SIMD_ALIGN = 32The alignment every core.simd staging buffer must carry.
Templates 9
Whether the core.simd 32-byte vector path is compiled in for T: the vector type must exist on this target - core.simd guards its own aliases with the same is(Vector!(...)) idiom - and the lanes must be wide enough to pay off (narrow elements and every target without the vector types take the branchless scalar packing instead, which the probes measured at ~0.75 ms per million doubles against 14.7 ms for the per-element path, so the fallback stays fast).
Parameters
T | the element type |
Returns
true when the SIMD kernels instantiate for T.Whether the vector form of one comparison compiles for T on this compiler: DMD rejects `==`/`!=` between vectors (the ordered comparisons compile), so the two equality ops take the branchless scalar packing under DMD while every compiler that accepts them (LDC, GDC) keeps the vector path. Detected through __traits(compiles) rather than versioned, so a future DMD that accepts vector equality lights up on its own.
Parameters
T | the element type |
op | the comparison operator text |
Returns
true when the SIMD compare kernel instantiates for T op.
Whether the vector form of one arithmetic operation compiles for T on this compiler: DMD accepts the floating vector arithmetic but rejects the integer forms, so integer `+`-`*` take the branchless scalar loop under DMD while LDC and GDC keep the vector path. Detected through __traits(compiles) so compilers that grow the missing forms light up on their own.
Params: T = the element type op = the arithmetic operator text
Returns: true when the SIMD arithmetic kernel instantiates.
Whether the vector form of one comparison compiles for T on this compiler: DMD rejects `==`/`!=` between vectors (the ordered comparisons compile), so the two equality ops take the branchless scalar packing under DMD while every compiler that accepts them (LDC, GDC) keeps the vector path. Detected through __traits(compiles) rather than versioned, so a future DMD that accepts vector equality lights up on its own.
Parameters
T | the element type |
op | the comparison operator text |
Returns
true when the SIMD compare kernel instantiates for T op.Whether T is one of the ten numeric physical element types.
Parameters
T | the type to test |
Returns
true for the integer and floating element types.Whether T is one of the integral physical element types.
Parameters
T | the type to test |
Returns
true for the integer element types.Whether T is a floating physical element type.
Parameters
T | the type to test |
Returns
true for float and double.Whether op is one of the five arithmetic operators.
Parameters
op | the operator text |
Returns
true for `"+"`, `"-"`, `"*"`, `"/"` and `"%"`.The unsigned lane type of a comparison mask vector at element size.
One 32-byte core.simd vector of T.