hawk.runtime

hawk.runtime#

The thin Python face over hawk._core — descriptor, bind, launch.

This module splits the host path by LIFETIME: everything a launch pays for is C++ (the void*[], the mirror marshal, the entry cache, the serial driver), and what stays in Python happens once per kernel — turning a slot’s declared role into the by-value ABI shape it rides as, and handing the descriptor to hawk._core.ArgBlock. It is not a runtime; it is the last mile of the DEFINITION.

The descriptor is derived, never re-tabulated: a slot’s kind comes from the same role -> mirror map the emitter generated the entry signature from (hawk.emit.aether.MIRROR_OF), so a slot cannot be described one way to the compiler and another to the marshal — the failure mode being a 32-byte handle where the kernel reads a 40-byte mirror, which is deterministic garbage, not a crash.

No numpy, and no allocation: planes cross through the buffer protocol, so a numpy array, a torch CPU tensor and a plain array.array all bind the same way. Output planes are the caller’s.

The crossing budget is visible in the method split: HostKernel.bind_all() is per bind, HostKernel.rebind() is bulk (one crossing for the whole changed set), and HostKernel.launch() is the single per-launch crossing.

Every bound argument is validated against the declaration in HostKernel.bind_all(), before any address is taken (_check_bind()), since a wrong dtype or size used to run silently and return garbage or segfault. Dtype is exact and fixed by role (every integer-valued role rides a 64-bit wire regardless of how it was declared in Python, and Terminated is always bool); shape requires a per-sample plane at least n_samples long (shorter is always the bug) and a Reduce(op) output one slot per sample; every plane must be C-contiguous and a sink role’s plane writable, except a sample-major plane whose transpose is C-contiguous, which binds zero-copy (plane_layout()). A (w, w) plane reads both ways and is refused unless layout="samples_first"/"samples_last" (per call) or hawk.samples_first(x)/hawk.samples_last(x) (per array, which wins) says which axis holds the samples. HostKernel.rebind() takes raw pointers, which carry none of this, and is the unchecked fast path over an already-validated bind.

Threading. Two HostKernel (or ArgBlock) objects are independent and run freely in parallel. ONE object shared across threads is memory-safe but caller-ordered: its calls serialise on the object’s own lock, so concurrent bind and launch on it never tear an argument block, but which call lands first is the caller’s to order. Module-level state (the build counters, the primitive registry, the unit publisher) is always safe to use from any thread.

Module Attributes

DEVICE_CPU

The DLPack CPU device code every host plane's mirror carries.

Functions

descriptor_for_sidecar(…)

The per-slot kind list for a DEPLOYED artifact, from its sidecar alone.

kind_for(…)

The _core descriptor kind ONE slot binds as.

load(…)

Load <directory>/<kernel>.so, reading its sidecar JSON if omitted.

run(…)

Bind arrays and run the WHOLE range in ONE serial call.

Classes

ArgBlock

The contiguous void*[] of by-value mirror PODs one kernel DEFINITION binds through.

HostKernel

One HAWK host artifact's entry, self-checked at load and bound once.