From 3a24fbfa6d8a91e922466b96cf8c3d007643792a Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Sun, 24 Jan 2021 00:52:48 +0100 Subject: [PATCH] Moved examples around. Updated (and corrected) const initialization. Removed "in" keyword. Added "member" attribute domain. Many fixes in struct padding and alignment and tests. Fixed extern global. --- resources/examples/base64.c3 | 102 ++-- .../examples/{ => notworking}/binarydigits.c3 | 0 .../examples/{ => notworking}/functions.c3 | 0 resources/examples/notworking/globals.c3 | 36 ++ resources/examples/{ => notworking}/hash.c3 | 1 + resources/examples/{ => notworking}/http.c3 | 0 .../examples/{ => notworking}/levenshtein.c3 | 0 .../examples/{ => notworking}/madlibs.c3 | 0 resources/examples/{ => notworking}/map.c3 | 0 resources/examples/notworking/retry.c3 | 19 + resources/examples/notworking/swap.c3 | 21 + resources/examples/notworking/timeit.c3 | 13 + .../{ => notworking}/toml_parser_c2.c3 | 0 .../{ => notworking}/toml_tokenizer_c2.c3 | 0 resources/examples/{ => notworking}/vector.c3 | 0 resources/examples/notworking/virtuals.c3 | 51 ++ resources/examples/notworking/virtuals2.c3 | 17 + resources/examples/{ => notworking}/window.c3 | 0 resources/lib/std/math.c3 | 9 + resources/testfragments/structo.c3 | 18 + src/compiler/c_abi_internal.h | 2 +- src/compiler/compiler.c | 1 + src/compiler/compiler_internal.h | 40 +- src/compiler/enums.h | 4 +- src/compiler/llvm_codegen.c | 226 ++++++-- src/compiler/llvm_codegen_c_abi.c | 50 -- src/compiler/llvm_codegen_c_abi_x64.c | 2 +- src/compiler/llvm_codegen_expr.c | 353 +++++++------ src/compiler/llvm_codegen_function.c | 14 +- src/compiler/llvm_codegen_internal.h | 6 +- src/compiler/llvm_codegen_stmt.c | 114 ++-- src/compiler/llvm_codegen_type.c | 55 +- src/compiler/parse_expr.c | 4 +- src/compiler/parse_global.c | 3 +- src/compiler/parse_stmt.c | 3 +- src/compiler/parser.c | 2 +- src/compiler/sema_casts.c | 11 +- src/compiler/sema_decls.c | 109 +++- src/compiler/sema_expr.c | 488 ++++++++++++------ src/compiler/tokens.c | 2 - src/compiler/types.c | 4 +- test/test_suite/arrays/array_literal.c3t | 27 + test/test_suite/arrays/array_struct.c3t | 12 + .../test_suite/arrays/complex_array_const.c3t | 20 + .../expressions/fail_index_usize.c3 | 5 + .../test_suite/expressions/pointer_access.c3t | 79 +-- .../expressions/simple_float_sub_neg.c3t | 27 + test/test_suite/expressions/strings.c3t | 10 + test/test_suite/functions/assorted_tests.c3t | 118 +++++ test/test_suite/functions/double_return.c3t | 13 + test/test_suite/globals/external_global.c3t | 34 ++ test/test_suite/pointers/const_pointer.c3t | 14 + test/test_suite/pointers/pointer_index.c3t | 4 +- test/test_suite/statements/if_tests.c3t | 65 +++ test/test_suite/statements/while_switch.c3t | 71 +++ test/test_suite/struct/func_return_struct.c3 | 12 + test/test_suite/struct/simple_struct.c3t | 14 + test/test_suite/struct/struct_as_value.c3t | 15 + .../struct/struct_const_construct_simple.c3t | 33 ++ .../struct/struct_pack_and_align.c3t | 102 ++++ test/test_suite/struct/struct_params.c3 | 27 + test/test_suite/symbols/shadow_struct.c3 | 15 + test/test_suite/symbols/various.c3 | 1 + test/test_suite/union/empty_unions.c3 | 30 ++ test/test_suite/union/union_in_struct.c3t | 29 ++ .../test_suite/union/union_member_voidcast.c3 | 13 + .../visibility/local_global_func.c3t | 31 ++ 67 files changed, 1930 insertions(+), 671 deletions(-) rename resources/examples/{ => notworking}/binarydigits.c3 (100%) rename resources/examples/{ => notworking}/functions.c3 (100%) create mode 100644 resources/examples/notworking/globals.c3 rename resources/examples/{ => notworking}/hash.c3 (99%) rename resources/examples/{ => notworking}/http.c3 (100%) rename resources/examples/{ => notworking}/levenshtein.c3 (100%) rename resources/examples/{ => notworking}/madlibs.c3 (100%) rename resources/examples/{ => notworking}/map.c3 (100%) create mode 100644 resources/examples/notworking/retry.c3 create mode 100644 resources/examples/notworking/swap.c3 create mode 100644 resources/examples/notworking/timeit.c3 rename resources/examples/{ => notworking}/toml_parser_c2.c3 (100%) rename resources/examples/{ => notworking}/toml_tokenizer_c2.c3 (100%) rename resources/examples/{ => notworking}/vector.c3 (100%) create mode 100644 resources/examples/notworking/virtuals.c3 create mode 100644 resources/examples/notworking/virtuals2.c3 rename resources/examples/{ => notworking}/window.c3 (100%) create mode 100644 resources/lib/std/math.c3 create mode 100644 resources/testfragments/structo.c3 create mode 100644 test/test_suite/arrays/array_literal.c3t create mode 100644 test/test_suite/arrays/array_struct.c3t create mode 100644 test/test_suite/arrays/complex_array_const.c3t create mode 100644 test/test_suite/expressions/fail_index_usize.c3 create mode 100644 test/test_suite/expressions/simple_float_sub_neg.c3t create mode 100644 test/test_suite/expressions/strings.c3t create mode 100644 test/test_suite/functions/assorted_tests.c3t create mode 100644 test/test_suite/functions/double_return.c3t create mode 100644 test/test_suite/globals/external_global.c3t create mode 100644 test/test_suite/pointers/const_pointer.c3t create mode 100644 test/test_suite/statements/if_tests.c3t create mode 100644 test/test_suite/statements/while_switch.c3t create mode 100644 test/test_suite/struct/func_return_struct.c3 create mode 100644 test/test_suite/struct/simple_struct.c3t create mode 100644 test/test_suite/struct/struct_as_value.c3t create mode 100644 test/test_suite/struct/struct_const_construct_simple.c3t create mode 100644 test/test_suite/struct/struct_pack_and_align.c3t create mode 100644 test/test_suite/struct/struct_params.c3 create mode 100644 test/test_suite/symbols/shadow_struct.c3 create mode 100644 test/test_suite/union/empty_unions.c3 create mode 100644 test/test_suite/union/union_in_struct.c3t create mode 100644 test/test_suite/union/union_member_voidcast.c3 create mode 100644 test/test_suite/visibility/local_global_func.c3t diff --git a/resources/examples/base64.c3 b/resources/examples/base64.c3 index bf34ff049..5d0f9203f 100644 --- a/resources/examples/base64.c3 +++ b/resources/examples/base64.c3 @@ -1,7 +1,13 @@ module base64; // Based on the C2 version. -const char[] LUT_ENC = { +error InvalidCharacter +{ + int index; + char c; +} + +const char[64] LUT_ENC = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', @@ -14,27 +20,22 @@ const char[] LUT_ENC = { const byte ERR = 0xFF; -const byte[256] LUT_DEC = { - // '+', ',', '-', '.', '/', '0', '1', '2' - 62, ERR, ERR, ERR, 63, 52, 53, 54, - // '3', '4', '5', '6', '7', '8', '9', ':' - 55, 56, 57, 58, 59, 60, 61, ERR, - // ';', '<', '=', '>', '?', '@', 'A', 'B' - ERR, ERR, ERR, ERR, ERR, ERR, 0, 1, - // 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J' - 2, 3, 4, 5, 6, 7, 8, 9, - // 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R' - 10, 11, 12, 13, 14, 15, 16, 17, - // 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' - 18, 19, 20, 21, 22, 23, 24, 25, - // '[', '\', ']', '^', '_', '`', 'a', 'b' - ERR, ERR, ERR, ERR, ERR, ERR, 26, 27, - // 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j' - 28, 29, 30, 31, 32, 33, 34, 35, - // 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r' - 36, 37, 38, 39, 40, 41, 42, 43, - // 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' - 44, 45, 46, 47, 48, 49, 50, 51, +const byte[256] LUT_DEC = +{ + [0..255] = ERR, + ['A'] = 0, ['B'] = 1, ['C'] = 2, ['D'] = 3, ['E'] = 4, + ['F'] = 5, ['G'] = 6, ['H'] = 7, ['I'] = 8, ['J'] = 9, + ['K'] = 10, ['L'] = 11, ['M'] = 12, ['N'] = 13, ['O'] = 14, + ['P'] = 15, ['Q'] = 16, ['R'] = 17, ['S'] = 18, ['T'] = 19, + ['U'] = 20, ['V'] = 21, ['W'] = 22, ['X'] = 23, ['Y'] = 24, + ['Z'] = 25, ['a'] = 26, ['b'] = 27, ['c'] = 28, ['d'] = 29, + ['e'] = 30, ['f'] = 31, ['g'] = 32, ['h'] = 33, ['i'] = 34, + ['j'] = 35, ['k'] = 36, ['l'] = 37, ['m'] = 38, ['n'] = 39, + ['o'] = 40, ['p'] = 41, ['q'] = 42, ['r'] = 43, ['s'] = 44, + ['t'] = 45, ['u'] = 46, ['v'] = 47, ['w'] = 48, ['x'] = 49, + ['y'] = 50, ['z'] = 51, ['0'] = 52, ['1'] = 53, ['2'] = 54, + ['3'] = 55, ['4'] = 56, ['5'] = 57, ['6'] = 58, ['7'] = 59, + ['8'] = 60, ['9'] = 61, ['+'] = 62, ['/'] = 63 }; @@ -42,33 +43,26 @@ const char PAD = '='; const char FIRST = '+'; const char LAST = 'z'; -public error Base64Error -{ - INVALID_CHARACTER -} - -public func void encode(byte[] in, string *out) +public func void encode(byte[] in, char *out) { int j = 0; - - for (int i = 0; i < in.len; i++); + char c = LUT_ENC[1]; + for (int i = 0; i < in.len(); i++) { switch (i % 3) { case 0: out[j++] = LUT_ENC[(in[i] >> 2) & 0x3F]; case 1: - out[j++] = LUT_ENC[(in[i-1] & 0x3) << 4 + ((in[i] >> 4) & 0xF)]; + out[j++] = LUT_ENC[(in[i - 1] & 0x3) << 4 + ((in[i] >> 4) & 0xF)]; case 2: - out[j++] = LUT_ENC[(in[i-1] & 0xF) << 2 + ((in[i] >> 6) & 0x3)]; + out[j++] = LUT_ENC[(in[i - 1] & 0xF) << 2 + ((in[i] >> 6) & 0x3)]; out[j++] = LUT_ENC[in[i] & 0x3F]; } - i++; } // move back - int last = in.len - 1; - + usize last = in.len() - 1; // check the last and add padding switch (last % 3) { @@ -82,46 +76,54 @@ public func void encode(byte[] in, string *out) } } -error InvalidCharacter -{ - int index; - char c; -} -public func int! decode(string in, byte[] out) +public func int! decode(char[] in, byte* out) { int j = 0; - for (int i = 0; i < len; i++) + for (int i = 0; i < in.len(); i++) { char value = in[i]; - if (value == PAD) return j; - if (value < FIRST || in[i] > LAST) return! InvalidCharacter(i, value); - byte c = LUT_DEC[in[i] - FIRST); - if (c == ERR) return! InvalidCharacter(i, value); + byte c = LUT_DEC[in[i]]; + if (c == ERR) return InvalidCharacter({i, value})!; switch (i % 4) { case 0: out[j] = c << 2; case 1: - out[j++] += (c >> 4) & 0x3; + out[j++] += c >> 4 & 0x3; // if not last char with padding - if (i < (len - 3) || in[len - 2] != PAD) + if (i < (in.len() - 3) || in[cast(in.len() as long) - 2] != PAD) { out[j] = (c & 0xF) << 4; } case 2: - out[j++] += (c >> 2) & 0xF; - if (i < (len -2) || in[len -1] != PAD) + out[j++] += c >> 2 & 0xF; + if (i < (in.len() - 2) || in[cast(in.len() as long) - 1] != PAD) { out[j] = (c & 0x3) << 6; } case 3: out[j++] += c; } + } return j; +} + +extern func void printf(char *fmt, ...); +public func void main() +{ + printf("Startin...\n"); + char *helloworld = "Hello World\n"; + char[1000] buffer; + encode(cast(helloworld as byte*)[0..12], &buffer); + printf("Printres\n"); + printf("Result: %s\n", &buffer); + char *to_decode = "aGVsbG8gd29ybGRcMA=="; + decode(to_decode[0..19], cast(&buffer as byte*)); + printf("2Result: %s\n", &buffer); } \ No newline at end of file diff --git a/resources/examples/binarydigits.c3 b/resources/examples/notworking/binarydigits.c3 similarity index 100% rename from resources/examples/binarydigits.c3 rename to resources/examples/notworking/binarydigits.c3 diff --git a/resources/examples/functions.c3 b/resources/examples/notworking/functions.c3 similarity index 100% rename from resources/examples/functions.c3 rename to resources/examples/notworking/functions.c3 diff --git a/resources/examples/notworking/globals.c3 b/resources/examples/notworking/globals.c3 new file mode 100644 index 000000000..18f32a47b --- /dev/null +++ b/resources/examples/notworking/globals.c3 @@ -0,0 +1,36 @@ +module globals; + + +const string CLICK_ME = "Click Me"; +var uint counter = 0; + +func void clickedme(GtkButton *o, void *d) +{ + cast(d as GtkLabel*).set_text(string.format("You clicked me %d times" as ++counter)); +} + +int main(int argc as string[] argv) +{ + gtk::init(&argc, &argv); + + GtkWindow *win = gtk::windowCreate(GtkWindow::TOPLEVEL); + win.set_title(CLICK_ME); + + GtkButton *button = gtk::buttonCreateWithLabel(CLICK_ME); + + GtkLabel *label = GtkLabel.new("There have been no clicks yet"); + label.setSingleLineMode(true); + + GtkVBox vbox = gtk::vBoxCreate(true, 1); + vbox.add(label); + vbox.add(button); + + win.add(vbox); + + win.connectSignal("delete-event", gtk::mainQuit, NULL); + button.connectSignal("clicked", &clickedme, label); + + win.showAll(); + gtk::main(); + return 0; +} diff --git a/resources/examples/hash.c3 b/resources/examples/notworking/hash.c3 similarity index 99% rename from resources/examples/hash.c3 rename to resources/examples/notworking/hash.c3 index a55595baa..53381ecfc 100644 --- a/resources/examples/hash.c3 +++ b/resources/examples/notworking/hash.c3 @@ -37,6 +37,7 @@ public func uint crc64(byte[*] data) return ~result; } + public func uint fnv32(byte[*] data) { uint h = 0x811c9dc5; diff --git a/resources/examples/http.c3 b/resources/examples/notworking/http.c3 similarity index 100% rename from resources/examples/http.c3 rename to resources/examples/notworking/http.c3 diff --git a/resources/examples/levenshtein.c3 b/resources/examples/notworking/levenshtein.c3 similarity index 100% rename from resources/examples/levenshtein.c3 rename to resources/examples/notworking/levenshtein.c3 diff --git a/resources/examples/madlibs.c3 b/resources/examples/notworking/madlibs.c3 similarity index 100% rename from resources/examples/madlibs.c3 rename to resources/examples/notworking/madlibs.c3 diff --git a/resources/examples/map.c3 b/resources/examples/notworking/map.c3 similarity index 100% rename from resources/examples/map.c3 rename to resources/examples/notworking/map.c3 diff --git a/resources/examples/notworking/retry.c3 b/resources/examples/notworking/retry.c3 new file mode 100644 index 000000000..8d8cf6aae --- /dev/null +++ b/resources/examples/notworking/retry.c3 @@ -0,0 +1,19 @@ +module test; + + +public macro retry(#function, int retries = 3) +{ + error e; + while (1) + { + auto! result = #function; + try (result) return result; + catch (e = result); + } while (retries-- > 0) + return e!; +} + +func void main() +{ + int! result = @retry(eventually_succeed()); +} \ No newline at end of file diff --git a/resources/examples/notworking/swap.c3 b/resources/examples/notworking/swap.c3 new file mode 100644 index 000000000..8ef12a9ca --- /dev/null +++ b/resources/examples/notworking/swap.c3 @@ -0,0 +1,21 @@ +public test; + +/** + * @require parse(a = b), parse(b = a) + */ +public macro void swap(&a, &b) +{ + typeof(a) temp = a; + a = b; + b = temp; +} + +/** + * @require parse(a = b), parse(b = a) + */ +public macro void swap2(auto &a, auto &b) +{ + auto temp = a; + a = b; + b = temp; +} \ No newline at end of file diff --git a/resources/examples/notworking/timeit.c3 b/resources/examples/notworking/timeit.c3 new file mode 100644 index 000000000..170cd9133 --- /dev/null +++ b/resources/examples/notworking/timeit.c3 @@ -0,0 +1,13 @@ +module test; +import std::time; +import std::io; + +public macro timeit(#call) +{ + Time t = time::current(); + typeof(#call) result = #call; + TimeDiff diff = time::current() - t; + io::printf("'%s' took %f ms\n", $stringify(#call), diff * 1000); + return result; +} + diff --git a/resources/examples/toml_parser_c2.c3 b/resources/examples/notworking/toml_parser_c2.c3 similarity index 100% rename from resources/examples/toml_parser_c2.c3 rename to resources/examples/notworking/toml_parser_c2.c3 diff --git a/resources/examples/toml_tokenizer_c2.c3 b/resources/examples/notworking/toml_tokenizer_c2.c3 similarity index 100% rename from resources/examples/toml_tokenizer_c2.c3 rename to resources/examples/notworking/toml_tokenizer_c2.c3 diff --git a/resources/examples/vector.c3 b/resources/examples/notworking/vector.c3 similarity index 100% rename from resources/examples/vector.c3 rename to resources/examples/notworking/vector.c3 diff --git a/resources/examples/notworking/virtuals.c3 b/resources/examples/notworking/virtuals.c3 new file mode 100644 index 000000000..cb21b7c25 --- /dev/null +++ b/resources/examples/notworking/virtuals.c3 @@ -0,0 +1,51 @@ +module comparable; +import std::math; + +interface Geometry +{ + func double area(); + func double perim(); +} + +struct Rect +{ + double width, height; +} + +struct Circle +{ + double radius; +} + +func double Rect.area(Rect *r) +{ + return r.width * r.height; +} + +func double Rect.perim(Rect *r) +{ + return 2 * r.width + 2 * r.height; +} + +func double Circle.area(Circle *c) +{ + return math::PI * c.radius * c.radius +} + +func double Circle.perim(Circle *c) +{ + return math::PI * c.radius * 2; +} + +func void measure(virtual Geometry g) +{ + printf("area: %f, perimeter: %f\n", g.area(), g.perim()); +} + +func void main() +{ + Rect r = { 3, 4 }; + Circle c = { 5 }; + measure(&r); + measure(&c); +} diff --git a/resources/examples/notworking/virtuals2.c3 b/resources/examples/notworking/virtuals2.c3 new file mode 100644 index 000000000..c24e692f1 --- /dev/null +++ b/resources/examples/notworking/virtuals2.c3 @@ -0,0 +1,17 @@ +module std::io; + +interface File : Closable, Readable, Seekable +{ + FileInfo[]! readdir(int count); + FileInfo! stat(); +} + + +interface File +{ + inline Closable; + inline Readable; + inline Seekable; + FileInfo[]! readdir(int count); + FileInfo! stat(); +} diff --git a/resources/examples/window.c3 b/resources/examples/notworking/window.c3 similarity index 100% rename from resources/examples/window.c3 rename to resources/examples/notworking/window.c3 diff --git a/resources/lib/std/math.c3 b/resources/lib/std/math.c3 new file mode 100644 index 000000000..1c2893aac --- /dev/null +++ b/resources/lib/std/math.c3 @@ -0,0 +1,9 @@ +module std::math; + +extern func double _log10(double) @cname("log10"); + +public func double log10(double d) @inline +{ + return _log10(d); +} + diff --git a/resources/testfragments/structo.c3 b/resources/testfragments/structo.c3 new file mode 100644 index 000000000..4fd582b14 --- /dev/null +++ b/resources/testfragments/structo.c3 @@ -0,0 +1,18 @@ +module struct2; + +struct Blend_Map_Entry +{ + union vals { + float[5] colour; + double[2] point_Slope; + } +} + +Blend_Map_Entry a = { .vals = { .colour = { 1, 2, 3, 4, 5 } } }; +Blend_Map_Entry b = { .vals = { .point_Slope = { 6, 7 } } }; +Blend_Map_Entry c = { .vals.colour[2] = 1 }; +Blend_Map_Entry d = { .vals.colour = { 1, 2, 3, 4, 5 } }; + +func void test(Blend_Map_Entry* foo) +{ +} \ No newline at end of file diff --git a/src/compiler/c_abi_internal.h b/src/compiler/c_abi_internal.h index f6f7e475d..00872e0cd 100644 --- a/src/compiler/c_abi_internal.h +++ b/src/compiler/c_abi_internal.h @@ -12,7 +12,7 @@ typedef enum } ByVal; static inline ABIArgInfo *abi_arg_by_reg_attr(ABIArgInfo *info); -ByteSize abi_arg_expanded_size(ABIArgInfo *type_info, Type *type); + bool abi_arg_is_indirect(ABIArgInfo *info); ABIArgInfo *abi_arg_ignore(void); ABIArgInfo *abi_arg_new_direct_pair(AbiType *low_type, AbiType *high_type); diff --git a/src/compiler/compiler.c b/src/compiler/compiler.c index 40852ac79..e22a1e389 100644 --- a/src/compiler/compiler.c +++ b/src/compiler/compiler.c @@ -82,6 +82,7 @@ void compiler_compile(BuildTarget *target) vec_add(target->sources, strformat("%s/std/io.c3", compiler.lib_dir)); vec_add(target->sources, strformat("%s/std/mem.c3", compiler.lib_dir)); vec_add(target->sources, strformat("%s/std/array.c3", compiler.lib_dir)); + vec_add(target->sources, strformat("%s/std/math.c3", compiler.lib_dir)); } VECEACH(target->sources, i) { diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 118471c0b..0f7563af0 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -17,6 +17,7 @@ typedef uint64_t ByteSize; typedef int64_t ArrayIndex; +typedef int64_t IndexDiff; typedef int32_t MemberIndex; typedef int32_t AlignSize; typedef int32_t ScopeId; @@ -269,6 +270,7 @@ typedef struct uint64_t size; Decl **members; MemberIndex union_rep; + int16_t padding; } StructDecl; @@ -442,7 +444,8 @@ typedef struct _Decl const char *cname; AlignSize alignment; const char *section; - ArrayIndex offset; + ArrayIndex offset : 32; + ArrayIndex padding : 32; /* bool is_exported : 1; bool is_used : 1; bool is_used_public : 1; @@ -603,44 +606,39 @@ typedef struct DesignatorElement_ typedef enum { CONST_INIT_ZERO, - CONST_INIT_EXPANDED, - CONST_SELECTED, - CONST_VALUE, - CONST_INIT_ARRAY_SPLIT, - CONST_INIT_ARRAY_RANGE_ZERO, - CONST_INIT_ARRAY_VALUE_FRAGMENT + CONST_INIT_STRUCT, + CONST_INIT_UNION, + CONST_INIT_VALUE, + CONST_INIT_ARRAY, + CONST_INIT_ARRAY_FULL, + CONST_INIT_ARRAY_VALUE, } ConstInitType; typedef struct ConstInitializer_ { - Type *type; ConstInitType kind; + // Type initialized + Type *type; union { - struct ConstInitializer_ **elements; - Expr *value; + struct ConstInitializer_ **init_struct; + Expr *init_value; struct { struct ConstInitializer_ *element; MemberIndex index; - } union_const; + } init_union; struct { - struct ConstInitializer_ *low; - struct ConstInitializer_ *mid; - struct ConstInitializer_ *hi; - } split_const; - struct - { - ArrayIndex low; - ArrayIndex high; - } array_range_zero; + struct ConstInitializer_ **elements; + } init_array; + struct ConstInitializer_ **init_array_full; struct { struct ConstInitializer_ *element; ArrayIndex index; - } single_array_index; + } init_array_value; }; } ConstInitializer; diff --git a/src/compiler/enums.h b/src/compiler/enums.h index 557062d0e..1e4ed957a 100644 --- a/src/compiler/enums.h +++ b/src/compiler/enums.h @@ -413,7 +413,6 @@ typedef enum TOKEN_GENERIC, TOKEN_IF, TOKEN_IMPORT, - TOKEN_IN, TOKEN_LOCAL, TOKEN_MACRO, TOKEN_MODULE, @@ -564,7 +563,8 @@ typedef enum ATTR_UNION = 1 << 4, ATTR_CONST = 1 << 5, ATTR_ERROR = 1 << 6, - ATTR_TYPEDEF = 1 << 7 + ATTR_TYPEDEF = 1 << 7, + ATTR_MEMBER = 1 << 8, } AttributeDomain; typedef enum diff --git a/src/compiler/llvm_codegen.c b/src/compiler/llvm_codegen.c index b9eaefe7b..6e565da6e 100644 --- a/src/compiler/llvm_codegen.c +++ b/src/compiler/llvm_codegen.c @@ -68,87 +68,207 @@ LLVMValueRef llvm_emit_memclear(GenContext *c, BEValue *ref) return llvm_emit_memclear_size_align(c, ref->value, type_size(ref->type), ref->alignment, true); } +LLVMValueRef llvm_emit_const_array_padding(LLVMTypeRef element_type, IndexDiff diff, bool *modified) +{ + if (diff == 1) return LLVMConstNull(element_type); + *modified = true; + LLVMTypeRef padding_type = LLVMArrayType(element_type, (unsigned)diff); + return LLVMConstNull(padding_type); +} + LLVMValueRef llvm_emit_const_initializer(GenContext *c, ConstInitializer *const_init, bool *modified) { switch (const_init->kind) { case CONST_INIT_ZERO: return LLVMConstNull(llvm_get_type(c, const_init->type)); - case CONST_VALUE: + case CONST_INIT_ARRAY_VALUE: + UNREACHABLE + case CONST_INIT_ARRAY_FULL: { - BEValue val; - llvm_emit_expr(c, &val, const_init->value); - return llvm_value_rvalue_store(c, &val); - } - case CONST_INIT_ARRAY_SPLIT: - { - *modified = true; - LLVMValueRef pieces[3]; - unsigned count = 0; - if (const_init->split_const.low) + bool was_modified = false; + Type *array_type = const_init->type; + Type *element_type = array_type->array.base; + LLVMTypeRef element_type_llvm = llvm_get_type(c, element_type); + ConstInitializer **elements = const_init->init_array_full; + ArrayIndex size = array_type->array.len; + assert(size > 0); + LLVMValueRef *parts = VECNEW(LLVMValueRef, size); + for (ArrayIndex i = 0; i < size; i++) { - pieces[count++] = llvm_emit_const_initializer(c, const_init->split_const.low, modified); + vec_add(parts, llvm_emit_const_initializer(c, elements[i], &was_modified)); } - pieces[count++] = llvm_emit_const_initializer(c, const_init->split_const.mid, modified); - if (const_init->split_const.hi) + if (was_modified) *modified = was_modified; + if (was_modified) { - pieces[count++] = llvm_emit_const_initializer(c, const_init->split_const.hi, modified); + return LLVMConstStructInContext(c->context, parts, vec_size(parts), true); } - if (count == 1) return pieces[0]; - return LLVMConstStructInContext(c->context, pieces, count, true); + return LLVMConstArray(element_type_llvm, parts, vec_size(parts)); } - case CONST_INIT_ARRAY_VALUE_FRAGMENT: - *modified = true; - return llvm_emit_const_initializer(c, const_init->single_array_index.element, modified); - case CONST_SELECTED: + + case CONST_INIT_ARRAY: { - Type *member_type = const_init->union_const.element->type; - ByteSize union_size = type_size(const_init->type); - Type *rep_type = const_init->type->decl->strukt.members[const_init->type->decl->strukt.union_rep]->type; - LLVMTypeRef rep_type_llvm = llvm_get_type(c, rep_type); - LLVMTypeRef member_type_llvm = llvm_get_type(c, member_type); - bool is_modified = rep_type_llvm != member_type_llvm; - if (is_modified) *modified = true; + bool was_modified = false; + Type *array_type = const_init->type; + Type *element_type = array_type->array.base; + LLVMTypeRef element_type_llvm = llvm_get_type(c, element_type); + ConstInitializer **elements = const_init->init_array.elements; + unsigned element_count = vec_size(elements); + assert(element_count > 0 && "Array should always have gotten at least one element."); + ArrayIndex current_index = 0; + unsigned alignment = 0; + LLVMValueRef *parts = NULL; + bool pack = false; + VECEACH(elements, i) + { + ConstInitializer *element = elements[i]; + assert(element->kind == CONST_INIT_ARRAY_VALUE); + ArrayIndex element_index = element->init_array_value.index; + IndexDiff diff = element_index - current_index; + unsigned curr_alignment = type_abi_alignment(element->type); + if (alignment && curr_alignment != alignment) + { + pack = true; + } + alignment = curr_alignment; + // Add zeroes + if (diff > 0) + { + vec_add(parts, llvm_emit_const_array_padding(element_type_llvm, diff, &was_modified)); + } + vec_add(parts, llvm_emit_const_initializer(c, element->init_array_value.element, &was_modified)); + current_index = element_index + 1; + } + + IndexDiff end_diff = array_type->array.len - current_index; + if (end_diff > 0) + { + vec_add(parts, llvm_emit_const_array_padding(element_type_llvm, end_diff, &was_modified)); + } + if (was_modified) *modified = was_modified; + if (was_modified) + { + return LLVMConstStructInContext(c->context, parts, vec_size(parts), pack); + } + return LLVMConstArray(element_type_llvm, parts, vec_size(parts)); + } + case CONST_INIT_UNION: + { + Decl *decl = const_init->type->decl; + // Get the type of the member that is used for this particular + // constant value, for example if this is a union { double, int } + // then the type may be double or int. + Type *member_type = decl->strukt.members[const_init->init_union.index]->type->canonical; // Emit our value. - LLVMValueRef result = llvm_emit_const_initializer(c, const_init->union_const.element, modified); + LLVMValueRef result = llvm_emit_const_initializer(c, const_init->init_union.element, modified); - LLVMValueRef values[2] = { - result, - NULL - }; + // Get the union value + LLVMTypeRef union_type_llvm = llvm_get_type(c, decl->type); - // There is a case, where we need additional padding for this member, in that case - // Create a struct for it. + // Take the first type in the union (note that there may be padding!) + LLVMTypeRef first_type = LLVMStructGetTypeAtIndex(union_type_llvm, 0); + + // We need to calculate some possible padding. + ByteSize union_size = type_size(const_init->type); ByteSize member_size = type_size(member_type); - if (union_size > member_size) + ByteSize padding = union_size - member_size; + + // Create the resulting values: + LLVMValueRef values[2] = { result, NULL }; + unsigned value_count = 1; + + // Add possible padding as and i8 array. + if (padding > 0) { - values[1] = llvm_const_padding(c, union_size - member_size); - // Now we use an unnamed struct. - return LLVMConstStructInContext(c->context, values, 2, const_init->type->decl->is_packed); + values[1] = llvm_emit_const_padding(c, padding); + value_count = 2; } - return LLVMConstNamedStruct(llvm_get_type(c, const_init->type), values, 1); + // Is this another type than usual for the union? + if (first_type != llvm_get_type(c, member_type)) + { + // Yes, so the type needs to be modified. + *modified = true; + } + + // If it is modified we simply create a packed struct for representation. + if (*modified) + { + return LLVMConstStructInContext(c->context, values, value_count, false); + } + + return LLVMConstNamedStruct(union_type_llvm, values, value_count); } - case CONST_INIT_EXPANDED: + case CONST_INIT_STRUCT: { - MemberIndex count = vec_size(const_init->type->decl->strukt.members); - LLVMValueRef *entries = MALLOC(sizeof(LLVMValueRef) * count); + Decl *decl = const_init->type->decl; + Decl **members = decl->strukt.members; + MemberIndex count = vec_size(members); + LLVMValueRef *entries = NULL; for (MemberIndex i = 0; i < count; i++) { - entries[i] = llvm_emit_const_initializer(c, const_init->elements[i], modified); + if (members[i]->padding) + { + vec_add(entries, llvm_emit_const_padding(c, members[i]->padding)); + } + vec_add(entries, llvm_emit_const_initializer(c, const_init->init_struct[i], modified)); + } + if (decl->strukt.padding) + { + vec_add(entries, llvm_emit_const_padding(c, decl->strukt.padding)); } if (*modified) { - return LLVMConstStructInContext(c->context, entries, count, const_init->type->decl->is_packed); + return LLVMConstStructInContext(c->context, entries, vec_size(entries), decl->is_packed); } - return LLVMConstNamedStruct(llvm_get_type(c, const_init->type), entries, count); + return LLVMConstNamedStruct(llvm_get_type(c, const_init->type), entries, vec_size(entries)); + } + case CONST_INIT_VALUE: + { + BEValue value; + llvm_emit_expr(c, &value, const_init->init_value); + LLVMValueRef llvm_value = llvm_value_rvalue_store(c, &value); + LLVMTypeRef expected_type = llvm_get_type(c, const_init->type); + if (expected_type != LLVMTypeOf(llvm_value)) *modified = true; + return llvm_value; } - case CONST_INIT_ARRAY_RANGE_ZERO: - return LLVMConstNull(llvm_get_type(c, const_init->type)); } UNREACHABLE } + +static LLVMValueRef llvm_emit_const_initializer_simple(GenContext *c, Expr *expr, bool *modified) +{ + Expr **elements = expr->initializer_expr.initializer_expr; + unsigned element_count = vec_size(elements); + LLVMValueRef* values = malloc_arena(element_count * sizeof(LLVMValueRef)); + Type *expr_type = expr->type->canonical; + LLVMTypeRef array_type = expr_type->type_kind == TYPE_ARRAY ? llvm_get_type(c, expr_type->array.base) : NULL; + assert(array_type || type_is_structlike(expr_type)); + for (unsigned i = 0; i < element_count; i++) + { + BEValue value; + llvm_emit_expr(c, &value, elements[i]); + LLVMValueRef llvm_value = llvm_value_rvalue_store(c, &value); + LLVMTypeRef expected_type = array_type ? array_type : llvm_get_type(c, expr_type->decl->strukt.members[0]->type); + if (expected_type != LLVMTypeOf(llvm_value)) *modified = true; + values[i] = llvm_value; + } + if (array_type) + { + if (*modified) + { + return LLVMConstStructInContext(c->context, values, element_count, true); + } + return LLVMConstArray(array_type, values, element_count); + } + if (*modified) + { + return LLVMConstStructInContext(c->context, values, element_count, true); + } + return LLVMConstNamedStruct(llvm_get_type(c, expr_type), values, element_count); +} + LLVMValueRef llvm_emit_const_aggregate(GenContext *c, Expr *expr, bool *modified) { switch (expr->initializer_expr.init_type) @@ -159,7 +279,7 @@ LLVMValueRef llvm_emit_const_aggregate(GenContext *c, Expr *expr, bool *modified case INITIALIZER_CONST: return llvm_emit_const_initializer(c, expr->initializer_expr.initializer, modified); case INITIALIZER_NORMAL: - TODO + return llvm_emit_const_initializer_simple(c, expr, modified); } UNREACHABLE } @@ -197,7 +317,10 @@ static void gencontext_emit_global_variable_definition(GenContext *c, Decl *decl // TODO fix name decl->backend_ref = LLVMAddGlobal(c->module, LLVMTypeOf(init_value), decl->name); LLVMSetAlignment(decl->backend_ref, alignment); - LLVMSetInitializer(decl->backend_ref, init_value); + if (decl->visibility != VISIBLE_EXTERN) + { + LLVMSetInitializer(decl->backend_ref, init_value); + } LLVMSetGlobalConstant(decl->backend_ref, decl->var.kind == VARDECL_CONST); @@ -210,6 +333,9 @@ static void gencontext_emit_global_variable_definition(GenContext *c, Decl *decl LLVMSetVisibility(decl->backend_ref, LLVMDefaultVisibility); break; case VISIBLE_EXTERN: + LLVMSetLinkage(decl->backend_ref, LLVMExternalLinkage); + //LLVMSetVisibility(decl->backend_ref, LLVMDefaultVisibility); + break; case VISIBLE_LOCAL: LLVMSetVisibility(decl->backend_ref, LLVMHiddenVisibility); break; diff --git a/src/compiler/llvm_codegen_c_abi.c b/src/compiler/llvm_codegen_c_abi.c index 0b6996c2e..590849c23 100644 --- a/src/compiler/llvm_codegen_c_abi.c +++ b/src/compiler/llvm_codegen_c_abi.c @@ -107,56 +107,6 @@ ABIArgInfo *abi_arg_new_indirect_not_by_val(void) return info; } -ByteSize abi_arg_expanded_size(ABIArgInfo *type_info, Type *type) -{ - switch (type->type_kind) - { - case TYPE_TYPEDEF: - return abi_arg_expanded_size(type_info, type->canonical); - case TYPE_ARRAY: - return abi_arg_expanded_size(type_info, type->array.base) * type->array.len; - case TYPE_STRUCT: - { - Decl **members = type->decl->strukt.members; - ByteSize result = 0; - VECEACH(members, i) - { - members += abi_arg_expanded_size(type_info, members[i]->type); - } - return result; - } - case TYPE_UNION: - { - Type *max_union = type_find_largest_union_element(type); - if (!max_union) return 0; - return abi_arg_expanded_size(type_info, max_union); - } - case TYPE_COMPLEX: - case TYPE_SUBARRAY: - case TYPE_STRING: - // Complex is { real, real }, Sub array { pointer, len } = String? - return 2; - case TYPE_ERR_UNION: - case TYPE_VOID: - case TYPE_BOOL: - case ALL_FLOATS: - case ALL_INTS: - case TYPE_TYPEID: - case TYPE_POINTER: - case TYPE_ENUM: - case TYPE_ERRTYPE: - case TYPE_VARARRAY: - case TYPE_VECTOR: - return 1; - case TYPE_POISONED: - case TYPE_FUNC: - case TYPE_TYPEINFO: - case TYPE_MEMBER: - UNREACHABLE - } - UNREACHABLE -} - ABIArgInfo *abi_arg_new_direct_int_ext(Type *int_to_extend) { diff --git a/src/compiler/llvm_codegen_c_abi_x64.c b/src/compiler/llvm_codegen_c_abi_x64.c index c00f5c5ce..e796ffba4 100644 --- a/src/compiler/llvm_codegen_c_abi_x64.c +++ b/src/compiler/llvm_codegen_c_abi_x64.c @@ -412,7 +412,7 @@ Decl *x64_get_member_at_offset(Decl *decl, unsigned offset) Decl *last_match = NULL; VECEACH(members, i) { - if (members[i]->offset > offset) break; + if (members[i]->offset > (ArrayIndex)offset) break; last_match = members[i]; } assert(last_match); diff --git a/src/compiler/llvm_codegen_expr.c b/src/compiler/llvm_codegen_expr.c index c3e9e20e8..f12d23e3d 100644 --- a/src/compiler/llvm_codegen_expr.c +++ b/src/compiler/llvm_codegen_expr.c @@ -21,9 +21,16 @@ LLVMValueRef llvm_emit_is_no_error(GenContext *c, LLVMValueRef error) return LLVMBuildICmp(c->builder, LLVMIntEQ, domain, llvm_get_zero(c, type_usize), "noerr"); } -LLVMValueRef llvm_const_padding(GenContext *c, ByteSize size) +LLVMTypeRef llvm_const_padding_type(GenContext *c, ByteSize size) { - return LLVMGetUndef(LLVMArrayType(llvm_get_type(c, type_byte), size)); + assert(size > 0); + if (size == 1) return llvm_get_type(c, type_byte); + return LLVMArrayType(llvm_get_type(c, type_byte), size); +} + +LLVMValueRef llvm_emit_const_padding(GenContext *c, ByteSize size) +{ + return LLVMGetUndef(llvm_const_padding_type(c, size)); } static inline LLVMValueRef gencontext_emit_add_int(GenContext *context, Type *type, bool use_mod, LLVMValueRef left, LLVMValueRef right) @@ -300,7 +307,7 @@ static int find_member_index(Decl *parent, Decl *member) return -1; } -static LLVMValueRef gencontext_emit_member_addr(GenContext *context, LLVMValueRef value, Decl *parent, Decl *member) +static void gencontext_emit_member_addr(GenContext *c, BEValue *value, Decl *parent, Decl *member) { assert(member->resolve_status == RESOLVE_DONE); @@ -314,20 +321,18 @@ static LLVMValueRef gencontext_emit_member_addr(GenContext *context, LLVMValueRe switch (parent->type->canonical->type_kind) { case TYPE_UNION: - value = LLVMBuildBitCast(context->builder, value, LLVMPointerType(llvm_get_type(context, found->type), 0), name); + llvm_value_addr(c, value); + llvm_value_set_address_align(value, llvm_emit_bitcast(c, value->value, type_get_ptr(found->type)), found->type, value->alignment); break; case TYPE_ERRTYPE: - value = LLVMBuildStructGEP2(context->builder, llvm_get_type(context, parent->type), value, index + 1, name); - break; case TYPE_STRUCT: - value = LLVMBuildStructGEP2(context->builder, llvm_get_type(context, parent->type), value, index, name); + llvm_value_struct_gep(c, value, value, index); break; default: UNREACHABLE } parent = found; } while (found != member); - return value; } @@ -337,7 +342,7 @@ static inline void gencontext_emit_access_addr(GenContext *context, BEValue *be_ llvm_emit_expr(context, be_value, parent); Decl *member = expr->access_expr.ref; - llvm_value_set_address(be_value, gencontext_emit_member_addr(context, be_value->value, parent->type->canonical->decl, member), expr->type); + gencontext_emit_member_addr(context, be_value, parent->type->canonical->decl, member); } static void gencontext_emit_scoped_expr(GenContext *context, BEValue *value, Expr *expr) @@ -390,6 +395,8 @@ LLVMValueRef gencontext_emit_value_bitcast(GenContext *context, LLVMValueRef val LLVMValueRef ptr_cast = llvm_emit_bitcast(context, ptr, type_get_ptr(to_type)); return gencontext_emit_load(context, to_type, ptr_cast); } + + static LLVMValueRef gencontext_emit_cast_inner(GenContext *c, CastKind cast_kind, BEValue *value, Type *to_type, Type *from_type) { switch (cast_kind) @@ -404,10 +411,18 @@ static LLVMValueRef gencontext_emit_cast_inner(GenContext *c, CastKind cast_kind UNREACHABLE case CAST_PTRPTR: llvm_value_rvalue(c, value); - return LLVMBuildPointerCast(c->builder, value->value, llvm_get_type(c, to_type), "ptrptr"); + if (c->builder) + { + return LLVMBuildPointerCast(c->builder, value->value, llvm_get_type(c, to_type), "ptrptr"); + } + return LLVMConstPointerCast(value->value, llvm_get_type(c, to_type)); case CAST_PTRXI: llvm_value_rvalue(c, value); - return LLVMBuildPtrToInt(c->builder, value->value, llvm_get_type(c, to_type), "ptrxi"); + if (c->builder) + { + return LLVMBuildPtrToInt(c->builder, value->value, llvm_get_type(c, to_type), "ptrxi"); + } + return LLVMConstBitCast(value->value, llvm_get_type(c, to_type)); case CAST_APTSA: gencontext_emit_arr_to_subarray_cast(c, value, to_type, from_type); return value->value; @@ -524,8 +539,6 @@ static inline void gencontext_emit_cast_expr(GenContext *context, BEValue *be_va expr->cast_expr.expr->type->canonical); } -static LLVMValueRef llvm_emit_initializer_list_expr_const(GenContext *c, Expr *expr, bool *modified); - static LLVMValueRef llvm_recursive_set_value(GenContext *c, DesignatorElement **current_element_ptr, LLVMValueRef parent, DesignatorElement **last_element_ptr, Expr *value) { @@ -588,73 +601,14 @@ static LLVMValueRef llvm_recursive_set_const_value(GenContext *context, Designat } -static LLVMValueRef llvm_emit_initializer_list_expr_const(GenContext *c, Expr *expr, bool *modified) -{ - Type *canonical = expr->type->canonical; - LLVMTypeRef type = llvm_get_type(c, canonical); - - if (expr->initializer_expr.init_type == INITIALIZER_CONST) - { - return llvm_emit_const_aggregate(c, expr, modified); - } - - - bool is_error = expr->type->canonical->type_kind == TYPE_ERRTYPE; - - if (is_error) - { - TODO - } - - Expr **elements = expr->initializer_expr.initializer_expr; - - bool is_union = expr->type->canonical->type_kind == TYPE_UNION; - - if (expr->initializer_expr.init_type == INITIALIZER_NORMAL && is_union) - { - assert(vec_size(elements) == 1); - - TODO - // gencontext_construct_const_union_struct(c, be_value, canonical, elements[0]); - } - - if (expr->initializer_expr.init_type == INITIALIZER_NORMAL) - { - LLVMValueRef value = LLVMGetUndef(type); - VECEACH(elements, i) - { - Expr *element = elements[i]; - if (element->expr_kind == EXPR_INITIALIZER_LIST) - { - value = LLVMConstInsertValue(value, llvm_emit_initializer_list_expr_const(c, element, modified), &i, 1); - continue; - } - BEValue be_value; - llvm_emit_expr(c, &be_value, element); - value = LLVMConstInsertValue(value, llvm_value_rvalue_store(c, &be_value), &i, 1); - } - return value; - } - - LLVMValueRef value = LLVMConstNull(type); - VECEACH(elements, i) - { - Expr *element = elements[i]; - Type *parent_type = expr->type->canonical; - value = llvm_recursive_set_const_value(c, - element->designator_expr.path, - value, - parent_type, - element->designator_expr.value); - } - return value; -} - -static inline void llvm_emit_initialize_reference_small_const(GenContext *c, BEValue *ref, Expr *expr) +static inline void llvm_emit_initialize_reference_temporary_const(GenContext *c, BEValue *ref, Expr *expr) { bool modified = false; // First create the constant value. - LLVMValueRef value = llvm_emit_initializer_list_expr_const(c, expr, &modified); + + Type *canonical = expr->type->canonical; + + LLVMValueRef value = llvm_emit_const_aggregate(c, expr, &modified); // Create a global const. LLVMTypeRef type = modified ? LLVMTypeOf(value) : llvm_get_type(c, expr->type); @@ -675,6 +629,116 @@ static inline void llvm_emit_initialize_reference_small_const(GenContext *c, BEV llvm_emit_memcpy(c, ref->value, ref->alignment, global_copy, alignment, type_size(expr->type)); } +static void llvm_emit_inititialize_reference_const(GenContext *c, BEValue *ref, ConstInitializer *const_init) +{ + switch (const_init->kind) + { + case CONST_INIT_ZERO: + if (type_is_builtin(ref->type->canonical->type_kind) || ref->type->canonical->type_kind == TYPE_ARRAY) + { + llvm_store_bevalue_raw(c, ref, llvm_get_zero(c, ref->type)); + return; + } + llvm_emit_memclear(c, ref); + return; + case CONST_INIT_ARRAY_VALUE: + UNREACHABLE + case CONST_INIT_ARRAY_FULL: + { + LLVMValueRef array_ref = ref->value; + Type *array_type = const_init->type; + Type *element_type = array_type->array.base; + ArrayIndex size = array_type->array.len; + LLVMTypeRef array_type_llvm = llvm_get_type(c, array_type); + LLVMTypeRef element_type_llvm = llvm_get_type(c, element_type); + assert(size <= UINT32_MAX); + for (ArrayIndex i = 0; i < size; i++) + { + LLVMValueRef index = llvm_const_int(c, type_uint, i); + LLVMValueRef array_pointer = LLVMBuildInBoundsGEP2(c->builder, element_type_llvm, array_ref, &index, 1, ""); + BEValue value; + llvm_value_set_address(&value, array_pointer, element_type); + llvm_emit_inititialize_reference_const(c, &value, const_init->init_array_full[i]); + } + return; + } + case CONST_INIT_ARRAY: + { + LLVMValueRef array_ref = ref->value; + llvm_emit_memclear(c, ref); + Type *array_type = const_init->type; + Type *element_type = array_type->array.base; + LLVMTypeRef element_type_llvm = llvm_get_type(c, element_type); + ConstInitializer **elements = const_init->init_array.elements; + unsigned element_count = vec_size(elements); + ArrayIndex current_index = 0; + LLVMValueRef *parts = NULL; + VECEACH(elements, i) + { + ConstInitializer *element = elements[i]; + assert(element->kind == CONST_INIT_ARRAY_VALUE); + ArrayIndex element_index = element->init_array_value.index; + LLVMValueRef index = llvm_const_int(c, element_index <= UINT32_MAX ? type_uint : type_usize, element_index); + LLVMValueRef array_pointer = LLVMBuildInBoundsGEP2(c->builder, element_type_llvm, array_ref, &index, 1, ""); + BEValue value; + llvm_value_set_address(&value, array_pointer, element_type); + llvm_emit_inititialize_reference_const(c, &value, element->init_array_value.element); + } + return; + } + case CONST_INIT_UNION: + { + Decl *decl = const_init->type->decl; + MemberIndex index = const_init->init_union.index; + Type *type = decl->strukt.members[index]->type->canonical; + // Bitcast. + BEValue value = *ref; + llvm_value_set_address(&value, llvm_emit_bitcast(c, ref->value, type_get_ptr(type)), type); + // Emit our value. + llvm_emit_inititialize_reference_const(c, &value, const_init->init_union.element); + return; + } + case CONST_INIT_STRUCT: + { + Decl *decl = const_init->type->decl; + Decl **members = decl->strukt.members; + MemberIndex count = vec_size(members); + LLVMValueRef *entries = NULL; + for (MemberIndex i = 0; i < count; i++) + { + BEValue value; + llvm_value_struct_gep(c, &value, ref, i); + llvm_emit_inititialize_reference_const(c, &value, const_init->init_struct[i]); + } + return; + } + case CONST_INIT_VALUE: + { + BEValue value; + llvm_emit_expr(c, &value, const_init->init_value); + llvm_store_bevalue(c, ref, &value); + return; + } + } + UNREACHABLE + +} +static inline void llvm_emit_initialize_reference_const(GenContext *c, BEValue *ref, Expr *expr) +{ + // Getting ready to initialize, get the real type. + Type *real_type = ref->type->canonical; + ConstInitializer *initializer = expr->initializer_expr.initializer; + + // Make sure we have an address. + llvm_value_addr(c, ref); + + Type *canonical = expr->type->canonical; + LLVMTypeRef type = llvm_get_type(c, canonical); + + llvm_emit_inititialize_reference_const(c, ref, initializer); + +} + static inline void llvm_emit_initialize_reference_list(GenContext *c, BEValue *ref, Expr *expr) { // Getting ready to initialize, get the real type. @@ -696,45 +760,46 @@ static inline void llvm_emit_initialize_reference_list(GenContext *c, BEValue *r LLVMTypeRef llvm_type = llvm_get_type(c, real_type); bool is_struct = type_is_structlike(real_type); bool is_array = real_type->type_kind == TYPE_ARRAY; - + Type *array_element_type = is_array ? real_type->array.base : NULL; + LLVMTypeRef array_element_type_llvm = is_array ? llvm_get_type(c, real_type->array.base) : NULL; // Now walk through the elements. VECEACH(elements, i) { Expr *element = elements[i]; - BEValue init_value; if (element->expr_kind == EXPR_COMPOUND_LITERAL) { element = element->expr_compound_literal.initializer; } unsigned offset = 0; - LLVMValueRef pointer; + BEValue pointer; if (is_struct) { Decl *member = real_type->decl->strukt.members[i]; offset = member->offset; - pointer = LLVMBuildStructGEP2(c->builder, llvm_type, value, i, ""); + llvm_value_struct_gep(c, &pointer, ref, i); } else if (is_array) { // Todo optimize - offset = i * type_size(element->type); - LLVMValueRef indices[2] = { 0, llvm_const_int(c, type_uint, i) }; - pointer = LLVMBuildInBoundsGEP2(c->builder, llvm_type, value, indices, 2, ""); + offset = i * type_size(array_element_type); + LLVMValueRef index = llvm_const_int(c, type_uint, i); + LLVMValueRef ptr = LLVMBuildInBoundsGEP2(c->builder, array_element_type_llvm, value, &index, 1, ""); + unsigned alignment = aligned_offset(offset, ref->alignment); + llvm_value_set_address_align(&pointer, ptr, element->type, alignment); } else { - pointer = value; + llvm_value_set_address_align(&pointer, value, element->type, 0); } - unsigned alignment = aligned_offset(offset, ref->alignment); // If this is an initializer, we want to actually run the initialization recursively. if (element->expr_kind == EXPR_INITIALIZER_LIST) { - llvm_value_set_address_align(&init_value, pointer, element->type, alignment); - llvm_emit_initialize_reference(c, &init_value, element); + llvm_emit_initialize_reference(c, &pointer, element); continue; } + BEValue init_value; llvm_emit_expr(c, &init_value, element); - llvm_store_aligned(c, pointer, llvm_value_rvalue_store(c, &init_value), alignment); + llvm_store_bevalue(c, &pointer, &init_value); } } @@ -854,9 +919,7 @@ static void llvm_emit_initialize_designated(GenContext *c, BEValue *ref, uint64_ } else { - unsigned alignment = 1; - LLVMValueRef ptr = llvm_emit_struct_gep(c, ref->value, llvm_get_type(c, ref->type), curr->index, decl_alignment, offset, &alignment); - llvm_value_set_address_align(&value, ptr, type, alignment); + llvm_value_struct_gep(c, &value, ref, curr->index); } llvm_emit_initialize_designated(c, &value, offset, current + 1, last, expr, emitted_value); break; @@ -934,7 +997,7 @@ static inline void llvm_emit_initialize_reference_designated(GenContext *c, BEVa static inline void llvm_emit_initialize_reference(GenContext *c, BEValue *ref, Expr *expr) { // In the case of a const, we have some optimizations: - if (expr->constant) + if (expr->initializer_expr.init_type == INITIALIZER_CONST) { ConstInitializer *initializer = expr->initializer_expr.initializer; if (initializer->kind == CONST_INIT_ZERO) @@ -943,10 +1006,10 @@ static inline void llvm_emit_initialize_reference(GenContext *c, BEValue *ref, E llvm_emit_memclear(c, ref); return; } - // In case of small const initializers, use copy. - if (type_size(expr->type) <= 1000 * type_size(type_voidptr) * 4) + // In case of small const initializers, or full arrays - use copy. + if (initializer->kind == CONST_INIT_ARRAY_FULL || type_size(expr->type) <= 32) { - llvm_emit_initialize_reference_small_const(c, ref, expr); + llvm_emit_initialize_reference_temporary_const(c, ref, expr); return; } } @@ -955,7 +1018,7 @@ static inline void llvm_emit_initialize_reference(GenContext *c, BEValue *ref, E { case INITIALIZER_CONST: // Just clear memory - llvm_emit_memclear(c, ref); + llvm_emit_initialize_reference_const(c, ref, expr); break; case INITIALIZER_NORMAL: llvm_emit_initialize_reference_list(c, ref, expr); @@ -966,61 +1029,6 @@ static inline void llvm_emit_initialize_reference(GenContext *c, BEValue *ref, E default: UNREACHABLE } - - - return; - TODO -/* - VECEACH(elements, i) - { - if (is_error) TODO - Expr *field = elements[i]; - - DesignatedPath *path = field->designated_init_expr.path; - BEValue sub_value; - llvm_emit_expr(c, &sub_value, field->designated_init_expr.value); - LLVMValueRef sub_ref = ref; - Type *parent_type = expr->type->canonical; - while (path) - { - switch (path->kind) - { - case DESIGNATED_IDENT: - if (parent_type->canonical->type_kind == TYPE_UNION) - { - sub_ref = LLVMBuildBitCast(c->builder, sub_ref, llvm_get_ptr_type(c, path->type), path->type->name); - } - else - { - sub_ref = LLVMBuildStructGEP2(c->builder, llvm_get_type(c, parent_type), sub_ref, path->index, path->type->name); - } - break; - case DESIGNATED_SUBSCRIPT: - { - // TODO range, more arrays - LLVMValueRef zero = llvm_get_zero(c, type_int); - BEValue index; - llvm_emit_expr(c, &index, path->index_expr); - LLVMValueRef indices[2] = { - zero, - llvm_value_rvalue_store(c, &index), - }; - sub_ref = LLVMBuildInBoundsGEP2(c->builder, - llvm_get_type(c, parent_type), - sub_ref, indices, 2, "arrsub"); - break; - } - default: - UNREACHABLE; - - } - parent_type = path->type; - path = path->sub_path; - } - LLVMBuildStore(c->builder, llvm_value_rvalue_store(c, &sub_value), sub_ref); - } - return ref; - */ } static inline void llvm_emit_inc_dec_change(GenContext *c, bool use_mod, BEValue *addr, BEValue *after, BEValue *before, Expr *expr, int diff) @@ -2351,24 +2359,35 @@ static void llvm_expand_type_to_args(GenContext *context, Type *param_type, LLVM } } -LLVMValueRef llvm_emit_struct_gep(GenContext *context, LLVMValueRef ptr, LLVMTypeRef struct_type, unsigned index, unsigned struct_alignment, unsigned offset, unsigned *alignment) +LLVMValueRef llvm_emit_struct_gep_raw(GenContext *context, LLVMValueRef ptr, LLVMTypeRef struct_type, unsigned index, unsigned struct_alignment, unsigned offset, unsigned *alignment) { *alignment = type_min_alignment(offset, struct_alignment); LLVMValueRef addr = LLVMBuildStructGEP2(context->builder, struct_type, ptr, index, ""); return addr; } + void llvm_value_struct_gep(GenContext *c, BEValue *element, BEValue *struct_pointer, unsigned index) { llvm_value_fold_failable(c, struct_pointer); - Decl *member = struct_pointer->type->decl->strukt.members[index]; + ArrayIndex actual_index = -1; + Decl *member; + for (ArrayIndex i = 0; i <= index; i++) + { + member = struct_pointer->type->decl->strukt.members[i]; + if (member->padding) + { + actual_index++; + } + actual_index++; + } unsigned alignment; - LLVMValueRef ref = llvm_emit_struct_gep(c, - struct_pointer->value, - llvm_get_type(c, struct_pointer->type), - index, - struct_pointer->alignment, - member->offset, - &alignment); + LLVMValueRef ref = llvm_emit_struct_gep_raw(c, + struct_pointer->value, + llvm_get_type(c, struct_pointer->type), + actual_index, + struct_pointer->alignment, + member->offset, + &alignment); llvm_value_set_address(element, ref, member->type); element->alignment = alignment; } @@ -2649,9 +2668,9 @@ void llvm_emit_call_expr(GenContext *context, BEValue *be_value, Expr *expr) // Find the address to the low value unsigned alignment; - LLVMValueRef lo = llvm_emit_struct_gep(context, coerce, coerce_type, ret_info->coerce_expand.lo_index, - type_abi_alignment(return_type), - ret_info->coerce_expand.offset_lo, &alignment); + LLVMValueRef lo = llvm_emit_struct_gep_raw(context, coerce, coerce_type, ret_info->coerce_expand.lo_index, + type_abi_alignment(return_type), + ret_info->coerce_expand.offset_lo, &alignment); // If there is only a single field, we simply store the value. if (!ret_info->coerce_expand.hi) @@ -2669,9 +2688,9 @@ void llvm_emit_call_expr(GenContext *context, BEValue *be_value, Expr *expr) // Store the low value. llvm_store_aligned(context, lo, lo_value, alignment); - LLVMValueRef hi = llvm_emit_struct_gep(context, coerce, coerce_type, ret_info->coerce_expand.hi_index, - type_abi_alignment(return_type), - ret_info->coerce_expand.offset_hi, &alignment); + LLVMValueRef hi = llvm_emit_struct_gep_raw(context, coerce, coerce_type, ret_info->coerce_expand.hi_index, + type_abi_alignment(return_type), + ret_info->coerce_expand.offset_hi, &alignment); // Store the high value. llvm_store_aligned(context, lo, lo_value, alignment); diff --git a/src/compiler/llvm_codegen_function.c b/src/compiler/llvm_codegen_function.c index 7c4452347..ba4a18ae6 100644 --- a/src/compiler/llvm_codegen_function.c +++ b/src/compiler/llvm_codegen_function.c @@ -296,10 +296,10 @@ void llvm_emit_return_abi(GenContext *c, BEValue *return_value, BEValue *failabl // We might have only one value, in that case, build a GEP to that one. LLVMValueRef lo_val; unsigned alignment; - LLVMValueRef lo = llvm_emit_struct_gep(c, coerce, coerce_type, info->coerce_expand.lo_index, - return_value->alignment, - info->coerce_expand.offset_lo, &alignment); - LLVMTypeRef lo_type = llvm_abi_type(c, info->coerce_expand.hi); + LLVMValueRef lo = llvm_emit_struct_gep_raw(c, coerce, coerce_type, info->coerce_expand.lo_index, + return_value->alignment, + info->coerce_expand.offset_lo, &alignment); + LLVMTypeRef lo_type = llvm_abi_type(c, info->coerce_expand.lo); lo_val = llvm_emit_load_aligned(c, lo_type, lo, alignment, ""); // We're done if there's a single field. @@ -310,9 +310,9 @@ void llvm_emit_return_abi(GenContext *c, BEValue *return_value, BEValue *failabl } // Let's make a first class aggregate - LLVMValueRef hi = llvm_emit_struct_gep(c, coerce, coerce_type, info->coerce_expand.hi_index, - return_value->alignment, - info->coerce_expand.offset_hi, &alignment); + LLVMValueRef hi = llvm_emit_struct_gep_raw(c, coerce, coerce_type, info->coerce_expand.hi_index, + return_value->alignment, + info->coerce_expand.offset_hi, &alignment); LLVMTypeRef hi_type = llvm_abi_type(c, info->coerce_expand.hi); LLVMValueRef hi_val = llvm_emit_load_aligned(c, hi_type, hi, alignment, ""); diff --git a/src/compiler/llvm_codegen_internal.h b/src/compiler/llvm_codegen_internal.h index 65d7f5af1..eb8d6165b 100644 --- a/src/compiler/llvm_codegen_internal.h +++ b/src/compiler/llvm_codegen_internal.h @@ -201,7 +201,8 @@ void llvm_attribute_add_string(GenContext *c, LLVMValueRef value_to_add_attribut void llvm_attribute_add_int(GenContext *c, LLVMValueRef value_to_add_attribute_to, unsigned attribute_id, uint64_t val, int index); LLVMBasicBlockRef llvm_basic_block_new(GenContext *c, const char *name); static inline LLVMValueRef llvm_const_int(GenContext *c, Type *type, uint64_t val); - +LLVMValueRef llvm_emit_const_padding(GenContext *c, ByteSize size); +LLVMTypeRef llvm_const_padding_type(GenContext *c, ByteSize size); LLVMValueRef llvm_emit_alloca(GenContext *context, LLVMTypeRef type, unsigned alignment, const char *name); LLVMValueRef llvm_emit_alloca_aligned(GenContext *c, Type *type, const char *name); LLVMValueRef llvm_emit_assign_expr(GenContext *context, LLVMValueRef ref, Expr *expr, LLVMValueRef failable); @@ -215,7 +216,6 @@ void llvm_emit_function_body(GenContext *context, Decl *decl); void llvm_emit_function_decl(GenContext *c, Decl *decl); LLVMValueRef llvm_emit_call_intrinsic(GenContext *c, unsigned intrinsic_id, LLVMTypeRef *types, unsigned type_count, LLVMValueRef *values, unsigned arg_count); void llvm_emit_cast(GenContext *c, CastKind cast_kind, BEValue *value, Type *to_type, Type *from_type); -LLVMValueRef llvm_const_padding(GenContext *c, ByteSize size); void llvm_emit_cond_br(GenContext *context, BEValue *value, LLVMBasicBlockRef then_block, LLVMBasicBlockRef else_block); void llvm_emit_debug_function(GenContext *c, Decl *decl); void llvm_emit_debug_location(GenContext *context, SourceSpan location); @@ -237,7 +237,7 @@ static inline LLVMValueRef llvm_emit_store(GenContext *context, Decl *decl, LLVM void llvm_emit_panic_on_true(GenContext *c, LLVMValueRef value, const char *panic_name); void llvm_emit_return_abi(GenContext *c, BEValue *return_value, BEValue *failable); void llvm_emit_return_implicit(GenContext *c); -LLVMValueRef llvm_emit_struct_gep(GenContext *context, LLVMValueRef ptr, LLVMTypeRef struct_type, unsigned index, unsigned struct_alignment, unsigned offset, unsigned *alignment); +LLVMValueRef llvm_emit_struct_gep_raw(GenContext *context, LLVMValueRef ptr, LLVMTypeRef struct_type, unsigned index, unsigned struct_alignment, unsigned offset, unsigned *alignment); LLVMValueRef llvm_get_next_param(GenContext *context, unsigned *index); LLVMTypeRef llvm_get_coerce_type(GenContext *c, ABIArgInfo *arg_info); static inline LLVMBasicBlockRef llvm_get_current_block_if_in_use(GenContext *context); diff --git a/src/compiler/llvm_codegen_stmt.c b/src/compiler/llvm_codegen_stmt.c index d3d641624..5a977edb0 100644 --- a/src/compiler/llvm_codegen_stmt.c +++ b/src/compiler/llvm_codegen_stmt.c @@ -6,6 +6,12 @@ void gencontext_emit_try_stmt(GenContext *context, Ast *pAst); +bool ast_is_empty_compound_stmt(Ast *ast) +{ + if (ast->ast_kind != AST_COMPOUND_STMT) return false; + return !vec_size(ast->compound_stmt.stmts) && ast->compound_stmt.defer_list.start == ast->compound_stmt.defer_list.end; +} + void llvm_emit_compound_stmt(GenContext *context, Ast *ast) { if (llvm_use_debug(context)) @@ -61,8 +67,9 @@ static LLVMValueRef llvm_emit_decl(GenContext *c, Ast *ast) } else { + Type *type = decl->type->canonical; // Normal case, zero init. - if (type_is_builtin(decl->type->canonical->type_kind)) + if (type_is_builtin(type->type_kind) || type->type_kind == TYPE_POINTER) { llvm_emit_store(c, decl, LLVMConstNull(alloc_type)); } @@ -208,65 +215,80 @@ static inline void gencontext_emit_return(GenContext *c, Ast *ast) -void gencontext_emit_if(GenContext *c, Ast *ast) +/** + * Emit if (...) { ... } else { ... } + * + * This code is slightly optimized to omit branches when not needed. This is something LLVM + * will optimize as well, but it is convenient to make the code slightly smaller for LLVM to work with: + * 1. If the "then" branch is empty, replace it with "exit". + * 2. If the "else" branch is empty or missing, replace if with "exit". + * 3. If both "else" and "then" branches are empty, replace it with just the condition and remove the "exit" + */ +void llvm_emit_if(GenContext *c, Ast *ast) { // We need at least the exit block and the "then" block. LLVMBasicBlockRef exit_block = llvm_basic_block_new(c, "if.exit"); - LLVMBasicBlockRef then_block = llvm_basic_block_new(c, "if.then"); - LLVMBasicBlockRef else_block = NULL; + LLVMBasicBlockRef then_block = exit_block; + LLVMBasicBlockRef else_block = exit_block; + + // Only generate a target if + if (!ast_is_empty_compound_stmt(ast->if_stmt.then_body)) + { + then_block = llvm_basic_block_new(c, "if.then"); + } // We have an optional else block. - if (ast->if_stmt.else_body) + if (ast->if_stmt.else_body && !ast_is_empty_compound_stmt(ast->if_stmt.else_body)) { else_block = llvm_basic_block_new(c, "if.else"); } - else - { - else_block = exit_block; - } ast->if_stmt.break_block = exit_block; + // Output boolean value and switch. - PUSH_ERROR(); - - c->catch_block = else_block ?: exit_block; - c->error_var = NULL; - - BEValue be_value = {}; - gencontext_emit_decl_expr_list(c, &be_value, ast->if_stmt.cond, true); - llvm_value_rvalue(c, &be_value); - - assert(llvm_value_is_bool(&be_value)); - - if (llvm_value_is_const(&be_value)) - { - if (LLVMConstIntGetZExtValue(be_value.value)) - { - llvm_emit_br(c, then_block); - else_block = NULL; - } - else - { - llvm_emit_br(c, else_block); - then_block = NULL; - } - } - else - { - llvm_emit_cond_br(c, &be_value, then_block, else_block); - } - - POP_ERROR(); - Decl *label = ast->if_stmt.flow.label; if (label) { label->label.break_target = exit_block; } + BEValue be_value = {}; + gencontext_emit_decl_expr_list(c, &be_value, ast->if_stmt.cond, true); + llvm_value_rvalue(c, &be_value); + + assert(llvm_value_is_bool(&be_value)); + + bool exit_in_use = true; + + if (llvm_value_is_const(&be_value) && then_block != else_block) + { + if (LLVMConstIntGetZExtValue(be_value.value)) + { + llvm_emit_br(c, then_block); + else_block = exit_block; + } + else + { + llvm_emit_br(c, else_block); + then_block = exit_block; + } + } + else + { + if (then_block != else_block) + { + llvm_emit_cond_br(c, &be_value, then_block, else_block); + } + else + { + exit_in_use = LLVMGetFirstUse(LLVMBasicBlockAsValue(exit_block)) != NULL; + if (exit_in_use) llvm_emit_br(c, exit_block); + } + } + // Emit the 'then' code. - if (then_block) + if (then_block != exit_block) { llvm_emit_block(c, then_block); llvm_emit_stmt(c, ast->if_stmt.then_body); @@ -275,9 +297,8 @@ void gencontext_emit_if(GenContext *c, Ast *ast) llvm_emit_br(c, exit_block); } - // Emit the 'else' branch if present. - if (else_block && else_block != exit_block) + if (else_block != exit_block) { llvm_emit_block(c, else_block); llvm_emit_stmt(c, ast->if_stmt.else_body); @@ -285,7 +306,10 @@ void gencontext_emit_if(GenContext *c, Ast *ast) } // And now we just emit the exit block. - llvm_emit_block(c, exit_block); + if (exit_in_use) + { + llvm_emit_block(c, exit_block); + } } @@ -1044,7 +1068,7 @@ void llvm_emit_stmt(GenContext *c, Ast *ast) gencontext_emit_continue(c, ast); break; case AST_IF_STMT: - gencontext_emit_if(c, ast); + llvm_emit_if(c, ast); break; case AST_RETURN_STMT: gencontext_emit_return(c, ast); diff --git a/src/compiler/llvm_codegen_type.c b/src/compiler/llvm_codegen_type.c index dfa64ac56..1f1573edb 100644 --- a/src/compiler/llvm_codegen_type.c +++ b/src/compiler/llvm_codegen_type.c @@ -5,7 +5,7 @@ #include "llvm_codegen_internal.h" -static inline LLVMTypeRef llvm_type_from_decl(GenContext *context, Decl *decl) +static inline LLVMTypeRef llvm_type_from_decl(GenContext *c, Decl *decl) { static LLVMTypeRef params[MAX_PARAMS]; switch (decl->decl_kind) @@ -19,49 +19,43 @@ static inline LLVMTypeRef llvm_type_from_decl(GenContext *context, Decl *decl) { VECEACH(decl->func.function_signature.params, i) { - params[i] = llvm_get_type(context, decl->func.function_signature.params[i]->type); + params[i] = llvm_get_type(c, decl->func.function_signature.params[i]->type); } unsigned param_size = vec_size(decl->func.function_signature.params); - return LLVMFunctionType(llvm_get_type(context, decl->func.function_signature.rtype->type), + return LLVMFunctionType(llvm_get_type(c, decl->func.function_signature.rtype->type), params, param_size, decl->func.function_signature.variadic); } case DECL_TYPEDEF: - return llvm_get_type(context, decl->typedef_decl.type_info->type); + return llvm_get_type(c, decl->typedef_decl.type_info->type); case DECL_STRUCT: { LLVMTypeRef *types = NULL; - LLVMTypeRef type = LLVMStructCreateNamed(context->context, decl->external_name); + LLVMTypeRef type = LLVMStructCreateNamed(c->context, decl->external_name); // Avoid recursive issues. decl->type->backend_type = type; Decl **members = decl->strukt.members; VECEACH(members, i) { - vec_add(types, llvm_get_type(context, members[i]->type)); + Decl *member = members[i]; + if (member->padding) + { + vec_add(types, llvm_const_padding_type(c, member->padding)); + } + vec_add(types, llvm_get_type(c, members[i]->type)); } - if (decl->needs_additional_pad) + if (decl->strukt.padding) { - Decl *last_member = VECLAST(members); - unsigned member_end = last_member->offset + type_size(last_member->type); - unsigned bytes = decl->strukt.size - member_end; - assert(bytes > 0); - if (bytes == 1) - { - vec_add(types, llvm_get_type(context, type_byte)); - } - else - { - vec_add(types, LLVMArrayType(llvm_get_type(context, type_byte), bytes)); - } + vec_add(types, llvm_const_padding_type(c, decl->strukt.padding)); } LLVMStructSetBody(type, types, vec_size(types), decl->is_packed); return type; } case DECL_UNION: { - LLVMTypeRef type = LLVMStructCreateNamed(context->context, decl->external_name); + LLVMTypeRef type = LLVMStructCreateNamed(c->context, decl->external_name); // Avoid recursive issues. decl->type->backend_type = type; Decl **members = decl->strukt.members; @@ -70,14 +64,13 @@ static inline LLVMTypeRef llvm_type_from_decl(GenContext *context, Decl *decl) Decl *rep_type = members[decl->strukt.union_rep]; LLVMTypeRef type_ref[2] = { - llvm_get_type(context, rep_type->type), + llvm_get_type(c, rep_type->type), NULL }; unsigned elements = 1; - if (decl->needs_additional_pad) + if (decl->strukt.padding) { - type_ref[elements++] = LLVMArrayType(llvm_get_type(context, type_bool), type_size(decl->type) - type_size(rep_type->type)); - + type_ref[elements++] = llvm_const_padding_type(c, decl->strukt.padding); } LLVMStructSetBody(type, type_ref, elements, decl->is_packed); } @@ -88,15 +81,14 @@ static inline LLVMTypeRef llvm_type_from_decl(GenContext *context, Decl *decl) return type; } case DECL_ENUM: - return llvm_get_type(context, decl->type); + return llvm_get_type(c, decl->type); case DECL_ERR: { - LLVMTypeRef err_type = LLVMStructCreateNamed(context->context, decl->external_name); + LLVMTypeRef err_type = LLVMStructCreateNamed(c->context, decl->external_name); // Avoid recursive issues. decl->type->backend_type = err_type; LLVMTypeRef *types = NULL; - vec_add(types, llvm_get_type(context, type_typeid)); - unsigned size = type_size(type_typeid); + unsigned size = 0; VECEACH(decl->strukt.members, i) { Type *type = decl->strukt.members[i]->type->canonical; @@ -106,12 +98,11 @@ static inline LLVMTypeRef llvm_type_from_decl(GenContext *context, Decl *decl) size += alignment - size % alignment; } size += type_size(type); - vec_add(types, llvm_get_type(context, type)); + vec_add(types, llvm_get_type(c, type)); } - unsigned padding = type_size(type_error) - size; - if (padding > 0) + if (decl->strukt.padding) { - vec_add(types, LLVMIntTypeInContext(context->context, padding * 8)); + vec_add(types, llvm_const_padding_type(c, decl->strukt.padding)); } LLVMStructSetBody(err_type, types, vec_size(types), false); return err_type; diff --git a/src/compiler/parse_expr.c b/src/compiler/parse_expr.c index 154695aa4..b3823aa2b 100644 --- a/src/compiler/parse_expr.c +++ b/src/compiler/parse_expr.c @@ -436,10 +436,10 @@ static Expr *parse_subscript_expr(Context *context, Expr *left) else { index = EXPR_NEW_TOKEN(EXPR_CONST, context->tok); - index->type = type_usize; + index->type = type_uint; index->constant = true; index->resolve_status = RESOLVE_DONE; - expr_const_set_int(&index->const_expr, 0, type_usize->canonical->type_kind); + expr_const_set_int(&index->const_expr, 0, type_uint->type_kind); } if (try_consume(context, TOKEN_DOTDOT)) { diff --git a/src/compiler/parse_global.c b/src/compiler/parse_global.c index b2accdf30..0c2a8f590 100644 --- a/src/compiler/parse_global.c +++ b/src/compiler/parse_global.c @@ -1190,6 +1190,7 @@ bool parse_struct_body(Context *context, Decl *parent) return false; } advance(context); + if (!parse_attributes(context, member)) return false; if (!try_consume(context, TOKEN_COMMA)) break; if (was_inline) { @@ -1888,7 +1889,7 @@ Decl *parse_top_level_statement(Context *context) { Ast *ast = TRY_AST_OR(parse_ct_assert_stmt(context), false); vec_add(context->ct_asserts, ast); - return poisoned_decl; + return NULL; } case TOKEN_CT_IF: if (!check_no_visibility_before(context, visibility)) return poisoned_decl; diff --git a/src/compiler/parse_stmt.c b/src/compiler/parse_stmt.c index 01148e1ed..69e7dd61b 100644 --- a/src/compiler/parse_stmt.c +++ b/src/compiler/parse_stmt.c @@ -786,7 +786,7 @@ static inline Ast* parse_ct_for_stmt(Context *context) } ast->ct_for_stmt.value = context->tok.id; TRY_CONSUME_OR(TOKEN_CT_IDENT, "Expected a compile time variable", poisoned_ast); - TRY_CONSUME_OR(TOKEN_IN, "Expected 'in'.", poisoned_ast); + TRY_CONSUME_OR(TOKEN_COLON, "Expected ':'.", poisoned_ast); ast->ct_for_stmt.expr = TRY_EXPR_OR(parse_expr(context), poisoned_ast); CONSUME_OR(TOKEN_RPAREN, poisoned_ast); ast->ct_for_stmt.body = TRY_AST(parse_stmt(context)); @@ -1086,7 +1086,6 @@ Ast *parse_stmt(Context *context) case TOKEN_CT_ENDIF: case TOKEN_CT_ENDSWITCH: case TOKEN_RBRAPIPE: - case TOKEN_IN: case TOKEN_BANGBANG: SEMA_TOKEN_ERROR(context->tok, "Unexpected '%s' found when expecting a statement.", token_type_to_string(context->tok.type)); advance(context); diff --git a/src/compiler/parser.c b/src/compiler/parser.c index 2e43a0695..0a9e218a2 100644 --- a/src/compiler/parser.c +++ b/src/compiler/parser.c @@ -120,7 +120,7 @@ static inline void parse_translation_unit(Context *context) while (!TOKEN_IS(TOKEN_EOF)) { Decl *decl = parse_top_level_statement(context); - assert(decl); + if (!decl) continue; if (decl_ok(decl)) { vec_add(context->global_decls, decl); diff --git a/src/compiler/sema_casts.c b/src/compiler/sema_casts.c index 10a2d8eeb..361c124ff 100644 --- a/src/compiler/sema_casts.c +++ b/src/compiler/sema_casts.c @@ -496,7 +496,7 @@ bool ixxbo(Context *context, Expr *left, Type *type) /** * Cast comptime, signed or unsigned -> pointer. - * @return true unless the constant value evaluates to zero. + * @return true if the cast succeeds. */ bool xipt(Context *context, Expr *left, Type *from, Type *canonical, Type *type, CastType cast_type) { @@ -504,14 +504,11 @@ bool xipt(Context *context, Expr *left, Type *from, Type *canonical, Type *type, if (left->expr_kind == EXPR_CONST) { RETURN_NON_CONST_CAST(CAST_XIPTR); - if (bigint_cmp_zero(&left->const_expr.i) != CMP_EQ) + if (bigint_cmp_zero(&left->const_expr.i) == CMP_EQ) { - SEMA_ERROR(left, "Cannot cast non zero constants into pointers."); - return false; + expr_const_set_null(&left->const_expr); + left->type = type; } - expr_const_set_null(&left->const_expr); - left->type = type; - return true; } if (type_size(from) < type_size(type_voidptr)) { diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index 25f64defb..9ac5f4b52 100644 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -106,12 +106,24 @@ static bool sema_analyse_union_members(Context *context, Decl *decl, Decl **memb decl->strukt.union_rep = max_alignment_element; + if (!max_size) + { + decl->strukt.size = 0; + decl->alignment = 1; + return true; + } + // The actual size might be larger than the max size due to alignment. unsigned size = aligned_offset(max_size, decl->alignment); + unsigned rep_size = type_size(members[max_alignment_element]->type); + // If the actual size is bigger than the real size, add // padding. - decl->needs_additional_pad = size > max_size; + if (size > rep_size) + { + decl->strukt.padding = size - rep_size; + } decl->strukt.size = size; @@ -146,36 +158,56 @@ static bool sema_analyse_struct_members(Context *context, Decl *decl, Decl **mem if (!decl_ok(decl)) return false; - AlignSize member_alignment = type_abi_alignment(member->type); + AlignSize member_natural_alignment = type_abi_alignment(member->type); + AlignSize member_alignment = is_packed ? 1 : member_natural_alignment; + Attr **attributes = member->attributes; + unsigned count = vec_size(attributes); + for (unsigned j = 0; j < count; j++) + { + Attr *attribute = attributes[j]; + if (!sema_analyse_attribute(context, attribute, ATTR_VAR)) return false; + if (TOKSTR(attribute->name) == kw_align) + { + member_alignment = attribute->alignment; + // Update total alignment if we have a member that has bigger alignment. + if (member_alignment > decl->alignment) decl->alignment = member_alignment; + } + } // If the member alignment is higher than the currently detected alignment, // then we update the natural alignment - if (member_alignment > natural_alignment) + if (member_natural_alignment > natural_alignment) { - natural_alignment = member_alignment; + natural_alignment = member_natural_alignment; } + // In the case of a struct, we will align this to the next offset, // using the alignment of the member. unsigned align_offset = aligned_offset(offset, member_alignment); - // Usually we align things, but if this is a packed struct we don't - // so offsets may mismatch - if (align_offset > offset) + unsigned natural_align_offset = aligned_offset(offset, member_natural_alignment); + + // If the natural align is different from the aligned offset we have two cases: + if (natural_align_offset != align_offset) { - if (is_packed) + // If the natural alignment is greater, in this case the struct is unaligned. + if (member_natural_alignment > member_alignment) { - // If it is packed, we note that the packing made it unaligned. + assert(natural_align_offset > align_offset); is_unaligned = true; } else { - // Otherwise we insert the (implicit) padding by moving to the aligned offset. - offset = align_offset; + // Otherwise we have a greater offset, and in this case + // we add padding for the difference. + assert(natural_align_offset < align_offset); + member->padding = align_offset - offset; } } + + offset = align_offset; member->offset = offset; offset += type_size(member->type); - ; } // Set the alignment: @@ -194,7 +226,7 @@ static bool sema_analyse_struct_members(Context *context, Decl *decl, Decl **mem // in this case we need an additional padding if (size > aligned_offset(offset, natural_alignment)) { - decl->needs_additional_pad = true; + decl->strukt.padding = size - offset; } // If the size is smaller the naturally aligned struct, then it is also unaligned @@ -204,7 +236,8 @@ static bool sema_analyse_struct_members(Context *context, Decl *decl, Decl **mem } if (is_unaligned && size > offset) { - decl->needs_additional_pad = true; + assert(!decl->strukt.padding); + decl->strukt.padding = size - offset; } decl->is_packed = is_unaligned; decl->strukt.size = size; @@ -535,6 +568,8 @@ static const char *attribute_domain_to_string(AttributeDomain domain) { switch (domain) { + case ATTR_MEMBER: + return "member"; case ATTR_FUNC: return "function"; case ATTR_VAR: @@ -564,11 +599,11 @@ static AttributeType sema_analyse_attribute(Context *context, Attr *attr, Attrib } static AttributeDomain attribute_domain[NUMBER_OF_ATTRIBUTES] = { [ATTRIBUTE_WEAK] = ATTR_FUNC | ATTR_CONST | ATTR_VAR, - [ATTRIBUTE_CNAME] = 0xFF, + [ATTRIBUTE_CNAME] = ~0, [ATTRIBUTE_SECTION] = ATTR_FUNC | ATTR_CONST | ATTR_VAR, [ATTRIBUTE_PACKED] = ATTR_STRUCT | ATTR_UNION | ATTR_ERROR, [ATTRIBUTE_NORETURN] = ATTR_FUNC, - [ATTRIBUTE_ALIGN] = ATTR_FUNC | ATTR_CONST | ATTR_VAR | ATTR_STRUCT | ATTR_UNION, + [ATTRIBUTE_ALIGN] = ATTR_FUNC | ATTR_CONST | ATTR_VAR | ATTR_STRUCT | ATTR_UNION | ATTR_MEMBER, [ATTRIBUTE_INLINE] = ATTR_FUNC, [ATTRIBUTE_NOINLINE] = ATTR_FUNC, [ATTRIBUTE_OPAQUE] = ATTR_STRUCT | ATTR_UNION, @@ -756,14 +791,35 @@ static inline bool sema_analyse_global(Context *context, Decl *decl) if (!sema_resolve_type_info(context, decl->var.type_info)) return false; decl->type = decl->var.type_info->type; } + // Check the initializer. if (decl->var.init_expr && decl->type) { - if (!sema_analyse_expr_of_required_type(context, decl->type, decl->var.init_expr, false)) return false; - if (!decl->var.init_expr->constant) + Expr *init_expr = decl->var.init_expr; + // 1. Check type. + if (!sema_analyse_expr_of_required_type(context, decl->type, init_expr, false)) return false; + // 2. Check const-ness + if (!init_expr->constant) { - - SEMA_ERROR(decl->var.init_expr, "The expression must be a constant value."); - return false; + // 3. Special case is when the init expression is the reference + // to a constant global structure. + if (init_expr->expr_kind == EXPR_CONST_IDENTIFIER) + { + // 4. If so we copy the init expression, which should always be constant. + *init_expr = *init_expr->identifier_expr.decl->var.init_expr; + assert(init_expr->constant); + } + else + { + if (init_expr->expr_kind == EXPR_CAST) + { + SEMA_ERROR(decl->var.init_expr, "The expression may not be a non constant cast."); + } + else + { + SEMA_ERROR(decl->var.init_expr, "The expression must be a constant value."); + } + return false; + } } if (!decl->type) decl->type = decl->var.init_expr->type; } @@ -838,11 +894,18 @@ static inline bool sema_analyse_define(Context *context, Decl *decl) static inline bool sema_analyse_error(Context *context __unused, Decl *decl) { if (!sema_analyse_struct_union(context, decl)) return false; - if (decl->strukt.size > type_size(type_usize)) + ByteSize error_full_size = type_size(type_voidptr); + if (decl->strukt.size > error_full_size) { - SEMA_ERROR(decl, "Error type may not exceed pointer size (%d bytes) it was %d bytes.", type_size(type_usize), decl->strukt.size); + SEMA_ERROR(decl, "Error type may not exceed pointer size (%d bytes) it was %d bytes.", error_full_size, decl->strukt.size); return false; } + + if (decl->strukt.size < error_full_size) + { + decl->strukt.padding = error_full_size - decl->strukt.size; + decl->strukt.size = error_full_size; + } return true; } diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index 529dbd4b7..57408a371 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -199,6 +199,7 @@ static inline bool sema_cast_ident_rvalue(Context *context, Type *to, Expr *expr switch (decl->var.kind) { case VARDECL_CONST: + if (!type_is_builtin(decl->type->type_kind)) break; expr_replace(expr, expr_copy_from_macro(context, decl->var.init_expr)); return sema_analyse_expr(context, to, expr); case VARDECL_PARAM_EXPR: @@ -254,9 +255,70 @@ static inline bool sema_type_error_on_binop(Context *context, Expr *expr) static bool expr_cast_to_index(Context *context, Expr *index) { - printf("TODO: consider changing indexing size\n"); - if (index->type->canonical->type_kind == type_usize->canonical->type_kind) return true; - return cast_implicit(context, index, type_isize); + switch (index->type->canonical->type_kind) + { + case TYPE_IXX: + if (!bigint_fits_in_bits(&index->const_expr.i, 64, true)) + { + SEMA_ERROR(index, "The index is out of range, it must fit in a signed 64 bit integer."); + return false; + } + return cast_implicit(context, index, type_isize); + case TYPE_I8: + case TYPE_I16: + return cast_implicit(context, index, type_int); + case TYPE_U8: + case TYPE_U16: + return cast_implicit(context, index, type_uint); + case TYPE_I32: + case TYPE_U32: + case TYPE_I64: + case TYPE_U64: + // This is fine. + return true; + case TYPE_U128: + SEMA_ERROR(index, "You need to explicitly cast this to a uint or ulong."); + return false; + case TYPE_I128: + SEMA_ERROR(index, "You need to explicitly cast this to a int or long."); + return false; + default: + SEMA_ERROR(index, "Cannot implicitly convert '%s' to an index.", type_to_error_string(index->type)); + return false; + } +} + +static bool expr_cast_to_index_size(Context *context, Expr *index) +{ + switch (index->type->canonical->type_kind) + { + case TYPE_IXX: + if (!bigint_fits_in_bits(&index->const_expr.i, 64, true)) + { + SEMA_ERROR(index, "The index is out of range, it must fit in a signed 64 bit integer."); + return false; + } + return cast_implicit(context, index, type_isize); + case TYPE_U8: + case TYPE_U16: + case TYPE_U32: + case TYPE_U64: + return cast_implicit(context, index, type_usize); + case TYPE_I8: + case TYPE_I16: + case TYPE_I32: + case TYPE_I64: + return cast_implicit(context, index, type_isize); + case TYPE_U128: + SEMA_ERROR(index, "You need to explicitly cast this to a usize."); + return false; + case TYPE_I128: + SEMA_ERROR(index, "You need to explicitly cast this to a size."); + return false; + default: + SEMA_ERROR(index, "Cannot implicitly convert '%s' to an index.", type_to_error_string(index->type)); + return false; + } } static inline bool sema_expr_analyse_ternary(Context *context, Type *to, Expr *expr) @@ -1204,6 +1266,9 @@ static inline bool sema_expr_analyse_call(Context *context, Type *to, Expr *expr return true; } FALLTHROUGH; + case EXPR_TYPEINFO: + SEMA_ERROR(expr, "A type cannot be followed by (), did you mean to use ({})?"); + return false; default: SEMA_ERROR(expr, "This value cannot be invoked, did you accidentally add ()?"); return false; @@ -1358,12 +1423,12 @@ static inline bool sema_expr_analyse_subscript(Context *context, Expr *expr) SEMA_ERROR(subscripted, "Cannot index '%s'.", type_to_error_string(type)); return false; } - if (!sema_analyse_expr(context, type_isize, index)) return false; + if (!sema_analyse_expr(context, type_int, index)) return false; expr->constant = index->constant & subscripted->constant; expr->pure = index->pure & subscripted->pure; - // Unless we already have type_usize, cast to type_isize; + // Cast to an appropriate type for index. if (!expr_cast_to_index(context, index)) return false; // Check range @@ -1397,16 +1462,16 @@ static inline bool sema_expr_analyse_slice(Context *context, Expr *expr) } expr->slice_expr.expr = current_expr; - if (!sema_analyse_expr(context, type_isize, start)) return false; + if (!sema_analyse_expr(context, type_int, start)) return false; expr->pure &= start->pure; expr->constant &= start->constant; - if (end && !sema_analyse_expr(context, type_isize, end)) return false; + if (end && !sema_analyse_expr(context, type_int, end)) return false; expr->pure &= !end || end->pure; expr->constant &= !end || end->constant; - // Unless we already have type_usize, cast to type_isize; - if (!expr_cast_to_index(context, start)) return false; - if (end && !expr_cast_to_index(context, end)) return false; + // Fix index sizes + if (!expr_cast_to_index_size(context, start)) return false; + if (end && !expr_cast_to_index_size(context, end)) return false; // Check range if (type->type_kind == TYPE_POINTER) @@ -1992,7 +2057,7 @@ static Decl *sema_resolve_element_for_name(Decl** decls, DesignatorElement **ele static int64_t sema_analyse_designator_index(Context *context, Expr *index) { - if (!sema_analyse_expr_value(context, type_usize, index)) + if (!sema_analyse_expr_value(context, type_uint, index)) { return -1; } @@ -2093,12 +2158,13 @@ static Type *sema_expr_analyse_designator(Context *context, Type *current, Expr bool did_report_error = false; for (unsigned i = 0; i < vec_size(path); i++) { - current = sema_find_type_of_element(context, current, path, &i, &is_constant, &did_report_error); - if (!current) break; - } - if (!current && !did_report_error) - { - SEMA_ERROR(expr, "This is not a valid member of '%s'.", type_to_error_string(current)); + Type *new_current = sema_find_type_of_element(context, current, path, &i, &is_constant, &did_report_error); + if (!new_current) + { + if (!did_report_error) SEMA_ERROR(expr, "This is not a valid member of '%s'.", type_to_error_string(current)); + return NULL; + } + current = new_current; } expr->constant = is_constant; expr->pure = is_constant; @@ -2112,15 +2178,17 @@ static void sema_update_const_initializer_with_designator(ConstInitializer *cons static void sema_create_const_initializer_value(ConstInitializer *const_init, Expr *value) { + // Possibly this is already a const initializers, in that case + // overwrite what is inside, eg [1] = { .a = 1 } if (value->expr_kind == EXPR_INITIALIZER_LIST && value->initializer_expr.init_type == INITIALIZER_CONST) { *const_init = *value->initializer_expr.initializer; value->initializer_expr.initializer = const_init; return; } + const_init->init_value = value; const_init->type = value->type->canonical; - const_init->kind = CONST_VALUE; - const_init->value = value; + const_init->kind = CONST_INIT_VALUE; } static bool is_empty_initializer_list(Expr *value) @@ -2129,44 +2197,71 @@ static bool is_empty_initializer_list(Expr *value) } static void sema_create_const_initializer(ConstInitializer *const_init, Expr *initializer); +/** + * Update a struct element, e.g. { .a = 1 } or { .a[12] = { .b } } + */ static inline void sema_update_const_initializer_with_designator_struct(ConstInitializer *const_init, DesignatorElement **curr, DesignatorElement **end, Expr *value) { + // Get the current path element that we're processing DesignatorElement *element = curr[0]; assert(element->kind == DESIGNATOR_FIELD); DesignatorElement **next_element = curr + 1; - bool at_end = next_element == end; - // Optimize in case this is a zero. - if (at_end && is_empty_initializer_list(value)) + bool is_last_path_element = next_element == end; + + // Optimize in case this is a zero, e.g. [12].b = {} + if (is_last_path_element && is_empty_initializer_list(value)) { const_init->kind = CONST_INIT_ZERO; return; } + Decl **elements = const_init->type->decl->strukt.members; + + // Convert a zero struct and expand it into all its parts. if (const_init->kind == CONST_INIT_ZERO) { - Decl **elements = const_init->type->decl->strukt.members; + // Allocate array containing all elements { a, b, c ... } ConstInitializer **const_inits = MALLOC(sizeof(ConstInitializer *) * vec_size(elements)); VECEACH(elements, i) { - ConstInitializer *element_init = CALLOCS(ConstInitializer); + // Create zero initializers for each of those { a: zeroinit, b: zeroinit, ... } + ConstInitializer *element_init = MALLOC(sizeof(ConstInitializer)); element_init->type = elements[i]->type->canonical; element_init->kind = CONST_INIT_ZERO; const_inits[i] = element_init; } - const_init->elements = const_inits; - const_init->kind = CONST_INIT_EXPANDED; + // Change type to CONST_INIT_STRUCT since we expanded. + const_init->init_struct = const_inits; + const_init->kind = CONST_INIT_STRUCT; } - ConstInitializer *sub_element = const_init->elements[element->index]; - if (!at_end) + + // We should always have expanded the struct at this point. + assert(const_init->kind == CONST_INIT_STRUCT); + + // Find the ConstInitializer to change + ConstInitializer *sub_element = const_init->init_struct[element->index]; + + // If this isn't the last element, we recurse. + if (!is_last_path_element) { sema_update_const_initializer_with_designator(sub_element, next_element, end, value); return; } + + // Otherwise we update the value in that particular element. sema_create_const_initializer_value(sub_element, value); } +/** + * Update a union element, which is different from structs, since we're here + * only keeping a single value. + * Note that if we have two fields "a" and "b", then in this case { .b = 2, .a = 1 }, + * we will completely discard the information in ".b = 2", even if there had been + * an overlap. In essence we're implicitly clearing the memory when we assign to .a, meaning + * we are allowed to completely overwrite the "memory" of .b + */ static inline void sema_update_const_initializer_with_designator_union(ConstInitializer *const_init, DesignatorElement **curr, DesignatorElement **end, @@ -2174,123 +2269,58 @@ static inline void sema_update_const_initializer_with_designator_union(ConstInit { DesignatorElement *element = curr[0]; assert(element->kind == DESIGNATOR_FIELD); - ConstInitializer *sub_element = const_init->union_const.element; + ConstInitializer *sub_element = const_init->init_union.element; + // If it's an empty initializer, just clear everything back to CONST_INIT_ZERO + // That is we get for example: { .b = { 1, 3 }, .a = { } }. This would then simply + // become { } DesignatorElement **next_element = curr + 1; - bool is_at_end = next_element == end; - if (is_at_end && is_empty_initializer_list(value)) + bool is_at_last_path_element = next_element == end; + if (is_at_last_path_element && is_empty_initializer_list(value)) { const_init->kind = CONST_INIT_ZERO; return; } + + // If we currently have a zero, then we create a sub element that is zero. if (const_init->kind == CONST_INIT_ZERO) { - sub_element = const_init->union_const.element = CALLOCS(ConstInitializer); + sub_element = const_init->init_union.element = CALLOCS(ConstInitializer); sub_element->kind = CONST_INIT_ZERO; - const_init->union_const.element = sub_element; + const_init->init_union.element = sub_element; } - else if (element->index != const_init->union_const.index) + else if (element->index != const_init->init_union.index) { + // We will zero out the sub element if it wasn't a union sub_element->kind = CONST_INIT_ZERO; - sub_element->type = value->type; } + + // Update of the sub element. sub_element->type = const_init->type->decl->strukt.members[element->index]->type->canonical; - const_init->union_const.index = element->index; - const_init->kind = CONST_SELECTED; - if (!is_at_end) + + // And the index + const_init->init_union.index = element->index; + + // And the type + const_init->kind = CONST_INIT_UNION; + + // If we're not at the last element in the path, descend further. + if (!is_at_last_path_element) { sema_update_const_initializer_with_designator(sub_element, next_element, end, value); return; } + + // Otherwise just set the current type. sema_create_const_initializer_value(sub_element, value); } -static inline ConstInitializer *sema_find_const_init_array_slice(ConstInitializer *const_init, ArrayIndex index) -{ - if (!const_init) return NULL; - switch (const_init->kind) - { - case CONST_INIT_ARRAY_SPLIT: - { - ConstInitializer *found; - if ((found = sema_find_const_init_array_slice(const_init->split_const.low, index))) return found; - if ((found = sema_find_const_init_array_slice(const_init->split_const.mid, index))) return found; - if ((found = sema_find_const_init_array_slice(const_init->split_const.hi, index))) return found; - return false; - } - case CONST_INIT_ARRAY_RANGE_ZERO: - if (const_init->array_range_zero.low > index || const_init->array_range_zero.high < index) return NULL; - return const_init; - case CONST_INIT_ARRAY_VALUE_FRAGMENT: - return const_init->single_array_index.index == index ? const_init : NULL; - default: - UNREACHABLE - } -} - -static inline ConstInitializer *sema_split_const_init_array_slice(ConstInitializer *const_init, ArrayIndex index) -{ - switch (const_init->kind) - { - case CONST_INIT_ARRAY_SPLIT: - UNREACHABLE - case CONST_INIT_ARRAY_RANGE_ZERO: - { - // Special case: single element. - if (const_init->array_range_zero.low == const_init->array_range_zero.high) - { - const_init->kind = CONST_INIT_ARRAY_VALUE_FRAGMENT; - const_init->type = const_init->type->array.base; - const_init->single_array_index.index = index; - const_init->single_array_index.element = MALLOC(sizeof(ConstInitializer)); - const_init->single_array_index.element->type = const_init->type->array.base; - const_init->single_array_index.element->kind = CONST_INIT_ZERO; - return const_init; - } - ConstInitializer *low = NULL; - ConstInitializer *hi = NULL; - if (const_init->array_range_zero.low < index) - { - low = MALLOC(sizeof(ConstInitializer)); - low->kind = CONST_INIT_ARRAY_RANGE_ZERO; - low->array_range_zero.low = const_init->array_range_zero.low; - low->array_range_zero.high = index - 1; - low->type = type_get_array(const_init->type->array.base, low->array_range_zero.high - low->array_range_zero.low + 1); - } - if (const_init->array_range_zero.high > index) - { - hi = MALLOC(sizeof(ConstInitializer)); - hi->kind = CONST_INIT_ARRAY_RANGE_ZERO; - hi->array_range_zero.low = index + 1; - hi->array_range_zero.high = const_init->array_range_zero.high; - hi->type = type_get_array(const_init->type->array.base, hi->array_range_zero.high - hi->array_range_zero.low + 1); - } - ConstInitializer *mid = MALLOC(sizeof(ConstInitializer)); - mid->kind = CONST_INIT_ARRAY_VALUE_FRAGMENT; - mid->type = const_init->type->array.base; - mid->single_array_index.index = index; - mid->single_array_index.element = MALLOC(sizeof(ConstInitializer)); - mid->single_array_index.element->type = const_init->type->array.base; - mid->single_array_index.element->kind = CONST_INIT_ZERO; - const_init->split_const.low = low; - const_init->split_const.mid = mid; - const_init->split_const.hi = hi; - const_init->kind = CONST_INIT_ARRAY_SPLIT; - return mid; - } - case CONST_INIT_ARRAY_VALUE_FRAGMENT: - return const_init; - case CONST_INIT_ZERO: - case CONST_INIT_EXPANDED: - case CONST_SELECTED: - case CONST_VALUE: - return NULL; - } - UNREACHABLE -} +/** + * Update an array { [2] = 1 } + */ static inline void sema_update_const_initializer_with_designator_array(ConstInitializer *const_init, DesignatorElement **curr, DesignatorElement **end, @@ -2300,25 +2330,91 @@ static inline void sema_update_const_initializer_with_designator_array(ConstInit ArrayIndex low_index = element->index; ArrayIndex high_index = element->kind == DESIGNATOR_RANGE ? element->index_end : element->index; assert(element->kind == DESIGNATOR_ARRAY || element->kind == DESIGNATOR_RANGE); + + // Expand zero into array. if (const_init->kind == CONST_INIT_ZERO) { - const_init->kind = CONST_INIT_ARRAY_RANGE_ZERO; - const_init->array_range_zero.low = 0; - const_init->array_range_zero.high = const_init->type->array.len - 1; + const_init->kind = CONST_INIT_ARRAY; + const_init->init_array.elements = NULL; } + Type *element_type = const_init->type->array.base; DesignatorElement **next_element = curr + 1; - bool is_direct_update = next_element == end; + bool is_last_path_element = next_element == end; + + // Get all the elements in the array + ConstInitializer **array_elements = const_init->init_array.elements; + + unsigned array_count = vec_size(array_elements); + + ArrayIndex insert_index = 0; + for (ArrayIndex index = low_index; index <= high_index; index++) { - ConstInitializer *sub_element = sema_find_const_init_array_slice(const_init, index); - sub_element = sema_split_const_init_array_slice(sub_element, index)->single_array_index.element; - if (!is_direct_update) + // Walk to the insert point or until we reached the end of the array. + while (insert_index < array_count && array_elements[insert_index]->init_array_value.index < index) { - sema_update_const_initializer_with_designator(sub_element, next_element, end, value); + insert_index++; + } + // Pick up the initializer at the insert point. + ConstInitializer *initializer = insert_index < array_count ? array_elements[insert_index] : NULL; + ConstInitializer *inner_value; + + // If we don't have an initializer, the location needs to be at the end. + // Create and append: + if (!initializer) + { + initializer = MALLOC(sizeof(ConstInitializer)); + initializer->type = element_type; + initializer->kind = CONST_INIT_ARRAY_VALUE; + initializer->init_array_value.index = index; + inner_value = MALLOC(sizeof(ConstInitializer)); + inner_value->type = element_type; + inner_value->kind = CONST_INIT_ZERO; + initializer->init_array_value.element = inner_value; + vec_add(array_elements, initializer); + array_count++; + } + else + { + // If we already have an initializer "found" + // it might be after the index. In this case, we + // need to do an insert. + if (initializer->init_array_value.index != insert_index) + { + assert(initializer->init_array_value.index > insert_index); + + // First we add a null at the end. + vec_add(array_elements, NULL); + // Shift all values one step up: + for (unsigned i = array_count; i > insert_index; i--) + { + array_elements[i] = array_elements[i - 1]; + } + // Then we create our new entry. + initializer = MALLOC(sizeof(ConstInitializer)); + initializer->type = element_type; + initializer->kind = CONST_INIT_ARRAY_VALUE; + initializer->init_array_value.index = index; + inner_value = MALLOC(sizeof(ConstInitializer)); + inner_value->type = element_type; + inner_value->kind = CONST_INIT_ZERO; + initializer->init_array_value.element = inner_value; + // And assign it to the location. + array_elements[insert_index] = initializer; + } + } + + const_init->init_array.elements = array_elements; + inner_value = initializer->init_array_value.element; + + // Update + if (!is_last_path_element) + { + sema_update_const_initializer_with_designator(inner_value, next_element, end, value); continue; } - sema_create_const_initializer_value(sub_element, value); + sema_create_const_initializer_value(inner_value, value); } } @@ -2347,10 +2443,11 @@ static inline void sema_update_const_initializer_with_designator( } } + static void sema_create_const_initializer(ConstInitializer *const_init, Expr *initializer) { - const_init->type = initializer->type->canonical; const_init->kind = CONST_INIT_ZERO; + const_init->type = initializer->type->canonical; Expr **init_expressions = initializer->initializer_expr.initializer_expr; VECEACH(init_expressions, i) { @@ -2377,56 +2474,57 @@ static void debug_dump_const_initializer(ConstInitializer *init, const char *nam case CONST_INIT_ZERO: printf(" = 0\n"); return; - case CONST_SELECTED: + case CONST_INIT_UNION: { printf(" ->\n"); Decl** members = init->type->decl->strukt.members; - debug_dump_const_initializer(init->union_const.element, members[init->union_const.index]->name, indent + 1); + debug_dump_const_initializer(init->init_union.element, members[init->init_union.index]->name, indent + 1); return; } - case CONST_INIT_EXPANDED: + case CONST_INIT_STRUCT: { printf(" ->\n"); Decl** members = init->type->decl->strukt.members; unsigned member_count = vec_size(members); for (unsigned i = 0; i < member_count; i++) { - debug_dump_const_initializer(init->elements[i], members[i]->name, indent + 1); + debug_dump_const_initializer(init->init_struct[i], members[i]->name, indent + 1); } return; } - case CONST_INIT_ARRAY_VALUE_FRAGMENT: + case CONST_INIT_ARRAY_VALUE: { - printf(" [%llu] ->\n", (unsigned long long)init->single_array_index.index); - debug_dump_const_initializer(init->single_array_index.element, "", indent + 1); + printf(" [%llu] ->\n", (unsigned long long)init->init_array_value.index); + debug_dump_const_initializer(init->init_array_value.element, "", indent + 1); return; } - case CONST_VALUE: + case CONST_INIT_VALUE: { - assert(init->value->expr_kind == EXPR_CONST); + assert(init->init_value->expr_kind == EXPR_CONST); printf(" = "); - expr_const_fprint(stdout, &init->value->const_expr); + expr_const_fprint(stdout, &init->init_value->const_expr); puts(""); return; } - case CONST_INIT_ARRAY_SPLIT: + case CONST_INIT_ARRAY: printf(" [//] ->\n"); - if (init->split_const.low) { - debug_dump_const_initializer(init->split_const.low, NULL, indent + 1); - } - if (init->split_const.mid) - { - debug_dump_const_initializer(init->split_const.mid, NULL, indent + 1); - } - if (init->split_const.hi) - { - debug_dump_const_initializer(init->split_const.hi, NULL, indent + 1); + VECEACH(init->init_array.elements, i) + { + debug_dump_const_initializer(init->init_array.elements[i], NULL, indent + 1); + } } return; - case CONST_INIT_ARRAY_RANGE_ZERO: - printf(" [%llu .. %llu] = 0\n", (unsigned long long)init->array_range_zero.low, (unsigned long long)init->array_range_zero.high); + case CONST_INIT_ARRAY_FULL: + printf(" [: ->\n"); + { + VECEACH(init->init_array_full, i) + { + debug_dump_const_initializer(init->init_array_full[i], NULL, indent + 1); + } + } return; + } UNREACHABLE } @@ -2456,6 +2554,7 @@ static bool sema_expr_analyse_designated_initializer(Context *context, Type *ass { ConstInitializer *const_init = MALLOC(sizeof(ConstInitializer)); sema_create_const_initializer(const_init, initializer); + debug_dump_const_initializer(const_init, "TOP", 0); initializer->initializer_expr.init_type = INITIALIZER_CONST; initializer->initializer_expr.initializer = const_init; return true; @@ -2523,7 +2622,8 @@ static inline bool sema_expr_analyse_struct_plain_initializer(Context *context, if (initializer->constant) { ConstInitializer *const_init = CALLOCS(ConstInitializer); - const_init->kind = CONST_INIT_EXPANDED; + const_init->kind = CONST_INIT_STRUCT; + const_init->type = initializer->type->canonical; ConstInitializer **inits = MALLOC(sizeof(ConstInitializer *) * vec_size(elements)); VECEACH(elements, i) { @@ -2538,7 +2638,11 @@ static inline bool sema_expr_analyse_struct_plain_initializer(Context *context, sema_create_const_initializer_value(element_init, expr); inits[i] = element_init; } + const_init->init_struct = inits; + initializer->initializer_expr.init_type = INITIALIZER_CONST; + initializer->initializer_expr.initializer = const_init; } + // 7. Done! return true; } @@ -2593,6 +2697,30 @@ static inline bool sema_expr_analyse_array_plain_initializer(Context *context, T return false; } + if (initializer->constant) + { + ConstInitializer *const_init = CALLOCS(ConstInitializer); + const_init->kind = CONST_INIT_ARRAY_FULL; + const_init->type = initializer->type->canonical; + ConstInitializer **inits = MALLOC(sizeof(ConstInitializer *) * vec_size(elements)); + VECEACH(elements, i) + { + Expr *expr = elements[i]; + if (expr->expr_kind == EXPR_INITIALIZER_LIST) + { + assert(expr->constant); + inits[i] = expr->initializer_expr.initializer; + continue; + } + ConstInitializer *element_init = MALLOC(sizeof(ConstInitializer)); + sema_create_const_initializer_value(element_init, expr); + inits[i] = element_init; + } + const_init->init_array_full = inits; + initializer->initializer_expr.init_type = INITIALIZER_CONST; + initializer->initializer_expr.initializer = const_init; + } + // 7. Done! return true; } @@ -3116,7 +3244,7 @@ static bool sema_expr_analyse_sub(Context *context, Type *to, Expr *expr, Expr * // 6. No need for further casts, just it is an integer. if (!type_is_integer(right_type)) { - SEMA_ERROR(expr, "Cannot subtract '%s' from '%s'", type_to_error_string(left_type), type_to_error_string(right_type)); + SEMA_ERROR(expr, "Cannot subtract '%s' from '%s'", type_to_error_string(right_type), type_to_error_string(left_type)); return false; } @@ -3443,10 +3571,13 @@ static bool sema_expr_analyse_mod(Context *context, Type *to, Expr *expr, Expr * // 1. Analyse both sides. if (!sema_expr_analyse_binary_sub_expr(context, to, left, right)) return false; - // 2. Make sure we have some sort of integer on both sides. - if (!type_is_any_integer(right->type->canonical) || !type_is_any_integer(left->type->canonical)) + Type *left_type = left->type->canonical; + Type *right_type = right->type->canonical; + + // 2. Perform promotion to a common type. + if (!binary_arithmetic_promotion(context, left, right, left_type, right_type, expr, "Cannot divide '%s' by '%s'.")) { - return sema_type_error_on_binop(context, expr); + return false; } // 3. a % 0 is not valid, so detect it. @@ -3852,13 +3983,16 @@ static bool sema_expr_analyse_deref(Context *context, Expr *expr, Expr *inner) return true; } -static inline bool sema_take_addr_of_var(Expr *expr, Decl *decl) +static inline bool sema_take_addr_of_var(Expr *expr, Decl *decl, bool *is_constant) { if (decl->decl_kind != DECL_VAR) return false; + *is_constant = false; switch (decl->var.kind) { - case VARDECL_LOCAL: case VARDECL_GLOBAL: + *is_constant = true; + return true; + case VARDECL_LOCAL: case VARDECL_PARAM: case VARDECL_PARAM_REF: return true; @@ -3886,7 +4020,7 @@ static inline bool sema_take_addr_of_var(Expr *expr, Decl *decl) UNREACHABLE } -static inline bool sema_take_addr_of_ident(Expr *inner) +static inline bool sema_take_addr_of_ident(Expr *inner, bool *is_constant) { Decl *decl = inner->identifier_expr.decl; decl = decl_raw(decl); @@ -3894,16 +4028,17 @@ static inline bool sema_take_addr_of_ident(Expr *inner) { case DECL_ENUM_CONSTANT: case DECL_FUNC: + *is_constant = true; return true; case DECL_VAR: - return sema_take_addr_of_var(inner, decl); + return sema_take_addr_of_var(inner, decl, is_constant); default: SEMA_ERROR(inner, "It is not possible to take the address of a '%s'.", type_to_error_string(inner->type)); return false; } } -static bool sema_take_addr_of(Expr *inner) +static bool sema_take_addr_of(Expr *inner, bool *is_constant) { switch (inner->expr_kind) { @@ -3916,18 +4051,28 @@ static bool sema_take_addr_of(Expr *inner) return false; case EXPR_IDENTIFIER: case EXPR_CONST_IDENTIFIER: - return sema_take_addr_of_ident(inner); + return sema_take_addr_of_ident(inner, is_constant); case EXPR_UNARY: + *is_constant = inner->constant; if (inner->unary_expr.operator == UNARYOP_DEREF) return true; break; case EXPR_ACCESS: - return sema_take_addr_of(inner->access_expr.parent); + return sema_take_addr_of(inner->access_expr.parent, is_constant); case EXPR_GROUP: - return sema_take_addr_of(inner->group_expr); + return sema_take_addr_of(inner->group_expr, is_constant); case EXPR_SUBSCRIPT: - return sema_take_addr_of(inner->subscript_expr.expr); + { + bool index_was_const = false; + if (!sema_take_addr_of(inner->subscript_expr.expr, &index_was_const)) return false; + *is_constant = index_was_const && inner->constant; + return true; + } case EXPR_SLICE: - return sema_take_addr_of(inner->slice_expr.expr); + { + *is_constant = false; + bool dummy; + return sema_take_addr_of(inner->slice_expr.expr, &dummy); + } default: break; } @@ -3958,11 +4103,12 @@ static bool sema_expr_analyse_addr(Context *context, Type *to, Expr *expr, Expr } // 2. Take the address. - if (!sema_take_addr_of(inner)) return expr_poison(expr); + bool is_constant = false; + if (!sema_take_addr_of(inner, &is_constant)) return expr_poison(expr); // 3. Get the pointer of the underlying type. expr->type = type_get_ptr(inner->type); - expr->constant = inner->constant; + expr->constant = is_constant; expr->pure = inner->pure; return true; diff --git a/src/compiler/tokens.c b/src/compiler/tokens.c index 7094f8134..a16af23d2 100644 --- a/src/compiler/tokens.c +++ b/src/compiler/tokens.c @@ -232,8 +232,6 @@ const char *token_type_to_string(TokenType type) return "if"; case TOKEN_IMPORT: return "import"; - case TOKEN_IN: - return "in"; case TOKEN_LOCAL: return "local"; case TOKEN_MACRO: diff --git a/src/compiler/types.c b/src/compiler/types.c index 4f047bc0a..f9d8a5687 100644 --- a/src/compiler/types.c +++ b/src/compiler/types.c @@ -186,7 +186,7 @@ ByteSize type_size(Type *type) case TYPE_ENUM: return type->decl->enums.type_info->type->canonical->builtin.bytesize; case TYPE_ERRTYPE: - return alignment_error_code; + return type_size(type_usize->canonical); case TYPE_STRUCT: case TYPE_UNION: assert(type->decl->resolve_status == RESOLVE_DONE); @@ -607,7 +607,7 @@ AlignSize type_abi_alignment(Type *type) case TYPE_ENUM: return type->decl->enums.type_info->type->canonical->builtin.abi_alignment; case TYPE_ERRTYPE: - return alignment_error_code; + return t_usz.canonical->builtin.abi_alignment; case TYPE_STRUCT: case TYPE_UNION: return type->decl->alignment; diff --git a/test/test_suite/arrays/array_literal.c3t b/test/test_suite/arrays/array_literal.c3t new file mode 100644 index 000000000..f0f60b9b9 --- /dev/null +++ b/test/test_suite/arrays/array_literal.c3t @@ -0,0 +1,27 @@ +module testing; + +func double test(uint x) +{ + double[30] student_t = { 0.0 , 12.706 , 4.303 , 3.182 , 2.776 , 2.571 , + 2.447 , 2.365 , 2.306 , 2.262 , 2.228 , + 2.201 , 2.179 , 2.160 , 2.145 , 2.131 , + 2.120 , 2.110 , 2.101 , 2.093 , 2.086 , + 2.080 , 2.074 , 2.069 , 2.064 , 2.060 , + 2.056 , 2.052 , 2.048 , 2.045 }; + return student_t[x]; +} + +// #expect: array_literal.ll + +@0 = constant [30 x double] [double 0.000000e+00, double 1.270600e+01, double 4.303000e+00, double 3.182000e+00, double 2.776000e+00, double 2.571000e+00, double 2.447000e+00, double 2.365000e+00, double 2.306000e+00, double 2.262000e+00, double 2.228000e+00, double 2.201000e+00, double 2.179000e+00, double 2.160000e+00, double 2.145000e+00, double 2.131000e+00, double 2.120000e+00, double 2.110000e+00, double 2.101000e+00, double 2.093000e+00, double 2.086000e+00, double 2.080000e+00, double 2.074000e+00, double 2.069000e+00, double 2.064000e+00, double 2.060000e+00, double 2.056000e+00, double 2.052000e+00, double 2.048000e+00, double 2.045000e+00], align 8 + +entry: + %x = alloca i32, align 4 + %student_t = alloca [30 x double], align 8 + store i32 %0, i32* %x, align 4 + %1 = bitcast [30 x double]* %student_t to i8* + call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 8 %1, i8* align 8 bitcast ([30 x double]* @0 to i8*), i32 240, i1 false) + %2 = load i32, i32* %x, align 4 + %arridx = getelementptr inbounds [30 x double], [30 x double]* %student_t, i32 0, i32 %2 + %3 = load double, double* %arridx, align 8 + ret double %3 diff --git a/test/test_suite/arrays/array_struct.c3t b/test/test_suite/arrays/array_struct.c3t new file mode 100644 index 000000000..70e42e4b5 --- /dev/null +++ b/test/test_suite/arrays/array_struct.c3t @@ -0,0 +1,12 @@ +module test; + +struct Foo +{ + int x, y; +} + +Foo[10] array; + +// #expect: array_struct.ll + +@array = protected global [10 x %test.Foo] zeroinitializer, align 4 \ No newline at end of file diff --git a/test/test_suite/arrays/complex_array_const.c3t b/test/test_suite/arrays/complex_array_const.c3t new file mode 100644 index 000000000..00c024bf2 --- /dev/null +++ b/test/test_suite/arrays/complex_array_const.c3t @@ -0,0 +1,20 @@ +module test; + +struct Connection +{ + long to; + char* type; + long length; +} + +Connection[3] link += { {1, "link1", 10}, + {2, "link2", 20}, + {3, "link3", 30} }; + +// #expect: complex_array_const.ll + +@0 = internal constant [6 x i8] c"link1\00" +@1 = internal constant [6 x i8] c"link2\00" +@2 = internal constant [6 x i8] c"link3\00" +@link = protected global [3 x %test.Connection] [%test.Connection { i64 1, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @0, i32 0, i32 0), i64 10 }, %test.Connection { i64 2, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @1, i32 0, i32 0), i64 20 }, %test.Connection { i64 3, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @2, i32 0, i32 0), i64 30 }], align 8 diff --git a/test/test_suite/expressions/fail_index_usize.c3 b/test/test_suite/expressions/fail_index_usize.c3 new file mode 100644 index 000000000..d165fcb0e --- /dev/null +++ b/test/test_suite/expressions/fail_index_usize.c3 @@ -0,0 +1,5 @@ +func void test(int* array, usize n) +{ + array[n] = 33; + n[array] = 33; // #error: Cannot index +} diff --git a/test/test_suite/expressions/pointer_access.c3t b/test/test_suite/expressions/pointer_access.c3t index 6c2ab5ef7..38ef3c901 100644 --- a/test/test_suite/expressions/pointer_access.c3t +++ b/test/test_suite/expressions/pointer_access.c3t @@ -46,32 +46,53 @@ func void testSimple() entry: %a = alloca %pointer_access.ExtraSimple, align 8 - %0 = bitcast %pointer_access.ExtraSimple* %a to i8* - call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 8 %0, i8* align 8 bitcast (%pointer_access.ExtraSimple* @0 to i8*), i32 72, i1 false) - %c = getelementptr inbounds %pointer_access.ExtraSimple, %pointer_access.ExtraSimple* %a, i32 0, i32 2 - %j = getelementptr inbounds %pointer_access.c, %pointer_access.c* %c, i32 0, i32 4 - store double 3.400000e+00, double* %j, align 8 - %a1 = getelementptr inbounds %pointer_access.ExtraSimple, %pointer_access.ExtraSimple* %a, i32 0, i32 0 - %1 = load i32, i32* %a1, align 4 - %c2 = getelementptr inbounds %pointer_access.ExtraSimple, %pointer_access.ExtraSimple* %a, i32 0, i32 2 - %e = getelementptr inbounds %pointer_access.c, %pointer_access.c* %c2, i32 0, i32 0 - %2 = load double, double* %e, align 8 - %c3 = getelementptr inbounds %pointer_access.ExtraSimple, %pointer_access.ExtraSimple* %a, i32 0, i32 2 - %f = getelementptr inbounds %pointer_access.c, %pointer_access.c* %c3, i32 0, i32 3 - %3 = load double, double* %f, align 8 - %c4 = getelementptr inbounds %pointer_access.ExtraSimple, %pointer_access.ExtraSimple* %a, i32 0, i32 2 - %j5 = getelementptr inbounds %pointer_access.c, %pointer_access.c* %c4, i32 0, i32 4 - %4 = load double, double* %j5, align 8 - %g = getelementptr inbounds %pointer_access.ExtraSimple, %pointer_access.ExtraSimple* %a, i32 0, i32 5 - %5 = load i32, i32* %g, align 4 - %anon = getelementptr inbounds %pointer_access.ExtraSimple, %pointer_access.ExtraSimple* %a, i32 0, i32 4 - %o0 = bitcast %pointer_access.anon.0* %anon to double* - %6 = load double, double* %o0, align 8 - %anon6 = getelementptr inbounds %pointer_access.ExtraSimple, %pointer_access.ExtraSimple* %a, i32 0, i32 3 - %r = getelementptr inbounds %pointer_access.anon, %pointer_access.anon* %anon6, i32 0, i32 0 - %7 = load i32, i32* %r, align 4 - %anon7 = getelementptr inbounds %pointer_access.ExtraSimple, %pointer_access.ExtraSimple* %a, i32 0, i32 3 - %s = getelementptr inbounds %pointer_access.anon, %pointer_access.anon* %anon7, i32 0, i32 1 - %8 = load i32, i32* %s, align 4 - call void (i8*, ...) @printf(i8* getelementptr inbounds ([71 x i8], [71 x i8]* @1, i32 0, i32 0), i32 %1, double %2, double %3, double %4, i32 %5, double %6, i32 %7, i32 %8) - ret void \ No newline at end of file + %0 = getelementptr inbounds %pointer_access.ExtraSimple, %pointer_access.ExtraSimple* %a, i32 0, i32 0 + store i32 0, i32* %0, align 8 + %1 = getelementptr inbounds %pointer_access.ExtraSimple, %pointer_access.ExtraSimple* %a, i32 0, i32 1 + store i32 0, i32* %1, align 4 + %2 = getelementptr inbounds %pointer_access.ExtraSimple, %pointer_access.ExtraSimple* %a, i32 0, i32 2 + %3 = getelementptr inbounds %pointer_access.c, %pointer_access.c* %2, i32 0, i32 0 + store double 0.000000e+00, double* %3, align 8 + %4 = getelementptr inbounds %pointer_access.c, %pointer_access.c* %2, i32 0, i32 1 + store double 0.000000e+00, double* %4, align 8 + %5 = getelementptr inbounds %pointer_access.c, %pointer_access.c* %2, i32 0, i32 2 + store double 0.000000e+00, double* %5, align 8 + %6 = getelementptr inbounds %pointer_access.c, %pointer_access.c* %2, i32 0, i32 3 + store double 0.000000e+00, double* %6, align 8 + %7 = getelementptr inbounds %pointer_access.c, %pointer_access.c* %2, i32 0, i32 4 + store double 3.300000e+00, double* %7, align 8 + %8 = getelementptr inbounds %pointer_access.ExtraSimple, %pointer_access.ExtraSimple* %a, i32 0, i32 3 + %9 = bitcast %pointer_access.anon* %8 to i8* + call void @llvm.memset.p0i8.i64(i8* align 8 %9, i8 0, i64 8, i1 false) + %10 = getelementptr inbounds %pointer_access.ExtraSimple, %pointer_access.ExtraSimple* %a, i32 0, i32 4 + %11 = bitcast %pointer_access.anon.0* %10 to i8* + call void @llvm.memset.p0i8.i64(i8* align 8 %11, i8 0, i64 8, i1 false) + %12 = getelementptr inbounds %pointer_access.ExtraSimple, %pointer_access.ExtraSimple* %a, i32 0, i32 5 + store i32 0, i32* %12, align 8 + %13 = getelementptr inbounds %pointer_access.ExtraSimple, %pointer_access.ExtraSimple* %a, i32 0, i32 2 + %14 = getelementptr inbounds %pointer_access.c, %pointer_access.c* %13, i32 0, i32 4 + store double 3.400000e+00, double* %14, align 8 + %15 = getelementptr inbounds %pointer_access.ExtraSimple, %pointer_access.ExtraSimple* %a, i32 0, i32 0 + %16 = load i32, i32* %15, align 8 + %17 = getelementptr inbounds %pointer_access.ExtraSimple, %pointer_access.ExtraSimple* %a, i32 0, i32 2 + %18 = getelementptr inbounds %pointer_access.c, %pointer_access.c* %17, i32 0, i32 0 + %19 = load double, double* %18, align 8 + %20 = getelementptr inbounds %pointer_access.ExtraSimple, %pointer_access.ExtraSimple* %a, i32 0, i32 2 + %21 = getelementptr inbounds %pointer_access.c, %pointer_access.c* %20, i32 0, i32 3 + %22 = load double, double* %21, align 8 + %23 = getelementptr inbounds %pointer_access.ExtraSimple, %pointer_access.ExtraSimple* %a, i32 0, i32 2 + %24 = getelementptr inbounds %pointer_access.c, %pointer_access.c* %23, i32 0, i32 4 + %25 = load double, double* %24, align 8 + %26 = getelementptr inbounds %pointer_access.ExtraSimple, %pointer_access.ExtraSimple* %a, i32 0, i32 5 + %27 = load i32, i32* %26, align 8 + %28 = getelementptr inbounds %pointer_access.ExtraSimple, %pointer_access.ExtraSimple* %a, i32 0, i32 4 + %29 = bitcast %pointer_access.anon.0* %28 to double* + %30 = load double, double* %29, align 8 + %31 = getelementptr inbounds %pointer_access.ExtraSimple, %pointer_access.ExtraSimple* %a, i32 0, i32 3 + %32 = getelementptr inbounds %pointer_access.anon, %pointer_access.anon* %31, i32 0, i32 0 + %33 = load i32, i32* %32, align 8 + %34 = getelementptr inbounds %pointer_access.ExtraSimple, %pointer_access.ExtraSimple* %a, i32 0, i32 3 + %35 = getelementptr inbounds %pointer_access.anon, %pointer_access.anon* %34, i32 0, i32 1 + %36 = load i32, i32* %35, align 4 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([71 x i8], [71 x i8]* @0, i32 0, i32 0), i32 %16, double %19, double %22, double %25, i32 %27, double %30, i32 %33, i32 %36) + ret void diff --git a/test/test_suite/expressions/simple_float_sub_neg.c3t b/test/test_suite/expressions/simple_float_sub_neg.c3t new file mode 100644 index 000000000..aa2f4808c --- /dev/null +++ b/test/test_suite/expressions/simple_float_sub_neg.c3t @@ -0,0 +1,27 @@ +module test; + +func double test(double a, double b, double c, double d) +{ + return -(a-b) - (c-d); +} + +// #expect: simple_float_sub_neg.ll + +entry: + %a = alloca double, align 8 + %b = alloca double, align 8 + %c = alloca double, align 8 + %d = alloca double, align 8 + store double %0, double* %a, align 8 + store double %1, double* %b, align 8 + store double %2, double* %c, align 8 + store double %3, double* %d, align 8 + %4 = load double, double* %a, align 8 + %5 = load double, double* %b, align 8 + %fsub = fsub double %4, %5 + %fneg = fneg double %fsub + %6 = load double, double* %c, align 8 + %7 = load double, double* %d, align 8 + %fsub1 = fsub double %6, %7 + %fsub2 = fsub double %fneg, %fsub1 + ret double %fsub2 \ No newline at end of file diff --git a/test/test_suite/expressions/strings.c3t b/test/test_suite/expressions/strings.c3t new file mode 100644 index 000000000..b44a0ccb1 --- /dev/null +++ b/test/test_suite/expressions/strings.c3t @@ -0,0 +1,10 @@ +module test; + +func char* foo() +{ + return "*** Word \"%s\" on line %d is not"; +} + +// #expect: strings.ll + +@0 = internal constant [32 x i8] c"*** Word \22%s\22 on line %d is not\00" \ No newline at end of file diff --git a/test/test_suite/functions/assorted_tests.c3t b/test/test_suite/functions/assorted_tests.c3t new file mode 100644 index 000000000..a67de38fd --- /dev/null +++ b/test/test_suite/functions/assorted_tests.c3t @@ -0,0 +1,118 @@ +module test; + +func int foo1() +{ + byte *pp = void; + uint w_cnt = void; + + w_cnt += *pp; + + return cast(w_cnt as int); +} + +extern func void test2(int, double, float); + +func void foo2(int x) +{ + test2(x, x ? 1.0 : 12.5, 1.0); +} + +func int trys(char* s, int x) +{ + int asa = void; + double val = void; + int lls = void; + if (x) + { + asa = lls + asa; + } + else + { + } + return asa + cast(val as int); +} + +struct InternalFPF +{ + byte type; +} + +local func void setInternalFPFZero(InternalFPF* dest) @noinline +{ + dest.type = 0; +} + +func void denormalize(InternalFPF* ptr) +{ + setInternalFPFZero(ptr); +} + + +// #expect: assorted_tests.ll + + %pp = alloca i8*, align 8 + %w_cnt = alloca i32, align 4 + %0 = load i32, i32* %w_cnt, align 4 + %1 = load i8*, i8** %pp, align 8 + %2 = load i8, i8* %1, align 8 + %uiuiext = zext i8 %2 to i32 + %uadd = add nuw i32 %0, %uiuiext + store i32 %uadd, i32* %w_cnt, align 4 + %3 = load i32, i32* %w_cnt, align 4 + ret i32 %3 + + %x = alloca i32, align 4 + store i32 %0, i32* %x, align 4 + %1 = load i32, i32* %x, align 4 + %2 = load i32, i32* %x, align 4 + %intbool = icmp ne i32 %2, 0 + br i1 %intbool, label %cond.lhs, label %cond.rhs +cond.lhs: + br label %cond.phi +cond.rhs: + br label %cond.phi +cond.phi: + %val = phi double [ 1.000000e+00, %cond.lhs ], [ 1.250000e+01, %cond.rhs ] + call void @test2(i32 %1, double %val, float 1.000000e+00) + ret void + + %s = alloca i8*, align 8 + %x = alloca i32, align 4 + %asa = alloca i32, align 4 + %val = alloca double, align 8 + %lls = alloca i32, align 4 + store i32 %1, i32* %x, align 4 + %2 = load i32, i32* %x, align 4 + %intbool = icmp ne i32 %2, 0 + br i1 %intbool, label %if.then, label %if.exit + +if.then: + %3 = load i32, i32* %lls, align 4 + %4 = load i32, i32* %asa, align 4 + %add = add nsw i32 %3, %4 + store i32 %add, i32* %asa, align 4 + br label %if.exit + +if.exit: + %5 = load i32, i32* %asa, align 4 + %6 = load double, double* %val, align 8 + %fpui = fptoui double %6 to i32 + %add1 = add nsw i32 %5, %fpui + ret i32 %add1 + + +void @test.setInternalFPFZero(%test.InternalFPF* %0) + %dest = alloca %test.InternalFPF*, align 8 + store %test.InternalFPF* %0, %test.InternalFPF** %dest, align 8 + %1 = load %test.InternalFPF*, %test.InternalFPF** %dest, align 8 + %2 = getelementptr inbounds %test.InternalFPF, %test.InternalFPF* %1, i32 0, i32 0 + store i8 0, i8* %2, align 1 + ret void + +void @test.denormalize(%test.InternalFPF* %0) +entry: + %ptr = alloca %test.InternalFPF*, align 8 + store %test.InternalFPF* %0, %test.InternalFPF** %ptr, align 8 + %1 = load %test.InternalFPF*, %test.InternalFPF** %ptr, align 8 + call void @test.setInternalFPFZero(%test.InternalFPF* %1) + ret void diff --git a/test/test_suite/functions/double_return.c3t b/test/test_suite/functions/double_return.c3t new file mode 100644 index 000000000..9ebc8c9a7 --- /dev/null +++ b/test/test_suite/functions/double_return.c3t @@ -0,0 +1,13 @@ +module test; + +func int test(bool pos, bool color) +{ + return 0; + return cast(pos && color as int); +} + +// #expect: double_return.ll + +entry: + ret i32 0 +} diff --git a/test/test_suite/globals/external_global.c3t b/test/test_suite/globals/external_global.c3t new file mode 100644 index 000000000..5dabb1ef5 --- /dev/null +++ b/test/test_suite/globals/external_global.c3t @@ -0,0 +1,34 @@ +module test; + +struct MinInfo +{ + long offset; + uint file_attr; +} + +struct UzGlobs +{ + char answerbuf; + MinInfo[1] info; + MinInfo* pInfo; +} + +extern UzGlobs g; + +func int extract_or_test_files() +{ + g.pInfo = &g.info; + return 0; +} + +// #expect: external_global.ll + +%test.UzGlobs = type { i8, [1 x %test.MinInfo], %test.MinInfo* } +%test.MinInfo = type { i64, i32 } + +@g = external global %test.UzGlobs, align 8 + +entry: + store %test.MinInfo* getelementptr inbounds (%test.UzGlobs, %test.UzGlobs* @g, i32 0, i32 1, i32 0), %test.MinInfo** getelementptr inbounds (%test.UzGlobs, %test.UzGlobs* @g, i32 0, i32 2), align 8 + ret i32 0 +} \ No newline at end of file diff --git a/test/test_suite/pointers/const_pointer.c3t b/test/test_suite/pointers/const_pointer.c3t new file mode 100644 index 000000000..83469ced5 --- /dev/null +++ b/test/test_suite/pointers/const_pointer.c3t @@ -0,0 +1,14 @@ +module test; + +double foo = 17; +double bar = 12.0; +float xx = 12.0; + +void*[3] data = { &foo, &bar, &xx }; + +// #expect: const_pointer.ll + +@foo = protected global double 1.700000e+01, align 8 +@bar = protected global double 1.200000e+01, align 8 +@xx = protected global float 1.200000e+01, align 4 +@data = protected global [3 x i8*] [i8* bitcast (double* @foo to i8*), i8* bitcast (double* @bar to i8*), i8* bitcast (float* @xx to i8*)], align 8 diff --git a/test/test_suite/pointers/pointer_index.c3t b/test/test_suite/pointers/pointer_index.c3t index fa31f0887..2ba2c8c8e 100644 --- a/test/test_suite/pointers/pointer_index.c3t +++ b/test/test_suite/pointers/pointer_index.c3t @@ -30,13 +30,13 @@ public func void test3(long* x) %c = alloca i64, align 8 store i64* %0, i64** %x %1 = load i64*, i64** %x, align 8 - %ptridx = getelementptr inbounds i64, i64* %1, i64 0 + %ptridx = getelementptr inbounds i64, i64* %1, i32 0 %2 = load i64, i64* %ptridx, align 8 store i64 %2, i64* %a, align 8 %3 = load i64*, i64** %x, align 8 %4 = load i64, i64* %3, align 8 store i64 %4, i64* %b, align 8 %5 = load i64*, i64** %x, align 8 - %ptridx1 = getelementptr inbounds i64, i64* %5, i64 1 + %ptridx1 = getelementptr inbounds i64, i64* %5, i32 1 %6 = load i64, i64* %ptridx1, align 8 store i64 %6, i64* %c, align 8 \ No newline at end of file diff --git a/test/test_suite/statements/if_tests.c3t b/test/test_suite/statements/if_tests.c3t new file mode 100644 index 000000000..d751af9c6 --- /dev/null +++ b/test/test_suite/statements/if_tests.c3t @@ -0,0 +1,65 @@ +module iftest; + +func void test1(int x) +{ + if (x > 0) + { + defer x += 1; + } + else + {} +} + +func void test2(int x) +{ + if (x > 0) + { + } + else + {} +} + +func void test3(int x) +{ + if (x > 0) + {} + else { x += 1; } +} + +// #expect: if_tests.ll + + %x = alloca i32, align 4 + store i32 %0, i32* %x, align 4 + %1 = load i32, i32* %x, align 4 + %gt = icmp sgt i32 %1, 0 + br i1 %gt, label %if.then, label %if.exit +if.then: + %2 = load i32, i32* %x, align 4 + %add = add nsw i32 %2, 1 + store i32 %add, i32* %x, align 4 + br label %exit +exit: + br label %if.exit +if.exit: + ret void + +define void @iftest.test2(i32 %0) + %x = alloca i32, align 4 + store i32 %0, i32* %x, align 4 + %1 = load i32, i32* %x, align 4 + %gt = icmp sgt i32 %1, 0 + ret void + +define void @iftest.test3(i32 %0) + %x = alloca i32, align 4 + store i32 %0, i32* %x, align 4 + %1 = load i32, i32* %x, align 4 + %gt = icmp sgt i32 %1, 0 + br i1 %gt, label %if.exit, label %if.else +if.else: + %2 = load i32, i32* %x, align 4 + %add = add nsw i32 %2, 1 + store i32 %add, i32* %x, align 4 + br label %if.exit +if.exit: + ret void diff --git a/test/test_suite/statements/while_switch.c3t b/test/test_suite/statements/while_switch.c3t new file mode 100644 index 000000000..d56762d8b --- /dev/null +++ b/test/test_suite/statements/while_switch.c3t @@ -0,0 +1,71 @@ +module test; + + +extern func int printf(char*, ...); +extern func int foo(); + +func int main() +{ + while (foo()) + { + switch (foo()) + { + case 0: + case 1: + case 2: + case 3: + printf("3"); + nextcase; + case 4: + printf("4"); + nextcase; + case 5: + case 6: + default: + break; + } + } + return 0; +} + +// #expect: while_switch.ll + + +@0 = internal constant [2 x i8] c"3\00" +@1 = internal constant [2 x i8] c"4\00" + +entry: + %switch = alloca i32, align 4 + br label %while.begin +while.begin: + %0 = call i32 @foo() + %intbool = icmp ne i32 %0, 0 + br i1 %intbool, label %while.body, label %while.exit +while.body: + %1 = call i32 @foo() + store i32 %1, i32* %switch, align 4 + br label %switch.entry +switch.entry: + %2 = load i32, i32* %switch, align 4 + switch i32 %2, label %switch.default [ + i32 0, label %switch.case + i32 1, label %switch.case + i32 2, label %switch.case + i32 3, label %switch.case + i32 4, label %switch.case1 + i32 5, label %switch.default + i32 6, label %switch.default + ] +switch.case: + %3 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([2 x i8], [2 x i8]* @0, i32 0, i32 0)) + br label %switch.case1 +switch.case1: + %4 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([2 x i8], [2 x i8]* @1, i32 0, i32 0)) + br label %switch.default +switch.default: + br label %switch.exit +switch.exit: + br label %while.begin +while.exit: + ret i32 0 + diff --git a/test/test_suite/struct/func_return_struct.c3 b/test/test_suite/struct/func_return_struct.c3 new file mode 100644 index 000000000..c9c3d5ca7 --- /dev/null +++ b/test/test_suite/struct/func_return_struct.c3 @@ -0,0 +1,12 @@ +struct Test +{ + int i; + short s1, s2; +} + +extern func Test func_returning_struct(); + +func void loop() +{ + func_returning_struct(); +} diff --git a/test/test_suite/struct/simple_struct.c3t b/test/test_suite/struct/simple_struct.c3t new file mode 100644 index 000000000..35573c6ba --- /dev/null +++ b/test/test_suite/struct/simple_struct.c3t @@ -0,0 +1,14 @@ +module test; + +Foo a; + +struct Foo +{ + int x; + double a; +} + +// #expect: simple_struct.ll + +%test.Foo = type { i32, double } +@a = protected global %test.Foo zeroinitializer, align 8 \ No newline at end of file diff --git a/test/test_suite/struct/struct_as_value.c3t b/test/test_suite/struct/struct_as_value.c3t new file mode 100644 index 000000000..7d2db133e --- /dev/null +++ b/test/test_suite/struct/struct_as_value.c3t @@ -0,0 +1,15 @@ +module test; + +struct Event +{ + int op; +} + +func Event test(int x) +{ + Event foo = { 1 }; + Event bar = { 2 }; + return x ? foo : bar; +} + +// TODO possibly look at the IR \ No newline at end of file diff --git a/test/test_suite/struct/struct_const_construct_simple.c3t b/test/test_suite/struct/struct_const_construct_simple.c3t new file mode 100644 index 000000000..618c4ea36 --- /dev/null +++ b/test/test_suite/struct/struct_const_construct_simple.c3t @@ -0,0 +1,33 @@ +module structo; + +struct Foo +{ + int foo; + long bar; +} + +usize x = Foo.sizeof; + +Foo foo1 = { 1, 2 }; +Foo foo2 = { .foo = 2 }; +Foo foo3 = { .bar = 3 }; +Foo foo4 = { .bar = 4, .foo = 4, .bar = 1 }; +Foo foo5 = {}; +Foo foo6; +const Foo FOO7 = { 1, 2 }; +Foo foo8 = FOO7; + +// #expect: struct_const_construct_simple.ll + +%structo.Foo = type { i32, i64 } + +@Foo = linkonce_odr constant i8 1 +@x = protected global i64 16, align 8 +@foo1 = protected global %structo.Foo { i32 1, i64 2 }, align 8 +@foo2 = protected global %structo.Foo { i32 2, i64 0 }, align 8 +@foo3 = protected global %structo.Foo { i32 0, i64 3 }, align 8 +@foo4 = protected global %structo.Foo { i32 4, i64 1 }, align 8 +@foo5 = protected global %structo.Foo zeroinitializer, align 8 +@foo6 = protected global %structo.Foo zeroinitializer, align 8 +@FOO7 = protected constant %structo.Foo { i32 1, i64 2 }, align 8 +@foo8 = protected global %structo.Foo { i32 1, i64 2 }, align 8 diff --git a/test/test_suite/struct/struct_pack_and_align.c3t b/test/test_suite/struct/struct_pack_and_align.c3t new file mode 100644 index 000000000..07ffc6d8e --- /dev/null +++ b/test/test_suite/struct/struct_pack_and_align.c3t @@ -0,0 +1,102 @@ +module struct2; + +// <{ i64, i8, [3 x i8] }> +struct Foo1 @packed @align(4) +{ + long bar; + char foo; +} + +$assert(Foo1.sizeof == 12); +public Foo1 foo1 = { 1, 2 }; + +// <{ i8, i64, [3 x i8] }> +struct Foo2 @packed @align(4) +{ + char foo; + long bar; +} + +$assert(Foo2.sizeof == 12); +public Foo2 foo2 = { 1, 2 }; + +// <{ i8, i64, [7 x i8] }> +struct Foo3 @packed @align(8) +{ + char foo; + long bar; +} + +public Foo3 foo3 = { 1, 2 }; +$assert(Foo3.sizeof == 16); + +// <{ i8, i64 }> +struct Foo4 @packed +{ + char foo; + long bar; +} + +$assert(Foo4.sizeof == 9); +public Foo4 foo4 = { 1, 2 }; + +// { i32, [12 x i8], i8, [15 x i8] } +struct Foo5 +{ + int bar @align(16); + char foo @align(16); +} + +$assert(Foo5.sizeof == 32); +public Foo5 foo5 = { 1, 2 }; + +func int test5(char x) +{ + Foo5 y = { .foo = x }; + return y.foo + y.bar; +} + +// { i32, i16, i16 } +struct Foo6 @packed +{ + int a; + short b; + short c; +} + +$assert(Foo6.sizeof == 8); +public Foo6 foo6 = { 1, 2, 3 }; + +// #expect: struct_pack_and_align.ll + +%struct2.Foo1 = type <{ i64, i8, [3 x i8] }> +%struct2.Foo2 = type <{ i8, i64, [3 x i8] }> +%struct2.Foo3 = type <{ i8, i64, [7 x i8] }> +%struct2.Foo4 = type <{ i8, i64 }> +%struct2.Foo5 = type { i32, [12 x i8], i8, [15 x i8] } +%struct2.Foo6 = type { i32, i16, i16 } + +@foo1 = global %struct2.Foo1 <{ i64 1, i8 2, [3 x i8] undef }>, align 4 +@foo2 = global %struct2.Foo2 <{ i8 1, i64 2, [3 x i8] undef }>, align 4 +@foo3 = global %struct2.Foo3 <{ i8 1, i64 2, [7 x i8] undef }>, align 8 +@foo4 = global %struct2.Foo4 <{ i8 1, i64 2 }>, align 1 +@foo5 = global %struct2.Foo5 { i32 1, [12 x i8] undef, i8 2, [15 x i8] undef }, align 16 +@foo6 = global %struct2.Foo6 { i32 1, i16 2, i16 3 }, align 1 + +@struct2.test5 +entry: + %x = alloca i8, align 1 + %y = alloca %struct2.Foo5, align 16 + store i8 %0, i8* %x, align 1 + %1 = bitcast %struct2.Foo5* %y to i8* + call void @llvm.memset.p0i8.i64(i8* align 16 %1, i8 0, i64 32, i1 false) + %2 = getelementptr inbounds %struct2.Foo5, %struct2.Foo5* %y, i32 0, i32 2 + %3 = load i8, i8* %x, align 1 + store i8 %3, i8* %2, align 16 + %4 = getelementptr inbounds %struct2.Foo5, %struct2.Foo5* %y, i32 0, i32 2 + %5 = load i8, i8* %4, align 16 + %sisiext = sext i8 %5 to i32 + %6 = getelementptr inbounds %struct2.Foo5, %struct2.Foo5* %y, i32 0, i32 0 + %7 = load i32, i32* %6, align 16 + %add = add nsw i32 %sisiext, %7 + ret i32 %add diff --git a/test/test_suite/struct/struct_params.c3 b/test/test_suite/struct/struct_params.c3 new file mode 100644 index 000000000..6eb30e873 --- /dev/null +++ b/test/test_suite/struct/struct_params.c3 @@ -0,0 +1,27 @@ +module test; + +struct Foo +{ + char p; + short q; + char r; + int x; + short y, z; + int q2; +} + +extern func int test(Foo, float); +extern func int testE(char,short,char,int,int,float); + +func void test3(Foo *x) +{ + x.q = 1; +} + +func void test2(Foo y) +{ + testE(y.p, y.q, y.r, y.x, y.y, 0.1); + test(y, 0.1); + test2(y); + test3(&y); +} diff --git a/test/test_suite/symbols/shadow_struct.c3 b/test/test_suite/symbols/shadow_struct.c3 new file mode 100644 index 000000000..95ea26ba0 --- /dev/null +++ b/test/test_suite/symbols/shadow_struct.c3 @@ -0,0 +1,15 @@ +struct Foo +{ + Foo *x; + int y; +} + +typedef float as Foo; // #error: shadow a previous declaration + +enum Bar +{ + TEST1, + TEST2 +} + +typedef float as Bar; // #error: shadow a previous declaration \ No newline at end of file diff --git a/test/test_suite/symbols/various.c3 b/test/test_suite/symbols/various.c3 index 8977c08c3..b6539128a 100644 --- a/test/test_suite/symbols/various.c3 +++ b/test/test_suite/symbols/various.c3 @@ -196,3 +196,4 @@ func void test26() } + diff --git a/test/test_suite/union/empty_unions.c3 b/test/test_suite/union/empty_unions.c3 new file mode 100644 index 000000000..d0b403b88 --- /dev/null +++ b/test/test_suite/union/empty_unions.c3 @@ -0,0 +1,30 @@ +module test; + +struct Empty {} +union Foo {} +union Qu +{ + Qu *x; +} + +union Xe +{ + char c; + int a, z; + long b; + void *b1; + struct qu + { + int a; + long z; + } +} + +func Xe foo(Xe a) +{ + a.c = 123; + a.a = 39249; + a.b = 12301230123123; + a.z = 1; + return a; +} \ No newline at end of file diff --git a/test/test_suite/union/union_in_struct.c3t b/test/test_suite/union/union_in_struct.c3t new file mode 100644 index 000000000..c5800d286 --- /dev/null +++ b/test/test_suite/union/union_in_struct.c3t @@ -0,0 +1,29 @@ +module test; + +struct Blend_Map_Entry +{ + union vals { + float[5] colour; + double[2] point_Slope; + } +} + +Blend_Map_Entry a = { .vals = { .colour = { 1, 2, 3, 4, 5 } } }; +Blend_Map_Entry b = { .vals = { .point_Slope = { 6, 7 } } }; +Blend_Map_Entry c = { .vals.colour[2] = 1 }; +Blend_Map_Entry d = { .vals.colour = { 1, 2, 3, 4, 5 } }; + +func void test(Blend_Map_Entry* foo) +{ +} + +// #expect: union_in_struct.ll + + +%test.Blend_Map_Entry = type { %test.vals } +%test.vals = type { [2 x double], [8 x i8] } + +@a = protected global { { [5 x float], [4 x i8] } } { { [5 x float], [4 x i8] } { [5 x float] [float 1.000000e+00, float 2.000000e+00, float 3.000000e+00, float 4.000000e+00, float 5.000000e+00], [4 x i8] undef } }, align 8 +@b = protected global %test.Blend_Map_Entry { %test.vals { [2 x double] [double 6.000000e+00, double 7.000000e+00], [8 x i8] undef } }, align 8 +@c = protected global { { { [2 x float], float, [2 x float] }, [4 x i8] } } { { { [2 x float], float, [2 x float] }, [4 x i8] } { { [2 x float], float, [2 x float] } { [2 x float] zeroinitializer, float 1.000000e+00, [2 x float] zeroinitializer }, [4 x i8] undef } }, align 8 +@d = protected global { { [5 x float], [4 x i8] } } { { [5 x float], [4 x i8] } { [5 x float] [float 1.000000e+00, float 2.000000e+00, float 3.000000e+00, float 4.000000e+00, float 5.000000e+00], [4 x i8] undef } }, align 8 diff --git a/test/test_suite/union/union_member_voidcast.c3 b/test/test_suite/union/union_member_voidcast.c3 new file mode 100644 index 000000000..4d2866085 --- /dev/null +++ b/test/test_suite/union/union_member_voidcast.c3 @@ -0,0 +1,13 @@ +module test; + +union Xu +{ + void *b; +} + +func Xu foo() +{ + Xu a; + a.b = cast(123 as void*); + return a; +} \ No newline at end of file diff --git a/test/test_suite/visibility/local_global_func.c3t b/test/test_suite/visibility/local_global_func.c3t new file mode 100644 index 000000000..81157292d --- /dev/null +++ b/test/test_suite/visibility/local_global_func.c3t @@ -0,0 +1,31 @@ +module test; + +int x; +local int y = 12; + +local func void foo(int z) @noinline +{ + y = z; +} + +func void* test() +{ + foo(12); + return &y; +} + +// #expect: local_global_func.ll + +@x = protected global i32 0, align 4 +@y = hidden global i32 12, align 4 + +define internal void @test.foo(i32 %0) + + %z = alloca i32, align 4 + store i32 %0, i32* %z, align 4 + %1 = load i32, i32* %z, align 4 + store i32 %1, i32* @y, align 4 + ret void +define i8* @test.test() + call void @test.foo(i32 12) + ret i8* bitcast (i32* @y to i8*)