ABI Specification
This document details the Application Binary Interface (ABI) used for lowering typed Kina IR to textual MLIR, and the integration points with the Rust runtime.
Tensor Representation
All Tensors within the compiler pipeline are lowered to memrefs during bufferization. However, when interfacing with the Rust Runtime via C-Interop (_mlir_ciface_...), ranked and dynamically sized memrefs are cast into Unranked Memref Descriptors.
In the LLVM IR and C ABI, the unranked memref descriptor corresponds to the following C-struct:
struct UnrankedMemRefDescriptor {
int64_t rank;
void *descriptor;
};
Where descriptor points to a ranked memref descriptor of the appropriate rank. This structure enables the Rust runtime to dynamically query rank, shape, and strides at runtime while minimizing interface permutations.
GPU Memory Allocation
During the Phase 1 GPU Execution implementation, the compiler intercepts the allocation of memory using tensor.empty(). It generates a call to runtime_gpu_alloc_unranked, passing the dynamically casted memref<*xf32> unranked memref to the C-Interop boundary.
runtime_gpu_alloc_unranked
The ABI export maps to:
#[no_mangle]
pub unsafe extern "C" fn _mlir_ciface_runtime_gpu_alloc_unranked(
out: *mut UnrankedMemRefDescriptor,
element_size: i32,
) -> FfiErrorCode;
When invoked:
- The Rust runtime unwraps the
outunranked descriptor and extracts thesizesfrom the nested ranked descriptor. - The exact size is calculated as the product of the shape multiplied by
element_size. - Memory is dynamically allocated via
cudaMallocManagedto allow unified CPU and GPU reads/writes. - The
allocatedandalignedpointers within the underlying descriptor are mutated to point to the new device/host memory address.
runtime_gpu_free_all
The compiler provides an exit-point mechanism for manual freeing:
#[no_mangle]
pub extern "C" fn runtime_gpu_free_all() -> FfiErrorCode;
This iterates over all pointers dynamically registered by the allocations and issues cudaFree commands, effectively preventing device memory leaks across iterations or executions.
Execution and Error Codes
All runtime interface methods return an integer error code (FfiErrorCode in Rust), mapping to the following exit statuses:
0: Success1: Validation Error2: Resource Error3: GPU Error4: Unimplemented Error5: Internal Error6: Type Error
A wrapper macro intercepts Rust-level panics and converts them seamlessly into InternalError exit codes to prevent ABI unwinding crashes.