Native memory: ppy.native and ppy.ffi¶
This page covers typed native memory in ppy.native and the C binding layer
ppy.ffi built on it.
Example¶
ppy.native is still the directive it was (@ppy.native,
@ppy.native(require=True)). It is also the namespace of typed native memory:
from ppy import native
@native
def fill(p: native.ptr[float], n: int) -> None:
for i in range(n):
native.store(native.offset(p, i), 0.5 * i)
def scratch() -> int:
buf = native.stack_alloc[int](8) # the function's own memory
native.store(buf, native.sizeof[int]())
return native.load(buf)
Reference¶
native.ptr[T], native.const_ptr[T] |
a typed pointer. T is int, float, bool, or any fixed-width marker (ppy.i8, u8, i16, u16, i32, u32, i64, u64, f32, f64), each stored at its own width. A const_ptr refuses store (E1631). |
native.load(p), native.store(p, v), native.offset(p, n) |
read, write, move. Reading a byte hands out an int, as a buffer does. |
native.cast[U](p) |
the same memory read as U. |
native.sizeof[T](), native.alignof[T]() |
constants the checker knows. |
native.stack_alloc[T](n) |
n zeroed elements the function owns. Native code needs a constant n, and the memory cannot be returned or stored; the IR's verifier holds that. |
@native.extern("sin", library="m", pure=True) |
a stub is a C function. Under CPython the call goes through ctypes; in native code, straight to the symbol. The directive says what the C function does (pure=True or, by default, reads and writes of native memory), and the signature must be fully annotated (E1633). |
native.compiled(f) |
whether calling f runs its native form here: True under ppy run or a built artifact once the runtime has bound it, False under plain CPython. It answers for the object standing in the namespace, which under ppy run may be the C entry point itself (named module.f). |
@native.export(name="ppy_dot") |
a public C symbol in the built library, declared in the header ppy build writes beside it. A C caller has no Python to fall back to, so a failed guard traps. |
Under CPython¶
Under plain CPython each of these has a reference implementation over array
memory, so the three paths agree. A function whose parameter is a pointer has
no Python boundary and is called only from native code.
ppy.ffi¶
ppy.ffi is the binding layer over extern:
ffi.library("m")loads a library.@ffi.bind(lib, symbol="sin", pure=True)binds a symbol.ffi.nullable[native.ptr[T]]is a pointer that may be null.ffi.LengthOf("xs")is an integer that is another parameter's length.ppy.Owned/ppy.Borrowedsay who keeps memory.
ppy bind header foo.h writes such a module from a C header
(CLI).
Examples: Native memory, The toolbox.