Skip to content

Design and Implementation

This section explains how Tack works and why it is built the way it is. It is written for contributors, and for advanced users who want to understand what happens between a @tack.kernel call and the machine code that runs.

Each page covers one part of the system at three levels:

  • Design: the problem, the approach taken, the alternatives that were rejected, and the tradeoffs accepted.
  • Implementation: the modules, classes, functions, data structures and algorithms involved, and the order in which they run. Source locations are given as file paths plus function or class names.
  • Contract: what the part guarantees, what it requires of callers, and what happens when a requirement is violated (which exception, raised where).

How this section relates to the others

Section Answers Use it when
Contracts What is guaranteed: the kernel language contract, backend capabilities, the runtime API, and how conformance is tested you need to know whether a behavior can be relied on
Design and Implementation (this section) Why it is designed this way and how it is implemented you need to understand, review or change a part of the system
Developer's Guide How to carry out a task: module layout, each compiler stage, adding a feature you are about to make a change and want a walkthrough

Where a design page and the normative kernel language contract disagree, the contract states the intended behavior and the code shows the current behavior. The design pages describe the code and point out such gaps.

Reading order

Start with Principles; most later decisions follow from it. Then read Compilation Pipeline and Specialization and Caching together: between them they describe the life of every kernel call. The remaining pages can be read in any order, as the topic requires.

flowchart LR
    P[Principles] --> CP[Compilation Pipeline]
    CP --> SC[Specialization and Caching]
    SC --> NS[Numerical Semantics]
    SC --> PE[Parallel Execution]
    SC --> MA[Memory and Aliasing]
    PE --> CT[CPU Threading Policy]
    MA --> BI[Backend Implementations]
    BI --> VW[Vendor Workarounds]
    BI --> IO[Interoperability]
    IO --> DA[Dataset API]
    DA --> PH[Polyhedra]

Pages

Array API. Typed backend storage, element dtypes, logical array shape, NumPy transfer semantics, and the distinction from dataset fields.

Principles. The ideas the rest of the system follows: Python syntax with Tack's own semantics; translate or reject, never silently drop; one kernel source with backend capabilities declared rather than probed; specialization with a cache key that names everything specialized on; correctness by default, with faster paths only where a per-call check justifies them; runtime policies measured rather than hard-coded; and independent oracles as the definition of correctness. Each principle comes with its costs.

Compilation Pipeline. The end-to-end path from Kernel.__call__ through resolve_variant, template rewrite, source validation, the AST transform and device-function inlining, the immutable IR template, the per-variant passes, verification at every pass boundary, code generation and native compilation, to dispatch. For each stage: its input and output, the invariants it establishes, what it rejects and with which exception, and whether it runs on a cache hit.

Specialization and Caching. What a compiled variant is and exactly what goes into its key: argument types and categories, vector widths, texture extents, template structure and constants, the shape signature of dimensions baked into code, and the CPU disjoint-arrays bit, and how debug mode's checked kernels are kept apart. It covers cache structure, lifetime and thread safety, the dispatch fast path that skips key derivation for a repeated call, the correctness argument behind the key, worked examples of which calls reuse a variant and which recompile, how to avoid unnecessary variants, and the disk cache that keeps lowered and compiled kernels across processes.

Numerical Semantics. How Tack implements its numerical rules: fixed-width integer types and promotion, division and power, floating-point precision and exceptional values, the compiler settings that keep fast math off, and reductions.

Parallel Execution. The execution model: one top-level parallel iteration space, sequential inner loops, ordering within an iteration, workgroups, barriers, block reductions and atomics, and how each backend maps them onto threads or grids.

Memory and Aliasing. Arrays, views and imported storage that may share memory; why generated code makes no unconditional no-alias promise; and how the CPU's disjoint-arrays specialization recovers performance safely.

CPU Threading Policy. How the CPU backend decides, per dispatch, whether to run a loop range serially or spread it across its thread pool, a pool JIT'd from LLVM IR, using measured per-kernel cost and measured fan-out cost rather than a fixed threshold.

Backend Implementations. How each of the five backends (CPU through LLVM, Metal, CUDA, HIP and Level Zero) turns the shared IR into native code, allocates and moves memory, and launches work (queued in order on the GPU backends), along with the device-specific fallbacks each one needs.

Vendor Workarounds. Each place generated code or a runtime call is shaped around a defect in a vendor compiler, driver or binding rather than by the language contract: the symptom, the workaround, the test that guards it, and when it can go.

Interoperability. Sharing memory with other libraries and frameworks without copying: wrapping external pointers, DLPack, exported device memory, and VTK interop.

The two design records of tack.dataset are kept with the Visualization Guide:

Dataset API. The design record of tack.dataset: how its data model (topologies, spaces, fields, sets), its algorithms and its partitioned data and ghost marks were reasoned out. The API as it stands is the Dataset API reference.

Polyhedra. The design record of the polyhedral path in tack.dataset: faces shared between cells, their orientation, and the algorithms that run on arbitrary polyhedra.