silk.mem.stack.
The native stack of the running thread: the address of the current frame, and scratch storage that lives on the stack instead of in an allocator.
When to use#
Use Stack.withStorage for a small scratch buffer whose size is known only at run time and
that must not allocate. Use Stack.frameAddress to find where the running code is on the
stack, for example to measure how deep a recursion went or to bound a conservative scan of the
stack.
Details#
The stack of every supported target grows down: a function that calls another has its frame at
the same or higher addresses than the callee. Stack storage is not initialized and has no
cleanup. The compiler does not check how much stack is left, so a large request can run past the
end of the stack. That is why Stack.withStorage is unsafe.
Examples#
Sum values in stack scratch storage#
import silk.mem.pointer { Pointer }
import silk.mem.stack { Stack }
fn fill(storage: [*]mut i32) -> i32 {
let mut total = 0
let mut at: usize = 0
while at < 4 {
unsafe {
let slot = Pointer.atMut<i32>(storage, at)
Pointer.write<i32>(slot, 10)
total = total + Pointer.read<i32>(slot)
}
at = at + 1
}
return total
}
pub fn main() -> i32 {
let total = unsafe Stack.withStorage<i32, i32>(4, fill)
if total != 40 {
return 1
}
return 0
}Import as Stack with import silk.mem.stack { Stack }.
Public declarations: 1.
Stack#
pub struct StackOperations on the native stack of the running thread.
Implementation Stack#
impl StackAssociated function frameAddress#
pub fn frameAddress() -> [*]const u8Returns the address of the stack frame of the function that runs this call.
Details#
The address is inside the running thread's stack. The stack grows down on every supported
target, so every frame of a caller that is still running is at the same or a higher address.
The compiler can inline frameAddress into its caller, so the frame is either the caller's
frame or a frame below it. On wasm32 the address is in the stack in linear memory, and a
function that keeps nothing there has the same address as its caller. Reading through the
address is unsafe and needs the usual pointer proofs.
Gotchas#
The address stops being part of a frame when the function that took it returns. Compare addresses taken on the same thread only.
Associated function withStorage#
pub unsafe fn withStorage<T, A>(count: usize, use: once fn([*]mut T) -> A) -> ACalls use one time with stack storage for count values of T and returns its result.
Details#
The storage is naturally aligned for T and not initialized. It is released when
withStorage returns, so use must not keep the pointer. Values written to the storage are
not dropped. A count whose byte size does not fit in usize traps.
Safety#
The caller proves that count values of T fit in the stack that the running thread has
left. The compiler does not check the size, and storage past the end of the stack is
undefined behavior.
Category: Memory.
Profiles: aarch64-apple-darwin, aarch64-unknown-linux-gnu, aarch64-unknown-linux-gnu-no-libc, x86_64-unknown-linux-gnu, x86_64-unknown-linux-gnu-no-libc, wasm32-unknown-unknown, wasm32-wasip1, wasm32-wasip2.