Skip to content
Open
2 changes: 1 addition & 1 deletion build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
SRCS=$(find "src" -type f -name "*.c")
OUT="metagenc"

#CFLAGS="-Isrc -Wall -Wpedantic -Wextra -Wshadow -std=c11 -03"
#CFLAGS="-Isrc -Wall -Wpedantic -Wextra -Wshadow -std=c11 -O3"
CFLAGS="-Isrc -Wall -Wpedantic -Wextra -Wshadow -std=c11 -g"
#CFLAGS="-Isrc -Wall -Wpedantic -Wextra -Wshadow -std=c11 -g -fsanitize=address -fsanitize=undefined"

Expand Down
7 changes: 3 additions & 4 deletions docs/bytecode_vm.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,21 +15,20 @@ Layout:
- Main function
- Every other function

Note that currently, every local variable is aligned to 8-byte boundary (sizeof(BytecodeWord)).
NOTE: The VM currently only supports load and stores in 8-byte chunks (sizeof(BytecodeWord)). Therefore, every variable is aligned to the 8-byte boundary.


## VM:
The VM executes bytecode.

The VM consits of four mutable properties:
1. instruction pointer (ip)
1. instruction pointer / program counter (pc)
2. stack pointer (sp)
3. base pointer (bp)
4. stack
5. flags (currently unused)

### Execution
The VM executes one instruction at a time until it reaches the OP_EXIT instruction. The next instruction is determined by the ip. The ip is incremented upon reading an instruction, but can also be changed by branch and jump instructions.
The VM executes one instruction at a time until it reaches the OP_EXIT instruction. The next instruction is determined by the pc. The pc is incremented upon reading an instruction, but can also be changed by branch and jump instructions.

### Function Prologue and Epilogue

