Skip to content

Commit 587d59b

Browse files
michalharakalclaude
andcommitted
feat(backend-native-cpu): add linuxArm64 (board) K/N target
Promote the K/N cinterop path from the linuxX64 POC to the real board target: - linuxArm64 target with the same skainet_kernels cinterop; links the aarch64 cross-built static archive (cmake-build-arm64/libskainet_kernels.a, NEON). - Shared `nativeMain` source set holds NativeKn*MatmulKernel + the provider, so linuxX64 and linuxArm64 share one implementation (cinterop bindings are commonized across both targets). - linuxArm64 link tasks depend on the aarch64 cross-build only under -PcrossArm64 (toolchain present); a plain host build still compiles linuxArm64 to a klib. Verified on host: compileKotlinLinuxArm64 + cinteropSkainetKernelsLinuxArm64 succeed (cross-compiled from x86); linuxX64Test still green (6 tests) on the shared nativeMain. Final aarch64 binary link + NEON runtime are board-verify-pending. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 1b6f7f6 commit 587d59b

3 files changed

Lines changed: 31 additions & 11 deletions

File tree

skainet-backends/skainet-backend-native-cpu/build.gradle.kts

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,25 +7,29 @@ plugins {
77
val nativeIncludeDir: String = layout.projectDirectory.dir("native/include").asFile.absolutePath
88
val staticArchivePath: String =
99
layout.buildDirectory.file("native/cmake-build/libskainet_kernels.a").get().asFile.absolutePath
10+
// aarch64 cross-built static archive (produced by buildNativeKernelsArm64 with
11+
// -PcrossArm64; carries the NEON paths). Linked into linuxArm64 binaries.
12+
val staticArchiveArm64Path: String =
13+
layout.buildDirectory.file("native/cmake-build-arm64/libskainet_kernels.a").get().asFile.absolutePath
1014

1115
kotlin {
1216
explicitApi()
1317
jvm()
1418

15-
// Kotlin/Native: POC on the host (linuxX64); linuxArm64 is the board target.
16-
// Exposes the hand-written C/NEON kernels to K/N via cinterop to the static
17-
// archive libskainet_kernels.a (CMake `skainet_kernels_static`). This is the
18-
// board-consumption path — the JVM consumes the same kernels via FFM instead.
19-
linuxX64 {
19+
// Kotlin/Native consumption of the hand-written C/NEON kernels via cinterop
20+
// to the static archive libskainet_kernels.a (CMake `skainet_kernels_static`).
21+
// linuxX64 = host (POC / CI-runnable); linuxArm64 = the SL2610 board target
22+
// (its archive is the aarch64 cross-build with NEON). The JVM consumes the
23+
// same kernels via FFM instead. Shared K/N code lives in `nativeMain`.
24+
fun org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget.wireSkainetKernels(archive: String) {
2025
compilations.getByName("main").cinterops.create("skainetKernels") {
2126
defFile(project.file("src/nativeInterop/cinterop/skainet_kernels.def"))
2227
includeDirs(nativeIncludeDir)
2328
}
24-
binaries.all {
25-
// Link the static C archive into every linuxX64 binary (incl. tests).
26-
linkerOpts(staticArchivePath)
27-
}
29+
binaries.all { linkerOpts(archive) }
2830
}
31+
linuxX64 { wireSkainetKernels(staticArchivePath) }
32+
linuxArm64 { wireSkainetKernels(staticArchiveArm64Path) }
2933

3034
sourceSets {
3135
val jvmMain by getting {
@@ -44,11 +48,17 @@ kotlin {
4448
implementation(libs.kotlinx.coroutines)
4549
}
4650
}
47-
val linuxX64Main by getting {
51+
// Shared K/N kernels (NativeKn*MatmulKernel + provider), consumed by both
52+
// linuxX64 and linuxArm64. The cinterop bindings are commonized across the
53+
// two targets so this source set can reference sk.ainet.kernels.cinterop.
54+
val nativeMain by creating {
55+
dependsOn(commonMain.get())
4856
dependencies {
4957
implementation(project(":skainet-backends:skainet-backend-api"))
5058
}
5159
}
60+
val linuxX64Main by getting { dependsOn(nativeMain) }
61+
val linuxArm64Main by getting { dependsOn(nativeMain) }
5262
val linuxX64Test by getting {
5363
dependencies {
5464
implementation(libs.kotlin.test)
@@ -122,7 +132,7 @@ val buildNativeKernels by tasks.registering(Exec::class) {
122132
}
123133

124134
// The linuxX64 (K/N) binaries link libskainet_kernels.a (built by CMake into
125-
// cmakeBuildPath), so the static archive must exist before the K/N link step.
135+
// cmakeBuildPath), so the host static archive must exist before the K/N link.
126136
tasks.matching { it.name.startsWith("link") && it.name.endsWith("LinuxX64") }.configureEach {
127137
dependsOn(buildNativeKernels)
128138
}
@@ -211,6 +221,16 @@ tasks.named("jvmProcessResources") {
211221
if (crossArm64Enabled) dependsOn(packageNativeKernelsArm64)
212222
}
213223

224+
// linuxArm64 binaries link the aarch64 cross-built archive. Only wired with
225+
// -PcrossArm64 (cross toolchain present): a plain host build still compiles
226+
// linuxArm64 to a klib (no archive needed) — only a final binary/test link
227+
// needs it, which is a board/CI concern.
228+
if (crossArm64Enabled) {
229+
tasks.matching { it.name.startsWith("link") && it.name.endsWith("LinuxArm64") }.configureEach {
230+
dependsOn(buildNativeKernelsArm64)
231+
}
232+
}
233+
214234
// Forward `-Dskainet.runBench=true` from Gradle CLI to the forked test
215235
// JVM so Q4KMatmulMicrobenchTest activates. Skipped silently otherwise.
216236
val runBenchProperty = providers.systemProperty("skainet.runBench")

skainet-backends/skainet-backend-native-cpu/src/linuxX64Main/kotlin/sk/ainet/exec/kernel/NativeKnKernelProvider.kt renamed to skainet-backends/skainet-backend-native-cpu/src/nativeMain/kotlin/sk/ainet/exec/kernel/NativeKnKernelProvider.kt

File renamed without changes.

skainet-backends/skainet-backend-native-cpu/src/linuxX64Main/kotlin/sk/ainet/exec/kernel/NativeKnQ5KMatmulKernel.kt renamed to skainet-backends/skainet-backend-native-cpu/src/nativeMain/kotlin/sk/ainet/exec/kernel/NativeKnQ5KMatmulKernel.kt

File renamed without changes.

0 commit comments

Comments
 (0)