diff --git a/Makefile b/Makefile index ac845a0..f9a9385 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ CFLAGS=-Wall -g LIBS=-lm TARGET = picoc -SRCS = picoc.c table.c str.c parse.c lex.c intrinsic.c +SRCS = picoc.c table.c str.c parse.c lex.c intrinsic.c heap.c OBJS := $(SRCS:%.c=%.o) all: $(TARGET) diff --git a/heap.c b/heap.c new file mode 100644 index 0000000..52ddf61 --- /dev/null +++ b/heap.c @@ -0,0 +1,135 @@ +/* stack grows up from the bottom and heap grows down from the top of heap space */ +#include +#include "picoc.h" + +#define FREELIST_BUCKETS 8 /* freelists for 4, 8, 12 ... 32 byte allocs */ +#define SPLIT_MEM_THRESHOLD 16 /* don't split memory which is close in size */ + +struct AllocNode +{ + int Size; + struct AllocNode *NextFree; +}; + +static unsigned char HeapMemory[HEAP_SIZE]; /* all memory - stack and heap */ +static void *StackFrame = &HeapMemory; /* the current stack frame */ +static void *StackTop = &HeapMemory; /* the top of the stack */ + +static void *HeapBottom = &HeapMemory[HEAP_SIZE]; /* the bottom of the (downward-growing) heap */ +static struct AllocNode *FreeListBucket[FREELIST_BUCKETS]; /* we keep a pool of freelist buckets to reduce fragmentation */ +static struct AllocNode *FreeListBig; /* free memory which doesn't fit in a bucket */ + +/* initialise the stack and heap storage */ +void HeapInit() +{ + int Count; + + StackFrame = &HeapMemory; + StackTop = &HeapMemory; + *(void **)StackFrame = NULL; + HeapBottom = &HeapMemory[HEAP_SIZE]; + FreeListBig = NULL; + for (Count = 0; Count < FREELIST_BUCKETS; Count++) + FreeListBucket[Count] = NULL; +} + +/* allocate some space on the stack, in the current stack frame + * clears memory. can return NULL if out of stack space */ +void *HeapAllocStack(int Size) +{ + void *NewMem = StackTop; + void *NewTop = StackTop + MEM_ALIGN(Size); + if (NewTop > HeapBottom) + return NULL; + + StackTop = NewTop; + memset(NewMem, '\0', Size); + return NewMem; +} + +/* push a new stack frame on to the stack */ +void HeapPushStackFrame() +{ + *(void **)StackTop = StackFrame; + StackFrame = StackTop; + StackTop += sizeof(void *); +} + +/* pop the current stack frame, freeing all memory in the frame. can return NULL */ +int HeapPopStackFrame() +{ + if (*(void **)StackFrame == NULL) + { + StackTop = StackFrame; + StackFrame = *(void **)StackFrame; + return TRUE; + } + else + return FALSE; +} + +/* allocate some dynamically allocated memory. memory is cleared. can return NULL if out of memory */ +void *HeapAlloc(int Size) +{ + struct AllocNode *NewMem = NULL; + struct AllocNode **FreeNode; + int AllocSize = MEM_ALIGN(Size) + sizeof(NewMem->Size); + int Bucket = AllocSize >> 2; + + if (Bucket < FREELIST_BUCKETS && FreeListBucket[Bucket] != NULL) + { /* try to allocate from a freelist bucket first */ + NewMem = FreeListBucket[Bucket]; + FreeListBucket[Bucket] = *(struct AllocNode **)NewMem; + NewMem->Size = AllocSize; + } + else if (FreeListBig != NULL) + { /* grab the first item from the "big" freelist we can fit in */ + for (FreeNode = &FreeListBig; *FreeNode != NULL && (*FreeNode)->Size < AllocSize; FreeNode = &(*FreeNode)->NextFree) + {} + + if (*FreeNode != NULL) + { + if ((*FreeNode)->Size < Size + SPLIT_MEM_THRESHOLD) + { /* close in size - reduce fragmentation by not splitting */ + NewMem = *FreeNode; + *FreeNode = NewMem->NextFree; + } + else + { /* split this big memory chunk */ + NewMem = *FreeNode + (*FreeNode)->Size - AllocSize; + (*FreeNode)->Size -= AllocSize; + NewMem->Size = AllocSize; + } + } + } + + if (NewMem == NULL) + { /* couldn't allocate from a freelist - try to increase the size of the heap area */ + if (HeapBottom - AllocSize < StackTop) + return NULL; + + HeapBottom -= AllocSize; + NewMem = HeapBottom; + NewMem->Size = AllocSize; + } + + memset(&NewMem->NextFree, '\0', AllocSize-sizeof(NewMem->Size)); + return (void *)&NewMem->NextFree; +} + +/* free some dynamically allocated memory */ +void HeapFree(void *Mem) +{ + int Bucket = ((struct AllocNode *)Mem)->Size >> 2; + + if (Bucket < FREELIST_BUCKETS) + { /* we can fit it in a bucket */ + *(struct AllocNode **)Mem = FreeListBucket[Bucket]; + FreeListBucket[Bucket] = (struct AllocNode *)Mem; + } + else + { /* put it in the big memory freelist */ + ((struct AllocNode *)Mem)->NextFree = FreeListBig; + FreeListBig = (struct AllocNode *)Mem; + } +} diff --git a/picoc.h b/picoc.h index 478b9e4..afcefff 100644 --- a/picoc.h +++ b/picoc.h @@ -4,13 +4,14 @@ #include /* configurable options */ -#define USE_MALLOC +#define HEAP_SIZE 2048 /* space for the heap and the stack */ #define GLOBAL_TABLE_SIZE 397 /* global variable table */ #define FUNCTION_STORE_MAX 200 /* maximum number of used-defined functions and macros */ #define STACK_MAX 10 /* maximum function call stack depth */ #define PARAMETER_MAX 10 /* maximum number of parameters to a function */ #define LOCAL_TABLE_SIZE 11 /* maximum number of local variables */ #define LARGE_INT_POWER_OF_TEN 1000000000 /* the largest power of ten which fits in an int on this architecture */ +#define ARCH_ALIGN_WORDSIZE sizeof(int) /* memory alignment boundary on this architecture */ /* handy definitions */ #ifndef TRUE @@ -26,6 +27,8 @@ #define min(x,y) (((x)<(y))?(x):(y)) #endif +#define MEM_ALIGN(x) (((x) + ARCH_ALIGN_WORDSIZE - 1) & ~(ARCH_ALIGN_WORDSIZE-1)) + #define LOG10E 0.43429448190325182765 #ifndef PATH_MAX @@ -112,27 +115,57 @@ struct FuncDef }; /* values */ -enum ValueType +enum BaseType { - TypeVoid, - TypeInt, - TypeFP, - TypeString, - TypeFunction, - TypeMacro + TypeVoid, /* no type */ + TypeInt, /* integer */ + TypeFP, /* floating point */ + TypeChar, /* a single character - acts like an integer except in machine memory access */ + TypeString, /* a constant source text string with C string emulation */ + TypeFunction, /* a function */ + TypeMacro, /* a macro */ + TypePointer, /* a pointer */ + TypeArray, /* an array of a sub-type */ + TypeType /* a type (eg. typedef) */ +}; + +struct ValueType +{ + enum BaseType Base; /* what kind of type this is */ + struct ValueType *SubType; /* sub-type for pointer and array types */ +}; + +struct ArrayValue +{ + unsigned int Size; /* the number of elements in the array */ + void *Data; /* pointer to the array data */ +}; + +struct PointerValue +{ + struct Value *Segment; /* array or basic value which this points to, NULL for machine memory access */ + union s { + unsigned int Offset; /* index into an array */ + void *Memory; /* machine memory pointer for raw memory access */ + } Data; }; union AnyValue { - int Integer; - double FP; + unsigned char *Character; + short *ShortInteger; + int *Integer; + double *FP; Str String; + struct ArrayValue Array; + struct PointerValue Pointer; }; struct Value { - enum ValueType Typ; - union AnyValue Val; + struct ValueType Typ; + char MustFree; + union AnyValue *Val; }; /* hash table data structure */ @@ -200,12 +233,20 @@ void LexToEndOfLine(struct LexState *Lexer); /* parse.c */ void ParseInit(void); void Parse(const Str *FileName, const Str *Source, int RunIt); -int ParseType(struct LexState *Lexer, enum ValueType *Typ); +int ParseType(struct LexState *Lexer, struct ValueType *Typ); /* intrinsic.c */ void IntrinsicInit(struct Table *GlobalTable); void IntrinsicGetLexer(struct LexState *Lexer, int IntrinsicId); -void IntrinsicCall(struct LexState *Lexer, struct Value *Result, enum ValueType ReturnType, int IntrinsicId); +void IntrinsicCall(struct LexState *Lexer, struct Value *Result, struct ValueType ReturnType, int IntrinsicId); + +/* heap.c */ +void HeapInit(); +void *HeapAllocStack(int Size); +void HeapPushStackFrame(); +int HeapPopStackFrame(); +void *HeapAlloc(int Size); +void HeapFree(void *Mem); #endif /* PICOC_H */