blob: cdf7895a42962dafb4242bc75dcde31a4aa45500 [file]
// Copyright 2026 The IREE Authors
//
// Licensed under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// sbrk() for wasm32 — thin wrapper over the memory.grow instruction.
//
// The wasm linear memory is divided into three regions by wasm-ld:
//
// [0, __data_end) Static data (.data, .rodata, .bss segments).
// [__data_end, __heap_base) Stack (when linked with --stack-first, the
// stack is placed between data and heap).
// [__heap_base, ...) Heap (managed by dlmalloc via this sbrk).
//
// memory.grow operates in 64KB page granularity and returns the previous
// memory size in pages on success, or (size_t)-1 on failure. It never traps —
// failure is indicated by the return value. Growth is one-way: memory cannot
// be returned to the host.
//
// This sbrk is initialized lazily from __heap_base on first call.
#include <errno.h>
#include <stddef.h>
#include <stdint.h>
// Provided by wasm-ld. First byte after static data + stack.
extern unsigned char __heap_base;
#define WASM_PAGE_SIZE 65536
static unsigned char* heap_end = NULL;
void* sbrk(ptrdiff_t increment) {
if (!heap_end) {
heap_end = &__heap_base;
}
uintptr_t old_end = (uintptr_t)heap_end;
uintptr_t heap_base = (uintptr_t)&__heap_base;
if (increment > 0) {
uintptr_t uincrement = (uintptr_t)increment;
// Check for 32-bit address space overflow before forming the pointer.
if (uincrement > UINTPTR_MAX - old_end) {
errno = ENOMEM;
return (void*)-1;
}
uintptr_t new_end = old_end + uincrement;
// Compute how many new pages are needed beyond what's already mapped.
// Use 64-bit arithmetic to avoid overflow at the 4GB boundary (65536
// pages * 65536 bytes = 2^32, which wraps to 0 in 32-bit math).
uint64_t current_memory_bytes =
(uint64_t)__builtin_wasm_memory_size(0) * WASM_PAGE_SIZE;
if (new_end > current_memory_bytes) {
uint64_t deficit = (uint64_t)new_end - current_memory_bytes;
size_t pages_needed =
(size_t)((deficit + WASM_PAGE_SIZE - 1) / WASM_PAGE_SIZE);
size_t result = __builtin_wasm_memory_grow(0, pages_needed);
if (result == (size_t)-1) {
errno = ENOMEM;
return (void*)-1;
}
}
} else if (increment < 0) {
uintptr_t magnitude = (uintptr_t)(-(uintptr_t)increment);
// Negative sbrk: just move the pointer back. Memory pages are never
// returned (wasm doesn't support shrinking linear memory). dlmalloc
// uses negative sbrk to "return" memory, but it's a no-op for us
// beyond bookkeeping.
if (magnitude > old_end - heap_base) {
errno = ENOMEM;
return (void*)-1;
}
}
heap_end = (unsigned char*)(old_end + (uintptr_t)increment);
return (void*)old_end;
}