236 lines
7.5 KiB
C
236 lines
7.5 KiB
C
/**
|
|
* memory/pso.c
|
|
*
|
|
* Paged space objects.
|
|
*
|
|
* Broadly, it should be save to cast any paged space object to a pso2, since
|
|
* that is the smallest actually used size class. This should work to extract
|
|
* the tag and size class fields from the header, for example. I'm not
|
|
* confident enough of my understanding of C to know whether it is similarly
|
|
* safe to cast something passed to you as a pso2 up to something larger, even
|
|
* if you know from the size class field that it actually is something larger.
|
|
*
|
|
* (c) 2026 Simon Brooke <simon@journeyman.cc>
|
|
* Licensed under GPL version 2.0, or, at your option, any later version.
|
|
*/
|
|
|
|
#include <math.h>
|
|
#include <stdbool.h>
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <wchar.h>
|
|
|
|
#include "debug.h"
|
|
|
|
#include "memory/destroy.h"
|
|
#include "memory/header.h"
|
|
#include "memory/memory.h"
|
|
#include "memory/node.h"
|
|
#include "memory/page.h"
|
|
#include "memory/pointer.h"
|
|
#include "memory/pso.h"
|
|
#include "memory/pso4.h"
|
|
#include "memory/tags.h"
|
|
|
|
#include "ops/truth.h"
|
|
|
|
/**
|
|
* @brief a means of creating a cons cell without using a stack frame, to
|
|
* prevent runaway recursion.
|
|
*
|
|
* @param car
|
|
* @param cdr
|
|
*
|
|
* return cons
|
|
*/
|
|
struct pso_pointer cheaty_make_cons( struct pso_pointer car,
|
|
struct pso_pointer cdr ) {
|
|
struct pso_pointer result = allocate( nil, CONSTAG, 2 );
|
|
struct pso2 *obj = pointer_to_object( result );
|
|
|
|
obj->payload.cons.car = car;
|
|
obj->payload.cons.cdr = cdr;
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* @brief Allocate an object of this `size_class` with this `tag`.
|
|
*
|
|
* All objects that are allocated (after completion of init)) should be linked
|
|
* onto the `locals` slot of a stack frame. This guarantees
|
|
* 1. that they do get `inc_ref`ed; and that,
|
|
* 2. if nothing else hangs onto them they will be reclaimed when that stack
|
|
* frame is reclaimed.
|
|
* for some objects (e.g. those cons cells on the locals list) this isn't
|
|
* possible due to infinite recursion, but those special cases need to be
|
|
* audited carefully.
|
|
*
|
|
* @param frame_pointer pointer to an active stack frame (or
|
|
* nil, but only during initialisation).
|
|
* @param tag The tag. Only the first three bytes will be used;
|
|
* @param size_class The size class for the object to be allocated;
|
|
* @return struct pso_pointer a pointer to the newly allocated object
|
|
*/
|
|
struct pso_pointer allocate( struct pso_pointer frame_pointer, char *tag,
|
|
uint8_t size_class ) {
|
|
// todo: issue #21: must have stack frame passed in.
|
|
|
|
#ifdef DEBUG
|
|
debug_printf( DEBUG_ALLOC, 0,
|
|
L"Allocating object of size class %d with tag `%s`... ",
|
|
size_class, tag );
|
|
#endif
|
|
|
|
struct pso_pointer result = pop_freelist( size_class );
|
|
struct pso4 *frame = pointer_to_pso4( frame_pointer );
|
|
|
|
if ( !c_nilp( result ) ) {
|
|
strncpy( ( char * ) ( pointer_to_object( result )->header.tag.bytes.
|
|
mnemonic ), tag, TAGLENGTH );
|
|
|
|
debug_printf( DEBUG_ALLOC, 0, L"at page %d, offset %d... ",
|
|
result.page, result.offset );
|
|
if ( stackp( frame_pointer ) ) {
|
|
// You can't make a stack frame in the middle of making a stack
|
|
// frame. Infinite recursion. So we have to cheat.
|
|
struct pso_pointer locals = cheaty_make_cons( result,
|
|
frame->
|
|
payload.stack_frame.
|
|
locals );
|
|
frame->payload.stack_frame.locals = locals;
|
|
|
|
} else if ( memory_initialised ) {
|
|
fputws( L"WARNING: No stack frame passed to `allocate`.\n",
|
|
stderr );
|
|
}
|
|
} else {
|
|
// TODO: throw exception
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
debug_print( exceptionp( result ) ? L"fail\n" : L"success\n", DEBUG_ALLOC,
|
|
0 );
|
|
#endif
|
|
|
|
return result;
|
|
}
|
|
|
|
int payload_size( struct pso2 *object ) {
|
|
// TODO: Unit tests DEFINITELY needed!
|
|
int sc = object->header.tag.bytes.size_class;
|
|
int hs = sizeof( struct pso_header ) / sizeof( uint64_t );
|
|
int p = pow( 2, sc );
|
|
|
|
int result = abs( p - hs );
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* increment the reference count of the object at this cons pointer.
|
|
*
|
|
* You can't roll over the reference count. Once it hits the maximum
|
|
* value you cannot increment further.
|
|
*
|
|
* Returns the `pointer`.
|
|
*/
|
|
struct pso_pointer inc_ref( struct pso_pointer pointer ) {
|
|
struct pso2 *object = pointer_to_object( pointer );
|
|
|
|
if ( object->header.count < MAXREFERENCE ) {
|
|
object->header.count++;
|
|
#ifdef DEBUG
|
|
debug_printf( DEBUG_ALLOC, 0,
|
|
L"\nIncremented object of type %3.3s at page %u, offset %u to count %u",
|
|
( ( char * ) &object->header.tag.bytes.mnemonic[0] ),
|
|
pointer.page, pointer.offset, object->header.count );
|
|
if ( vectorpointp( pointer ) ) {
|
|
debug_printf( DEBUG_ALLOC, 0,
|
|
L"; pointer to vector object of type %3.3s.\n",
|
|
( ( char * )
|
|
&( object->payload.vectorp.tag.bytes[0] ) ) );
|
|
} else {
|
|
debug_println( DEBUG_ALLOC );
|
|
}
|
|
#endif
|
|
}
|
|
|
|
return pointer;
|
|
}
|
|
|
|
/**
|
|
* Decrement the reference count of the object at this cons pointer.
|
|
*
|
|
* If a count has reached MAXREFERENCE it cannot be decremented.
|
|
* If a count is decremented to zero the object should be freed.
|
|
*
|
|
* Returns the `pointer`, or, if the object has been freed, a pointer to `nil`.
|
|
*/
|
|
struct pso_pointer dec_ref( struct pso_pointer pointer ) {
|
|
struct pso2 *object = pointer_to_object( pointer );
|
|
|
|
if ( !c_nilp( pointer ) && object->header.count > 0
|
|
&& object->header.count != MAXREFERENCE ) {
|
|
object->header.count--;
|
|
#ifdef DEBUG
|
|
debug_printf( DEBUG_ALLOC, 0,
|
|
L"\nDecremented object of type %3.3s at page %d, offset %d to count %d",
|
|
( ( char * ) ( object->header.tag.bytes.mnemonic ) ),
|
|
pointer.page, pointer.offset, object->header.count );
|
|
if ( vectorpointp( pointer ) ) {
|
|
debug_printf( DEBUG_ALLOC, 0,
|
|
L"; pointer to vector object of type %3.3s.\n",
|
|
( ( char * )
|
|
&( object->payload.vectorp.tag.bytes ) ) );
|
|
} else {
|
|
debug_println( DEBUG_ALLOC );
|
|
}
|
|
#endif
|
|
|
|
if ( object->header.count == 0 ) {
|
|
free_object( pointer );
|
|
pointer = nil;
|
|
}
|
|
}
|
|
|
|
return pointer;
|
|
}
|
|
|
|
/**
|
|
* @brief Prevent an object ever being dereferenced.
|
|
*
|
|
* @param pointer pointer to an object to lock.
|
|
*
|
|
* @return the `pointer`
|
|
*/
|
|
struct pso_pointer lock_object( struct pso_pointer pointer ) {
|
|
struct pso2 *object = pointer_to_object( pointer );
|
|
|
|
object->header.count = MAXREFERENCE;
|
|
|
|
return pointer;
|
|
}
|
|
|
|
/**
|
|
* @brief decrement all pointers pointed to by the object at this pointer;
|
|
* clear its memory, and return it to the freelist.
|
|
*/
|
|
struct pso_pointer free_object( struct pso_pointer p ) {
|
|
struct pso_pointer result = nil;
|
|
struct pso2 *obj = pointer_to_object( p );
|
|
uint32_t array_size = ( uint32_t ) payload_size( obj );
|
|
uint8_t size_class = ( obj->header.tag.bytes.size_class );
|
|
|
|
result = destroy( p );
|
|
|
|
/* will C just let me cheerfully walk off the end of the array I've declared? */
|
|
for ( int i = 0; i < array_size; i++ ) {
|
|
obj->payload.words[i] = 0;
|
|
}
|
|
|
|
push_freelist( p );
|
|
|
|
return result;
|
|
}
|