phasm/phasm/stdlib/alloc.py
Johan B.W. de Vries d97be81828 Optimise: Remove unused functions
By default, we add a lot of build in functions that may
never get called.

This commit adds a simple reachability graph algorithm
to remove functions that can't be called from outside.

Also, unmarks a lot of functions as being exported. It
was the default to export - now it's the default to not
export.

Also, some general cleanup to the wasm statement calls.
2025-05-25 16:39:25 +02:00

88 lines
2.2 KiB
Python

"""
stdlib: Memory allocation
"""
from phasm.wasmgenerator import Generator, func_wrapper
from phasm.wasmgenerator import VarType_i32 as i32
IDENTIFIER = 0xA1C0
ADR_IDENTIFIER = 0
ADR_RESERVED0 = ADR_IDENTIFIER + 4
ADR_FREE_BLOCK_PTR = ADR_RESERVED0 + 4
ADR_UNALLOC_PTR = ADR_FREE_BLOCK_PTR + 4
UNALLOC_PTR = ADR_UNALLOC_PTR + 4
# For memory initialization see phasm.compiler.module_data
@func_wrapper()
def __find_free_block__(g: Generator, alloc_size: i32) -> i32:
# Find out if we've freed any blocks at all so far
g.i32.const(ADR_FREE_BLOCK_PTR)
g.i32.load()
g.i32.const(0)
g.i32.eq()
with g.if_():
g.i32.const(0)
g.return_()
del alloc_size # TODO: Actual implement using a previously freed block
g.unreachable()
return i32('return') # To satisfy mypy
@func_wrapper(exported=True)
def __alloc__(g: Generator, alloc_size: i32) -> i32:
result = i32('result')
# Check if the memory is already initialized
g.i32.const(ADR_IDENTIFIER)
g.i32.load()
g.i32.const(IDENTIFIER)
g.i32.ne()
with g.if_():
# Not yet initialized, or memory corruption
g.unreachable()
# Try to claim a free block
g.local.get(alloc_size)
g.call(__find_free_block__)
g.local.set(result)
# Check if there was a free block
g.local.get(result)
g.i32.const(0)
g.i32.eq()
with g.if_():
# No free blocks, increase allocated memory usage
# Put the address on the stack in advance so we can store to it later
g.i32.const(ADR_UNALLOC_PTR)
# Get the current unalloc pointer value
g.i32.const(ADR_UNALLOC_PTR)
g.i32.load()
g.local.tee(result)
# Calculate new unalloc pointer value
g.i32.const(4) # Header size
g.i32.add()
g.local.get(alloc_size)
g.i32.add()
# Store new unalloc pointer value (address was set on stack in advance)
g.i32.store()
# Store block size in the header
g.local.get(result)
g.local.get(alloc_size)
g.i32.store()
# Return address of the allocated memory, skipping the allocator header
g.local.get(result)
g.i32.const(4) # Header size
g.i32.add()
return i32('return') # To satisfy mypy