From 6117a1be7cfd4fe556b11bbfd070d0605688f5e1 Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Thu, 16 Apr 2020 17:52:48 +0200 Subject: [PATCH] Lots of fixes and optimizations to structs. Now there are no unnecessary intermediary types. --- CMakeLists.txt | 4 +- resources/testfragments/super_simple.c3 | 69 +- resources/testfragments/testscrap.c3 | 839 ++++++++++++++++++++++++ src/compiler/ast.c | 9 - src/compiler/compiler_internal.h | 20 +- src/compiler/enums.h | 23 +- src/compiler/llvm_codegen.c | 70 ++ src/compiler/llvm_codegen_expr.c | 129 +++- src/compiler/llvm_codegen_function.c | 20 +- src/compiler/llvm_codegen_internal.h | 105 ++- src/compiler/llvm_codegen_stmt.c | 36 +- src/compiler/llvm_codegen_type.c | 24 +- src/compiler/sema_decls.c | 26 + src/compiler/sema_expr.c | 14 +- src/compiler/sema_stmts.c | 3 +- src/compiler/target.c | 15 +- src/compiler/types.c | 5 +- 17 files changed, 1298 insertions(+), 113 deletions(-) create mode 100644 resources/testfragments/testscrap.c3 diff --git a/CMakeLists.txt b/CMakeLists.txt index cdc92238b..791a8226d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -79,14 +79,14 @@ add_executable(c3c src/utils/stringutils.c src/compiler/dwarf.h src/compiler/llvm_codegen_stmt.c - src/compiler/llvm_codegen_internal.h src/compiler/llvm_codegen_expr.c src/compiler/llvm_codegen_debug_info.c src/compiler/llvm_codegen_module.c src/compiler/llvm_codegen_type.c src/compiler/llvm_codegen_function.c src/build/builder.c - src/utils/toml.c src/build/project.c src/build/build_internal.h src/compiler/sema_name_resolution.c src/target_info/target_info.c src/compiler/parse_expr.c src/compiler/parser_internal.h src/compiler/parse_stmt.c src/compiler/sema_passes.c src/compiler/sema_internal.h src/compiler/sema_decls.c src/compiler/sema_types.c src/compiler/sema_stmts.c src/compiler/number.c) + src/utils/toml.c src/build/project.c + src/compiler/sema_name_resolution.c src/target_info/target_info.c src/compiler/parse_expr.c src/compiler/parser_internal.h src/compiler/parse_stmt.c src/compiler/sema_passes.c src/compiler/sema_internal.h src/compiler/sema_decls.c src/compiler/sema_types.c src/compiler/sema_stmts.c src/compiler/number.c) target_compile_options(c3c PRIVATE -Wimplicit-int -Werror -Wall -Wextra -Wno-unused-function -Wno-unused-variable -Wno-unused-parameter) diff --git a/resources/testfragments/super_simple.c3 b/resources/testfragments/super_simple.c3 index f2a928056..7655192e6 100644 --- a/resources/testfragments/super_simple.c3 +++ b/resources/testfragments/super_simple.c3 @@ -64,7 +64,7 @@ enum EnumTestOverflowAfter - +/* error Error { BLURB, @@ -75,7 +75,7 @@ error OtherError { FOO_BAR } - +*/ enum Inf { A, @@ -119,6 +119,8 @@ union SimpleUnion func void testUnion() { + int wdw = 123; + wdw = 222; SimpleUnion s; s.a = 1; s.f = 1.0; @@ -143,7 +145,7 @@ func TestStruct2 structTest(int i) func void enumInferenceTest() { - OtherError e = OtherError.FOO_BAR; +// OtherError e = OtherError.FOO_BAR; Inf x = Inf.A; x = BooInf.B; x = A; @@ -175,10 +177,11 @@ func int jumptest() LABELX: return 2; } +/* func int borok() throws { return 1; -} +}*/ func void testNoReturn() { int i = 0; @@ -198,13 +201,13 @@ func int testReturnWithOtherAtEnd() if (i == 10) i++; i = i + 2; } - +/* func int testReturnWithError() throws Error { int i = 0; throw Error.NO_SUCH_FILE; i = i + 1; -} +}*/ func int testReturnWithConditional() { @@ -218,7 +221,7 @@ func int testReturnWithConditional() return 2; } } - +/* func int testReturnWithCondThrow() throws Error { int i = 0; @@ -231,7 +234,7 @@ func int testReturnWithCondThrow() throws Error throw Error.NO_SUCH_FILE; } } - +*/ func int testReturnSwitch() { int i = 0; @@ -246,6 +249,7 @@ func int testReturnSwitch() } } +/* func int barok() throws Error, OtherError { if (true) @@ -254,7 +258,7 @@ func int barok() throws Error, OtherError } return 100; } - +*/ struct SimpleStruct { @@ -302,24 +306,19 @@ struct AnonStruct int x; } +func AnonStruct sendAnonStruct() +{ + AnonStruct foo = Foo { a = 1 }; + foo.b2 = 123; + return foo; +} + func void testAnonStruct2() { - AnonStruct s = { b2 = 3, b1 = 7, xx.b = 1 }; - AnonStruct foo; - - printf("a = %d, b = %d (1), c = %d, b1 = %d (7), c1 = %d, b2 = %d (3), c2 = %d (3), x = %d\n", s.a, s.xx.b, s.xx.c, s.b1, s.c1, s.b2, s.c2, s.x); - - s.xx.b = 100; - s.xx.c = 99; - s.b1 = 3; - s.b2 = 5; - s.c2 = 7; - - printf("a = %d, b = %d (100), c = %d (99), b1 = %d (3), c1 = %d, b2 = %d (7), c2 = %d (7), x = %d\n", s.a, s.xx.b, s.xx.c, s.b1, s.c1, s.b2, s.c2, s.x); - - + sendAnonStruct(); } + func void testAnonStruct() { AnonStruct s = { b2 = 3, b1 = 7, xx.b = 1 }; @@ -346,6 +345,10 @@ func void testAnonStruct() printf("a = %d, b = %d, c = %d (2), b1 = %d, c1 = %d, b2 = %d, c2 = %d, x = %d\n", s.a, s.xx.b, s.xx.c, s.b1, s.c1, s.b2, s.c2, s.x); + s = sendAnonStruct(); + + printf("Got it sent: a = %d, b = %d (1), c = %d, b1 = %d (7), c1 = %d, b2 = %d (3), c2 = %d (3), x = %d\n", s.a, s.xx.b, s.xx.c, s.b1, s.c1, s.b2, s.c2, s.x); + } func int boba(int y, int j) { @@ -587,6 +590,25 @@ struct WithArray { int[4] x; } +/* +error Err +{ + TEST_ERR1 +} +*/ +/* +func int testThrow(int x) throws Err +{ + if (x < 0) throw Err.TEST_ERR1; + return x * x; +} +*/ +func void testErrors() +{ + //int x = try testThrow(20) else 0; + int x = 0; + printf("Value was %d, expected 400.\n", x); +} func void testArray() { @@ -648,6 +670,7 @@ JUMP: func int main(int x) { printf("Helo!\n"); + testErrors(); testDefault(y = 99); testPointers(2, 3); testDefer(); diff --git a/resources/testfragments/testscrap.c3 b/resources/testfragments/testscrap.c3 new file mode 100644 index 000000000..5933d68ba --- /dev/null +++ b/resources/testfragments/testscrap.c3 @@ -0,0 +1,839 @@ +module bar; + +typedef int as Bob; + +struct Test +{ + int a; +} + +struct Test2 +{ + Test t; + int b; +} + +union Test3 +{ + long eo; + Test t; + int b; +} + +struct Teob +{ + int x; + double y; + int xy; + int oekfeo; +} + +enum EnumWithData : ushort (int a, char[] x, long b = 4) +{ + // Currently the args are ignored TODO! + TEST1(42, "hello", 328) = 3, + TEST2(12, "world") +} + +/* +enum EnumTestNoOverflowAfterULong : ulong +{ + VALUE = 0xFFFF_FFFF_FFFF_FFFE, + VALUE_NO_EXCEED +} + + + +enum EnumTestOverflowAfterLong : long +{ + VALUE = 0x7FFF_FFFF_FFFF_FFFF, + VALUE_EXCEED +} + +enum EnumTestOverflowAfterULong : ulong +{ + VALUE = 0xFFFF_FFFF_FFFF_FFFF, + VALUE_EXCEED +} + +enum EnumTestOverflowAfter +{ + VALUE = 0x80000000 - 1, + VALUE_EXCEED +}*/ + + + +/* +error Error +{ + BLURB, + NO_SUCH_FILE, +} + +error OtherError +{ + FOO_BAR +} +*/ +enum Inf +{ + A, + B, + C = 10000 +} + +enum Inf2 : byte +{ + A, + B, + C = 129, +} + +typedef Inf as BooInf; + +struct TestStruct +{ + int a; +} + +struct TestStruct2 +{ + TestStruct a; + char xx; + TestStruct b; + int c; +} + +union TestUnion +{ + int a; + double f; + TestStruct2 e; +} +union SimpleUnion +{ + int a; + double f; +} + +func void testUnion() +{ + SimpleUnion s; + s.a = 1; + s.f = 1.0; + s = { 1 }; + int x = 2; + s = { (x = 2) }; + s = { f = 1.0 }; + TestUnion tu = { e = TestStruct2 { c = 1 } }; + tu.e = TestStruct2 { c = 1 }; +} + +func TestStruct2 structTest(int i) +{ + TestStruct foo = { i }; + TestStruct foo2 = { a = i }; + TestStruct foo3 = TestStruct { i }; + TestStruct2 bar = { c = 2 }; + int x = 3 * i; + TestStruct2 bar2 = { b.a = x, a.a = x + 1 }; + return bar2; +} + +func void enumInferenceTest() +{ +// OtherError e = OtherError.FOO_BAR; + Inf x = Inf.A; + x = BooInf.B; + x = A; + int x1 = 0; + bool y = x1 == x1; + Inf2 z = C; + if (z == Inf2.A) return; + if (z == 1) return; + z = 2; + switch (z) + { + case Inf2.A: + x1++; + return; + case B: + return; + case 111: + x1 += 1; + return; + default: + return; + } +} + +func int jumptest() +{ + if (1) goto LABELX; + return 1; + LABELX: + return 2; +} +/* +func int borok() throws +{ + return 1; +}*/ +func void testNoReturn() +{ + int i = 0; + i = -i; +} + +func int testReturn() +{ + int i = 0; + return i; +} + +func int testReturnWithOtherAtEnd() +{ + int i = 0; + return i; + if (i == 10) i++; + i = i + 2; +} +/* +func int testReturnWithError() throws Error +{ + int i = 0; + throw Error.NO_SUCH_FILE; + i = i + 1; +}*/ + +func int testReturnWithConditional() +{ + int i = 0; + if (i > 0) + { + return 1; + } + else + { + return 2; + } +} +/* +func int testReturnWithCondThrow() throws Error +{ + int i = 0; + if (i > 0) + { + throw Error.NO_SUCH_FILE; + } + else + { + throw Error.NO_SUCH_FILE; + } +} +*/ +func int testReturnSwitch() +{ + int i = 0; + switch (i) + { + case 1: + return 2; + case 2: + return 3; + default: + return 4; + } +} + +/* +func int barok() throws Error, OtherError +{ + if (true) + { + throw Error.NO_SUCH_FILE; + } + return 100; +} +*/ + +struct SimpleStruct +{ + int a; + int b; + double c; + double d; + char z1; + char z2; +} +func void testSimpleStruct(int x) +{ + SimpleStruct snoinit; + SimpleStruct sinit = { b = 1, d = 3.0, z1 = 1 }; + sinit.a = 1; + sinit.b = 2; + printf("a = %d, b = %d (1), c = %f, d = %f (3.0), z1 = %d (1), z2 = %d\n", sinit.a, sinit.b, sinit.c, sinit.d, cast(sinit.z1, int), cast(sinit.z2, int)); + snoinit.b = 1; + snoinit.a = 100; + snoinit.d = 3.0; + snoinit.c = 2.0; + snoinit.z1 = 1; + snoinit.z2 = 2; + printf("b = %d (1), d = %f (3.0), z1 = %d (1)\n", snoinit.b, snoinit.d, snoinit.z1); +} + +struct AnonStruct +{ + int a; + struct xx + { + int b; + int c; + } + struct + { + int b1; + int c1; + } + union + { + int b2; + int c2; + } + int x; +} + +func AnonStruct sendAnonStruct() +{ + AnonStruct foo = { }; + foo.b2 = 123; + return foo; +} + +func void testAnonStruct2() +{ + sendAnonStruct(); +} + + +func void testAnonStruct() +{ + AnonStruct s = { b2 = 3, b1 = 7, xx.b = 1 }; + AnonStruct foo; + + printf("a = %d, b = %d (1), c = %d, b1 = %d (7), c1 = %d, b2 = %d (3), c2 = %d (3), x = %d\n", s.a, s.xx.b, s.xx.c, s.b1, s.c1, s.b2, s.c2, s.x); + + s.xx.b = 100; + s.xx.c = 99; + s.b1 = 3; + s.b2 = 5; + s.c2 = 7; + + printf("a = %d, b = %d (100), c = %d (99), b1 = %d (3), c1 = %d, b2 = %d (7), c2 = %d (7), x = %d\n", s.a, s.xx.b, s.xx.c, s.b1, s.c1, s.b2, s.c2, s.x); + + s = { xx = { c = 2 }}; + + printf("a = %d, b = %d, c = %d (2), b1 = %d, c1 = %d, b2 = %d, c2 = %d, x = %d\n", s.a, s.xx.b, s.xx.c, s.b1, s.c1, s.b2, s.c2, s.x); + + s.xx.c = 3; + s.x = 1212; + s.a = 29183; + s = AnonStruct { xx = { c = 2 }}; + + printf("a = %d, b = %d, c = %d (2), b1 = %d, c1 = %d, b2 = %d, c2 = %d, x = %d\n", s.a, s.xx.b, s.xx.c, s.b1, s.c1, s.b2, s.c2, s.x); + + // s = sendAnonStruct(); + + printf("Got it sent: a = %d, b = %d (1), c = %d, b1 = %d (7), c1 = %d, b2 = %d (3), c2 = %d (3), x = %d\n", s.a, s.xx.b, s.xx.c, s.b1, s.c1, s.b2, s.c2, s.x); + +} +func int boba(int y, int j) +{ +// hello(); + //$e = type(Teob); + //Teob xbd = type(Teob); + //Teob xb = { 1, 1.0, 100, 1000 }; + //Test2 tee = { { 3 }, 4 }; + //Test3 xc = { eo = 1, t.a = 1 }; + // throw Error.NO_SUCH_FILE; + + for (int i = 0; i < 10; i++) + { + + } + for (int i = 0, int foo = 0; i < 10; i++) + { + + } + + for (int i = 0, j = 1; i < 10; i++, j++) + { + + } + + Test2 bar; + bar.b = 1; + //int w = y ? y : j; + int x = y * 2; + int z = j; + while (j > 10) + { + z--; + x = x + z * 2; + } + return x; +} +func int test(int x = 100) +{ + x = x + 1; + x = x +% 1; + x += 1; + x +%= 1; + Test3 foekf; + Test oef; + Test2 foek; + int i = x; + Bob foo = x; + Bob fe = 0; + fe++; + switch (fe + 1) + { + case 1: + i = i + 1; + next; + case 3: + case 2: + i = i + 2; + case 5: + default: + i = i * 100; + case 7: + } + i++; + int y = i--; + return y; +} + +func int* elvis(int *x, int *y) +{ + return x ?: y; +} + +func int test3() +{ + if (test() < 0) return -1; + return 5; +} + +typedef func void() as FEok; + +typedef func void(int) as Foo; +//typedef int as Foo; +extern func void printf(char *hello, ...); + +macro hello() +{ + printf("Hello world!\n"); +} + +func void bob() +{ + + byte a = 2; + short b = 3; + int c = 4; + bool eok = true; + long deee = (eok ? a : b) + c; +} + +func int if_test(int x) +{ + switch (x) + { + case 1: + x += 1; + if (x < 10) + { + defer x += 5; + if (x < 7) + { + defer x += 100; + next; + } + x += 99; + } + next; + default: + x += 2; + break; + } + return 1; +} +func int yyyy(int x) +{ + defer printf("A"); + if (x > 0) return 2; + defer printf("B"); + printf("C"); + return 1; +} + +func void zzzz() +{ + int x = 0; + defer + { + x += 1; + printf("A"); + } + defer + { + x += 2; + printf("B"); + } + printf("C"); + x += 3; +} + +func int jumpback(int x) +{ + { + defer x += 1; + { + defer x += 2; + LABELX: + x += 3; + } + } + + if (x < 100) goto LABELX; + return x + 1; +} + +func void test_expr_block(int x) +{ + int a = ({ + if (x > 0) return x * 2; + if (x == 0) return 100; + return -x; + }); + //printf("The result was %d\n", a); +} + +func int expr_block() +{ + int fok = ({ return ({ return 10; }); }); + int y = 2; + int x = ({ + if (y < 10) return 10; + return 2; + }); + + /* ({ + return 3; + });*/ + return x; + +} +func int xxxx(int x) +{ + + + { + x += 10; + defer printf("XXX"); + if (x < 100) goto LABELD; + defer printf("EODfe"); + } + { + defer printf("Defer says hello!\n"); + LABELD: + x--; + } + if (x > 0) goto LABELD; + return 1; +} + +func int testPointers(int x, int j = 0, double foo = 3.2) +{ + 1 ? 1 : 2; + int y = 0; + int* z = &y; + int d = *(z + y); + isize e = z - &y; + int* eff = &y + 1; + short x1 = 2; + float f = x1 +% x1 + 1.0; + float f2 = x1 -% x1 + 1.0; + usize ef = z - &y > 0 ? 1 : z - &y; + z - &y > 0 ? 1 : z - &y; + return 1; +} + +func void testDefault(int x = 2, int y = 100, int z = -100) +{ + printf("x = %d, y = %d, z = %d\n", x, y, z); +} + +func int testReturnDefer() +{ + int i = 0; + i++; + defer ++i; + return i; +} + +struct WithArray +{ + int[4] x; +} +/* +error Err +{ + TEST_ERR1 +} +*/ +/* +func int testThrow(int x) throws Err +{ + if (x < 0) throw Err.TEST_ERR1; + return x * x; +} +*/ +func void testErrors() +{ + //int x = try testThrow(20) else 0; + int x = 0; + printf("Value was %d, expected 400.\n", x); +} + +func void testArray() +{ + int[4] zebra = { [2] = 1 }; + int[4] deok = { 1, 2, 3, 4 }; + WithArray boo; + boo.x[0] = 2; + printf("boo.x[0] = %d\n", boo.x[0]); + printf("boo.x[2] = %d\n", boo.x[2]); + int[4] x; + x[1] = 1; + x[0] = 3; + int y = x[1]; + int z = 1; + int* b = &z; + printf("b: %d\n", *b); + *b = 3; + printf("b: %d\n", *b); + printf("z: %d\n", z); + + for (int i = 0; i < 4; i++) + { + printf("x[%d] = %d\n", i, x[i]); + } +} +func void testDefer() +{ + printf("1 == %d\n", testReturnDefer()); + printf("1"); + defer printf("8\n"); + printf("2"); + { + printf("3"); + defer printf("5"); + printf("4"); + } + int i = 0; + goto JUMP; + defer printf("ERROR"); + int r = 0; +JUMP: + defer printf("-JUMPDEFER-"); + if (i++ < 2) goto JUMP; + switch (int boo = 2) + { + case 0: + printf("\n0\n"); + case 2: + defer printf("*CaseDefer*"); + printf("-Case2-"); + default: + break; + } + defer printf("7"); + printf("6"); +} + + +func int main(int x) +{ + printf("Helo!\n"); + testErrors(); + testDefault(y = 99); + testPointers(2, 3); + testDefer(); + testArray(); + testAnonStruct(); + testSimpleStruct(0); + int efd = 9; + uint fefoek = 1; + printf("Helo: %d\n", efd + cast(fefoek, int)); + //long fefoek = -fefoek; + int okfe = 1; + return 1; + switch (int bobe = okfe > 0 ? 1 : 0) + { + case 0: + defer printf("case0-\n"); + case 1: + printf("case 1\n"); + defer printf("case1-\n"); + if (efd < 10) + { + { + defer printf("ef < 10\n"); + if (efd < 7) + { + defer printf("ef < 7\n"); + next; + } + } + } + next; + case 1000 >> 2: + printf("case 1000 >> 2\n"); + case (1 << 200) >> 197: + printf("case 1 << 3\n"); + default: + printf("default\n"); + } + int aa = x++; + int bb = x--; + int cc = ++x; + for (int ok = 0; ok < 10; ok++) + { + printf("ok"); + } + printf("\n"); + for (int ok = 0, int ko = 0, ok = 2; ok + ko < 10; ok++, ko++) + { + printf(":okko"); + } + printf("\n"); + while (int ok = 0; int j = ok++, ok < 10) + { + printf("foo"); + } + printf("\n"); + x = 3; + if (int odk = x, x > 0) + { + printf("helo\n"); + } + Test baok = { 1 }; + Test2 efe; + efe.t.a = 3; + if (efe.t.a > 2) printf("Works!\n"); + int ef = 3; + int *eff = &ef; + eff[0] = 4; + byte *ex = cast(eff, byte*); + ex[0] = 5; + if (eff[0] == 5) printf("Works-5!\n"); + ex[1] = 5; + if (eff[0] == 5 + 5 * 256) printf("Works-5*256!\n"); + if (ef == 4) printf("Works5!\n"); + if (ef == 4) printf("Works1!\n"); + ef = 0; + + byte a = 2; + short b = 3; + int c = 4; + bool eok = true; + long deee = (eok ? a : b) + (eok ? b : c); +int i = 0; + + JUMP: + i = i + 1; + //@hello(); + printf("Hello worldABC" "D" "E\u2701\n"); + float f = 3.0; + float* pf = &f; + switch (i) + { + case 0: + printf("c0\n"); + case 1: + printf("c1\n"); + case 2: + printf("c2\n"); + case 3: + printf("c3\n"); + default: + printf("default\n"); + } + if (*pf > i) goto JUMP; + goto EX; + YEF: + return 4; + EX: + printf("EX\n"); + goto YEF; + return 1; +} + +func void test2(int* x, int y, int z) +{ + *(&(&x)[0]); + float cheat = cast(x, int); + + x++; + z = 0; + z ? y : z; + x += 1; + y += z; + x -= 1; + y -= z; + y *= 2; + y /= 2; + y /= *x; + y |= 2; + y ^= 2; + y &= 2; + z ^ y; + z | y; + int g = z & y; + g <<= 2; + g <<= z; + g >>= 2; + g >>= z; + int fz = 100; + y && z; + y || z; + y >> z; + z << y; + ~z; + !z; + -z; + + int i = 3; + uint ui = 2; + int j = 129; + float f = 23.2; + f = f + 1.0; + f = f - 1.0; + f = f * 2.0; + f = f / 3.0; + i = i * 2; + ui = ui * 2; + i = i / 2; + ui = ui / 2; + i = i + 1; + ui = ui + 1; + i = i - 1; + ui = ui - 1; + x + 1; + int j1 = x[0]; + j1 = *x; +} + diff --git a/src/compiler/ast.c b/src/compiler/ast.c index 02d1d38d1..151a4fc7a 100644 --- a/src/compiler/ast.c +++ b/src/compiler/ast.c @@ -180,15 +180,6 @@ Type* type_get_unsigned(Type *type) */ -bool func_return_value_as_out(FunctionSignature *func_sig) -{ - Type *return_type = func_sig->rtype->type->canonical; - if (return_type->type_kind == TYPE_VOID) return false; - if (func_has_error_return(func_sig)) return true; - // TODO improve - return type_size(return_type) > 8 * 4; -} - BinaryOp binary_op[TOKEN_LAST + 1] = { diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index d345b9502..46f494ab8 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -294,13 +294,20 @@ typedef struct TypeInfo *type_info; } EnumDecl; - +typedef enum +{ + ERROR_RETURN_NONE = 0, + ERROR_RETURN_PARAM = 1, + ERROR_RETURN_RETURN = 2, +} ErrorReturn; typedef struct _FunctionSignature { CallConvention convention : 4; bool variadic : 1; bool has_default : 1; bool throw_any : 1; + bool return_param : 1; + ErrorReturn error_return : 3; TypeInfo *rtype; Decl** params; Decl** throws; @@ -941,7 +948,7 @@ extern Type *type_byte, *type_ushort, *type_uint, *type_ulong, *type_usize; extern Type *type_compint, *type_compfloat; extern Type *type_c_short, *type_c_int, *type_c_long, *type_c_longlong; extern Type *type_c_ushort, *type_c_uint, *type_c_ulong, *type_c_ulonglong; -extern Type *type_typeid, *type_error; +extern Type *type_typeid, *type_error, *type_error_union; extern const char *main_name; @@ -1095,11 +1102,6 @@ void fprint_decl(FILE *file, Decl *dec); void fprint_type_info_recursive(FILE *file, TypeInfo *type_info, int indent); void fprint_expr_recursive(FILE *file, Expr *expr, int indent); -bool func_return_value_as_out(FunctionSignature *func_sig); -static inline bool func_has_error_return(FunctionSignature *func_sig) -{ - return func_sig->throws || func_sig->throw_any; -} #pragma mark --- Lexer functions @@ -1190,7 +1192,7 @@ bool type_is_subtype(Type *type, Type *possible_subtype); Type *type_find_common_ancestor(Type *left, Type *right); const char *type_to_error_string(Type *type); size_t type_size(Type *canonical); -size_t type_abi_alignment(Type *canonical); +unsigned int type_abi_alignment(Type *canonical); void type_append_signature_name(Type *type, char *dst, size_t *offset); Type *type_find_max_type(Type *type, Type *other); @@ -1336,8 +1338,6 @@ static inline const char* struct_union_name_from_token(TokenType type) return type == TOKEN_STRUCT ? "struct" : "union"; } -#define llvm_type(type) gencontext_get_llvm_type(context, type) -#define DEBUG_TYPE(type) gencontext_get_debug_type(context, type) void advance(Context *context); diff --git a/src/compiler/enums.h b/src/compiler/enums.h index 1ca919048..18ed3794f 100644 --- a/src/compiler/enums.h +++ b/src/compiler/enums.h @@ -568,4 +568,25 @@ typedef enum typedef enum { CALL_CONVENTION_NORMAL = 0, -} CallConvention; \ No newline at end of file +} CallConvention; + +typedef enum +{ + CALL_X86_STD, + CALL_X86_FAST, + CALL_X86_THIS, + CALL_X86_VECTOR, + CALL_X86_PASCAL, + CALL_WIN64, + CALL_X64_SYSV, + CALL_X86_REG, + CALL_AAPCS, + CALL_AAPCS_VFP, + CALL_INTEL_OCL_BICC, + CALL_SPIR_FUNCTION, + CALL_OPENCL_KERNEL, + CALL_PRESERVE_MOST, + CALL_PRESERVE_ALL, + CALL_AARCH64_VECTOR, +} CallABI; + diff --git a/src/compiler/llvm_codegen.c b/src/compiler/llvm_codegen.c index 189968791..f51171415 100644 --- a/src/compiler/llvm_codegen.c +++ b/src/compiler/llvm_codegen.c @@ -5,11 +5,35 @@ #include "llvm_codegen_internal.h" +static void diagnostics_handler(LLVMDiagnosticInfoRef ref, void *context) +{ + char *message = LLVMGetDiagInfoDescription(ref); + LLVMDiagnosticSeverity severity = LLVMGetDiagInfoSeverity(ref); + const char *severerity_name = "unknown"; + switch (severity) + { + case LLVMDSError: + error_exit("LLVM error generating code for %s: %s", ((GenContext *)context)->ast_context->module->name, message); + break; + case LLVMDSWarning: + severerity_name = "warning"; + break; + case LLVMDSRemark: + severerity_name = "remark"; + break; + case LLVMDSNote: + severerity_name = "note"; + break; + } + DEBUG_LOG("LLVM message [%s]: %s ", severerity_name, message); + LLVMDisposeMessage(message); +} static void gencontext_init(GenContext *context, Context *ast_context) { memset(context, 0, sizeof(GenContext)); context->context = LLVMContextCreate(); + LLVMContextSetDiagnosticHandler(context->context, &diagnostics_handler, context); context->ast_context = ast_context; } @@ -18,6 +42,23 @@ static void gencontext_destroy(GenContext *context) LLVMContextDispose(context->context); } +LLVMValueRef gencontext_emit_memclear_size_align(GenContext *context, LLVMValueRef ref, uint64_t size, unsigned int align, bool bitcast) +{ + LLVMValueRef target = bitcast ? LLVMBuildBitCast(context->builder, ref, llvm_type(type_get_ptr(type_byte)), "") : ref; + return LLVMBuildMemSet(context->builder, target, LLVMConstInt(llvm_type(type_byte), 0, false), + LLVMConstInt(llvm_type(type_ulong), size, false), align); + +} + +LLVMValueRef gencontext_emit_memclear(GenContext *context, LLVMValueRef ref, Type *type) +{ + Type *canonical = type->canonical; + // TODO avoid bitcast on those that do not need them. + return gencontext_emit_memclear_size_align(context, ref, type_size(canonical), + type_abi_alignment(canonical), true); +} + + static LLVMValueRef gencontext_emit_null_constant(GenContext *context, Type *type) { @@ -162,6 +203,11 @@ static inline unsigned lookup_intrinsic(const char *name) return LLVMLookupIntrinsicID(name, strlen(name)); } +static inline unsigned lookup_attribute(const char *name) +{ + return LLVMGetEnumAttributeKindForName(name, strlen(name)); +} + static bool intrinsics_setup = false; unsigned ssub_overflow_intrinsic_id; unsigned usub_overflow_intrinsic_id; @@ -171,6 +217,15 @@ unsigned smul_overflow_intrinsic_id; unsigned umul_overflow_intrinsic_id; unsigned trap_intrinsic_id; +unsigned noinline_attribute; +unsigned alwaysinline_attribute; +unsigned inlinehint_attribute; +unsigned noreturn_attribute; +unsigned nounwind_attribute; +unsigned writeonly_attribute; +unsigned readonly_attribute; +unsigned optnone_attribute; + void llvm_codegen_setup() { assert(intrinsics_setup == false); @@ -182,6 +237,15 @@ void llvm_codegen_setup() umul_overflow_intrinsic_id = lookup_intrinsic("llvm.umul.with.overflow"); trap_intrinsic_id = lookup_intrinsic("llvm.trap"); + noinline_attribute = lookup_attribute("noinline"); + alwaysinline_attribute = lookup_attribute("alwaysinline"); + inlinehint_attribute = lookup_attribute("inlinehint"); + noreturn_attribute = lookup_attribute("noreturn"); + nounwind_attribute = lookup_attribute("nounwind"); + writeonly_attribute = lookup_attribute("writeonly"); + readonly_attribute = lookup_attribute("readonly"); + optnone_attribute = lookup_attribute("optnone"); + intrinsics_setup = true; } @@ -251,3 +315,9 @@ void llvm_codegen(Context *context) gencontext_end_module(&gen_context); gencontext_destroy(&gen_context); } + +void gencontext_add_attribute(GenContext context, unsigned attribute_id, LLVMValueRef value_to_add_attribute_to) +{ + LLVMAttributeRef llvm_attr = LLVMCreateEnumAttribute(context.context, attribute_id, 0); + LLVMAddAttributeAtIndex(value_to_add_attribute_to, -1, llvm_attr); +} \ No newline at end of file diff --git a/src/compiler/llvm_codegen_expr.c b/src/compiler/llvm_codegen_expr.c index f14542b50..b39cbf670 100644 --- a/src/compiler/llvm_codegen_expr.c +++ b/src/compiler/llvm_codegen_expr.c @@ -308,18 +308,19 @@ static inline LLVMValueRef gencontext_emit_cast_expr(GenContext *context, Expr * * Improve: Direct assign in the case where this is assigning to a variable. * Improve: Create constant initializer for the constant case and do a memcopy */ -static inline LLVMValueRef gencontext_emit_initializer_list_expr(GenContext *context, Expr *expr) +static inline LLVMValueRef gencontext_emit_initializer_list_expr(GenContext *context, Expr *expr, LLVMValueRef optional_ref) { LLVMTypeRef type = llvm_type(expr->type); - LLVMValueRef ref = gencontext_emit_alloca(context, type, "literal"); + LLVMValueRef ref = optional_ref ?: gencontext_emit_alloca(context, type, "literal"); + + Type *canonical = expr->type->canonical; + if (expr->expr_initializer.init_type != INITIALIZER_NORMAL) + { + gencontext_emit_memclear(context, ref, canonical); + } if (expr->expr_initializer.init_type == INITIALIZER_ZERO) { - LLVMBuildMemSet(context->builder, - ref, - LLVMConstInt(llvm_type(type_byte), 0, false), - LLVMConstInt(llvm_type(type_ulong), expr->type->decl->strukt.size, false), - expr->type->decl->strukt.abi_alignment); return LLVMBuildLoad2(context->builder, type, ref, ""); } @@ -346,12 +347,6 @@ static inline LLVMValueRef gencontext_emit_initializer_list_expr(GenContext *con return LLVMBuildLoad2(context->builder, type, ref, ""); } - - // Clear the temp. - LLVMBuildMemSet(context->builder, ref, LLVMConstInt(llvm_type(type_byte), 0, false), - LLVMConstInt(llvm_type(type_ulong), expr->type->decl->strukt.size, false), expr->type->decl->strukt.abi_alignment); - - VECEACH(elements, i) { Expr *element = elements[i]; @@ -814,6 +809,28 @@ LLVMValueRef gencontext_emit_post_unary_expr(GenContext *context, Expr *expr) return gencontext_emit_post_inc_dec(context, expr->post_expr.expr, expr->post_expr.operator == POSTUNARYOP_INC ? 1 : -1, false); } +LLVMValueRef gencontext_emit_try_expr(GenContext *context, Expr *expr) +{ + if (expr->try_expr.else_expr) + { + LLVMBasicBlockRef catch_block = gencontext_create_free_block(context, "catchblock"); + LLVMBasicBlockRef after_catch = gencontext_create_free_block(context, "aftercatch"); + LLVMValueRef res = gencontext_emit_alloca(context, llvm_type(expr->try_expr.else_expr->type), "catch"); + gencontext_push_catch(context, NULL, catch_block); + LLVMValueRef normal_res = gencontext_emit_expr(context, expr->try_expr.expr); + gencontext_pop_catch(context); + LLVMBuildStore(context->builder, normal_res, res); + gencontext_emit_br(context, after_catch); + gencontext_emit_block(context, catch_block); + LLVMValueRef catch_value = gencontext_emit_expr(context, expr->try_expr.else_expr); + LLVMBuildStore(context->builder, catch_value, res); + gencontext_emit_br(context, after_catch); + gencontext_emit_block(context, after_catch); + return LLVMBuildLoad2(context->builder, llvm_type(expr->try_expr.else_expr->type), res, ""); + } + TODO +} + static LLVMValueRef gencontext_emit_binary_expr(GenContext *context, Expr *expr) { BinaryOp binary_op = expr->binary_expr.operator; @@ -829,9 +846,7 @@ static LLVMValueRef gencontext_emit_binary_expr(GenContext *context, Expr *expr) if (binary_op == BINARYOP_ASSIGN) { LLVMValueRef addr = gencontext_emit_address(context, expr->binary_expr.left); - LLVMValueRef value = gencontext_emit_expr(context, expr->binary_expr.right); - LLVMBuildStore(context->builder, value, addr); - return value; + return gencontext_emit_assign_expr(context, addr, expr->binary_expr.right); } return gencontext_emit_binary(context, expr, NULL, binary_op); } @@ -939,22 +954,79 @@ LLVMValueRef gencontext_emit_const_expr(GenContext *context, Expr *expr) } } +static inline void gencontext_emit_throw_branch(GenContext *context, LLVMValueRef value) +{ + LLVMBasicBlockRef after_block = gencontext_create_free_block(context, ""); + size_t catch_index = context->catch_stack_index; + while (catch_index > 0) + { + catch_index--; + // TODO check error + Catch *current_catch = &context->catch_stack[catch_index]; + if (!current_catch->decl) + { + gencontext_emit_cond_br(context, value, current_catch->catch_block, after_block); + gencontext_emit_block(context, after_block); + return; + } + } + TODO +} LLVMValueRef gencontext_emit_call_expr(GenContext *context, Expr *expr) { size_t args = vec_size(expr->call_expr.arguments); + Decl *function_decl = expr->call_expr.function->identifier_expr.decl; + FunctionSignature *signature = &function_decl->func.function_signature; + LLVMValueRef return_param = NULL; + LLVMValueRef error_param = NULL; + if (signature->return_param) + { + return_param = gencontext_emit_alloca(context, llvm_type(signature->rtype->type), "returnparam"); + args++; + } + if (signature->error_return == ERROR_RETURN_PARAM) + { + error_param = gencontext_emit_alloca(context, llvm_type(type_error_union), "errorparam"); + args++; + } LLVMValueRef *values = args ? malloc_arena(args * sizeof(LLVMValueRef)) : NULL; + unsigned param_index = 0; + if (return_param) + { + values[param_index++] = return_param; + } + if (error_param) + { + values[param_index++] = error_param; + } VECEACH(expr->call_expr.arguments, i) { - values[i] = gencontext_emit_expr(context, expr->call_expr.arguments[i]); + values[param_index++] = gencontext_emit_expr(context, expr->call_expr.arguments[i]); } - Decl *function = expr->call_expr.function->identifier_expr.decl; LLVMValueRef func = function->func.backend_value; LLVMTypeRef func_type = llvm_type(function->type); - // TODO fix throws and return optimization LLVMValueRef call = LLVMBuildCall2(context->builder, func_type, func, values, args, "call"); + if (signature->error_return) + { + if (error_param) + { + LLVMValueRef maybe_error = LLVMBuildLoad2(context->builder, llvm_type(type_error_union), error_param, ""); + TODO // Incorrect, must get subset if this is 128 bits + gencontext_emit_throw_branch(context, maybe_error); + } + else + { + gencontext_emit_throw_branch(context, call); + } + } + // If we used a return param, then load that info here. + if (return_param) + { + call = LLVMBuildLoad2(context->builder, llvm_type(signature->rtype->type), return_param, ""); + } /* if (function->func.function_signature.convention) { @@ -1016,6 +1088,20 @@ LLVMValueRef gencontext_emit_call_intrinsic(GenContext *context, unsigned intrin return LLVMBuildCall2(context->builder, type, decl, values, arg_count, ""); } +LLVMValueRef gencontext_emit_assign_expr(GenContext *context, LLVMValueRef ref, Expr *expr) +{ + switch (expr->expr_kind) + { + case EXPR_INITIALIZER_LIST: + return gencontext_emit_initializer_list_expr(context, expr, ref); + default: + break; + } + LLVMValueRef value = gencontext_emit_expr(context, expr); + LLVMBuildStore(context->builder, value, ref); + return value; +} + LLVMValueRef gencontext_emit_expr(GenContext *context, Expr *expr) { @@ -1042,10 +1128,11 @@ LLVMValueRef gencontext_emit_expr(GenContext *context, Expr *expr) return gencontext_emit_ternary_expr(context, expr); case EXPR_POST_UNARY: return gencontext_emit_post_unary_expr(context, expr); + case EXPR_TRY: + return gencontext_emit_try_expr(context, expr); case EXPR_TYPE: case EXPR_SIZEOF: case EXPR_TYPE_ACCESS: - case EXPR_TRY: case EXPR_MACRO_EXPR: // These are folded in the semantic analysis step. UNREACHABLE @@ -1058,7 +1145,7 @@ LLVMValueRef gencontext_emit_expr(GenContext *context, Expr *expr) case EXPR_GROUP: return gencontext_emit_expr(context, expr->group_expr); case EXPR_INITIALIZER_LIST: - return gencontext_emit_initializer_list_expr(context, expr); + return gencontext_emit_initializer_list_expr(context, expr, NULL); case EXPR_EXPRESSION_LIST: return gencontext_emit_expression_list_expr(context, expr); case EXPR_CAST: diff --git a/src/compiler/llvm_codegen_function.c b/src/compiler/llvm_codegen_function.c index 6eefbfe1f..cb8d03aab 100644 --- a/src/compiler/llvm_codegen_function.c +++ b/src/compiler/llvm_codegen_function.c @@ -84,7 +84,7 @@ static inline void gencontext_emit_parameter(GenContext *context, Decl *decl, un void gencontext_emit_implicit_return(GenContext *context) { - if (func_has_error_return(&context->cur_func_decl->func.function_signature)) + if (context->cur_func_decl->func.function_signature.error_return == ERROR_RETURN_RETURN) { LLVMBuildRet(context->builder, LLVMConstInt(llvm_type(type_ulong), 0, false)); } @@ -115,22 +115,27 @@ void gencontext_emit_function_body(GenContext *context, Decl *decl) LLVMValueRef alloca_point = LLVMBuildAlloca(context->builder, LLVMInt32TypeInContext(context->context), "alloca_point"); context->alloca_point = alloca_point; - unsigned return_parameter = func_return_value_as_out(&decl->func.function_signature) ? 1 : 0; - - if (return_parameter) + FunctionSignature *signature = &decl->func.function_signature; + int arg = 0; + if (signature->return_param) { - context->return_out = gencontext_emit_alloca(context, llvm_type(decl->func.function_signature.rtype->type), "retval"); - LLVMBuildStore(context->builder, LLVMGetParam(context->function, 0), context->return_out); + context->return_out = LLVMGetParam(context->function, arg++); } else { context->return_out = NULL; } + if (signature->error_return == ERROR_RETURN_PARAM) + { + context->error_out = gencontext_emit_alloca(context, llvm_type(type_error_union), "errorval"); + LLVMBuildStore(context->builder, LLVMGetParam(context->function, arg++), context->error_out); + } + // Generate LLVMValueRef's for all parameters, so we can use them as local vars in code VECEACH(decl->func.function_signature.params, i) { - gencontext_emit_parameter(context, decl->func.function_signature.params[i], i + return_parameter); + gencontext_emit_parameter(context, decl->func.function_signature.params[i], arg++); } VECEACH(decl->func.labels, i) @@ -179,6 +184,7 @@ void gencontext_emit_function_decl(GenContext *context, Decl *decl) decl->func.backend_value = LLVMAddFunction(context->module, decl->external_name, llvm_type(decl->type)); + // Specify appropriate storage class, visibility and call convention // extern functions (linkedited in separately): /* diff --git a/src/compiler/llvm_codegen_internal.h b/src/compiler/llvm_codegen_internal.h index 148fe2ce5..5a3171ae3 100644 --- a/src/compiler/llvm_codegen_internal.h +++ b/src/compiler/llvm_codegen_internal.h @@ -27,6 +27,12 @@ typedef struct LLVMBasicBlockRef next_block; } BreakContinue; +typedef struct +{ + Decl *decl; + LLVMBasicBlockRef catch_block; +} Catch; + typedef struct { LLVMDIBuilderRef builder; @@ -39,6 +45,7 @@ typedef struct } DebugContext; #define BREAK_STACK_MAX 256 +#define CATCH_STACK_MAX 256 typedef struct { @@ -60,9 +67,12 @@ typedef struct Ast **defer_stack; DebugContext debug; Context *ast_context; + Catch catch_stack[CATCH_STACK_MAX]; + size_t catch_stack_index; BreakContinue break_continue_stack[BREAK_STACK_MAX]; size_t break_continue_stack_index; LLVMValueRef return_out; + LLVMValueRef error_out; LLVMBasicBlockRef expr_block_exit; bool current_block_is_target : 1; bool did_call_stack_save : 1; @@ -76,21 +86,46 @@ extern unsigned smul_overflow_intrinsic_id; extern unsigned umul_overflow_intrinsic_id; extern unsigned trap_intrinsic_id; +// No function inlining +extern unsigned noinline_attribute; +// Force inlining +extern unsigned alwaysinline_attribute; +// "Inline possibly" +extern unsigned inlinehint_attribute; +// No function return +extern unsigned noreturn_attribute; +// No exceptions +extern unsigned nounwind_attribute; +// Argument (no writes through the pointer) or function (no writes) +extern unsigned writeonly_attribute; +// Argument (no reads through the pointer) or function (no reads) +extern unsigned readonly_attribute; +// Disable optimization. +extern unsigned optnone_attribute; + + void gencontext_begin_module(GenContext *context); void gencontext_end_module(GenContext *context); + + +void gencontext_add_attribute(GenContext context, unsigned attribute_id, LLVMValueRef value_to_add_attribute_to); void gencontext_emit_stmt(GenContext *context, Ast *ast); +void gencontext_push_catch(GenContext *context, Decl *error_type, LLVMBasicBlockRef catch_block); +void gencontext_pop_catch(GenContext *context); LLVMValueRef gencontext_emit_call_intrinsic(GenContext *context, unsigned intrinsic_id, LLVMTypeRef *types, LLVMValueRef *values, unsigned arg_count); void gencontext_emit_panic_on_true(GenContext *context, LLVMValueRef value, const char *panic_name); void gencontext_emit_defer(GenContext *context, Ast *defer_start, Ast *defer_end); LLVMValueRef gencontext_emit_expr(GenContext *context, Expr *expr); -LLVMValueRef gencontext_emit_ast_expr(GenContext *context, Ast *expr); +LLVMValueRef gencontext_emit_assign_expr(GenContext *context, LLVMValueRef ref, Expr *expr); LLVMMetadataRef gencontext_get_debug_type(GenContext *context, Type *type); void gencontext_emit_debug_location(GenContext *context, SourceRange location); LLVMMetadataRef gencontext_create_builtin_debug_type(GenContext *context, Type *builtin_type); LLVMValueRef gencontext_emit_alloca(GenContext *context, LLVMTypeRef type, const char *name); void gencontext_emit_compound_stmt(GenContext *context, Ast *ast); void gencontext_emit_block(GenContext *context, LLVMBasicBlockRef next_block); +LLVMValueRef gencontext_emit_memclear_size_align(GenContext *context, LLVMValueRef ref, uint64_t size, unsigned align, bool bitcast); +LLVMValueRef gencontext_emit_memclear(GenContext *context, LLVMValueRef ref, Type *type); void gencontext_emit_br(GenContext *context, LLVMBasicBlockRef next_block); bool gencontext_check_block_branch_emit(GenContext *context); void gencontext_emit_cond_br(GenContext *context, LLVMValueRef value, LLVMBasicBlockRef thenBlock, LLVMBasicBlockRef elseBlock); @@ -98,20 +133,86 @@ static inline LLVMBasicBlockRef gencontext_create_free_block(GenContext *context { return LLVMCreateBasicBlockInContext(context->context, name); } + void gencontext_emit_function_body(GenContext *context, Decl *decl); void gencontext_emit_implicit_return(GenContext *context); void gencontext_emit_function_decl(GenContext *context, Decl *decl); void gencontext_emit_extern_decl(GenContext *context, Decl *decl); LLVMValueRef gencontext_emit_address(GenContext *context, Expr *expr); -#define LLVMTYPE(type) type->backend_type static inline LLVMValueRef gencontext_load_expr(GenContext *context, LLVMValueRef value) { return LLVMBuildLoad(context->builder, value, ""); } LLVMTypeRef gencontext_get_llvm_type(GenContext *context, Type *type); LLVMValueRef gencontext_emit_cast(GenContext *context, CastKind cast_kind, LLVMValueRef value, Type *type, Type *target_type); +static inline bool gencontext_func_pass_return_by_param(GenContext *context, Type *first_param_type) { return false; }; +static inline bool gencontext_func_pass_param_by_reference(GenContext *context, Type *param_type) { return false; } + static inline bool gencontext_use_debug(GenContext *context) { return context && context->debug.builder != NULL; } +static inline bool call_supports_variadic(CallABI abi) +{ + switch (abi) + { + case CALL_X86_STD: + case CALL_X86_REG: + case CALL_X86_THIS: + case CALL_X86_FAST: + case CALL_X86_PASCAL: + case CALL_X86_VECTOR: + case CALL_SPIR_FUNCTION: + case CALL_OPENCL_KERNEL: + return false; + default: + return true; + + } +} + +static inline LLVMCallConv llvm_call_convention_from_call(CallABI abi) +{ + switch (abi) + { + case CALL_X86_STD: + return LLVMX86StdcallCallConv; + case CALL_X86_FAST: + return LLVMX86FastcallCallConv; + case CALL_X86_PASCAL: + return LLVMCCallConv; + case CALL_X86_REG: + return LLVMX86RegCallCallConv; + case CALL_X86_THIS: + return LLVMX86ThisCallCallConv; + case CALL_X86_VECTOR: + return LLVMX86VectorCallCallConv; + case CALL_WIN64: + return LLVMWin64CallConv; + case CALL_X64_SYSV: + return LLVMX8664SysVCallConv; + case CALL_AAPCS: + return LLVMARMAAPCSCallConv; + case CALL_AAPCS_VFP: + return LLVMARMAAPCSVFPCallConv; + case CALL_INTEL_OCL_BICC: + return LLVMIntelOCLBICallConv; + case CALL_AARCH64_VECTOR: + TODO + case CALL_SPIR_FUNCTION: + return LLVMSPIRFUNCCallConv; + case CALL_OPENCL_KERNEL: + TODO // Target dependent. + case CALL_PRESERVE_ALL: + return LLVMPreserveAllCallConv; + case CALL_PRESERVE_MOST: + return LLVMPreserveMostCallConv; + default: + return LLVMCCallConv; + } + +} + +#define llvm_type(type) gencontext_get_llvm_type(context, type) +#define DEBUG_TYPE(type) gencontext_get_debug_type(context, type) diff --git a/src/compiler/llvm_codegen_stmt.c b/src/compiler/llvm_codegen_stmt.c index 9f41a100e..c08b152f2 100644 --- a/src/compiler/llvm_codegen_stmt.c +++ b/src/compiler/llvm_codegen_stmt.c @@ -42,18 +42,7 @@ static LLVMValueRef gencontext_emit_decl(GenContext *context, Ast *ast) */ if (decl->var.init_expr) { - Expr *expr = decl->var.init_expr; - // Quick path for empty initializer list - if (expr->expr_kind == EXPR_INITIALIZER_LIST && expr->expr_initializer.init_type == INITIALIZER_ZERO) - { - LLVMBuildMemSet(context->builder, decl->var.backend_ref, LLVMConstInt(llvm_type(type_byte), 0, false), - LLVMConstInt(llvm_type(type_ulong), expr->type->decl->strukt.size, false), - expr->type->decl->strukt.abi_alignment); - return decl->var.backend_ref; - } - - LLVMValueRef value = gencontext_emit_expr(context, decl->var.init_expr); - LLVMBuildStore(context->builder, value, decl->var.backend_ref); + gencontext_emit_assign_expr(context, decl->var.backend_ref, decl->var.init_expr); return decl->var.backend_ref; } return decl->var.backend_ref; @@ -207,10 +196,25 @@ void gencontext_emit_if(GenContext *context, Ast *ast) } +void gencontext_push_catch(GenContext *context, Decl *error_type, LLVMBasicBlockRef catch_block) +{ + size_t index = context->catch_stack_index++; + if (index == CATCH_STACK_MAX - 1) + { + error_exit("Exhausted catch stack - exceeded %d entries.", CATCH_STACK_MAX); + } + context->catch_stack[index].decl = error_type; + context->catch_stack[index].catch_block = catch_block; +} -static void -gencontext_push_break_continue(GenContext *context, LLVMBasicBlockRef break_block, LLVMBasicBlockRef continue_block, - LLVMBasicBlockRef next_block) +void gencontext_pop_catch(GenContext *context) +{ + assert(context->catch_stack_index > 0); + context->catch_stack_index--; +} + +static void gencontext_push_break_continue(GenContext *context, LLVMBasicBlockRef break_block, + LLVMBasicBlockRef continue_block, LLVMBasicBlockRef next_block) { size_t index = context->break_continue_stack_index++; if (index == BREAK_STACK_MAX - 1) @@ -622,7 +626,9 @@ void gencontext_emit_stmt(GenContext *context, Ast *ast) gencontext_emit_scoped_stmt(context, ast); break; case AST_EXPR_STMT: + { gencontext_emit_expr(context, ast->expr_stmt); + } break; case AST_DECLARE_STMT: gencontext_emit_decl(context, ast); diff --git a/src/compiler/llvm_codegen_type.c b/src/compiler/llvm_codegen_type.c index 8208b08c1..3f2aa4bbf 100644 --- a/src/compiler/llvm_codegen_type.c +++ b/src/compiler/llvm_codegen_type.c @@ -123,31 +123,37 @@ LLVMTypeRef llvm_func_type(LLVMContextRef context, Type *type) { LLVMTypeRef *params = NULL; FunctionSignature *signature = type->func.signature; - bool return_parameter = func_return_value_as_out(signature); - bool return_error = func_has_error_return(signature); - unsigned parameters = vec_size(signature->params) + return_parameter; + unsigned parameters = vec_size(signature->params); + if (signature->return_param) parameters++; + if (signature->error_return == ERROR_RETURN_PARAM) parameters++; if (parameters) { params = malloc_arena(sizeof(LLVMTypeRef) * parameters); - if (return_parameter) + unsigned index = 0; + if (signature->return_param) { - params[0] = llvm_get_type(context, signature->rtype->type); + params[index++] = llvm_get_type(context, type_get_ptr(signature->rtype->type)); + } + if (signature->error_return == ERROR_RETURN_PARAM) + { + params[index++] = llvm_get_type(context, type_get_ptr(type_error_union)); } VECEACH(signature->params, i) { - params[i + return_parameter] = llvm_get_type(context, signature->params[i]->type->canonical); + params[index++] = llvm_get_type(context, signature->params[i]->type->canonical); } } LLVMTypeRef ret_type; - if (return_error) + if (signature->error_return == ERROR_RETURN_RETURN) { ret_type = llvm_get_type(context, type_ulong); } else { - ret_type = return_parameter ? llvm_get_type(context, type_void) : llvm_get_type(context, type->func.signature->rtype->type); + ret_type = signature->return_param ? llvm_get_type(context, type_void) : llvm_get_type(context, type->func.signature->rtype->type); } - return LLVMFunctionType( ret_type, params, parameters, signature->variadic); + LLVMTypeRef functype = LLVMFunctionType(ret_type, params, parameters, signature->variadic); + return functype; } diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index 6bbf06594..8dfa8b01a 100644 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -272,6 +272,32 @@ static inline Type *sema_analyse_function_signature(Context *context, FunctionSi type_append_signature_name(err_decl->type, buffer, &buffer_write_offset); } } + + unsigned error_types = vec_size(signature->throws); + if (signature->throw_any || error_types > 1) + { + signature->error_return = ERROR_RETURN_PARAM; + } + else if (error_types == 1) + { + signature->error_return = ERROR_RETURN_RETURN; + } + else + { + signature->error_return = ERROR_RETURN_NONE; + } + + Type *return_type = signature->rtype->type->canonical; + signature->return_param = false; + if (return_type->type_kind != TYPE_VOID) + { + // TODO fix this number with ABI compatibility + if (signature->error_return == ERROR_RETURN_RETURN || type_size(return_type) > 8 * 2) + { + signature->return_param = true; + } + } + if (!all_ok) return NULL; TokenType type = TOKEN_INVALID_TOKEN; signature->mangled_signature = symtab_add(buffer, buffer_write_offset, fnv1a(buffer, buffer_write_offset), &type); diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index a9673c6d8..231a14ba3 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -247,7 +247,11 @@ static inline bool sema_expr_analyse_func_call(Context *context, Type *to, Expr unsigned error_params = signature->throw_any || signature->throws; if (error_params) { - TODO + if (context->try_nesting == 0) + { + SEMA_ERROR(expr, "Function '%s' throws errors, this call must be prefixed 'try'.", decl->name); + return false; + } } unsigned func_param_count = vec_size(func_params); unsigned num_args = vec_size(args); @@ -2185,14 +2189,16 @@ static inline bool sema_expr_analyse_post_unary(Context *context, Type *to, Expr static inline bool sema_expr_analyse_try(Context *context, Type *to, Expr *expr) { - if (!sema_analyse_expr(context, to, expr->try_expr.expr)) return false; + context->try_nesting++; + bool success = sema_analyse_expr(context, to, expr->try_expr.expr); + context->try_nesting--; + if (!success) return false; expr->type = expr->try_expr.expr->type; if (expr->try_expr.else_expr) { if (!sema_analyse_expr(context, to, expr->try_expr.else_expr)) return false; } - // Check errors! - TODO + // TODO Check errors! return true; } diff --git a/src/compiler/sema_stmts.c b/src/compiler/sema_stmts.c index 015eac40c..8a2824da1 100644 --- a/src/compiler/sema_stmts.c +++ b/src/compiler/sema_stmts.c @@ -754,8 +754,9 @@ static bool sema_analyse_throw_stmt(Context *context, Ast *statement) SEMA_ERROR(throw_value, "Only 'error' types can be thrown, this is a '%s'.", type->name); return false; } - if (!context->try_nesting && !func_has_error_return(&context->active_function_for_analysis->func.function_signature)) + if (!context->try_nesting && !context->active_function_for_analysis->func.function_signature.error_return) { + // TODO check error type SEMA_ERROR(statement, "This 'throw' is not handled, please add a 'throws %s' clause to the function signature or use try-catch.", type->name); return false; } diff --git a/src/compiler/target.c b/src/compiler/target.c index db35a19b2..58a7a3582 100644 --- a/src/compiler/target.c +++ b/src/compiler/target.c @@ -102,13 +102,14 @@ void target_setup() build_target.alloca_address_space = 0; - LLVMTypeRef byte_type = LLVMIntType(8); - LLVMTypeRef short_type = LLVMIntType(16); - LLVMTypeRef int_type = LLVMIntType(32); - LLVMTypeRef long_type = LLVMIntType(64); - LLVMTypeRef float_type = LLVMFloatType(); - LLVMTypeRef double_type = LLVMDoubleType(); - LLVMTypeRef quad_type = LLVMFP128Type(); + LLVMContextRef context = LLVMContextCreate(); + LLVMTypeRef byte_type = LLVMIntTypeInContext(context, 8); + LLVMTypeRef short_type = LLVMIntTypeInContext(context, 16); + LLVMTypeRef int_type = LLVMIntTypeInContext(context, 32); + LLVMTypeRef long_type = LLVMIntTypeInContext(context, 64); + LLVMTypeRef float_type = LLVMFloatTypeInContext(context); + LLVMTypeRef double_type = LLVMDoubleTypeInContext(context); + LLVMTypeRef quad_type = LLVMFP128TypeInContext(context); LLVMTypeRef pointer_type = LLVMPointerType(int_type, 0); build_target.align_byte = LLVMABIAlignmentOfType(build_target.llvm_data_layout, byte_type); build_target.align_short = LLVMABIAlignmentOfType(build_target.llvm_data_layout, short_type); diff --git a/src/compiler/types.c b/src/compiler/types.c index 3153da554..50967f679 100644 --- a/src/compiler/types.c +++ b/src/compiler/types.c @@ -11,7 +11,7 @@ static Type t_usz, t_isz; static Type t_cus, t_cui, t_cul, t_cull; static Type t_cs, t_ci, t_cl, t_cll; static Type t_voidstar, t_typeid; -static Type t_err; +static Type t_err, t_error_union; Type *type_bool = &t_u1; Type *type_void = &t_u0; @@ -20,6 +20,7 @@ Type *type_voidptr = &t_voidstar; Type *type_float = &t_f32; Type *type_double = &t_f64; Type *type_error = &t_err; +Type *type_error_union = &t_error_union; Type *type_typeid = &t_typeid; Type *type_char = &t_i8; Type *type_short = &t_i16; @@ -225,7 +226,7 @@ size_t type_size(Type *canonical) UNREACHABLE } -size_t type_abi_alignment(Type *canonical) +unsigned int type_abi_alignment(Type *canonical) { assert(canonical && canonical->canonical == canonical); switch (canonical->type_kind)