diff --git a/.gitignore b/.gitignore index c53e40b..97616a4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ speedcat speedcat.exe speedcat.pdb +test.exe +hello.ll \ No newline at end of file diff --git a/src/llvm_emitter.odin b/src/llvm_emitter.odin new file mode 100644 index 0000000..2388288 --- /dev/null +++ b/src/llvm_emitter.odin @@ -0,0 +1,208 @@ +package main + +import "core:fmt" +import "core:strings" + +LLVMScope :: struct { + name: cstring, + basic_block: LLVMBasicBlockRef, + definitions: map[int]LLVMValueRef, + types: map[int]LLVMTypeRef, +} + +g_llvm_scope_stack := [dynamic]LLVMScope{} + +llvm_scope_find_definition :: proc(name: ^[dynamic]u8) -> LLVMValueRef { + #reverse for &scope in g_llvm_scope_stack { + value, ok := scope.definitions[get_character_sum_of_dyn_arr(name)] + if ok { + return value + } + } + return nil +} + +llvm_scope_find_type :: proc(name: ^[dynamic]u8) -> LLVMTypeRef { + #reverse for &scope in g_llvm_scope_stack { + value, ok := scope.types[get_character_sum_of_dyn_arr(name)] + if ok { + return value + } + } + return nil +} + +llvm_scope_enter :: proc(name: cstring, basic_block: LLVMBasicBlockRef) { + append(&g_llvm_scope_stack, LLVMScope{ + name = name, + basic_block = basic_block, + definitions = make(map[int]LLVMValueRef), + types = make(map[int]LLVMTypeRef), + }) +} + +llvm_scope_leave :: proc() { + delete(g_llvm_scope_stack[len(g_llvm_scope_stack)-1].definitions) + delete(g_llvm_scope_stack[len(g_llvm_scope_stack)-1].types) + pop(&g_llvm_scope_stack) +} + +llvm_top_scope :: proc() -> ^LLVMScope { + return &g_llvm_scope_stack[len(g_llvm_scope_stack)-1] +} + +generate_llvm :: proc(ctx: LLVMContextRef, mod: LLVMModuleRef, builder: LLVMBuilderRef, node: ^Node) { + // FIXME: Everything leaks memory. + scope_number : i32 = 0 + + int_32_type := LLVMInt32TypeInContext(ctx) + main_function_type := LLVMFunctionType(int_32_type, nil, 0, LLVMBool(0)) + main_function := LLVMAddFunction(mod, "main", main_function_type) + + get_basic_block_name :: proc(scope_num: i32) -> cstring { + return fmt.caprintf("scope_%d", scope_num) + } + + generate_llvm_integer :: proc(ctx: LLVMContextRef, mod: LLVMModuleRef, builder: LLVMBuilderRef, node: ^Node) -> LLVMValueRef { + bit_size := uint(node.return_type.bit_size) + value := node.value.(u64) + is_signed := node.return_type.is_signed + return LLVMConstInt(LLVMIntTypeInContext(ctx, bit_size), value, LLVMBool(is_signed)) + } + + generate_llvm_float :: proc(ctx: LLVMContextRef, mod: LLVMModuleRef, builder: LLVMBuilderRef, node: ^Node) -> LLVMValueRef { + bit_size := uint(node.return_type.bit_size) + value := node.value.(f64) + if bit_size == 64 { + return LLVMConstReal(LLVMDoubleTypeInContext(ctx), value) + } else if bit_size == 32 { + return LLVMConstReal(LLVMFloatTypeInContext(ctx), value) + } + + append( + &g_message_list, + message_create( + .Error, + fmt.aprintf("Unsupported float bit size: %d", bit_size), + node.range, + ), + ) + return nil + } + + generate_llvm_type_from_node :: proc(ctx: LLVMContextRef, mod: LLVMModuleRef, builder: LLVMBuilderRef, node: ^Node) -> LLVMTypeRef { + type := node.return_type + if type == nil { + return LLVMVoidTypeInContext(ctx) + } + + switch type.kind { + case .Integer: + return LLVMIntTypeInContext(ctx, uint(type.bit_size)) + case .Float: + if type.bit_size == 64 { + return LLVMDoubleTypeInContext(ctx) + } else if type.bit_size == 32 { + return LLVMFloatTypeInContext(ctx) + } else { + fmt.panicf("LLVM-IR: Unsupported float type bit size: {}", type.bit_size) + } + case .Array: + panic("FIXME: Implement array LLVM IR generation") + case .Pointer: + panic("FIXME: Implement pointer LLVM IR generation") + } + panic("LLVM-IR: Invalid type") + } + + generate_llvm_function_extern :: proc( + ctx: LLVMContextRef, mod: LLVMModuleRef, builder: LLVMBuilderRef, + fn: ^Node, scope_number: ^i32 + ) -> (type: LLVMTypeRef, value: LLVMValueRef) { + function_args_type := [dynamic]LLVMTypeRef{} + for arg in fn.children { + if arg.kind != .VariableDeclaration { + continue + } + append(&function_args_type, generate_llvm_type_from_node(ctx, mod, builder, arg)) + } + function_return_type := generate_llvm_type_from_node(ctx, mod, builder, fn) + + type = LLVMFunctionType(function_return_type, raw_data(function_args_type[:]), len(function_args_type), LLVMBool(0)) + value = LLVMAddFunction(mod, strings.clone_to_cstring(string(fn.value.([dynamic]u8)[:])), type) + return + } + + generate_llvm_function_call :: proc( + ctx: LLVMContextRef, mod: LLVMModuleRef, builder: LLVMBuilderRef, + fn_call: ^Node, + ) -> LLVMValueRef { + return nil + } + + generate_llvm_value :: proc( + ctx: LLVMContextRef, mod: LLVMModuleRef, builder: LLVMBuilderRef, + node: ^Node, + ) -> LLVMValueRef { + if node.kind == .Integer { + return generate_llvm_integer(ctx, mod, builder, node) + } else if node.kind == .Float { + return generate_llvm_float(ctx, mod, builder, node) + } else if node.kind == .FunctionCall { + return generate_llvm_function_call(ctx, mod, builder, node) + } + + panic("FIXME: Implement other node kinds") + } + + generate_llvm_scope :: proc(ctx: LLVMContextRef, mod: LLVMModuleRef, builder: LLVMBuilderRef, function: LLVMValueRef, scope: ^Node, scope_number: ^i32) { + scope_name := get_basic_block_name(scope_number^) + basic_block := LLVMAppendBasicBlockInContext(ctx, function, scope_name) + llvm_scope_enter(scope_name, basic_block) + LLVMPositionBuilderAtEnd(builder, basic_block) + scope_number^ += 1 + for node in scope.children { + #partial switch node.kind { + case .Function: + panic("FIXME: Implement function delarations") + case .ExternFunction: + type, value := generate_llvm_function_extern(ctx, mod, builder, node, scope_number) + //name := LLVMGetValueName2(value, nil) + llvm_top_scope().definitions[get_character_sum_of_dyn_arr(&node.value.([dynamic]u8))] = value + llvm_top_scope().types[get_character_sum_of_dyn_arr(&node.value.([dynamic]u8))] = type + case .FunctionCall: + // FIXME: Add support for dot access + name := &node.children[0].value.([dynamic]u8) + fn_type := llvm_scope_find_type(name) + fn_value := llvm_scope_find_definition(name) + if fn_type == nil || fn_value == nil { + append( + &g_message_list, + message_create( + .Error, + fmt.aprintf("Function '%s' not found", name), + node.range, + ), + ) + return + } + + fn_args := [dynamic]LLVMValueRef{} + for &arg in node.children[1:] { + value := generate_llvm_value(ctx, mod, builder, arg) + append(&fn_args, value) + } + + LLVMBuildCall2(builder, fn_type, fn_value, raw_data(fn_args[:]), len(fn_args), cstring(raw_data(name[:]))) + case: + panic("FIXME: Implement other node kinds") + } + } + llvm_scope_leave() + } + + generate_llvm_scope(ctx, mod, builder, main_function, node, &scope_number) + //basic_block := LLVMAppendBasicBlockInContext(ctx, main_function, "end") + //LLVMPositionBuilderAtEnd(builder, basic_block) + LLVMBuildRet(builder, LLVMConstInt(int_32_type, 0, LLVMBool(1))) +} diff --git a/src/main.odin b/src/main.odin index 834d55a..13eadd4 100644 --- a/src/main.odin +++ b/src/main.odin @@ -4,43 +4,6 @@ import "core:fmt" import "core:os" main :: proc() { - // ctx := LLVMContextCreate() - // defer LLVMContextDispose(ctx) - // module := LLVMModuleCreateWithNameInContext("hello", ctx) - // defer LLVMDisposeModule(module) - // builder := LLVMCreateBuilderInContext(ctx) - - // int_8_type := LLVMInt8TypeInContext(ctx) - // int_8_type_ptr := LLVMPointerType(int_8_type, 0) - // int_32_type := LLVMInt32TypeInContext(ctx) - - // puts_function_args_type := []LLVMTypeRef{ - // int_8_type_ptr, - // } - - // puts_function_type := LLVMFunctionType(int_32_type, raw_data(puts_function_args_type), 1, LLVMBool(0)) - // puts_function := LLVMAddFunction(module, "puts", puts_function_type) - - // main_function_type := LLVMFunctionType(int_32_type, nil, 0, LLVMBool(0)) - // main_function := LLVMAddFunction(module, "main", main_function_type) - - // entry := LLVMAppendBasicBlockInContext(ctx, main_function, "entry") - // LLVMPositionBuilderAtEnd(builder, entry) - - // puts_function_args := []LLVMValueRef { - // LLVMBuildPointerCast( - // builder, - // LLVMBuildGlobalString(builder, "Hello world!\n", "hello"), - // int_8_type_ptr, - // "0", - // ), - // } - - // LLVMBuildCall2(builder, puts_function_type, puts_function, raw_data(puts_function_args), len(puts_function_args), "i") - // LLVMBuildRet(builder, LLVMConstInt(int_32_type, 0, LLVMBool(0))) - - // LLVMPrintModuleToFile(module, "hello.ll", nil) - handle: os.Handle if len(os.args) >= 2 { errno: os.Errno diff --git a/test.c b/test.c new file mode 100644 index 0000000..c1e243f --- /dev/null +++ b/test.c @@ -0,0 +1,7 @@ +#include +#include + +int32_t put_i32(int32_t x) { + printf("%d\n", x); + return 0; +}