Skip to content

Add androidNativeArm32 target support (random-access I/O + eager backend) - #990

Closed
michalharakal wants to merge 3 commits into
developfrom
feature/androidnative-arm32-random-access
Closed

michalharakal wants to merge 3 commits into
developfrom
feature/androidnative-arm32-random-access

Conversation

@michalharakal

@michalharakal michalharakal commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Adds androidNativeArm32 as a real target across the dependency chain a downstream consumer (SKaiNET-transformers' eager FunctionGemma port) needs: skainet-io-core, skainet-io-gguf, skainet-lang-dag, skainet-compile-dag, skainet-compile-opt, skainet-backend-api, skainet-backend-cpu.
  • androidNativeArm32's POSIX ssize_t/off_t are Int where every other native target (including androidNativeArm64) is Long — mixing them in one shared native source set fails Kotlin/Native's metadata compile ("numbers with different bit widths"). Added Posix32PreadRandomAccessSource (skainet-io-core) as a dedicated 32-bit-only actual, same contract as the existing 64-bit one, instead of trying to share it.
  • skainet-io-gguf's host-side export tooling (GgufExportFacade/GGUFWriter, needs skainet-compile-dag) moved from commonMain into a new exportMain source set shared by every target except androidNativeArm32 (model export isn't an on-device concern).
  • skainet-backend-cpu's DirectCpuExecutionContext is pure Kotlin (no cinterop) — got a portable-fallback platformDefaultCpuOpsFactory actual, identical to the existing Linux one.
  • No behavior change for any existing target — verified each one still compiles after every change in this PR (jvm, android AAR, linux{X64,Arm64}, macosArm64, ios{Arm64,SimulatorArm64}, js, wasmJs, wasmWasi where applicable per module).

