Imagine writing *embedded* apps in a *Kotlin DSL* ...
# embedded-kotlin
m
Imagine writing embedded apps in a Kotlin DSL and compiling them through MLIR/IREE straight to an NPU — like Synaptics' Torq, the kind of on-device AI Google showed at I/O. Not hypothetical. Here's the Moonshine speech-to-text encoder as real Kotlin — this compiles to StableHLO → IREE and runs on the SL2610 NPU:
Copy code
public fun <T : DType, V> moonshineEncoder(
    cfg: MoonshineConfig,
    dtype: KClass<T>,
): Module<T, V> {
    val dim = cfg.dim
    val eps = cfg.layerNormEps
    val nnCtx = DefaultNeuralNetworkExecutionContext()
    val dsl = NeuralNetworkDslImpl<T, V>(nnCtx, dtype)

    for (layer in 0 until cfg.encoderLayers) {
        val stage = StageImpl<T, V>(nnCtx, "enc.$layer", dtype)

        // x + Attn(LN(x)). Layer-qualify every id ("enc.$layer.*") so the attention /
        // LayerNorm parameter names are UNIQUE across layers — matching the FFN naming below.
        // Without the prefix, `attn.q_proj.weight` / `attn_norm.weight` repeat every layer and
        // by-name weight loading can't tell the layers apart.
        stage.layerNorm(intArrayOf(dim), eps.toDouble(), id = "enc.$layer.attn_norm")
        stage.multiHeadAttention(
            dim = dim,
            nHeads = cfg.nHeads,
            nKVHeads = cfg.nHeads,
            causal = false, // encoder = bidirectional self-attention
            bias = false,
            id = "enc.$layer.attn",
        ) {
            rope(
                headDim = cfg.headDim,
                maxSeqLen = cfg.maxFrames,
                mode = RoPEMode.INTERLEAVED, // Moonshine rope = adjacent-pair (verified bit-exact vs ONNX Mul_4)
                base = cfg.ropeBase,
                partialRotaryFactor = cfg.partialRotaryFactor,
                freqDenomRotaryDim = true, // Moonshine inv_freq uses rotaryDim (32), not headDim (36)
            )
        }
        stage.residual()

        // x + MLP(LN(x)) — plain GELU MLP (not gated). VoidDense carries explicit
        // in/out dims (no eager placeholder alloc, and no reliance on DSL
        // dimension-tracking through the attention/residual sub-blocks).
        stage.layerNorm(intArrayOf(dim), eps.toDouble(), id = "enc.$layer.ffn_norm")
        // Moonshine MLP is a biased fc1 -> GELU -> biased fc2 (the reference model
        // carries `mlp.fc1.bias`/`mlp.fc2.bias`). addBias=true keeps the trace faithful.
        stage.modules += VoidDense<T, V>("enc.$layer.ffn_up", cfg.ffnDim, dim, dtype, addBias = true)
        stage.modules += GELU<T, V>(name = "enc.$layer.ffn_gelu")
        stage.modules += VoidDense<T, V>("enc.$layer.ffn_down", dim, cfg.ffnDim, dtype, addBias = true)
        stage.residual()

        dsl.modules += HybridTransformerBlock(stage.modules.toList(), name = "enc.$layer")
    }

    dsl.layerNorm(intArrayOf(dim), eps.toDouble(), id = "enc_out_norm")
    return dsl.create()
}
Same code runs as a JVM reference on your laptop and cross-compiles to a native aarch64 board binary — no Python in the loop. Stil WIP. What would you build with it? 👀