Compare commits

...

2 Commits

Author SHA1 Message Date
dcf6a09dae minor refactoring 2023-06-03 01:39:35 -05:00
e0faa14b35 implemented jump table dispatch
- currently only enabled on supported platforms (GNU C Compiler + Clang)
- when enabled, sped up examples/fibtest.cosmo by about 20% (avg of 11.179s prior and 8.799 after)

NOTE: malicious dumps can trivially cause crashes now by having junk function chunks
2023-06-03 01:17:28 -05:00
10 changed files with 684 additions and 624 deletions

View File

@ -9,53 +9,53 @@ void printIndent(int indent)
printf("\t"); printf("\t");
} }
int simpleInstruction(const char *name, int offset) static int simpleInstruction(const char *name, int offset)
{ {
printf("%s", name); printf("%s", name);
return offset + 1; // consume opcode return offset + 1; // consume opcode
} }
int u8OperandInstruction(const char *name, CChunk *chunk, int offset) static int u8OperandInstruction(const char *name, CChunk *chunk, int offset)
{ {
printf("%-16s [%03d]", name, readu8Chunk(chunk, offset + 1)); printf("%-16s [%03d]", name, readu8Chunk(chunk, offset + 1));
return offset + 2; return offset + 2;
} }
int u16OperandInstruction(const char *name, CChunk *chunk, int offset) static int u16OperandInstruction(const char *name, CChunk *chunk, int offset)
{ {
printf("%-16s [%05d]", name, readu16Chunk(chunk, offset + 1)); printf("%-16s [%05d]", name, readu16Chunk(chunk, offset + 1));
return offset + 1 + (sizeof(uint16_t) / sizeof(INSTRUCTION)); return offset + 1 + (sizeof(uint16_t) / sizeof(INSTRUCTION));
} }
int JumpInstruction(const char *name, CChunk *chunk, int offset, int dir) static int JumpInstruction(const char *name, CChunk *chunk, int offset, int dir)
{ {
int jmp = ((int)readu16Chunk(chunk, offset + 1)) * dir; int jmp = ((int)readu16Chunk(chunk, offset + 1)) * dir;
printf("%-16s [%05d] - jumps to %04d", name, jmp, offset + 3 + jmp); printf("%-16s [%05d] - jumps to %04d", name, jmp, offset + 3 + jmp);
return offset + 1 + (sizeof(uint16_t) / sizeof(INSTRUCTION)); return offset + 1 + (sizeof(uint16_t) / sizeof(INSTRUCTION));
} }
int u8u8OperandInstruction(const char *name, CChunk *chunk, int offset) static int u8u8OperandInstruction(const char *name, CChunk *chunk, int offset)
{ {
printf("%-16s [%03d] [%03d]", name, readu8Chunk(chunk, offset + 1), printf("%-16s [%03d] [%03d]", name, readu8Chunk(chunk, offset + 1),
readu8Chunk(chunk, offset + 2)); readu8Chunk(chunk, offset + 2));
return offset + 3; // op + u8 + u8 return offset + 3; // op + u8 + u8
} }
int u8u16OperandInstruction(const char *name, CChunk *chunk, int offset) static int u8u16OperandInstruction(const char *name, CChunk *chunk, int offset)
{ {
printf("%-16s [%03d] [%05d]", name, readu8Chunk(chunk, offset + 1), printf("%-16s [%03d] [%05d]", name, readu8Chunk(chunk, offset + 1),
readu16Chunk(chunk, offset + 2)); readu16Chunk(chunk, offset + 2));
return offset + 4; // op + u8 + u16 return offset + 4; // op + u8 + u16
} }
int u8u8u16OperandInstruction(const char *name, CChunk *chunk, int offset) static int u8u8u16OperandInstruction(const char *name, CChunk *chunk, int offset)
{ {
printf("%-16s [%03d] [%03d] [%05d]", name, readu8Chunk(chunk, offset + 1), printf("%-16s [%03d] [%03d] [%05d]", name, readu8Chunk(chunk, offset + 1),
readu8Chunk(chunk, offset + 2), readu16Chunk(chunk, offset + 3)); readu8Chunk(chunk, offset + 2), readu16Chunk(chunk, offset + 3));
return offset + 5; // op + u8 + u8 + u16 return offset + 5; // op + u8 + u8 + u16
} }
int constInstruction(const char *name, CChunk *chunk, int offset) static int constInstruction(const char *name, CChunk *chunk, int offset)
{ {
int index = readu16Chunk(chunk, offset + 1); int index = readu16Chunk(chunk, offset + 1);
printf("%-16s [%05d] - ", name, index); printf("%-16s [%05d] - ", name, index);

View File

@ -150,7 +150,7 @@ static bool match(CLexState *state, char expected)
return true; return true;
} }
char peek(CLexState *state) static char peek(CLexState *state)
{ {
return *state->currentChar; return *state->currentChar;
} }
@ -163,7 +163,7 @@ static char peekNext(CLexState *state)
return state->currentChar[1]; return state->currentChar[1];
} }
char next(CLexState *state) static char next(CLexState *state)
{ {
if (isEnd(state)) if (isEnd(state))
return '\0'; // return a null terminator return '\0'; // return a null terminator
@ -171,12 +171,12 @@ char next(CLexState *state)
return state->currentChar[-1]; return state->currentChar[-1];
} }
bool isHex(char c) static bool isHex(char c)
{ {
return isNumerical(c) || ('A' <= c && 'F' >= c) || ('a' <= c && 'f' >= c); return isNumerical(c) || ('A' <= c && 'F' >= c) || ('a' <= c && 'f' >= c);
} }
CTokenType identifierType(CLexState *state) static CTokenType identifierType(CLexState *state)
{ {
int length = state->currentChar - state->startChar; int length = state->currentChar - state->startChar;
@ -192,7 +192,7 @@ CTokenType identifierType(CLexState *state)
return TOKEN_IDENTIFIER; return TOKEN_IDENTIFIER;
} }
void skipWhitespace(CLexState *state) static void skipWhitespace(CLexState *state)
{ {
while (true) { while (true) {
char c = peek(state); char c = peek(state);
@ -235,7 +235,7 @@ void skipWhitespace(CLexState *state)
} }
} }
CToken parseString(CLexState *state) static CToken parseString(CLexState *state)
{ {
makeBuffer(state); // buffer mode makeBuffer(state); // buffer mode
while (peek(state) != '"' && !isEnd(state)) { while (peek(state) != '"' && !isEnd(state)) {
@ -341,7 +341,7 @@ CToken parseString(CLexState *state)
return makeToken(state, TOKEN_STRING); return makeToken(state, TOKEN_STRING);
} }
CToken parseNumber(CLexState *state) static CToken parseNumber(CLexState *state)
{ {
switch (peek(state)) { switch (peek(state)) {
case 'x': // hexadecimal number case 'x': // hexadecimal number
@ -380,7 +380,7 @@ CToken parseNumber(CLexState *state)
return makeToken(state, TOKEN_NUMBER); return makeToken(state, TOKEN_NUMBER);
} }
CToken parseIdentifier(CLexState *state) static CToken parseIdentifier(CLexState *state)
{ {
// read literal // read literal
while ((isAlpha(peek(state)) || isNumerical(peek(state))) && !isEnd(state)) while ((isAlpha(peek(state)) || isNumerical(peek(state))) && !isEnd(state))

View File

@ -93,7 +93,7 @@ typedef struct
char *currentChar; char *currentChar;
char *startChar; char *startChar;
char *buffer; // if non-NULL & bufCount > 0, token->start & token->length will be set to buffer char *buffer; // if non-NULL & bufCount > 0, token->start & token->length will be set to buffer
// & bufCount respectively // & bufCount respectively. used exclusively for string literals
size_t bufCount; size_t bufCount;
size_t bufCap; size_t bufCap;
int line; // current line int line; // current line

View File

@ -51,10 +51,10 @@ COSMO_API bool cosmoM_checkGarbage(CState *state, size_t needed)
return false; return false;
} }
void markObject(CState *state, CObj *obj); static void markObject(CState *state, CObj *obj);
void markValue(CState *state, CValue val); static void markValue(CState *state, CValue val);
void markTable(CState *state, CTable *tbl) static void markTable(CState *state, CTable *tbl)
{ {
if (tbl->table == NULL) // table is still being initialized if (tbl->table == NULL) // table is still being initialized
return; return;
@ -68,7 +68,7 @@ void markTable(CState *state, CTable *tbl)
} }
// frees white members from the table // frees white members from the table
void tableRemoveWhite(CState *state, CTable *tbl) static void tableRemoveWhite(CState *state, CTable *tbl)
{ {
if (tbl->table == NULL) // table is still being initialized if (tbl->table == NULL) // table is still being initialized
return; return;
@ -86,7 +86,7 @@ void tableRemoveWhite(CState *state, CTable *tbl)
cosmoT_checkShrink(state, tbl); // recovers the memory we're no longer using cosmoT_checkShrink(state, tbl); // recovers the memory we're no longer using
} }
void markArray(CState *state, CValueArray *array) static void markArray(CState *state, CValueArray *array)
{ {
for (size_t i = 0; i < array->count; i++) { for (size_t i = 0; i < array->count; i++) {
markValue(state, array->values[i]); markValue(state, array->values[i]);
@ -95,7 +95,7 @@ void markArray(CState *state, CValueArray *array)
// mark all references associated with the object // mark all references associated with the object
// black = keep, white = discard // black = keep, white = discard
void blackenObject(CState *state, CObj *obj) static void blackenObject(CState *state, CObj *obj)
{ {
markObject(state, (CObj *)obj->proto); markObject(state, (CObj *)obj->proto);
switch (obj->type) { switch (obj->type) {
@ -161,7 +161,7 @@ void blackenObject(CState *state, CObj *obj)
} }
} }
void markObject(CState *state, CObj *obj) static void markObject(CState *state, CObj *obj)
{ {
if (obj == NULL || obj->isMarked) // skip if NULL or already marked if (obj == NULL || obj->isMarked) // skip if NULL or already marked
return; return;
@ -185,14 +185,14 @@ void markObject(CState *state, CObj *obj)
state->grayStack.array[state->grayStack.count++] = obj; state->grayStack.array[state->grayStack.count++] = obj;
} }
void markValue(CState *state, CValue val) static void markValue(CState *state, CValue val)
{ {
if (IS_REF(val)) if (IS_REF(val))
markObject(state, cosmoV_readRef(val)); markObject(state, cosmoV_readRef(val));
} }
// trace our gray references // trace our gray references
void traceGrays(CState *state) static void traceGrays(CState *state)
{ {
while (state->grayStack.count > 0) { while (state->grayStack.count > 0) {
CObj *obj = state->grayStack.array[--state->grayStack.count]; CObj *obj = state->grayStack.array[--state->grayStack.count];
@ -200,7 +200,7 @@ void traceGrays(CState *state)
} }
} }
void sweep(CState *state) static void sweep(CState *state)
{ {
CObj *prev = NULL; CObj *prev = NULL;
CObj *object = state->objects; CObj *object = state->objects;
@ -224,7 +224,7 @@ void sweep(CState *state)
} }
} }
void markUserRoots(CState *state) static void markUserRoots(CState *state)
{ {
CObj *root = state->userRoots; CObj *root = state->userRoots;
@ -235,7 +235,7 @@ void markUserRoots(CState *state)
} }
} }
void markRoots(CState *state) static void markRoots(CState *state)
{ {
// mark all values on the stack // mark all values on the stack
for (StkPtr value = state->stack; value < state->top; value++) { for (StkPtr value = state->stack; value < state->top; value++) {

View File

@ -67,6 +67,7 @@ COSMO_API void cosmoM_collectGarbage(CState *state);
COSMO_API void cosmoM_updateThreshhold(CState *state); COSMO_API void cosmoM_updateThreshhold(CState *state);
// lets the VM know you are holding a reference to a CObj and to not free it // lets the VM know you are holding a reference to a CObj and to not free it
// NOTE: prefer to use the stack when possible
COSMO_API void cosmoM_addRoot(CState *state, CObj *newRoot); COSMO_API void cosmoM_addRoot(CState *state, CObj *newRoot);
// lets the VM know this root is no longer held in a reference and is able to be freed // lets the VM know this root is no longer held in a reference and is able to be freed

View File

@ -64,7 +64,7 @@ typedef enum
OP_FALSE, OP_FALSE,
OP_NIL, OP_NIL,
OP_RETURN OP_RETURN,
} COPCODE; // there can be a max of 256 instructions } COPCODE; // there can be a max of 256 instructions
#endif #endif

View File

@ -14,7 +14,15 @@
performance, however this will produce undefined behavior as you reach the stack limit (and may performance, however this will produce undefined behavior as you reach the stack limit (and may
cause a seg fault!). It is recommended to keep this enabled. cause a seg fault!). It is recommended to keep this enabled.
*/ */
#define SAFE_STACK // #define SAFE_STACK
/*
NAN_BOXXED:
if undefined, the interpreter will use a tagged union to store values. This is the default.
Note that even though the sizeof(CValue) is 8 bytes for NAN_BOXXED (as opposed to 16 bytes for
the tagged union) no performance benefits were measured. I recommend keeping this undefined for
now.
*/
// #define NAN_BOXXED // #define NAN_BOXXED
// forward declare *most* stuff so our headers are cleaner // forward declare *most* stuff so our headers are cleaner

View File

@ -11,7 +11,7 @@
#define MIN_TABLE_CAPACITY ARRAY_START #define MIN_TABLE_CAPACITY ARRAY_START
// bit-twiddling hacks, gets the next power of 2 // bit-twiddling hacks, gets the next power of 2
unsigned int nextPow2(unsigned int x) static unsigned int nextPow2(unsigned int x)
{ {
if (x <= ARRAY_START - 1) if (x <= ARRAY_START - 1)
return ARRAY_START; // sanity check return ARRAY_START; // sanity check
@ -46,13 +46,14 @@ void cosmoT_initTable(CState *state, CTable *tbl, int startCap)
void cosmoT_addTable(CState *state, CTable *from, CTable *to) void cosmoT_addTable(CState *state, CTable *from, CTable *to)
{ {
CTableEntry *entry;
int cap = from->capacityMask + 1; int cap = from->capacityMask + 1;
for (int i = 0; i < cap; i++) { for (int i = 0; i < cap; i++) {
CTableEntry *entry = &from->table[i]; entry = &from->table[i];
if (!(IS_NIL(entry->key))) { if (!(IS_NIL(entry->key))) {
CValue *newVal = cosmoT_insert(state, to, entry->key); *cosmoT_insert(state, to, entry->key) = entry->val;
*newVal = entry->val;
} }
} }
} }
@ -62,7 +63,7 @@ void cosmoT_clearTable(CState *state, CTable *tbl)
cosmoM_freearray(state, CTableEntry, tbl->table, (tbl->capacityMask + 1)); cosmoM_freearray(state, CTableEntry, tbl->table, (tbl->capacityMask + 1));
} }
uint32_t getObjectHash(CObj *obj) static uint32_t getObjectHash(CObj *obj)
{ {
switch (obj->type) { switch (obj->type) {
case COBJ_STRING: case COBJ_STRING:
@ -72,7 +73,7 @@ uint32_t getObjectHash(CObj *obj)
} }
} }
uint32_t getValueHash(CValue *val) static uint32_t getValueHash(CValue *val)
{ {
switch (GET_TYPE(*val)) { switch (GET_TYPE(*val)) {
case COSMO_TREF: case COSMO_TREF:

1211
src/cvm.c

File diff suppressed because it is too large Load Diff

View File

@ -8,6 +8,19 @@
// #define VM_DEBUG // #define VM_DEBUG
/*
if we're using GNUC or clang, we can use computed gotos which speeds up
cosmoV_execute by about 20% from benchmarking. of course, if you know
your compiler supports computed gotos, you can define VM_JUMPTABLE
BTW: be weary of maliciously crafted cosmo dumps!! it's very easy to crash
cosmo with this enabled and reading invalid opcodes due to us just using the
opcode as an index into the jump table
*/
#if defined(__GNUC__) || defined(__clang__)
# define VM_JUMPTABLE
#endif
typedef enum typedef enum
{ {
COSMOVM_OK, COSMOVM_OK,