Test plan

  • Every touched module's every existing target still compiles (verified individually per commit, not just the new target)
  • New androidNativeArm32 target compiles for all 7 touched modules
  • Downstream consumer (SKaiNET-transformers#functiongemma-androidnative-eager) links a real executable against this and runs correctly on-device (SL2610 ARM32 board) — see that PR

androidNativeArm32's POSIX ssize_t/off_t are Int where every other native
target is Long, so its RandomAccessSource can't share native64Main's
pread-based implementation without failing Kotlin/Native's metadata compile.
Adds Posix32PreadRandomAccessSource (skainet-io-core) as a separate
androidNativeArm32-only actual, same contract as the 64-bit one.

skainet-io-gguf: adds the androidNativeArm32 target and a matching
RandomAccessSourceFactory actual. GgufExportFacade/GGUFWriter (host-side
export tooling, depends on skainet-compile-dag which doesn't build for
this target either) move from commonMain into a new exportMain source
set shared by every other target (jvm/android/native64/js/wasmJs/wasmWasi)
so androidNativeArm32 gets tokenizer/GGUF-read support without the
export-only dependency it can't resolve. No behavior change for existing
targets — verified each one still compiles.
DirectCpuExecutionContext (eager execution) is pure Kotlin — no cinterop
kernels in this module (that's skainet-backend-native-cpu, which stays
Linux-aarch64-only, a separate concern). skainet-backend-api needed the
target + androidNativeArm32Main wired into its native hierarchy;
skainet-backend-cpu needed the same plus a platformDefaultCpuOpsFactory
actual — identical to the linux one (portable scalar-kernel fallback,
no accelerated path yet, same as every other native target without
NEON cinterop). Verified every existing target (jvm, android, linux,
apple, js, wasmJs, wasmWasi) still compiles.
…net-compile-opt

Transitive requirement surfaced by llm-core (SKaiNET-transformers) needing
these for OptimizedLLMRuntime's graph tracing/fusion, itself needed by
FunctionGemma's eager facade. Pure Kotlin, no cinterop — default hierarchy
template wires androidNativeArm32Main automatically, nothing else to do.
@github-actions

Copy link
Copy Markdown

📖 Documentation Preview

The documentation has been built successfully for this PR.

Generated Files:

  • Operator documentation: docs/modules/operators/_generated_/
  • JSON schema output: operators.json

Artifacts:

  • Download the documentation-preview-990 artifact to view the complete documentation locally.

This comment will be updated automatically when the PR is updated.

michalharakal added a commit that referenced this pull request Aug 31, 2026
… modules

skainet-io-core, skainet-lang-core, skainet-compile-core, skainet-io-safetensors
and skainet-lang-ksp-annotations already target Android native. The rest of the
chain a device consumer needs does not, so SKaiNET-transformers' gemma-iree fails
to resolve at configuration time:

    Could not determine the dependencies of task
    ':llm-runtime:gemma-iree:compileKotlinAndroidNativeArm32'.
    > Could not resolve sk.ainet.core:skainet-compile-dag / -compile-opt /
      -io-gguf / -backend-api.

Adds the two targets to skainet-io-gguf, skainet-lang-dag, skainet-compile-dag,
skainet-compile-opt, skainet-backend-api and skainet-backend-cpu.

None of the five hand-declared modules carries posix-typed code, so arm32's
Int-width ssize_t/size_t needs no 64-bit split source set — the reason
skainet-io-core has one does not apply. skainet-backend-api wires the new source
sets onto nativeMain by hand, matching how it declares the rest of its hierarchy.

skainet-backend-cpu opts in through the convention plugin's existing
skainet.targets property and gets a platformDefaultCpuOpsFactory actual. That
actual is a copy of the Linux one rather than a dependsOn(linuxMain): Android
native is Linux-based but bionic, not glibc, and linuxMain is free to grow code
that assumes the latter.

Supersedes most of #990, whose io-core half develop has since landed
independently and whose io-gguf half conflicts with the RandomAccessSourceFactory
consolidation. Its exportMain split is no longer needed either: that existed to
keep export tooling off arm32 because it needs compile-dag, which now has the
target.

Verified: compileKotlinAndroidNativeArm32 and Arm64 green for all six; jvmTest
and repo-wide apiCheck unchanged; and via composite build, transformers'
:llm-runtime:gemma-iree:compileKotlinAndroidNativeArm32 — the task that fails on
SKaiNET-transformers#315 — now succeeds.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YKeDSK4JF295y53Uvez954
@michalharakal

Copy link
Copy Markdown
Contributor Author

Superseded by #1239 — recommend closing this one.

Checking it against current develop (269 commits on, mergeable: CONFLICTING), the work here has split three ways:

  • The skainet-io-core half has landed independently. develop already has androidNativeArm32() as a real target, the native64Main / androidNativeArm32Main split, and the 32-bit posix actual — carrying the same ssize_t/size_t width rationale this PR wrote up.
  • The skainet-io-gguf half no longer applies. RandomAccessSourceFactory's six platform actuals were consolidated into a single commonMain file that delegates to skainet-io-core, so the native.kt → native64.kt rename has no file to rename — it is one of the two conflicts a rebase hits.
  • The exportMain split is no longer needed. It existed to keep GgufExportFacade/GGUFWriter off arm32 because they need skainet-compile-dag; feat(build): publish androidNativeArm32/Arm64 from the six downstream modules #1239 gives compile-dag the target, so nothing has to be excluded.

What was left is the downstream target list, which #1239 does for all six modules, verified end to end: SKaiNET-transformers' :llm-runtime:gemma-iree:compileKotlinAndroidNativeArm32 — the task whose failure this arc is about, and the one turning SKaiNET-transformers#315 red — now compiles against it.

The diagnosis in this PR's description was right and is what made the smaller change obvious; it is only the implementation that develop has moved past.

@michalharakal

Copy link
Copy Markdown
Contributor Author

Closing in favour of #1239, which merged today and carries what was still needed from here — the androidNative target set across the downstream modules.

For anyone finding this later: the diagnosis in the description above was correct and is what made the smaller change obvious. It is only the implementation that develop moved past, in three separate places — the skainet-io-core half landed independently, the RandomAccessSourceFactory consolidation removed the file the io-gguf half wanted to rename, and giving skainet-compile-dag the target made the exportMain split unnecessary.

@michalharakal
michalharakal deleted the feature/androidnative-arm32-random-access branch August 31, 2026 14:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant