diff --git a/examples/basic.odin b/examples/basic.odin index 1c5b7b317..1f90efa22 100644 --- a/examples/basic.odin +++ b/examples/basic.odin @@ -6,3 +6,42 @@ print_string :: proc(s : string) { putchar(c); } } + +string_byte_reverse :: proc(s : string) { + n := len(s); + for i := 0; i < n/2; i++ { + s[i], s[n-1-i] = s[n-1-i], s[i]; + } +} + +print_int :: proc(i : int, base : int) { + NUM_TO_CHAR_TABLE := "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@$"; + + buf : [21]byte; + len := 0; + negative := false; + if i < 0 { + negative = true; + i = -i; + } + if i > 0 { + for i > 0 { + c : byte = NUM_TO_CHAR_TABLE[i % base]; + buf[len] = c; + len++; + i /= base; + } + } else { + buf[len] = '0'; + len++; + } + + if negative { + buf[len] = '-'; + len++; + } + + str := cast(string)buf[:len]; + string_byte_reverse(str); + print_string(str); +} diff --git a/examples/main.ll b/examples/main.ll new file mode 100644 index 000000000..2de35bd44 --- /dev/null +++ b/examples/main.ll @@ -0,0 +1,267 @@ +%-string = type {i8*, i64} ; Basic_string + +%-rawptr = type i8* ; Basic_rawptr + +define void @main() { +"entry - 0": + call void @print_int(i64 123, i64 10) + %0 = getelementptr inbounds [1 x i8], [1 x i8]* @.str0, i64 0, i64 0 + %1 = alloca %-string, align 8 + store %-string zeroinitializer, %-string* %1 + %2 = getelementptr inbounds %-string, %-string* %1, i64 0, i32 0 + %3 = getelementptr inbounds %-string, %-string* %1, i64 0, i32 1 + store i8* %0, i8** %2 + store i64 1, i64* %3 + %4 = load %-string, %-string* %1 + call void @print_string(%-string %4) + ret void +} + +declare i32 @putchar(i32 %c) ; foreign procedure + +define void @print_string(%-string %s) { +"entry - 0": + %0 = alloca %-string, align 8 ; s + store %-string zeroinitializer, %-string* %0 + store %-string %s, %-string* %0 + %1 = alloca i64, align 8 ; i + store i64 zeroinitializer, i64* %1 + store i64 0, i64* %1 + br label %"for.loop - 2" + +"for.body - 1": + %2 = alloca i32, align 4 ; c + store i32 zeroinitializer, i32* %2 + %3 = load i64, i64* %1 + %4 = getelementptr inbounds %-string, %-string* %0, i64 0, i32 0 + %5 = load i8*, i8** %4 + %6 = getelementptr i8, i8* %5, i64 %3 + %7 = load i8, i8* %6 + %8 = zext i8 %7 to i32 + store i32 %8, i32* %2 + %9 = load i32, i32* %2 + %10 = call i32 @putchar(i32 %9) + br label %"for.post - 3" + +"for.loop - 2": + %11 = load i64, i64* %1 + %12 = getelementptr inbounds %-string, %-string* %0, i64 0, i32 1 + %13 = load i64, i64* %12 + %14 = icmp slt i64 %11, %13 + br i1 %14, label %"for.body - 1", label %"for.done - 4" + +"for.post - 3": + %15 = load i64, i64* %1 + %16 = add i64 %15, 1 + store i64 %16, i64* %1 + br label %"for.loop - 2" + +"for.done - 4": + ret void +} + +define void @string_byte_reverse(%-string %s) { +"entry - 0": + %0 = alloca %-string, align 8 ; s + store %-string zeroinitializer, %-string* %0 + store %-string %s, %-string* %0 + %1 = alloca i64, align 8 ; n + store i64 zeroinitializer, i64* %1 + %2 = getelementptr inbounds %-string, %-string* %0, i64 0, i32 1 + %3 = load i64, i64* %2 + store i64 %3, i64* %1 + %4 = alloca i64, align 8 ; i + store i64 zeroinitializer, i64* %4 + store i64 0, i64* %4 + br label %"for.loop - 2" + +"for.body - 1": + %5 = load i64, i64* %4 + %6 = getelementptr inbounds %-string, %-string* %0, i64 0, i32 0 + %7 = load i8*, i8** %6 + %8 = getelementptr i8, i8* %7, i64 %5 + %9 = load i64, i64* %4 + %10 = load i64, i64* %1 + %11 = sub i64 %10, 1 + %12 = sub i64 %11, %9 + %13 = getelementptr inbounds %-string, %-string* %0, i64 0, i32 0 + %14 = load i8*, i8** %13 + %15 = getelementptr i8, i8* %14, i64 %12 + %16 = load i64, i64* %4 + %17 = load i64, i64* %1 + %18 = sub i64 %17, 1 + %19 = sub i64 %18, %16 + %20 = getelementptr inbounds %-string, %-string* %0, i64 0, i32 0 + %21 = load i8*, i8** %20 + %22 = getelementptr i8, i8* %21, i64 %19 + %23 = load i8, i8* %22 + %24 = load i64, i64* %4 + %25 = getelementptr inbounds %-string, %-string* %0, i64 0, i32 0 + %26 = load i8*, i8** %25 + %27 = getelementptr i8, i8* %26, i64 %24 + %28 = load i8, i8* %27 + store i8 %23, i8* %8 + store i8 %28, i8* %15 + br label %"for.post - 3" + +"for.loop - 2": + %29 = load i64, i64* %4 + %30 = load i64, i64* %1 + %31 = sdiv i64 %30, 2 + %32 = icmp slt i64 %29, %31 + br i1 %32, label %"for.body - 1", label %"for.done - 4" + +"for.post - 3": + %33 = load i64, i64* %4 + %34 = add i64 %33, 1 + store i64 %34, i64* %4 + br label %"for.loop - 2" + +"for.done - 4": + ret void +} + +define void @print_int(i64 %i, i64 %base) { +"entry - 0": + %0 = alloca i64, align 8 ; i + store i64 zeroinitializer, i64* %0 + store i64 %i, i64* %0 + %1 = alloca i64, align 8 ; base + store i64 zeroinitializer, i64* %1 + store i64 %base, i64* %1 + %2 = alloca %-string, align 8 ; NUM_TO_CHAR_TABLE + store %-string zeroinitializer, %-string* %2 + %3 = getelementptr inbounds [64 x i8], [64 x i8]* @.str1, i64 0, i64 0 + %4 = alloca %-string, align 8 + store %-string zeroinitializer, %-string* %4 + %5 = getelementptr inbounds %-string, %-string* %4, i64 0, i32 0 + %6 = getelementptr inbounds %-string, %-string* %4, i64 0, i32 1 + store i8* %3, i8** %5 + store i64 64, i64* %6 + %7 = load %-string, %-string* %4 + store %-string %7, %-string* %2 + %8 = alloca [21 x i8], align 1 ; buf + store [21 x i8] zeroinitializer, [21 x i8]* %8 + %9 = alloca i64, align 8 ; len + store i64 zeroinitializer, i64* %9 + store i64 0, i64* %9 + %10 = alloca i1, align 1 ; negative + store i1 zeroinitializer, i1* %10 + store i1 false, i1* %10 + %11 = load i64, i64* %0 + %12 = icmp slt i64 %11, 0 + br i1 %12, label %"if.then - 1", label %"if.done - 2" + +"if.then - 1": + store i1 true, i1* %10 + %13 = load i64, i64* %0 + %14 = sub i64 0, %13 + store i64 %14, i64* %0 + br label %"if.done - 2" + +"if.done - 2": + %15 = load i64, i64* %0 + %16 = icmp sgt i64 %15, 0 + br i1 %16, label %"if.then - 3", label %"if.else - 4" + +"if.then - 3": + br label %"for.loop - 6" + +"if.else - 4": + %17 = load i64, i64* %9 + %18 = getelementptr inbounds [21 x i8], [21 x i8]* %8, i64 0, i64 0 + %19 = getelementptr i8, i8* %18, i64 %17 + store i8 0, i8* %19 + %20 = load i64, i64* %9 + %21 = add i64 %20, 1 + store i64 %21, i64* %9 + br label %"if.done - 8" + +"for.body - 5": + %22 = alloca i8, align 1 ; c + store i8 zeroinitializer, i8* %22 + %23 = load i64, i64* %1 + %24 = load i64, i64* %0 + %25 = srem i64 %24, %23 + %26 = getelementptr inbounds %-string, %-string* %2, i64 0, i32 0 + %27 = load i8*, i8** %26 + %28 = getelementptr i8, i8* %27, i64 %25 + %29 = load i8, i8* %28 + store i8 %29, i8* %22 + %30 = load i64, i64* %9 + %31 = getelementptr inbounds [21 x i8], [21 x i8]* %8, i64 0, i64 0 + %32 = getelementptr i8, i8* %31, i64 %30 + %33 = load i8, i8* %22 + store i8 %33, i8* %32 + %34 = load i64, i64* %9 + %35 = add i64 %34, 1 + store i64 %35, i64* %9 + %36 = load i64, i64* %1 + %37 = load i64, i64* %0 + %38 = sdiv i64 %37, %36 + store i64 %38, i64* %0 + br label %"for.loop - 6" + +"for.loop - 6": + %39 = load i64, i64* %0 + %40 = icmp sgt i64 %39, 0 + br i1 %40, label %"for.body - 5", label %"for.done - 7" + +"for.done - 7": + br label %"if.done - 8" + +"if.done - 8": + %41 = load i1, i1* %10 + br i1 %41, label %"if.then - 9", label %"if.done - 10" + +"if.then - 9": + %42 = load i64, i64* %9 + %43 = getelementptr inbounds [21 x i8], [21 x i8]* %8, i64 0, i64 0 + %44 = getelementptr i8, i8* %43, i64 %42 + store i8 0, i8* %44 + %45 = load i64, i64* %9 + %46 = add i64 %45, 1 + store i64 %46, i64* %9 + br label %"if.done - 10" + +"if.done - 10": + %47 = alloca %-string, align 8 ; str + store %-string zeroinitializer, %-string* %47 + %48 = load i64, i64* %9 + %49 = sub i64 %48, 0 + %50 = sub i64 21, 0 + %51 = getelementptr inbounds [21 x i8], [21 x i8]* %8, i64 0, i64 0 + %52 = getelementptr i8, i8* %51, i64 0 + %53 = alloca {i8*, i64, i64}, align 8 + store {i8*, i64, i64} zeroinitializer, {i8*, i64, i64}* %53 + %54 = getelementptr inbounds {i8*, i64, i64}, {i8*, i64, i64}* %53, i64 0, i32 0 + store i8* %52, i8** %54 + %55 = getelementptr inbounds {i8*, i64, i64}, {i8*, i64, i64}* %53, i64 0, i32 1 + store i64 %49, i64* %55 + %56 = getelementptr inbounds {i8*, i64, i64}, {i8*, i64, i64}* %53, i64 0, i32 2 + store i64 %50, i64* %56 + %57 = load {i8*, i64, i64}, {i8*, i64, i64}* %53 + %58 = alloca {i8*, i64, i64}, align 8 + store {i8*, i64, i64} zeroinitializer, {i8*, i64, i64}* %58 + store {i8*, i64, i64} %57, {i8*, i64, i64}* %58 + %59 = getelementptr inbounds {i8*, i64, i64}, {i8*, i64, i64}* %58, i64 0, i32 0 + %60 = load i8*, i8** %59 + %61 = getelementptr inbounds {i8*, i64, i64}, {i8*, i64, i64}* %58, i64 0, i32 1 + %62 = load i64, i64* %61 + %63 = alloca %-string, align 8 + store %-string zeroinitializer, %-string* %63 + %64 = getelementptr inbounds %-string, %-string* %63, i64 0, i32 0 + %65 = getelementptr inbounds %-string, %-string* %63, i64 0, i32 1 + store i8* %60, i8** %64 + store i64 %62, i64* %65 + %66 = load %-string, %-string* %63 + store %-string %66, %-string* %47 + %67 = load %-string, %-string* %47 + call void @string_byte_reverse(%-string %67) + %68 = load %-string, %-string* %47 + call void @print_string(%-string %68) + ret void +} + +@.str0 = global [1 x i8] c"\0A" +@.str1 = global [64 x i8] c"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\40$" diff --git a/examples/main.odin b/examples/main.odin new file mode 100644 index 000000000..303dd9e41 --- /dev/null +++ b/examples/main.odin @@ -0,0 +1,6 @@ +import "basic" + +main :: proc() { + print_int(123, 10); + print_string("\n"); +} diff --git a/examples/other.odin b/examples/other.odin deleted file mode 100644 index 1e2db5d7d..000000000 --- a/examples/other.odin +++ /dev/null @@ -1 +0,0 @@ -TAU :: 6.28; diff --git a/examples/test.c b/examples/test.c deleted file mode 100644 index 49a4a64da..000000000 --- a/examples/test.c +++ /dev/null @@ -1,8 +0,0 @@ -void test(void) { -} - -int main() { - void (*f)(void) = test; - - return 0; -} diff --git a/examples/test.ll b/examples/test.ll deleted file mode 100644 index c95743eaa..000000000 --- a/examples/test.ll +++ /dev/null @@ -1,57 +0,0 @@ -define void @main() { -"entry - 0": - %0 = getelementptr inbounds [13 x i8], [13 x i8]* @.str0, i64 0, i64 0 - %1 = alloca {i8*, i64}, align 8 - store {i8*, i64} zeroinitializer, {i8*, i64}* %1 - %2 = getelementptr inbounds {i8*, i64}, {i8*, i64}* %1, i64 0, i32 0 - %3 = getelementptr inbounds {i8*, i64}, {i8*, i64}* %1, i64 0, i32 1 - store i8* %0, i8** %2 - store i64 13, i64* %3 - %4 = load {i8*, i64}, {i8*, i64}* %1 - call void @print_string({i8*, i64} %4) - ret void -} - -declare i32 @putchar(i32 %c) -define void @print_string({i8*, i64} %s) { -"entry - 0": - %0 = alloca {i8*, i64}, align 8 ; s - store {i8*, i64} zeroinitializer, {i8*, i64}* %0 - store {i8*, i64} %s, {i8*, i64}* %0 - %1 = alloca i64, align 8 ; i - store i64 zeroinitializer, i64* %1 - store i64 0, i64* %1 - br label %"for.loop - 2" - -"for.body - 1": - %2 = alloca i32, align 4 ; c - store i32 zeroinitializer, i32* %2 - %3 = load i64, i64* %1 - %4 = getelementptr inbounds {i8*, i64}, {i8*, i64}* %0, i64 0, i32 0 - %5 = load i8*, i8** %4 - %6 = getelementptr i8, i8* %5, i64 %3 - %7 = load i8, i8* %6 - %8 = zext i8 %7 to i32 - store i32 %8, i32* %2 - %9 = load i32, i32* %2 - %10 = call i32 @putchar(i32 %9) - br label %"for.post - 3" - -"for.loop - 2": - %11 = load i64, i64* %1 - %12 = getelementptr inbounds {i8*, i64}, {i8*, i64}* %0, i64 0, i32 1 - %13 = load i64, i64* %12 - %14 = icmp slt i64 %11, %13 - br i1 %14, label %"for.body - 1", label %"for.done - 4" - -"for.post - 3": - %15 = load i64, i64* %1 - %16 = add i64 %15, 1 - store i64 %16, i64* %1 - br label %"for.loop - 2" - -"for.done - 4": - ret void -} - -@.str0 = global [13 x i8] c"Hello\2C\20\E4\B8\96\E7\95\8C" diff --git a/examples/test.odin b/examples/test.odin deleted file mode 100644 index 54332a5ed..000000000 --- a/examples/test.odin +++ /dev/null @@ -1,5 +0,0 @@ -import "basic" - -main :: proc() { - print_string("Hello, 世界"); -} diff --git a/run.bat b/run.bat index 0afd252cf..696a902cf 100644 --- a/run.bat +++ b/run.bat @@ -1,9 +1,10 @@ @echo off -rem del "..\examples\test.bc" -call ..\bin\odin.exe ..\examples/test.odin && ..\misc\llvm-bin\lli.exe ..\examples/test.ll -call ..\misc\llvm-bin\opt.exe -mem2reg ..\examples/test.ll > ..\examples/test.bc -call llc ..\examples/test.bc -rem call llvm-dis ..\examples/test.bc -o ..\examples/test.ll -rem call clang ..\examples/test.c -O0 -S -emit-llvm -o ..\examples/test-c.ll +call ..\bin\odin.exe ..\examples/main.odin ^ + && ..\misc\llvm-bin\lli.exe ..\examples/main.ll + +rem call ..\misc\llvm-bin\opt.exe -mem2reg ..\examples/output.ll > ..\examples/main.bc +rem call llc ..\examples/main.bc +rem call llvm-dis ..\examples/main.bc -o ..\examples/output.ll +rem call clang ..\examples/main.c -O0 -S -emit-llvm -o ..\examples/main-c.ll diff --git a/src/checker/checker.cpp b/src/checker/checker.cpp index 98e371e33..1d402abe9 100644 --- a/src/checker/checker.cpp +++ b/src/checker/checker.cpp @@ -20,7 +20,7 @@ struct Operand { Type *type; ExactValue value; AstNode *expr; - BuiltinProcedureId builtin_id; + BuiltinProcId builtin_id; }; struct TypeAndValue { @@ -110,32 +110,34 @@ enum ExpressionKind { Expression_Statement, }; -enum BuiltinProcedureId { - BuiltinProcedure_Invalid, +enum BuiltinProcId { + BuiltinProc_Invalid, - BuiltinProcedure_size_of, - BuiltinProcedure_size_of_val, - BuiltinProcedure_align_of, - BuiltinProcedure_align_of_val, - BuiltinProcedure_offset_of, - BuiltinProcedure_offset_of_val, - BuiltinProcedure_static_assert, - BuiltinProcedure_len, - BuiltinProcedure_cap, - BuiltinProcedure_copy, - BuiltinProcedure_print, - BuiltinProcedure_println, + BuiltinProc_size_of, + BuiltinProc_size_of_val, + BuiltinProc_align_of, + BuiltinProc_align_of_val, + BuiltinProc_offset_of, + BuiltinProc_offset_of_val, + BuiltinProc_static_assert, + BuiltinProc_len, + BuiltinProc_cap, + BuiltinProc_copy, + BuiltinProc_append, + BuiltinProc_print, + BuiltinProc_println, - BuiltinProcedure_Count, + BuiltinProc_Count, }; -struct BuiltinProcedure { +struct BuiltinProc { String name; isize arg_count; b32 variadic; ExpressionKind kind; }; -gb_global BuiltinProcedure builtin_procedures[BuiltinProcedure_Count] = { +gb_global BuiltinProc builtin_procs[BuiltinProc_Count] = { {STR_LIT(""), 0, false, Expression_Statement}, + {STR_LIT("size_of"), 1, false, Expression_Expression}, {STR_LIT("size_of_val"), 1, false, Expression_Expression}, {STR_LIT("align_of"), 1, false, Expression_Expression}, @@ -143,9 +145,11 @@ gb_global BuiltinProcedure builtin_procedures[BuiltinProcedure_Count] = { {STR_LIT("offset_of"), 2, false, Expression_Expression}, {STR_LIT("offset_of_val"), 1, false, Expression_Expression}, {STR_LIT("static_assert"), 1, false, Expression_Statement}, + {STR_LIT("len"), 1, false, Expression_Expression}, {STR_LIT("cap"), 1, false, Expression_Expression}, {STR_LIT("copy"), 2, false, Expression_Expression}, + {STR_LIT("append"), 2, false, Expression_Expression}, {STR_LIT("print"), 1, true, Expression_Statement}, {STR_LIT("println"), 1, true, Expression_Statement}, }; @@ -172,14 +176,14 @@ struct Checker { AstFile * curr_ast_file; BaseTypeSizes sizes; Scope * global_scope; - gbArray(ProcedureInfo) procedures; // NOTE(bill): Procedures to check + gbArray(ProcedureInfo) procs; // NOTE(bill): Procedures to check gbArena arena; gbAllocator allocator; CheckerContext context; - gbArray(Type *) procedure_stack; + gbArray(Type *) proc_stack; b32 in_defer; // TODO(bill): Actually handle correctly ErrorCollector error_collector; @@ -334,10 +338,10 @@ void init_universal_scope(void) { add_global_constant(a, make_string("null"), t_untyped_pointer, make_exact_value_pointer(NULL)); // Builtin Procedures - for (isize i = 0; i < gb_count_of(builtin_procedures); i++) { - BuiltinProcedureId id = cast(BuiltinProcedureId)i; + for (isize i = 0; i < gb_count_of(builtin_procs); i++) { + BuiltinProcId id = cast(BuiltinProcId)i; Token token = {Token_Identifier}; - token.string = builtin_procedures[i].name; + token.string = builtin_procs[i].name; Entity *entity = alloc_entity(a, Entity_Builtin, NULL, token, t_invalid); entity->builtin.id = id; add_global_entity(entity); @@ -376,8 +380,8 @@ void init_checker(Checker *c, Parser *parser) { c->sizes.word_size = 8; c->sizes.max_align = 8; - gb_array_init(c->procedure_stack, a); - gb_array_init(c->procedures, a); + gb_array_init(c->proc_stack, a); + gb_array_init(c->procs, a); // NOTE(bill): Is this big enough or too small? isize item_size = gb_max(gb_max(gb_size_of(Entity), gb_size_of(Type)), gb_size_of(Scope)); @@ -397,8 +401,8 @@ void init_checker(Checker *c, Parser *parser) { void destroy_checker(Checker *c) { destroy_checker_info(&c->info); destroy_scope(c->global_scope); - gb_array_free(c->procedure_stack); - gb_array_free(c->procedures); + gb_array_free(c->proc_stack); + gb_array_free(c->procs); gb_arena_free(&c->arena); } @@ -500,7 +504,7 @@ void check_procedure_later(Checker *c, AstFile *file, Token token, DeclInfo *dec info.decl = decl; info.type = type; info.body = body; - gb_array_append(c->procedures, info); + gb_array_append(c->procs, info); } void check_add_deferred_stmt(Checker *c, AstNode *stmt) { @@ -509,12 +513,12 @@ void check_add_deferred_stmt(Checker *c, AstNode *stmt) { gb_array_append(c->context.scope->deferred_stmts, stmt); } -void push_procedure(Checker *c, Type *procedure_type) { - gb_array_append(c->procedure_stack, procedure_type); +void push_procedure(Checker *c, Type *type) { + gb_array_append(c->proc_stack, type); } void pop_procedure(Checker *c) { - gb_array_pop(c->procedure_stack); + gb_array_pop(c->proc_stack); } void add_curr_ast_file(Checker *c, AstFile *file) { @@ -655,8 +659,8 @@ void check_parsed_files(Checker *c) { // Check procedure bodies - gb_for_array(i, c->procedures) { - ProcedureInfo *pi = &c->procedures[i]; + gb_for_array(i, c->procs) { + ProcedureInfo *pi = &c->procs[i]; add_curr_ast_file(c, pi->file); check_proc_body(c, pi->token, pi->decl, pi->type, pi->body); } diff --git a/src/checker/entity.cpp b/src/checker/entity.cpp index 6a562ecf0..bd784bc4d 100644 --- a/src/checker/entity.cpp +++ b/src/checker/entity.cpp @@ -1,6 +1,6 @@ struct Scope; struct Checker; -enum BuiltinProcedureId; +enum BuiltinProcId; #define ENTITY_KINDS \ ENTITY_KIND(Invalid), \ @@ -47,7 +47,7 @@ struct Entity { struct {} type_name; struct {} alias_name; struct {} procedure; - struct { BuiltinProcedureId id; } builtin; + struct { BuiltinProcId id; } builtin; }; }; @@ -105,7 +105,7 @@ Entity *make_entity_procedure(gbAllocator a, Scope *parent, Token token, Type *s return entity; } -Entity *make_entity_builtin(gbAllocator a, Scope *parent, Token token, Type *type, BuiltinProcedureId id) { +Entity *make_entity_builtin(gbAllocator a, Scope *parent, Token token, Type *type, BuiltinProcId id) { Entity *entity = alloc_entity(a, Entity_Builtin, parent, token, type); entity->builtin.id = id; return entity; diff --git a/src/checker/expr.cpp b/src/checker/expr.cpp index 4129e6e73..fc5020f9b 100644 --- a/src/checker/expr.cpp +++ b/src/checker/expr.cpp @@ -105,11 +105,6 @@ Type *check_get_results(Checker *c, Scope *scope, AstNode *list, isize list_coun Entity *param = make_entity_param(c->allocator, scope, token, type); // NOTE(bill): No need to record variables[variable_index++] = param; - - if (get_base_type(type)->kind == Type_Array) { - // TODO(bill): Should I allow array's to returned? - error(&c->error_collector, token, "You cannot return an array from a procedure"); - } } tuple->tuple.variables = variables; tuple->tuple.variable_count = list_count; @@ -129,11 +124,11 @@ void check_procedure_type(Checker *c, Type *type, AstNode *proc_type_node) { Type *params = check_get_params(c, c->context.scope, pt->param_list, param_count); Type *results = check_get_results(c, c->context.scope, pt->result_list, result_count); - type->procedure.scope = c->context.scope; - type->procedure.params = params; - type->procedure.param_count = pt->param_count; - type->procedure.results = results; - type->procedure.result_count = pt->result_count; + type->proc.scope = c->context.scope; + type->proc.params = params; + type->proc.param_count = pt->param_count; + type->proc.results = results; + type->proc.result_count = pt->result_count; } @@ -292,7 +287,7 @@ Type *check_type_expr_extra(Checker *c, AstNode *e, Type *named_type) { case_end; case_ast_node(pt, ProcType, e); - Type *t = alloc_type(c->allocator, Type_Procedure); + Type *t = alloc_type(c->allocator, Type_Proc); set_base_type(named_type, t); check_open_scope(c, e); check_procedure_type(c, t, e); @@ -384,7 +379,7 @@ Type *check_type(Checker *c, AstNode *e, Type *named_type) { case_end; case_ast_node(pt, ProcType, e); - type = alloc_type(c->allocator, Type_Procedure); + type = alloc_type(c->allocator, Type_Proc); set_base_type(named_type, type); check_procedure_type(c, type, e); goto end; @@ -958,7 +953,7 @@ Entity *lookup_field(Type *type, AstNode *field_node, isize *index = NULL) { GB_ASSERT(field_node->kind == AstNode_Ident); type = get_base_type(type); if (type->kind == Type_Pointer) - type = get_base_type(type->pointer.element); + type = get_base_type(type->pointer.elem); ast_node(i, Ident, field_node); String field_str = i->token.string; @@ -1016,7 +1011,7 @@ void check_selector(Checker *c, Operand *operand, AstNode *node) { b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id) { GB_ASSERT(call->kind == AstNode_CallExpr); ast_node(ce, CallExpr, call); - BuiltinProcedure *bp = &builtin_procedures[id]; + BuiltinProc *bp = &builtin_procs[id]; { char *err = NULL; if (ce->arg_list_count < bp->arg_count) @@ -1033,9 +1028,9 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id) } switch (id) { - case BuiltinProcedure_size_of: - case BuiltinProcedure_align_of: - case BuiltinProcedure_offset_of: + case BuiltinProc_size_of: + case BuiltinProc_align_of: + case BuiltinProc_offset_of: // NOTE(bill): The first arg is a Type, this will be checked case by case break; default: @@ -1043,7 +1038,7 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id) } switch (id) { - case BuiltinProcedure_size_of: { + case BuiltinProc_size_of: { // size_of :: proc(Type) Type *type = check_type(c, ce->arg_list); if (!type) { @@ -1057,7 +1052,7 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id) } break; - case BuiltinProcedure_size_of_val: + case BuiltinProc_size_of_val: // size_of_val :: proc(val) check_assignment(c, operand, NULL, make_string("argument of `size_of`")); if (operand->mode == Addressing_Invalid) @@ -1068,7 +1063,7 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id) operand->type = t_int; break; - case BuiltinProcedure_align_of: { + case BuiltinProc_align_of: { // align_of :: proc(Type) Type *type = check_type(c, ce->arg_list); if (!type) { @@ -1080,7 +1075,7 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id) operand->type = t_int; } break; - case BuiltinProcedure_align_of_val: + case BuiltinProc_align_of_val: // align_of_val :: proc(val) check_assignment(c, operand, NULL, make_string("argument of `align_of`")); if (operand->mode == Addressing_Invalid) @@ -1091,7 +1086,7 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id) operand->type = t_int; break; - case BuiltinProcedure_offset_of: { + case BuiltinProc_offset_of: { // offset_val :: proc(Type, field) Type *type = get_base_type(check_type(c, ce->arg_list)); AstNode *field_arg = unparen_expr(ce->arg_list->next); @@ -1122,7 +1117,7 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id) operand->type = t_int; } break; - case BuiltinProcedure_offset_of_val: { + case BuiltinProc_offset_of_val: { // offset_val :: proc(val) AstNode *arg = unparen_expr(ce->arg_list); if (arg->kind != AstNode_SelectorExpr) { @@ -1140,7 +1135,7 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id) if (get_base_type(type)->kind == Type_Pointer) { Type *p = get_base_type(type); if (get_base_type(p)->kind == Type_Structure) - type = p->pointer.element; + type = p->pointer.elem; } isize index = 0; @@ -1158,7 +1153,7 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id) operand->type = t_int; } break; - case BuiltinProcedure_static_assert: + case BuiltinProc_static_assert: // static_assert :: proc(cond: bool) // TODO(bill): Should `static_assert` and `assert` be unified? @@ -1180,8 +1175,8 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id) break; // TODO(bill): Should these be procedures and are their names appropriate? - case BuiltinProcedure_len: - case BuiltinProcedure_cap: { + case BuiltinProc_len: + case BuiltinProc_cap: { Type *t = get_base_type(operand->type); AddressingMode mode = Addressing_Invalid; @@ -1189,7 +1184,7 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id) switch (t->kind) { case Type_Basic: - if (id == BuiltinProcedure_len) { + if (id == BuiltinProc_len) { if (is_type_string(t)) { if (operand->mode == Addressing_Constant) { mode = Addressing_Constant; @@ -1226,14 +1221,13 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id) } break; - // TODO(bill): copy() pointer version? - case BuiltinProcedure_copy: { + case BuiltinProc_copy: { // copy :: proc(x, y: []Type) -> int Type *dest_type = NULL, *src_type = NULL; Type *d = get_base_type(operand->type); if (d->kind == Type_Slice) - dest_type = d->slice.element; + dest_type = d->slice.elem; Operand op = {}; check_expr(c, &op, ce->arg_list->next); @@ -1241,7 +1235,7 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id) return false; Type *s = get_base_type(op.type); if (s->kind == Type_Slice) - src_type = s->slice.element; + src_type = s->slice.elem; if (dest_type == NULL || src_type == NULL) { error(&c->error_collector, ast_node_token(call), "`copy` only expects slices as arguments"); @@ -1258,20 +1252,56 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id) defer (gb_string_free(d_str)); defer (gb_string_free(s_str)); error(&c->error_collector, ast_node_token(call), - "Arguments to `copy`, %s, %s, have different element types: %s vs %s", + "Arguments to `copy`, %s, %s, have different elem types: %s vs %s", d_arg, s_arg, d_str, s_str); return false; } - operand->type = t_int; // Returns number of elements copied + operand->type = t_int; // Returns number of elems copied operand->mode = Addressing_Value; } break; - case BuiltinProcedure_print: - case BuiltinProcedure_println: { + case BuiltinProc_append: { + // append :: proc(x : ^[]Type, y : Type) -> bool + Type *x_type = NULL, *y_type = NULL; + x_type = get_base_type(operand->type); + + Operand op = {}; + check_expr(c, &op, ce->arg_list->next); + if (op.mode == Addressing_Invalid) + return false; + y_type = get_base_type(op.type); + + if (!(is_type_pointer(x_type) && is_type_slice(x_type->pointer.elem))) { + error(&c->error_collector, ast_node_token(call), "First argument to `append` must be a pointer to a slice"); + return false; + } + + Type *elem_type = x_type->pointer.elem->slice.elem; + if (!check_is_assignable_to(c, &op, elem_type)) { + gbString d_arg = expr_to_string(ce->arg_list); + gbString s_arg = expr_to_string(ce->arg_list->next); + gbString d_str = type_to_string(elem_type); + gbString s_str = type_to_string(y_type); + defer (gb_string_free(d_arg)); + defer (gb_string_free(s_arg)); + defer (gb_string_free(d_str)); + defer (gb_string_free(s_str)); + error(&c->error_collector, ast_node_token(call), + "Arguments to `append`, %s, %s, have different element types: %s vs %s", + d_arg, s_arg, d_str, s_str); + return false; + } + + operand->type = t_bool; // Returns if it was successful + operand->mode = Addressing_Value; + } break; + + case BuiltinProc_print: + case BuiltinProc_println: { for (AstNode *arg = ce->arg_list; arg != NULL; arg = arg->next) { // TOOD(bill): `check_assignment` doesn't allow tuples at the moment, should it? - // Or should we destruct the tuple and use each element? + // Or should we destruct the tuple and use each elem? check_assignment(c, operand, NULL, make_string("argument")); if (operand->mode == Addressing_Invalid) return false; @@ -1285,14 +1315,14 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id) void check_call_arguments(Checker *c, Operand *operand, Type *proc_type, AstNode *call) { GB_ASSERT(call->kind == AstNode_CallExpr); - GB_ASSERT(proc_type->kind == Type_Procedure); + GB_ASSERT(proc_type->kind == Type_Proc); ast_node(ce, CallExpr, call); isize error_code = 0; isize param_index = 0; isize param_count = 0; - if (proc_type->procedure.params) - param_count = proc_type->procedure.params->tuple.variable_count; + if (proc_type->proc.params) + param_count = proc_type->proc.params->tuple.variable_count; if (ce->arg_list_count == 0 && param_count == 0) return; @@ -1300,7 +1330,7 @@ void check_call_arguments(Checker *c, Operand *operand, Type *proc_type, AstNode if (ce->arg_list_count > param_count) { error_code = +1; } else { - Entity **sig_params = proc_type->procedure.params->tuple.variables; + Entity **sig_params = proc_type->proc.params->tuple.variables; AstNode *call_arg = ce->arg_list; for (; call_arg != NULL; call_arg = call_arg->next) { check_multi_expr(c, operand, call_arg); @@ -1376,11 +1406,11 @@ ExpressionKind check_call_expr(Checker *c, Operand *operand, AstNode *call) { if (!check_builtin_procedure(c, operand, call, id)) operand->mode = Addressing_Invalid; operand->expr = call; - return builtin_procedures[id].kind; + return builtin_procs[id].kind; } Type *proc_type = get_base_type(operand->type); - if (proc_type == NULL || proc_type->kind != Type_Procedure) { + if (proc_type == NULL || proc_type->kind != Type_Proc) { AstNode *e = operand->expr; gbString str = expr_to_string(e); defer (gb_string_free(str)); @@ -1395,7 +1425,7 @@ ExpressionKind check_call_expr(Checker *c, Operand *operand, AstNode *call) { check_call_arguments(c, operand, proc_type, call); - auto *proc = &proc_type->procedure; + auto *proc = &proc_type->proc; if (proc->result_count == 0) { operand->mode = Addressing_NoValue; } else if (proc->result_count == 1) { @@ -1448,6 +1478,14 @@ b32 check_castable_to(Checker *c, Operand *operand, Type *y) { return true; } + // []byte/[]u8 <-> string + if (is_type_byte_slice(xb) && is_type_string(yb)) { + return true; + } + if (is_type_string(xb) && is_type_byte_slice(yb)) { + return true; + } + return false; } @@ -1590,13 +1628,13 @@ ExpressionKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *typ case Type_Slice: case Type_Array: { - Type *element_type = NULL; + Type *elem_type = NULL; String context_name = {}; if (t->kind == Type_Slice) { - element_type = t->slice.element; + elem_type = t->slice.elem; context_name = make_string("slice literal"); } else { - element_type = t->array.element; + elem_type = t->array.elem; context_name = make_string("array literal"); } @@ -1612,8 +1650,8 @@ ExpressionKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *typ } Operand o = {}; - check_expr_with_type_hint(c, &o, e, element_type); - check_assignment(c, &o, element_type, context_name); + check_expr_with_type_hint(c, &o, e, elem_type); + check_assignment(c, &o, elem_type, context_name); } if (max < index) max = index; @@ -1693,19 +1731,19 @@ ExpressionKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *typ max_count = t->array.count; if (o->mode != Addressing_Variable) o->mode = Addressing_Value; - o->type = t->array.element; + o->type = t->array.elem; break; case Type_Slice: valid = true; - o->type = t->slice.element; + o->type = t->slice.elem; o->mode = Addressing_Variable; break; case Type_Pointer: valid = true; o->mode = Addressing_Variable; - o->type = get_base_type(t->pointer.element); + o->type = get_base_type(t->pointer.elem); break; } @@ -1757,7 +1795,7 @@ ExpressionKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *typ gb_string_free(str); goto error; } - o->type = make_type_slice(c->allocator, t->array.element); + o->type = make_type_slice(c->allocator, t->array.elem); o->mode = Addressing_Value; break; @@ -1768,7 +1806,7 @@ ExpressionKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *typ case Type_Pointer: valid = true; - o->type = make_type_slice(c->allocator, get_base_type(t->pointer.element)); + o->type = make_type_slice(c->allocator, get_base_type(t->pointer.elem)); o->mode = Addressing_Value; break; } @@ -1832,7 +1870,7 @@ ExpressionKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *typ Type *t = get_base_type(o->type); if (t->kind == Type_Pointer) { o->mode = Addressing_Variable; - o->type = t->pointer.element; + o->type = t->pointer.elem; } else { gbString str = expr_to_string(o->expr); error(&c->error_collector, ast_node_token(o->expr), "Cannot dereference `%s`", str); @@ -1911,7 +1949,6 @@ void check_multi_expr(Checker *c, Operand *o, AstNode *e) { o->mode = Addressing_Invalid; } -// TODO(bill): Should I remove this entirely? void check_not_tuple(Checker *c, Operand *o) { if (o->mode == Addressing_Value) { // NOTE(bill): Tuples are not first class thus never named diff --git a/src/checker/stmt.cpp b/src/checker/stmt.cpp index 67ade56a5..f3625def1 100644 --- a/src/checker/stmt.cpp +++ b/src/checker/stmt.cpp @@ -108,13 +108,13 @@ b32 check_is_assignable_to(Checker *c, Operand *operand, Type *type) { return true; if (sb->kind == Type_Array && tb->kind == Type_Array) { - if (are_types_identical(sb->array.element, tb->array.element)) { + if (are_types_identical(sb->array.elem, tb->array.elem)) { return sb->array.count == tb->array.count; } } if (sb->kind == Type_Slice && tb->kind == Type_Slice) { - if (are_types_identical(sb->slice.element, tb->slice.element)) { + if (are_types_identical(sb->slice.elem, tb->slice.elem)) { return true; } } @@ -391,7 +391,7 @@ void check_proc_body(Checker *c, Token token, DeclInfo *decl, Type *type, AstNod push_procedure(c, type); ast_node(bs, BlockStmt, body); check_stmt_list(c, bs->list, 0); - if (type->procedure.result_count > 0) { + if (type->proc.result_count > 0) { if (!check_is_terminating(c, body)) { error(&c->error_collector, bs->close, "Missing return statement at the end of the procedure"); } @@ -404,7 +404,7 @@ void check_proc_body(Checker *c, Token token, DeclInfo *decl, Type *type, AstNod void check_proc_decl(Checker *c, Entity *e, DeclInfo *d, b32 check_body_later) { GB_ASSERT(e->type == NULL); - Type *proc_type = make_type_procedure(c->allocator, e->parent, NULL, 0, NULL, 0); + Type *proc_type = make_type_proc(c->allocator, e->parent, NULL, 0, NULL, 0); e->type = proc_type; ast_node(pd, ProcDecl, d->proc_decl); @@ -701,7 +701,7 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) { case_end; case_ast_node(rs, ReturnStmt, node); - GB_ASSERT(gb_array_count(c->procedure_stack) > 0); + GB_ASSERT(gb_array_count(c->proc_stack) > 0); if (c->in_defer) { error(&c->error_collector, rs->token, "You cannot `return` within a defer statement"); @@ -709,17 +709,17 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) { break; } - Type *proc_type = c->procedure_stack[gb_array_count(c->procedure_stack)-1]; + Type *proc_type = c->proc_stack[gb_array_count(c->proc_stack)-1]; isize result_count = 0; - if (proc_type->procedure.results) - result_count = proc_type->procedure.results->tuple.variable_count; + if (proc_type->proc.results) + result_count = proc_type->proc.results->tuple.variable_count; if (result_count != rs->result_count) { error(&c->error_collector, rs->token, "Expected %td return %s, got %td", result_count, (result_count != 1 ? "values" : "value"), rs->result_count); } else if (result_count > 0) { - auto *tuple = &proc_type->procedure.results->tuple; + auto *tuple = &proc_type->proc.results->tuple; check_init_variables(c, tuple->variables, tuple->variable_count, rs->result_list, rs->result_count, make_string("return statement")); } diff --git a/src/checker/type.cpp b/src/checker/type.cpp index 04eefbcdd..fe9df4360 100644 --- a/src/checker/type.cpp +++ b/src/checker/type.cpp @@ -62,7 +62,7 @@ struct BasicType { TYPE_KIND(Named), \ TYPE_KIND(Alias), \ TYPE_KIND(Tuple), \ - TYPE_KIND(Procedure), \ + TYPE_KIND(Proc), \ TYPE_KIND(Count), enum TypeKind { @@ -82,11 +82,11 @@ struct Type { union { BasicType basic; struct { - Type *element; + Type *elem; i64 count; } array; struct { - Type *element; + Type *elem; } slice; struct { // Theses are arrays @@ -95,7 +95,7 @@ struct Type { i64 * offsets; b32 are_offsets_set; } structure; - struct { Type *element; } pointer; + struct { Type *elem; } pointer; struct { String name; Type * base; @@ -116,7 +116,7 @@ struct Type { Type * results; // Type_Tuple isize param_count; isize result_count; - } procedure; + } proc; }; }; @@ -152,16 +152,16 @@ Type *make_type_basic(gbAllocator a, BasicType basic) { return t; } -Type *make_type_array(gbAllocator a, Type *element, i64 count) { +Type *make_type_array(gbAllocator a, Type *elem, i64 count) { Type *t = alloc_type(a, Type_Array); - t->array.element = element; + t->array.elem = elem; t->array.count = count; return t; } -Type *make_type_slice(gbAllocator a, Type *element) { +Type *make_type_slice(gbAllocator a, Type *elem) { Type *t = alloc_type(a, Type_Slice); - t->array.element = element; + t->array.elem = elem; return t; } @@ -170,9 +170,9 @@ Type *make_type_structure(gbAllocator a) { return t; } -Type *make_type_pointer(gbAllocator a, Type *element) { +Type *make_type_pointer(gbAllocator a, Type *elem) { Type *t = alloc_type(a, Type_Pointer); - t->pointer.element = element; + t->pointer.elem = elem; return t; } @@ -197,20 +197,20 @@ Type *make_type_tuple(gbAllocator a) { return t; } -Type *make_type_procedure(gbAllocator a, Scope *scope, Type *params, isize param_count, Type *results, isize result_count) { - Type *t = alloc_type(a, Type_Procedure); - t->procedure.scope = scope; - t->procedure.params = params; - t->procedure.param_count = param_count; - t->procedure.results = results; - t->procedure.result_count = result_count; +Type *make_type_proc(gbAllocator a, Scope *scope, Type *params, isize param_count, Type *results, isize result_count) { + Type *t = alloc_type(a, Type_Proc); + t->proc.scope = scope; + t->proc.params = params; + t->proc.param_count = param_count; + t->proc.results = results; + t->proc.result_count = result_count; return t; } Type *type_deref(Type *t) { if (t != NULL && t->kind == Type_Pointer) - return t->pointer.element; + return t->pointer.elem; return t; } @@ -338,12 +338,26 @@ b32 is_type_int_or_uint(Type *t) { return (t->basic.kind == Basic_int) || (t->basic.kind == Basic_uint); return false; } - b32 is_type_rawptr(Type *t) { if (t->kind == Type_Basic) return t->basic.kind == Basic_rawptr; return false; } +b32 is_type_byte(Type *t) { + if (t->kind == Type_Basic) + return t->basic.kind == Basic_byte; + return false; +} +b32 is_type_slice(Type *t) { + return t->kind == Type_Slice; +} + + +b32 is_type_byte_slice(Type *t) { + if (t->kind == Type_Slice) + return is_type_byte(t->slice.elem); + return false; +} b32 is_type_comparable(Type *t) { t = get_base_type(t); @@ -360,7 +374,7 @@ b32 is_type_comparable(Type *t) { return true; } break; case Type_Array: - return is_type_comparable(t->array.element); + return is_type_comparable(t->array.elem); } return false; } @@ -382,7 +396,7 @@ b32 are_types_identical(Type *x, Type *y) { case Type_Array: if (y->kind == Type_Array) - return (x->array.count == y->array.count) && are_types_identical(x->array.element, y->array.element); + return (x->array.count == y->array.count) && are_types_identical(x->array.elem, y->array.elem); break; case Type_Structure: @@ -401,7 +415,7 @@ b32 are_types_identical(Type *x, Type *y) { case Type_Pointer: if (y->kind == Type_Pointer) - return are_types_identical(x->pointer.element, y->pointer.element); + return are_types_identical(x->pointer.elem, y->pointer.elem); break; @@ -426,10 +440,10 @@ b32 are_types_identical(Type *x, Type *y) { } break; - case Type_Procedure: - if (y->kind == Type_Procedure) { - return are_types_identical(x->procedure.params, y->procedure.params) && - are_types_identical(x->procedure.results, y->procedure.results); + case Type_Proc: + if (y->kind == Type_Proc) { + return are_types_identical(x->proc.params, y->proc.params) && + are_types_identical(x->proc.results, y->proc.results); } break; } @@ -457,7 +471,7 @@ Type *default_type(Type *type) { // NOTE(bill): Internal sizes of certain types // string: 2*word_size (ptr+len) // slice: 3*word_size (ptr+len+cap) -// array: count*size_of(element) aligned +// array: count*size_of(elem) aligned // NOTE(bill): Alignment of structures and other types are to be compatible with C @@ -497,7 +511,7 @@ i64 type_align_of(BaseTypeSizes s, gbAllocator allocator, Type *t) { switch (t->kind) { case Type_Array: - return type_align_of(s, allocator, t->array.element); + return type_align_of(s, allocator, t->array.elem); case Type_Structure: { i64 max = 1; for (isize i = 0; i < t->structure.field_count; i++) { @@ -555,8 +569,8 @@ i64 type_size_of(BaseTypeSizes s, gbAllocator allocator, Type *t) { i64 count = t->array.count; if (count == 0) return 0; - i64 align = type_align_of(s, allocator, t->array.element); - i64 size = type_size_of(s, allocator, t->array.element); + i64 align = type_align_of(s, allocator, t->array.elem); + i64 size = type_size_of(s, allocator, t->array.elem); i64 alignment = align_formula(size, align); return alignment*(count-1) + size; } break; @@ -600,12 +614,12 @@ gbString write_type_to_string(gbString str, Type *type) { case Type_Array: str = gb_string_appendc(str, gb_bprintf("[%td]", type->array.count)); - str = write_type_to_string(str, type->array.element); + str = write_type_to_string(str, type->array.elem); break; case Type_Slice: str = gb_string_appendc(str, "[]"); - str = write_type_to_string(str, type->array.element); + str = write_type_to_string(str, type->array.elem); break; case Type_Structure: { @@ -624,7 +638,7 @@ gbString write_type_to_string(gbString str, Type *type) { case Type_Pointer: str = gb_string_appendc(str, "^"); - str = write_type_to_string(str, type->pointer.element); + str = write_type_to_string(str, type->pointer.elem); break; case Type_Named: @@ -657,14 +671,14 @@ gbString write_type_to_string(gbString str, Type *type) { } break; - case Type_Procedure: + case Type_Proc: str = gb_string_appendc(str, "proc("); - if (type->procedure.params) - str = write_type_to_string(str, type->procedure.params); + if (type->proc.params) + str = write_type_to_string(str, type->proc.params); str = gb_string_appendc(str, ")"); - if (type->procedure.results) { + if (type->proc.results) { str = gb_string_appendc(str, " -> "); - str = write_type_to_string(str, type->procedure.results); + str = write_type_to_string(str, type->proc.results); } break; } diff --git a/src/codegen/codegen.cpp b/src/codegen/codegen.cpp index 412daae3d..eb05eacc1 100644 --- a/src/codegen/codegen.cpp +++ b/src/codegen/codegen.cpp @@ -20,7 +20,9 @@ b32 ssa_gen_init(ssaGen *s, Checker *c) { ssa_module_init(&s->module, c); - gbFileError err = gb_file_create(&s->output_file, "../examples/test.ll"); + // TODO(bill): generate appropriate output name + isize pos = string_extension_position(c->parser->init_fullpath); + gbFileError err = gb_file_create(&s->output_file, gb_bprintf("%.*s.ll", pos, c->parser->init_fullpath.text)); if (err != gbFileError_None) return false; @@ -83,245 +85,3 @@ void ssa_gen_code(ssaGen *s) { } - - - -#if 0 -#include "type.cpp" -#include "ir.cpp" - -struct Codegen { - Checker *checker; - gbFile file; - gbAllocator allocator; - - irModule module; - - ErrorCollector error_collector; -}; - -b32 init_codegen(Codegen *c, Checker *checker) { - c->checker = checker; - - if (c->error_collector.count != 0) - return false; - for (isize i = 0; i < gb_array_count(checker->parser->files); i++) { - AstFile *f = &checker->parser->files[i]; - if (f->error_collector.count != 0) - return false; - if (f->tokenizer.error_count != 0) - return false; - } - - c->allocator = gb_heap_allocator(); - - ir_module_init(&c->module, c->checker); - - return true; -} - -void destroy_codegen(Codegen *c) { - ir_module_destroy(&c->module); -} - -b32 is_blank_identifier(AstNode *identifier) { - if (identifier->kind == AstNode_Identifier) { - return are_strings_equal(identifier->identifier.token.string, make_string("_")); - } - return false; -} - - -irValue *ir_add_basic_block(gbAllocator a, irValue *p, String label) { - irValue *b = ir_make_value_basic_block(a, gb_array_count(p->procedure.blocks), label, p); - gb_array_append(p->procedure.blocks, b); - return b; -} - -irValue *ir_emit_from_block(irValue *b, irInstruction *i) { - GB_ASSERT(b->kind == irValue_BasicBlock); - i->block = b; - gb_array_append(b->basic_block.instructions, i); - return ir_make_value_instruction(gb_heap_allocator(), i); -} - - -irValue *ir_emit(irValue *p, irInstruction *i) { - GB_ASSERT(p->kind == irValue_Procedure); - return ir_emit_from_block(p->procedure.curr_block, i); -} - - -irInstruction *ir_add_local(irValue *p, Type *type, TokenPos pos) { - irInstruction *i = ir_alloc_instruction(gb_heap_allocator(), irInstruction_Alloca); - i->reg.type = type; - i->reg.pos = pos; - gb_array_append(p->procedure.locals, ir_emit(p, i)); - return i; -} - -irInstruction *ir_add_named_local(irValue *p, Entity *e) { - irInstruction *i = ir_add_local(p, e->type, e->token.pos); - i->alloca.label = e->token.string; - // map_set(&p->procedure.variables, hash_pointer(e), ); - return i; -} - -irInstruction *ir_add_local_for_identifier(irValue *p, AstNode *i) { - GB_ASSERT(p->kind == irValue_Procedure); - GB_ASSERT(i->kind == AstNode_Identifier); - auto *found = map_get(&p->procedure.module->checker->definitions, hash_pointer(i)); - return ir_add_named_local(p, *found); -} - - -void ir_build_variable_declaration(irValue *p, AstNode *d) { - GB_ASSERT(p->kind == irValue_Procedure); - auto *vd = &d->variable_declaration; - - if (vd->name_count == vd->value_count) { - AstNode *name = vd->name_list; - AstNode *value = vd->value_list; - for (; - name != NULL && value != NULL; - name = name->next, value = value->next) { - if (!is_blank_identifier(name)) { - ir_add_local_for_identifier(p, name); - } - // auto lvalue = build_address(p, name, false); - // build_assignment(p, lvalue, value, true, NULL); - } - } else if (vd->value_count == 0) { - AstNode *name = vd->name_list; - for (; - name != NULL; - name = name->next) { - if (!is_blank_identifier(name)) { - - } - - // build_assignment(p, ) - } - } else { - // TODO(bill): Tuple - } - -} - - -void ir_build_expression(irValue *p, AstNode *e) { - GB_ASSERT(p->kind == irValue_Procedure); - -} - - -void ir_build_statement(irValue *p, AstNode *s); - -void ir_build_statement_list(irValue *p, AstNode *list) { - GB_ASSERT(p->kind == irValue_Procedure); - for (AstNode *item = list; item != NULL; item = item->next) { - ir_build_statement(p, item); - } -} - -void ir_build_statement(irValue *p, AstNode *s) { - GB_ASSERT(p->kind == irValue_Procedure); - - switch (s->kind) { - case AstNode_EmptyStatement: - break; - - case AstNode_VariableDeclaration: { - auto *vd = &s->variable_declaration; - if (vd->kind == Declaration_Mutable) { - ir_build_variable_declaration(p, s); - } - } break; - - - case AstNode_ExpressionStatement: - ir_build_expression(p, s->expression_statement.expression); - break; - - case AstNode_BlockStatement: - ir_build_statement_list(p, s->block_statement.list); - break; - } - -} - - - - - -void ir_begin_procedure_body(irValue *p) { - gbAllocator a = gb_heap_allocator(); - p->procedure.curr_block = ir_add_basic_block(a, p, make_string("entry")); - map_init(&p->procedure.variables, a); -} - -void ir_end_procedure_body(irValue *p) { - p->procedure.curr_block = NULL; - map_destroy(&p->procedure.variables); -} - - -void ir_build_procedure(irModule *m, irValue *p) { - if (p->procedure.blocks != NULL) - return; - AstNode *proc_type = NULL; - AstNode *body = NULL; - switch (p->procedure.node->kind) { - case AstNode_ProcedureDeclaration: - proc_type = p->procedure.node->procedure_declaration.procedure_type; - body = p->procedure.node->procedure_declaration.body; - break; - case AstNode_ProcedureLiteral: - proc_type = p->procedure.node->procedure_literal.type; - body = p->procedure.node->procedure_literal.body; - break; - default: - return; - } - - if (body == NULL) { - // NOTE(bill): External procedure - return; - } - - defer (gb_printf("build procedure %.*s\n", LIT(p->procedure.token.string))); - - - ir_begin_procedure_body(p); - ir_build_statement(p, body); - ir_end_procedure_body(p); -} - -void ir_build_proc_decl(irModule *m, AstNode *decl) { - GB_ASSERT(decl != NULL); - auto *pd = &decl->procedure_declaration; - if (is_blank_identifier(pd->name)) - return; - - Entity *e = entity_of_identifier(m->checker, pd->name); - irValue *p = *map_get(&m->values, hash_pointer(e)); - ir_build_procedure(m, p); -} - - - -void generate_code(Codegen *c) { - gbAllocator a = gb_heap_allocator(); - - ir_module_create(&c->module); - - for (isize i = 0; i < gb_array_count(c->module.values.entries); i++) { - irValue *v = c->module.values.entries[i].value; - switch (v->kind) { - case irValue_Procedure: - ir_build_proc_decl(&c->module, v->procedure.node); - break; - } - } -} -#endif diff --git a/src/codegen/print_llvm.cpp b/src/codegen/print_llvm.cpp index 8f0d0a413..8d7fe3d87 100644 --- a/src/codegen/print_llvm.cpp +++ b/src/codegen/print_llvm.cpp @@ -48,6 +48,7 @@ void ssa_print_escape_string(gbFile *f, String name) { return; } + char hex_table[] = "0123456789ABCDEF"; isize buf_len = name.len + extra; u8 *buf = gb_alloc_array(gb_heap_allocator(), u8, buf_len); @@ -101,20 +102,20 @@ void ssa_print_type(gbFile *f, BaseTypeSizes s, Type *t) { case Basic_u64: ssa_fprintf(f, "i64"); break; case Basic_f32: ssa_fprintf(f, "float"); break; case Basic_f64: ssa_fprintf(f, "double"); break; - case Basic_rawptr: ssa_fprintf(f, "void*"); break; - case Basic_string: ssa_fprintf(f, "{i8*, i%lld}", word_bits); break; + case Basic_rawptr: ssa_fprintf(f, "%%-rawptr"); break; + case Basic_string: ssa_fprintf(f, "%%-string"); break; case Basic_uint: ssa_fprintf(f, "i%lld", word_bits); break; case Basic_int: ssa_fprintf(f, "i%lld", word_bits); break; } break; case Type_Array: ssa_fprintf(f, "[%lld x ", t->array.count); - ssa_print_type(f, s, t->array.element); + ssa_print_type(f, s, t->array.elem); ssa_fprintf(f, "]"); break; case Type_Slice: ssa_fprintf(f, "{"); - ssa_print_type(f, s, t->slice.element); + ssa_print_type(f, s, t->slice.elem); ssa_fprintf(f, "*, i%lld, i%lld}", word_bits, word_bits); break; case Type_Structure: @@ -128,7 +129,7 @@ void ssa_print_type(gbFile *f, BaseTypeSizes s, Type *t) { ssa_fprintf(f, "}"); break; case Type_Pointer: - ssa_print_type(f, s, t->pointer.element); + ssa_print_type(f, s, t->pointer.elem); ssa_fprintf(f, "*"); break; case Type_Named: @@ -149,18 +150,18 @@ void ssa_print_type(gbFile *f, BaseTypeSizes s, Type *t) { ssa_fprintf(f, "}"); } break; - case Type_Procedure: - if (t->procedure.result_count == 0) { + case Type_Proc: + if (t->proc.result_count == 0) { ssa_fprintf(f, "void"); } else { - ssa_print_type(f, s, t->procedure.results); + ssa_print_type(f, s, t->proc.results); } ssa_fprintf(f, " ("); - for (isize i = 0; i < t->procedure.param_count; i++) { + for (isize i = 0; i < t->proc.param_count; i++) { if (i > 0) { ssa_fprintf(f, ", "); } - ssa_print_type(f, s, &t->procedure.params[i]); + ssa_print_type(f, s, &t->proc.params[i]); } ssa_fprintf(f, ")*"); break; @@ -292,7 +293,7 @@ void ssa_print_instr(gbFile *f, ssaModule *m, ssaValue *value) { } break; case ssaInstr_GetElementPtr: { - Type *et = instr->get_element_ptr.element_type; + Type *et = instr->get_element_ptr.elem_type; ssa_fprintf(f, "%%%d = getelementptr ", value->id); if (instr->get_element_ptr.inbounds) ssa_fprintf(f, "inbounds "); @@ -478,6 +479,15 @@ void ssa_print_llvm_ir(gbFile *f, ssaModule *m) { ssa_fprintf(f, "target datalayout = %.*s\n", LIT(m->layout)); } + ssa_print_encoded_local(f, make_string("-string")); + ssa_fprintf(f, " = type {i8*, "); + ssa_print_type(f, m->sizes, t_int); + ssa_fprintf(f, "} ; Basic_string\n\n"); + + ssa_print_encoded_local(f, make_string("-rawptr")); + ssa_fprintf(f, " = type i8*"); + ssa_fprintf(f, " ; Basic_rawptr\n\n"); + gb_for_array(member_index, m->members.entries) { auto *entry = &m->members.entries[member_index]; ssaValue *v = entry->value; @@ -513,7 +523,7 @@ void ssa_print_llvm_ir(gbFile *f, ssaModule *m) { ssa_fprintf(f, "define "); } - auto *proc_type = &proc->entity->type->procedure; + auto *proc_type = &proc->entity->type->proc; if (proc_type->result_count == 0) { ssa_fprintf(f, "void"); @@ -540,7 +550,7 @@ void ssa_print_llvm_ir(gbFile *f, ssaModule *m) { ssa_fprintf(f, ") "); if (proc->body == NULL) { - ssa_fprintf(f, "\n"); + ssa_fprintf(f, "\t; foreign procedure\n\n"); } else { ssa_fprintf(f, "{\n"); gb_for_array(i, proc->blocks) { diff --git a/src/codegen/ssa.cpp b/src/codegen/ssa.cpp index db91a4093..a8f48a6c8 100644 --- a/src/codegen/ssa.cpp +++ b/src/codegen/ssa.cpp @@ -129,7 +129,7 @@ struct ssaInstr { struct { ssaValue *address; Type * result_type; - Type * element_type; + Type * elem_type; ssaValue *indices[2]; isize index_count; b32 inbounds; @@ -436,7 +436,7 @@ ssaValue *ssa_make_instr_get_element_ptr(ssaProcedure *p, ssaValue *address, i->get_element_ptr.indices[0] = index0; i->get_element_ptr.indices[1] = index1; i->get_element_ptr.index_count = index_count; - i->get_element_ptr.element_type = ssa_value_type(address); + i->get_element_ptr.elem_type = ssa_value_type(address); i->get_element_ptr.inbounds = inbounds; if (p->curr_block) { gb_array_append(p->curr_block->values, v); @@ -627,7 +627,8 @@ ssaValue *ssa_lvalue_address(ssaLvalue lval, ssaProcedure *p) { Type *ssa_lvalue_type(ssaLvalue lval) { switch (lval.kind) { case ssaLvalue_Address: - return type_deref(ssa_value_type(lval.address.value)); + // return type_deref(ssa_value_type(lval.address.value)); + return ssa_value_type(lval.address.value); } return NULL; } @@ -701,8 +702,8 @@ void ssa_begin_procedure_body(ssaProcedure *proc) { gb_array_init(proc->blocks, gb_heap_allocator()); proc->curr_block = ssa_add_block(proc, proc->type_expr, make_string("entry")); - if (proc->type->procedure.params != NULL) { - auto *params = &proc->type->procedure.params->tuple; + if (proc->type->proc.params != NULL) { + auto *params = &proc->type->proc.params->tuple; for (isize i = 0; i < params->variable_count; i++) { Entity *e = params->variables[i]; ssa_add_param(proc, e); @@ -711,7 +712,7 @@ void ssa_begin_procedure_body(ssaProcedure *proc) { } void ssa_end_procedure_body(ssaProcedure *proc) { - if (proc->type->procedure.result_count == 0) { + if (proc->type->proc.result_count == 0) { ssa_emit_ret(proc, NULL); } @@ -758,83 +759,6 @@ void ssa_pop_target_list(ssaProcedure *proc) { - -ssaValue *ssa_emit_conv(ssaProcedure *proc, ssaValue *value, Type *t) { - Type *src_type = ssa_value_type(value); - if (are_types_identical(t, src_type)) - return value; - - Type *src = get_base_type(src_type); - Type *dst = get_base_type(t); - - if (value->kind == ssaValue_Constant) { - if (dst->kind == Type_Basic) - return ssa_make_value_constant(proc->module->allocator, t, value->constant.value); - } - - // integer -> integer - if (is_type_integer(src) && is_type_integer(dst)) { - i64 sz = basic_type_sizes[src->basic.kind]; - i64 dz = basic_type_sizes[dst->basic.kind]; - ssaConvKind kind = ssaConv_trunc; - if (dz >= sz) { - kind = ssaConv_zext; - } - return ssa_emit(proc, ssa_make_instr_conv(proc, kind, value, src, dst)); - } - - // float -> float - if (is_type_float(src) && is_type_float(dst)) { - i64 sz = basic_type_sizes[src->basic.kind]; - i64 dz = basic_type_sizes[dst->basic.kind]; - ssaConvKind kind = ssaConv_fptrunc; - if (dz >= sz) { - kind = ssaConv_fpext; - } - return ssa_emit(proc, ssa_make_instr_conv(proc, kind, value, src, dst)); - } - - // float -> integer - if (is_type_float(src) && is_type_integer(dst)) { - ssaConvKind kind = ssaConv_fptosi; - if (is_type_unsigned(dst)) { - kind = ssaConv_fptoui; - } - return ssa_emit(proc, ssa_make_instr_conv(proc, kind, value, src, dst)); - } - - // integer -> float - if (is_type_integer(src) && is_type_float(dst)) { - ssaConvKind kind = ssaConv_sitofp; - if (is_type_unsigned(dst)) { - kind = ssaConv_uitofp; - } - return ssa_emit(proc, ssa_make_instr_conv(proc, kind, value, src, dst)); - } - - // Pointer to int - if (is_type_pointer(src) && is_type_integer(dst)) { - return ssa_emit(proc, ssa_make_instr_conv(proc, ssaConv_ptrtoint, value, src, dst)); - } - - // int to Pointer - if (is_type_integer(src) && is_type_pointer(dst)) { - return ssa_emit(proc, ssa_make_instr_conv(proc, ssaConv_inttoptr, value, src, dst)); - } - - // Pointer to Pointer - if (is_type_pointer(src) && is_type_pointer(dst)) { - return ssa_emit(proc, ssa_make_instr_conv(proc, ssaConv_bitcast, value, src, dst)); - } - - - GB_PANIC("TODO(bill): ssa_emit_conv"); - GB_PANIC("TODO(bill): string -> []byte"); - GB_PANIC("TODO(bill): []byte -> string"); - - return NULL; -} - ssaValue *ssa_emit_arith(ssaProcedure *proc, Token op, ssaValue *left, ssaValue *right, Type *type) { switch (op.kind) { case Token_AndNot: { @@ -860,6 +784,7 @@ ssaValue *ssa_emit_arith(ssaProcedure *proc, Token op, ssaValue *left, ssaValue } ssaValue *v = ssa_make_instr_binary_op(proc, op, left, right); + ssa_value_set_type(v, type); return ssa_emit(proc, v); } @@ -885,7 +810,7 @@ ssaValue *ssa_emit_ptr_offset(ssaProcedure *proc, ssaValue *ptr, ssaValue *offse ssaValue *gep = NULL; offset = ssa_emit_conv(proc, offset, t_int); gep = ssa_make_instr_get_element_ptr(proc, ptr, offset, NULL, 1, false); - gep->instr.get_element_ptr.element_type = type_deref(type); + gep->instr.get_element_ptr.elem_type = type_deref(type); gep->instr.get_element_ptr.result_type = type; return ssa_emit(proc, gep); } @@ -895,7 +820,7 @@ ssaValue *ssa_emit_struct_gep(ssaProcedure *proc, ssaValue *s, ssaValue *index, // NOTE(bill): For some weird legacy reason in LLVM, structure elements must be accessed as an i32 index = ssa_emit_conv(proc, index, t_i32); gep = ssa_make_instr_get_element_ptr(proc, s, v_zero, index, 2, true); - gep->instr.get_element_ptr.element_type = ssa_value_type(s); + gep->instr.get_element_ptr.elem_type = ssa_value_type(s); gep->instr.get_element_ptr.result_type = result_type; return ssa_emit(proc, gep); @@ -905,10 +830,10 @@ ssaValue *ssa_emit_struct_gep(ssaProcedure *proc, ssaValue *s, ssaValue *index, ssaValue *ssa_array_elem(ssaProcedure *proc, ssaValue *array) { Type *t = ssa_value_type(array); GB_ASSERT(t->kind == Type_Array); - Type *base_type = t->array.element; + Type *base_type = t->array.elem; ssaValue *elem = ssa_make_instr_get_element_ptr(proc, array, v_zero, v_zero, 2, true); Type *result_type = make_type_pointer(proc->module->allocator, base_type); - elem->instr.get_element_ptr.element_type = t; + elem->instr.get_element_ptr.elem_type = t; elem->instr.get_element_ptr.result_type = result_type; return ssa_emit(proc, elem); } @@ -925,7 +850,7 @@ ssaValue *ssa_slice_elem(ssaProcedure *proc, ssaValue *slice) { Type *t = ssa_value_type(slice); GB_ASSERT(t->kind == Type_Slice); - Type *result_type = make_type_pointer(proc->module->allocator, t->slice.element); + Type *result_type = make_type_pointer(proc->module->allocator, t->slice.elem); return ssa_emit_load(proc, ssa_emit_struct_gep(proc, slice, v_zero32, result_type)); } ssaValue *ssa_slice_len(ssaProcedure *proc, ssaValue *slice) { @@ -945,7 +870,7 @@ ssaValue *ssa_string_elem(ssaProcedure *proc, ssaValue *string) { Type *base_type = t_u8; ssaValue *elem = ssa_make_instr_get_element_ptr(proc, string, v_zero, v_zero32, 2, true); Type *result_type = make_type_pointer(proc->module->allocator, base_type); - elem->instr.get_element_ptr.element_type = t; + elem->instr.get_element_ptr.elem_type = t; elem->instr.get_element_ptr.result_type = result_type; ssa_emit(proc, elem); @@ -956,9 +881,6 @@ ssaValue *ssa_string_len(ssaProcedure *proc, ssaValue *string) { GB_ASSERT(t->kind == Type_Basic && t->basic.kind == Basic_string); return ssa_emit_load(proc, ssa_emit_struct_gep(proc, string, v_one32, t_int)); } -ssaValue *ssa_string_cap(ssaProcedure *proc, ssaValue *string) { - return ssa_string_len(proc, string); -} @@ -1070,7 +992,8 @@ ssaValue *ssa_add_global_string_array(ssaProcedure *proc, ExactValue value) { ssaValue *ssa_emit_string(ssaProcedure *proc, ssaValue *elem, ssaValue *len) { Type *t_u8_ptr = ssa_value_type(elem); GB_ASSERT(t_u8_ptr->kind == Type_Pointer); - GB_ASSERT(t_u8_ptr->pointer.element == t_u8); + + GB_ASSERT(is_type_byte(t_u8_ptr->pointer.elem)); ssaValue *str = ssa_add_local_generated(proc, t_string); ssaValue *str_elem = ssa_emit_struct_gep(proc, str, v_zero32, t_u8_ptr); @@ -1081,6 +1004,102 @@ ssaValue *ssa_emit_string(ssaProcedure *proc, ssaValue *elem, ssaValue *len) { } + + +ssaValue *ssa_emit_conv(ssaProcedure *proc, ssaValue *value, Type *t) { + Type *src_type = ssa_value_type(value); + if (are_types_identical(t, src_type)) + return value; + + Type *src = get_base_type(src_type); + Type *dst = get_base_type(t); + + if (value->kind == ssaValue_Constant) { + if (dst->kind == Type_Basic) + return ssa_make_value_constant(proc->module->allocator, t, value->constant.value); + } + + // integer -> integer + if (is_type_integer(src) && is_type_integer(dst)) { + i64 sz = basic_type_sizes[src->basic.kind]; + i64 dz = basic_type_sizes[dst->basic.kind]; + ssaConvKind kind = ssaConv_trunc; + if (dz >= sz) { + kind = ssaConv_zext; + } + return ssa_emit(proc, ssa_make_instr_conv(proc, kind, value, src, dst)); + } + + // float -> float + if (is_type_float(src) && is_type_float(dst)) { + i64 sz = basic_type_sizes[src->basic.kind]; + i64 dz = basic_type_sizes[dst->basic.kind]; + ssaConvKind kind = ssaConv_fptrunc; + if (dz >= sz) { + kind = ssaConv_fpext; + } + return ssa_emit(proc, ssa_make_instr_conv(proc, kind, value, src, dst)); + } + + // float <-> integer + if (is_type_float(src) && is_type_integer(dst)) { + ssaConvKind kind = ssaConv_fptosi; + if (is_type_unsigned(dst)) { + kind = ssaConv_fptoui; + } + return ssa_emit(proc, ssa_make_instr_conv(proc, kind, value, src, dst)); + } + if (is_type_integer(src) && is_type_float(dst)) { + ssaConvKind kind = ssaConv_sitofp; + if (is_type_unsigned(dst)) { + kind = ssaConv_uitofp; + } + return ssa_emit(proc, ssa_make_instr_conv(proc, kind, value, src, dst)); + } + + // Pointer <-> int + if (is_type_pointer(src) && is_type_int_or_uint(dst)) { + ssaValue *p = ssa_emit_load(proc, value); + return ssa_emit(proc, ssa_make_instr_conv(proc, ssaConv_ptrtoint, p, src, dst)); + } + if (is_type_int_or_uint(src) && is_type_pointer(dst)) { + ssaValue *i = ssa_emit_load(proc, value); + return ssa_emit(proc, ssa_make_instr_conv(proc, ssaConv_inttoptr, i, src, dst)); + } + + // Pointer <-> Pointer + if (is_type_pointer(src) && is_type_pointer(dst)) { + return ssa_emit(proc, ssa_make_instr_conv(proc, ssaConv_bitcast, value, src, dst)); + } + + + // []byte/[]u8 <-> string + if (is_type_byte_slice(src) && is_type_string(dst)) { + ssaValue *slice = ssa_add_local_generated(proc, src); + ssa_emit_store(proc, slice, value); + ssaValue *elem = ssa_slice_elem(proc, slice); + ssaValue *len = ssa_slice_len(proc, slice); + return ssa_emit_string(proc, elem, len); + } + if (is_type_string(src) && is_type_byte_slice(dst)) { + ssaValue *str = ssa_add_local_generated(proc, src); + ssa_emit_store(proc, str, value); + ssaValue *elem = ssa_string_elem(proc, str); + ssaValue *len = ssa_string_len(proc, str); + ssaValue *v = ssa_emit_slice(proc, dst, elem, v_zero, len, len); + return v; + } + + + GB_PANIC("Invalid type conversion: `%s` to `%s`", type_to_string(src_type), type_to_string(t)); + + return NULL; +} + + + + + ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue *tv) { switch (expr->kind) { case_ast_node(bl, BasicLit, expr); @@ -1117,8 +1136,10 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue case_ast_node(ue, UnaryExpr, expr); switch (ue->op.kind) { - case Token_Pointer: - return ssa_lvalue_address(ssa_build_addr(proc, ue->expr), proc); + case Token_Pointer: { + ssaLvalue lval = ssa_build_addr(proc, ue->expr); + return ssa_lvalue_address(lval, proc); + } case Token_Add: return ssa_build_expr(proc, ue->expr); case Token_Sub: { @@ -1181,8 +1202,7 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue case_end; case_ast_node(ce, CastExpr, expr); - ssaValue *v = ssa_build_expr(proc, ce->expr); - return ssa_emit_conv(proc, v, tv->type); + return ssa_emit_conv(proc, ssa_build_expr(proc, ce->expr), tv->type); case_end; case_ast_node(ce, CallExpr, expr); @@ -1192,7 +1212,8 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue if (found && (*found)->kind == Entity_Builtin) { Entity *e = *found; switch (e->builtin.id) { - case BuiltinProcedure_len: { + case BuiltinProc_len: { + // NOTE(bill): len of an array is a constant expression ssaValue *v = ssa_lvalue_address(ssa_build_addr(proc, ce->arg_list), proc); Type *t = get_base_type(ssa_value_type(v)); if (t == t_string) @@ -1200,22 +1221,27 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue else if (t->kind == Type_Slice) return ssa_slice_len(proc, v); } break; - case BuiltinProcedure_cap: { + case BuiltinProc_cap: { + // NOTE(bill): cap of an array is a constant expression ssaValue *v = ssa_lvalue_address(ssa_build_addr(proc, ce->arg_list), proc); Type *t = get_base_type(ssa_value_type(v)); - if (t == t_string) - return ssa_string_cap(proc, v); - else if (t->kind == Type_Slice) - return ssa_slice_cap(proc, v); + return ssa_slice_cap(proc, v); } break; - case BuiltinProcedure_copy: { - GB_PANIC("TODO(bill): BuiltinProcedure_copy"); + case BuiltinProc_copy: { + GB_PANIC("TODO(bill): BuiltinProc_copy"); + // TODO(bill): Should this be llvm.memmove internally? + // http://llvm.org/docs/LangRef.html#llvm-memmove-intrinsic + // declare void @llvm.memmove.p0i8.p0i8.i32(i8* , i8* , i32 , i32 , i1 ) + // declare void @llvm.memmove.p0i8.p0i8.i64(i8* , i8* , i64 , i32 , i1 ) } break; - case BuiltinProcedure_print: { - GB_PANIC("TODO(bill): BuiltinProcedure_print"); + case BuiltinProc_append: { + GB_PANIC("TODO(bill): BuiltinProc_append"); } break; - case BuiltinProcedure_println: { - GB_PANIC("TODO(bill): BuiltinProcedure_println"); + case BuiltinProc_print: { + GB_PANIC("TODO(bill): BuiltinProc_print"); + } break; + case BuiltinProc_println: { + GB_PANIC("TODO(bill): BuiltinProc_println"); } break; } } @@ -1225,8 +1251,8 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue // NOTE(bill): Regular call ssaValue *value = ssa_build_expr(proc, ce->proc); Type *proc_type_ = ssa_value_type(value); - GB_ASSERT(proc_type_->kind == Type_Procedure); - auto *type = &proc_type_->procedure; + GB_ASSERT(proc_type_->kind == Type_Proc); + auto *type = &proc_type_->proc; isize arg_index = 0; isize arg_count = type->param_count; @@ -1242,10 +1268,12 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue } } +#if 0 for (isize i = 0; i < arg_count; i++) { Entity *e = type->params->tuple.variables[i]; args[i] = ssa_emit_conv(proc, args[i], e->type); } +#endif ssaValue *call = ssa_make_instr_call(proc, value, args, arg_count, tv->type); return ssa_emit(proc, call); @@ -1391,7 +1419,7 @@ ssaLvalue ssa_build_addr(ssaProcedure *proc, AstNode *expr) { ssaValue *gep = ssa_make_instr_get_element_ptr(proc, e, NULL, NULL, 0, false); Type *t = type_deref(get_base_type(ssa_value_type(e))); gep->instr.get_element_ptr.result_type = t; - gep->instr.get_element_ptr.element_type = t; + gep->instr.get_element_ptr.elem_type = t; ssaValue *v = ssa_emit(proc, gep); return ssa_make_lvalue_address(v, expr); case_end; @@ -1484,7 +1512,8 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) { gb_for_array(i, inits) { - ssa_lvalue_store(lvals[i], proc, inits[i]); + ssaValue *v = ssa_emit_conv(proc, inits[i], ssa_lvalue_type(lvals[i])); + ssa_lvalue_store(lvals[i], proc, v); } } else if (vd->value_count == 0) { // declared and zero-initialized @@ -1583,8 +1612,8 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) { case_ast_node(rs, ReturnStmt, node); ssaValue *v = NULL; - auto *return_type_tuple = &proc->type->procedure.results->tuple; - isize return_count = proc->type->procedure.result_count; + auto *return_type_tuple = &proc->type->proc.results->tuple; + isize return_count = proc->type->proc.result_count; if (rs->result_count == 1 && return_count > 1) { GB_PANIC("ReturnStmt tuple return statement"); } else if (return_count == 1) { @@ -1595,7 +1624,7 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) { // No return values } else { // 1:1 multiple return values - Type *ret_type = proc->type->procedure.results; + Type *ret_type = proc->type->proc.results; v = ssa_add_local_generated(proc, ret_type); isize i = 0; AstNode *r = rs->result_list; diff --git a/src/parser.cpp b/src/parser.cpp index faf462cd8..afd8bb3df 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -58,6 +58,7 @@ struct AstScope { }; struct Parser { + String init_fullpath; gbArray(AstFile) files; gbArray(String) imports; isize import_index; @@ -2104,6 +2105,7 @@ ParseFileError parse_files(Parser *p, char *init_filename) { char *fullpath_str = gb_path_get_full_name(gb_heap_allocator(), init_filename); String init_fullpath = make_string(fullpath_str); gb_array_append(p->imports, init_fullpath); + p->init_fullpath = init_fullpath; gb_for_array(i, p->imports) { String import_path = p->imports[i]; diff --git a/src/string.cpp b/src/string.cpp index 44c814897..1581feab9 100644 --- a/src/string.cpp +++ b/src/string.cpp @@ -80,7 +80,7 @@ b32 string_contains_char(String s, u8 c) { return false; } -b32 unquote_char(String s, u8 quote, Rune *rune, b32 *multi, String *tail_string) { +b32 unquote_char(String s, u8 quote, Rune *rune, b32 *multiple_bytes, String *tail_string) { if (s.text[0] == quote && (quote == '\'' || quote == '"')) { return false; @@ -88,6 +88,7 @@ b32 unquote_char(String s, u8 quote, Rune *rune, b32 *multi, String *tail_string Rune r = -1; isize size = gb_utf8_decode(s.text, s.len, &r); *rune = r; + *multiple_bytes = true; *tail_string = make_string(s.text+size, s.len-size); return true; } else if (s.text[0] != '\\') { @@ -131,12 +132,12 @@ b32 unquote_char(String s, u8 quote, Rune *rune, b32 *multi, String *tail_string case '5': case '6': case '7': { - i32 r = c - '0'; + i32 r = gb_digit_to_int(c); if (s.len < 2) { return false; } for (isize i = 0; i < 2; i++) { - i32 d = s.text[i] - '0'; + i32 d = gb_digit_to_int(s.text[i]); if (d < 0 || d > 7) { return false; } @@ -152,25 +153,25 @@ b32 unquote_char(String s, u8 quote, Rune *rune, b32 *multi, String *tail_string case 'x': case 'u': case 'U': { - isize n = 0; + isize count = 0; switch (c) { - case 'x': n = 2; break; - case 'u': n = 4; break; - case 'U': n = 8; break; + case 'x': count = 2; break; + case 'u': count = 4; break; + case 'U': count = 8; break; } Rune r = 0; - if (s.len < n) { + if (s.len < count) { return false; } - for (isize i = 0; i < n; i++) { + for (isize i = 0; i < count; i++) { i32 d = gb_hex_digit_to_int(s.text[i]); if (d < 0) { return false; } r = (r<<4) | d; } - s = make_string(s.text+n, s.len-n); + s = make_string(s.text+count, s.len-count); if (c == 'x') { *rune = r; break; @@ -179,7 +180,7 @@ b32 unquote_char(String s, u8 quote, Rune *rune, b32 *multi, String *tail_string return false; } *rune = r; - *multi = true; + *multiple_bytes = true; } break; } *tail_string = s; @@ -229,27 +230,28 @@ i32 unquote_string(gbAllocator a, String *s_) { } } + u8 rune_temp[4] = {}; isize buf_len = 3*s.len / 2; u8 *buf = gb_alloc_array(a, u8, buf_len); - isize len = 0; + isize offset = 0; while (s.len > 0) { String tail_string = {}; Rune r = 0; - b32 multi = false; - b32 success = unquote_char(s, quote, &r, &multi, &tail_string); + b32 multiple_bytes = false; + b32 success = unquote_char(s, quote, &r, &multiple_bytes, &tail_string); if (!success) { gb_free(a, buf); return 0; } s = tail_string; - if (r < 0x80 || !multi) { - buf[len++] = cast(u8)r; + if (r < 0x80 || !multiple_bytes) { + buf[offset++] = cast(u8)r; } else { isize size = gb_utf8_encode_rune(rune_temp, r); - gb_memcopy(buf+len, rune_temp, size); - len += size; + gb_memcopy(buf+offset, rune_temp, size); + offset += size; } if (quote == '\'' && s.len != 0) { @@ -257,6 +259,6 @@ i32 unquote_string(gbAllocator a, String *s_) { return 0; } } - *s_ = make_string(buf, len); + *s_ = make_string(buf, offset); return 2; }