Expand Down
16 changes: 13 additions & 3 deletions src/ast.c
Original file line number Diff line number Diff line change
Expand Up @@ -265,14 +265,24 @@ static void ast_node_to_str(Str8Builder *sb, AstNode *head, u32 indent)
} break;
case EXPR_LITERAL: {
AstLiteral *lit = AS_LITERAL(head);
str_builder_append_str8(sb, lit->literal);
switch (lit->lit_type) {
case LIT_STR:
str_builder_append_str8(sb, STR8_LIT("(str) "));
break;
case LIT_NUM:
str_builder_append_str8(sb, STR8_LIT("(num) "));
break;
case LIT_IDENT:
case LIT_NULL:
break;
}
} break;
case EXPR_CALL: {
AstCall *call = AS_CALL(head);
if (call->is_comptime) {
str_builder_append_u8(sb, '@');
}
str_builder_sprintf(sb, "\"%s\"", 1, call->identifier.str);
str_builder_sprintf(sb, "%s", 1, call->identifier.str);
if (call->args) {
ast_node_to_str(sb, (AstNode *)call->args, indent + 1);
}
Expand Down Expand Up @@ -342,7 +352,7 @@ static void ast_node_to_str(Str8Builder *sb, AstNode *head, u32 indent)
if (func_decl->body == NULL) {
str_builder_append_str8(sb, STR8_LIT("compiler internal "));
}
str_builder_sprintf(sb, "\"%s\"", 1, func_decl->name.str);
str_builder_sprintf(sb, "%s", 1, func_decl->name.str);
str_builder_append_str8(sb, STR8_LIT(" params="));
ast_print_typed_var_list(sb, func_decl->parameters);
if (func_decl->body != NULL) {
Expand Down
14 changes: 7 additions & 7 deletions src/codegen/bytecode/vm.c
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,8 @@ static void dump_stack(MetagenVM vm, OpCode instruction)
if ((i + 1) % 8 == 0) {
u8 *chunk = &vm.stack[i - 7]; // Start of this 8-byte block
s64 as_s64 = *(s64 *)chunk;
s32 low = *(s32 *)chunk;
s32 high = *(s32 *)(chunk + 4);
// s32 low = *(s32 *)chunk;
// s32 high = *(s32 *)(chunk + 4);
// printf(" | s64: %lld | s32s: %d, %d", (long long)as_s64, low, high);
printf("%d: %lld", i / 8, (long long)as_s64);
printf("\n");
Expand All @@ -97,11 +97,11 @@ static void dump_stack(MetagenVM vm, OpCode instruction)
printf("\n");
}

BytecodeWord run(Bytecode bytecode, bool debug)
BytecodeWord run(Bytecode *bytecode, bool debug)
{
MetagenVM vm = { 0 };
MetagenVM vm;
vm.b = bytecode;
vm.pc = bytecode.code;
vm.pc = bytecode->code;
vm.sp = (u8 *)vm.stack;
vm.ss = vm.sp;
vm.bp = 0;
Expand Down Expand Up @@ -147,7 +147,7 @@ BytecodeWord run(Bytecode bytecode, bool debug)

/* Jumps and branches */
case OP_JMP:
vm.pc = bytecode.code + popw(&vm);
vm.pc = bytecode->code + popw(&vm);
break;
case OP_BIZ: {
BytecodeQuarter target = nextq(&vm);
Expand Down Expand Up @@ -230,7 +230,7 @@ BytecodeWord run(Bytecode bytecode, bool debug)
case OP_CALL: {
BytecodeWord callee_offset = popw(&vm);
pushw(&vm, (BytecodeWord)vm.pc);
vm.pc = bytecode.code + callee_offset;
vm.pc = bytecode->code + callee_offset;
} break;
case OP_NOP:
break;
Expand Down
4 changes: 2 additions & 2 deletions src/codegen/bytecode/vm.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ typedef enum {
} VMFlags;

typedef struct {
Bytecode b;
Bytecode *b;
u8 *pc;

u8 stack[STACK_MAX * 8]; // Byte-addressable stack
Expand All @@ -41,6 +41,6 @@ typedef struct {
} MetagenVM;


BytecodeWord run(Bytecode bytecode, bool debug);
BytecodeWord run(Bytecode *bytecode, bool debug);

#endif /* VM_H */
46 changes: 39 additions & 7 deletions src/codegen/c/gen.c
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,13 @@ static void write_base(void)
"typedef uint32_t u32;\n"
"typedef uint64_t u64;\n"
"typedef float f32;\n"
"typedef double f64;\n";

"typedef double f64;\n"
"\n"
"#define STR_LIT(literal) \\\n"
" (str) \\\n"
" { \\\n"
" (u8 *)literal, sizeof(literal) - 1 \\\n"
" }";
fprintf(f, "%s", base);
}

Expand Down Expand Up @@ -105,6 +110,11 @@ static u8 type_info_to_printf_format(TypeInfo *t)
case TYPE_BOOL:
case TYPE_INTEGER:
return 'd';
case TYPE_STRUCT: {
if (strcmp(t->generated_by.str, "str") == 0) {
return 's';
}
}
default:
return '?';
}
Expand Down Expand Up @@ -242,9 +252,14 @@ static void gen_expr(Compiler *compiler, AstExpr *head)
} break;
case EXPR_LITERAL: {
AstLiteral *lit = AS_LITERAL(head);
if (lit->lit_type == LIT_NULL) {
fprintf(f, "NULL");
} else if (lit->lit_type == LIT_NUM) {
switch (lit->lit_type) {
case LIT_STR:
fprintf(f, "STR_LIT(\"%s\")", lit->literal.str);
break;
case LIT_IDENT:
fprintf(f, "%s", lit->literal.str);
break;
case LIT_NUM: {
bool success;
u32 num = str_view_to_u32(lit->literal, &success);
if (!success) {
Expand All @@ -253,9 +268,26 @@ static void gen_expr(Compiler *compiler, AstExpr *head)
} else {
fprintf(f, "%d", num);
}
} else {
fprintf(f, "%s", lit->literal.str);
} break;
case LIT_NULL:
fprintf(f, "NULL");
break;
}

// if (lit->lit_type == LIT_NULL) {
// fprintf(f, "NULL");
// } else if (lit->lit_type == LIT_NUM) {
// bool success;
// u32 num = str_view_to_u32(lit->literal, &success);
// if (!success) {
// error_node(compiler->e, "Could not convert number literal to u32", (AstNode *)head);
// fprintf(f, "RIP");
// } else {
// fprintf(f, "%d", num);
// }
// } else {
// fprintf(f, "%s", lit->literal.str);
// }
} break;
case EXPR_CALL: {
AstCall *call = AS_CALL(head);
Expand Down
21 changes: 18 additions & 3 deletions src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ u32 compile(char *file_name, Str8 source)

Bytecode bytecode = ast_call_to_bytecode(compiler.symt_root, ast_root, call);
// disassemble(bytecode, source);
BytecodeWord result = run(bytecode, false);
BytecodeWord result = run(&bytecode, false);
// TODO: Temporary assume result is an s32, turn it into a source literal
Str8Builder sb = make_str_builder(compiler.persist_arena);
str_builder_sprintf(&sb, "%d", 1, result);
Expand Down Expand Up @@ -148,8 +148,7 @@ u32 compile(char *file_name, Str8 source)
if (options.debug_bytecode) {
disassemble(bytecode, source);
}
// run(bytecode, options.debug_bytecode);
run(bytecode, false);
run(&bytecode, false);
} else {
LOG_DEBUG_NOARG("Generating c-code");
transpile_to_c(&compiler);
Expand All @@ -172,6 +171,21 @@ u32 compile(char *file_name, Str8 source)
return e.n_errors;
}

static void print_help(void)
{
printf("Usage: program [options] <input_file>\n");
printf("Options:\n");
printf(" -l <level> Set log level (0-2).\n");
printf(" 0 = Errors only\n");
printf(" 1 = Warnings and errors\n");
printf(" 2 = Info, warnings, and errors\n");
printf(" -p Parse only. Prints the syntax tree and exits.\n");
printf(" -t <target> Target output. Supported targers:\n");
printf(" c\n");
printf(" bytecode\n");
printf(" -d Debug bytecode output.\n");
}

int main(int argc, char *argv[])
{
/*
Expand Down Expand Up @@ -212,6 +226,7 @@ int main(int argc, char *argv[])
break;
case '?':
fprintf(stderr, "Error: Bad usage.\n");
print_help();
return EXIT_FAILURE;
}
}
Expand Down
1 change: 1 addition & 0 deletions src/parser.c
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,7 @@ static AstExpr *parse_primary(Parser *parser)
ast_list_push_back(&comptime_calls, call_node);
return (AstExpr *)call;
}
case TOKEN_STR:
case TOKEN_NUM:
case TOKEN_IDENTIFIER: {
Token next = peek_token(parser);
Expand Down
29 changes: 22 additions & 7 deletions src/type.c
Original file line number Diff line number Diff line change
Expand Up @@ -506,13 +506,29 @@ static TypeInfo *typecheck_expr(Compiler *c, SymbolTable *symt_local, AstExpr *h
} break;
case EXPR_LITERAL: {
AstLiteral *lit = AS_LITERAL(head);
if (lit->lit_type == LIT_IDENT || lit->lit_type == LIT_NULL) {
Symbol *sym;
switch (lit->lit_type) {
case LIT_IDENT:
case LIT_NULL:
head->type = lit->sym->type_info;
} else {
// TODO: temporary assumption that every constant literal that is not an ident is a s32
Symbol *sym = get_sym_by_name(symt_local, (Str8){ .len = 3, .str = (u8 *)"s32" });
break;
case LIT_NUM:
// TODO: temporary assumption that every constant number that is an a s32
sym = get_sym_by_name(symt_local, STR8_LIT("s32"));
head->type = sym->type_info;
break;
case LIT_STR:
sym = get_sym_by_name(symt_local, STR8_LIT("str"));
head->type = sym->type_info;
break;
}
// if (lit->lit_type == LIT_IDENT || lit->lit_type == LIT_NULL) {
// head->type = lit->sym->type_info;
// } else {
// // TODO: temporary assumption that every constant literal that is not an ident is a s32
// Symbol *sym = get_sym_by_name(symt_local, (Str8){ .len = 3, .str = (u8 *)"s32" });
// head->type = sym->type_info;
// }
} break;
case EXPR_CALL: {
AstCall *call = AS_CALL(head);
Expand Down Expand Up @@ -706,11 +722,11 @@ static void add_builtin_integral_type(Compiler *c, bool is_signed, u32 bit_size)
static void fill_builtin_types(Compiler *c)
{
/* Integers */
// add_builtin_integral_type(c, true, 8);
//add_builtin_integral_type(c, true, 8);
// add_builtin_integral_type(c, true, 16);
add_builtin_integral_type(c, true, 32);
// add_builtin_integral_type(c, true, 64);
// add_builtin_integral_type(c, false, 8);
add_builtin_integral_type(c, false, 8);
// add_builtin_integral_type(c, false, 16);
// add_builtin_integral_type(c, false, 32);
// add_builtin_integral_type(c, false, 64);
Expand All @@ -724,7 +740,6 @@ static void fill_builtin_types(Compiler *c)
symt_new_sym(c, &c->symt_root, SYMBOL_TYPE, name, (TypeInfo *)bool_builtin, NULL);
}


static void typegen_from_intrinsic_func(Compiler *c, Str8 func_name)
{
TypeInfoFunc *t = make_type_info(c->persist_arena, TYPE_FUNC, func_name);
Expand Down
3 changes: 3 additions & 0 deletions src/type.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@

typedef struct compiler_t Compiler; // forward decl from compiler.h

// TODO: Natural slice type? Length and a pointer.
// Also, currently we represent a string as a struct with a length and a pointer, similar
// to Str8.
typedef enum {
TYPE_INTEGER = 0,
TYPE_BOOL,
Expand Down