diff options
| author | Colin Davidson <colrdavidson@gmail.com> | 2024-11-20 15:51:08 -0800 |
|---|---|---|
| committer | Colin Davidson <colrdavidson@gmail.com> | 2024-11-20 15:51:08 -0800 |
| commit | d60fb5a44e4d2e371562fd38947f8125b06bceb9 (patch) | |
| tree | 4e924ee102c2af7b30d29017ab716ed00c51ab26 /src | |
| parent | f3ab14b8ccb45d0fef8a96937635bdf0943ce7d6 (diff) | |
| parent | 3229f4668dfaa5f43a374bc549f42661b002699d (diff) | |
update to master
Diffstat (limited to 'src')
| -rw-r--r-- | src/big_int.cpp | 17 | ||||
| -rw-r--r-- | src/bug_report.cpp | 37 | ||||
| -rw-r--r-- | src/build_cpuid.cpp | 35 | ||||
| -rw-r--r-- | src/build_settings.cpp | 48 | ||||
| -rw-r--r-- | src/cached.cpp | 109 | ||||
| -rw-r--r-- | src/check_builtin.cpp | 29 | ||||
| -rw-r--r-- | src/check_decl.cpp | 11 | ||||
| -rw-r--r-- | src/check_expr.cpp | 116 | ||||
| -rw-r--r-- | src/check_stmt.cpp | 17 | ||||
| -rw-r--r-- | src/check_type.cpp | 68 | ||||
| -rw-r--r-- | src/checker.cpp | 43 | ||||
| -rw-r--r-- | src/docs_format.cpp | 3 | ||||
| -rw-r--r-- | src/docs_writer.cpp | 18 | ||||
| -rw-r--r-- | src/exact_value.cpp | 1 | ||||
| -rw-r--r-- | src/gb/gb.h | 6 | ||||
| -rw-r--r-- | src/linker.cpp | 89 | ||||
| -rw-r--r-- | src/llvm_abi.cpp | 21 | ||||
| -rw-r--r-- | src/llvm_backend.hpp | 4 | ||||
| -rw-r--r-- | src/llvm_backend_debug.cpp | 11 | ||||
| -rw-r--r-- | src/llvm_backend_expr.cpp | 89 | ||||
| -rw-r--r-- | src/llvm_backend_general.cpp | 198 | ||||
| -rw-r--r-- | src/llvm_backend_proc.cpp | 5 | ||||
| -rw-r--r-- | src/llvm_backend_stmt.cpp | 21 | ||||
| -rw-r--r-- | src/llvm_backend_type.cpp | 28 | ||||
| -rw-r--r-- | src/llvm_backend_utility.cpp | 20 | ||||
| -rw-r--r-- | src/main.cpp | 956 | ||||
| -rw-r--r-- | src/parser.cpp | 59 | ||||
| -rw-r--r-- | src/parser.hpp | 3 | ||||
| -rw-r--r-- | src/string.cpp | 31 | ||||
| -rw-r--r-- | src/types.cpp | 118 | ||||
| -rw-r--r-- | src/unicode.cpp | 13 |
31 files changed, 1121 insertions, 1103 deletions
diff --git a/src/big_int.cpp b/src/big_int.cpp index 83235483c..8e476f090 100644 --- a/src/big_int.cpp +++ b/src/big_int.cpp @@ -62,6 +62,7 @@ gb_internal void big_int_shl (BigInt *dst, BigInt const *x, BigInt const *y); gb_internal void big_int_shr (BigInt *dst, BigInt const *x, BigInt const *y); gb_internal void big_int_mul (BigInt *dst, BigInt const *x, BigInt const *y); gb_internal void big_int_mul_u64(BigInt *dst, BigInt const *x, u64 y); +gb_internal void big_int_exp_u64(BigInt *dst, BigInt const *x, u64 y, bool *success); gb_internal void big_int_quo_rem(BigInt const *x, BigInt const *y, BigInt *q, BigInt *r); gb_internal void big_int_quo (BigInt *z, BigInt const *x, BigInt const *y); @@ -250,9 +251,7 @@ gb_internal void big_int_from_string(BigInt *dst, String const &s, bool *success exp *= 10; exp += v; } - for (u64 x = 0; x < exp; x++) { - big_int_mul_eq(dst, &b); - } + big_int_exp_u64(dst, &b, exp, success); } if (is_negative) { @@ -328,6 +327,18 @@ gb_internal void big_int_mul_u64(BigInt *dst, BigInt const *x, u64 y) { big_int_dealloc(&d); } +gb_internal void big_int_exp_u64(BigInt *dst, BigInt const *x, u64 y, bool *success) { + if (y > INT_MAX) { + *success = false; + return; + } + + // Note: The cutoff for square-multiply being faster than the naive + // for loop is when exp > 4, but it probably isn't worth adding + // a fast path. + mp_err err = mp_expt_n(x, int(y), dst); + *success = err == MP_OKAY; +} gb_internal void big_int_mul(BigInt *dst, BigInt const *x, BigInt const *y) { mp_mul(x, y, dst); diff --git a/src/bug_report.cpp b/src/bug_report.cpp index fa7e156ce..d4517f9e3 100644 --- a/src/bug_report.cpp +++ b/src/bug_report.cpp @@ -2,12 +2,6 @@ Gather and print platform and version info to help with reporting Odin bugs. */ -#if !defined(GB_COMPILER_MSVC) - #if defined(GB_CPU_X86) - #include <cpuid.h> - #endif -#endif - #if defined(GB_SYSTEM_LINUX) #include <sys/utsname.h> #include <sys/sysinfo.h> @@ -154,21 +148,6 @@ gb_internal void report_windows_product_type(DWORD ProductType) { } #endif -gb_internal void odin_cpuid(int leaf, int result[]) { - #if defined(GB_CPU_ARM) || defined(GB_CPU_RISCV) - return; - - #elif defined(GB_CPU_X86) - - #if defined(GB_COMPILER_MSVC) - __cpuid(result, leaf); - #else - __get_cpuid(leaf, (unsigned int*)&result[0], (unsigned int*)&result[1], (unsigned int*)&result[2], (unsigned int*)&result[3]); - #endif - - #endif -} - gb_internal void report_cpu_info() { gb_printf("\tCPU: "); @@ -887,6 +866,10 @@ gb_internal void report_os_info() { {"21G816", {21, 6, 0}, "macOS", {"Monterey", {12, 7, 0}}}, {"21G920", {21, 6, 0}, "macOS", {"Monterey", {12, 7, 1}}}, {"21G1974", {21, 6, 0}, "macOS", {"Monterey", {12, 7, 2}}}, + {"21H1015", {21, 6, 0}, "macOS", {"Monterey", {12, 7, 3}}}, + {"21H1123", {21, 6, 0}, "macOS", {"Monterey", {12, 7, 4}}}, + {"21H1222", {21, 6, 0}, "macOS", {"Monterey", {12, 7, 5}}}, + {"21H1320", {21, 6, 0}, "macOS", {"Monterey", {12, 7, 6}}}, {"22A380", {13, 0, 0}, "macOS", {"Ventura", {22, 1, 0}}}, {"22A400", {13, 0, 1}, "macOS", {"Ventura", {22, 1, 0}}}, {"22C65", {13, 1, 0}, "macOS", {"Ventura", {22, 2, 0}}}, @@ -904,6 +887,15 @@ gb_internal void report_os_info() { {"22G120", {13, 6, 0}, "macOS", {"Ventura", {22, 6, 0}}}, {"22G313", {13, 6, 1}, "macOS", {"Ventura", {22, 6, 0}}}, {"22G320", {13, 6, 2}, "macOS", {"Ventura", {22, 6, 0}}}, + {"22G436", {22, 6, 0}, "macOS", {"Ventura", {13, 6, 3}}}, + {"22G513", {22, 6, 0}, "macOS", {"Ventura", {13, 6, 4}}}, + {"22G621", {22, 6, 0}, "macOS", {"Ventura", {13, 6, 5}}}, + {"22G630", {22, 6, 0}, "macOS", {"Ventura", {13, 6, 6}}}, + {"22G720", {22, 6, 0}, "macOS", {"Ventura", {13, 6, 7}}}, + {"22G820", {22, 6, 0}, "macOS", {"Ventura", {13, 6, 8}}}, + {"22G830", {22, 6, 0}, "macOS", {"Ventura", {13, 6, 9}}}, + {"22H123", {22, 6, 0}, "macOS", {"Ventura", {13, 7, 0}}}, + {"22H221", {22, 6, 0}, "macOS", {"Ventura", {13, 7, 1}}}, {"23A344", {23, 0, 0}, "macOS", {"Sonoma", {14, 0, 0}}}, {"23B74", {23, 1, 0}, "macOS", {"Sonoma", {14, 1, 0}}}, {"23B81", {23, 1, 0}, "macOS", {"Sonoma", {14, 1, 1}}}, @@ -920,7 +912,10 @@ gb_internal void report_os_info() { {"23G80", {23, 6, 0}, "macOS", {"Sonoma", {14, 6, 0}}}, {"23G93", {23, 6, 0}, "macOS", {"Sonoma", {14, 6, 1}}}, {"23H124", {23, 6, 0}, "macOS", {"Sonoma", {14, 7, 0}}}, + {"23H222", {23, 6, 0}, "macOS", {"Sonoma", {14, 7, 1}}}, {"24A335", {24, 0, 0}, "macOS", {"Sequoia", {15, 0, 0}}}, + {"24A348", {24, 0, 0}, "macOS", {"Sequoia", {15, 0, 1}}}, + {"24B83", {24, 1, 0}, "macOS", {"Sequoia", {15, 1, 0}}}, }; diff --git a/src/build_cpuid.cpp b/src/build_cpuid.cpp new file mode 100644 index 000000000..b7ba5dcdf --- /dev/null +++ b/src/build_cpuid.cpp @@ -0,0 +1,35 @@ +#if !defined(GB_COMPILER_MSVC) + #if defined(GB_CPU_X86) + #include <cpuid.h> + #endif +#endif + +gb_internal void odin_cpuid(int leaf, int result[]) { + #if defined(GB_CPU_ARM) || defined(GB_CPU_RISCV) + return; + + #elif defined(GB_CPU_X86) + + #if defined(GB_COMPILER_MSVC) + __cpuid(result, leaf); + #else + __get_cpuid(leaf, (unsigned int*)&result[0], (unsigned int*)&result[1], (unsigned int*)&result[2], (unsigned int*)&result[3]); + #endif + + #endif +} + +gb_internal bool should_use_march_native() { + #if !defined(GB_CPU_X86) + return false; + + #else + + int cpu[4]; + odin_cpuid(0x1, &cpu[0]); // Get feature information in ECX + EDX + + bool have_popcnt = cpu[2] & (1 << 23); // bit 23 in ECX = popcnt + return !have_popcnt; + + #endif +}
\ No newline at end of file diff --git a/src/build_settings.cpp b/src/build_settings.cpp index 7aff8e650..50fae93b8 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -2,6 +2,7 @@ #include <sys/types.h> #include <sys/sysctl.h> #endif +#include "build_cpuid.cpp" // #if defined(GB_SYSTEM_WINDOWS) // #define DEFAULT_TO_THREADED_CHECKER @@ -343,6 +344,22 @@ struct BuildCacheData { bool copy_already_done; }; + +enum LinkerChoice : i32 { + Linker_Invalid = -1, + Linker_Default = 0, + Linker_lld, + Linker_radlink, + + Linker_COUNT, +}; + +String linker_choices[Linker_COUNT] = { + str_lit("default"), + str_lit("lld"), + str_lit("radlink"), +}; + // This stores the information for the specify architecture of this build struct BuildContext { // Constants @@ -418,12 +435,13 @@ struct BuildContext { bool no_rpath; bool no_entry_point; bool no_thread_local; - bool use_lld; bool cross_compiling; bool different_os; bool keep_object_files; bool disallow_do; + LinkerChoice linker_choice; + StringSet custom_attributes; bool strict_style; @@ -453,7 +471,7 @@ struct BuildContext { bool no_threaded_checker; bool show_debug_messages; - + bool copy_file_contents; bool no_rtti; @@ -1870,7 +1888,7 @@ gb_internal bool init_build_paths(String init_filename) { return false; } - if (!build_context.use_lld && find_result.vs_exe_path.len == 0) { + if (build_context.linker_choice == Linker_Default && find_result.vs_exe_path.len == 0) { gb_printf_err("link.exe not found.\n"); return false; } @@ -2049,19 +2067,19 @@ gb_internal bool init_build_paths(String init_filename) { return false; } - gbFile output_file_test; - const char* output_file_name = (const char*)output_file.text; - gbFileError output_test_err = gb_file_open_mode(&output_file_test, gbFileMode_Append | gbFileMode_Rw, output_file_name); + // gbFile output_file_test; + // const char* output_file_name = (const char*)output_file.text; + // gbFileError output_test_err = gb_file_open_mode(&output_file_test, gbFileMode_Append | gbFileMode_Rw, output_file_name); - if (output_test_err == 0) { - gb_file_close(&output_file_test); - gb_file_remove(output_file_name); - } else { - String output_file = path_to_string(ha, bc->build_paths[BuildPath_Output]); - defer (gb_free(ha, output_file.text)); - gb_printf_err("No write permissions for output path: %.*s\n", LIT(output_file)); - return false; - } + // if (output_test_err == 0) { + // gb_file_close(&output_file_test); + // gb_file_remove(output_file_name); + // } else { + // String output_file = path_to_string(ha, bc->build_paths[BuildPath_Output]); + // defer (gb_free(ha, output_file.text)); + // gb_printf_err("No write permissions for output path: %.*s\n", LIT(output_file)); + // return false; + // } if (build_context.sanitizer_flags & SanitizerFlag_Address) { switch (build_context.metrics.os) { diff --git a/src/cached.cpp b/src/cached.cpp index 4ad65ee9e..efdadce7b 100644 --- a/src/cached.cpp +++ b/src/cached.cpp @@ -187,10 +187,7 @@ gb_internal bool try_copy_executable_from_cache(void) { extern char **environ; #endif -// returns false if different, true if it is the same -gb_internal bool try_cached_build(Checker *c, Array<String> const &args) { - TEMPORARY_ALLOCATOR_GUARD(); - +Array<String> cache_gather_files(Checker *c) { Parser *p = c->parser; auto files = array_make<String>(heap_allocator()); @@ -222,29 +219,11 @@ gb_internal bool try_cached_build(Checker *c, Array<String> const &args) { array_sort(files, string_cmp); - u64 crc = 0; - for (String const &path : files) { - crc = crc64_with_seed(path.text, path.len, crc); - } - - String base_cache_dir = build_context.build_paths[BuildPath_Output].basename; - base_cache_dir = concatenate_strings(permanent_allocator(), base_cache_dir, str_lit("/.odin-cache")); - (void)check_if_exists_directory_otherwise_create(base_cache_dir); - - gbString crc_str = gb_string_make_reserve(permanent_allocator(), 16); - crc_str = gb_string_append_fmt(crc_str, "%016llx", crc); - String cache_dir = concatenate3_strings(permanent_allocator(), base_cache_dir, str_lit("/"), make_string_c(crc_str)); - String files_path = concatenate3_strings(permanent_allocator(), cache_dir, str_lit("/"), str_lit("files.manifest")); - String args_path = concatenate3_strings(permanent_allocator(), cache_dir, str_lit("/"), str_lit("args.manifest")); - String env_path = concatenate3_strings(permanent_allocator(), cache_dir, str_lit("/"), str_lit("env.manifest")); - - build_context.build_cache_data.cache_dir = cache_dir; - build_context.build_cache_data.files_path = files_path; - build_context.build_cache_data.args_path = args_path; - build_context.build_cache_data.env_path = env_path; + return files; +} +Array<String> cache_gather_envs() { auto envs = array_make<String>(heap_allocator()); - defer (array_free(&envs)); { #if defined(GB_SYSTEM_WINDOWS) wchar_t *strings = GetEnvironmentStringsW(); @@ -275,19 +254,50 @@ gb_internal bool try_cached_build(Checker *c, Array<String> const &args) { #endif } array_sort(envs, string_cmp); + return envs; +} + +// returns false if different, true if it is the same +gb_internal bool try_cached_build(Checker *c, Array<String> const &args) { + TEMPORARY_ALLOCATOR_GUARD(); + + auto files = cache_gather_files(c); + auto envs = cache_gather_envs(); + defer (array_free(&envs)); + + u64 crc = 0; + for (String const &path : files) { + crc = crc64_with_seed(path.text, path.len, crc); + } + + String base_cache_dir = build_context.build_paths[BuildPath_Output].basename; + base_cache_dir = concatenate_strings(permanent_allocator(), base_cache_dir, str_lit("/.odin-cache")); + (void)check_if_exists_directory_otherwise_create(base_cache_dir); + + gbString crc_str = gb_string_make_reserve(permanent_allocator(), 16); + crc_str = gb_string_append_fmt(crc_str, "%016llx", crc); + String cache_dir = concatenate3_strings(permanent_allocator(), base_cache_dir, str_lit("/"), make_string_c(crc_str)); + String files_path = concatenate3_strings(permanent_allocator(), cache_dir, str_lit("/"), str_lit("files.manifest")); + String args_path = concatenate3_strings(permanent_allocator(), cache_dir, str_lit("/"), str_lit("args.manifest")); + String env_path = concatenate3_strings(permanent_allocator(), cache_dir, str_lit("/"), str_lit("env.manifest")); + + build_context.build_cache_data.cache_dir = cache_dir; + build_context.build_cache_data.files_path = files_path; + build_context.build_cache_data.args_path = args_path; + build_context.build_cache_data.env_path = env_path; if (check_if_exists_directory_otherwise_create(cache_dir)) { - goto write_cache; + return false; } if (check_if_exists_file_otherwise_create(files_path)) { - goto write_cache; + return false; } if (check_if_exists_file_otherwise_create(args_path)) { - goto write_cache; + return false; } if (check_if_exists_file_otherwise_create(env_path)) { - goto write_cache; + return false; } { @@ -297,7 +307,7 @@ gb_internal bool try_cached_build(Checker *c, Array<String> const &args) { LoadedFileError file_err = load_file_32( alloc_cstring(temporary_allocator(), files_path), &loaded_file, - false + true ); if (file_err > LoadedFile_Empty) { return false; @@ -315,7 +325,7 @@ gb_internal bool try_cached_build(Checker *c, Array<String> const &args) { } isize sep = string_index_byte(line, ' '); if (sep < 0) { - goto write_cache; + return false; } String timestamp_str = substring(line, 0, sep); @@ -325,21 +335,21 @@ gb_internal bool try_cached_build(Checker *c, Array<String> const &args) { path_str = string_trim_whitespace(path_str); if (file_count >= files.count) { - goto write_cache; + return false; } if (files[file_count] != path_str) { - goto write_cache; + return false; } u64 timestamp = exact_value_to_u64(exact_value_integer_from_string(timestamp_str)); gbFileTime last_write_time = gb_file_last_write_time(alloc_cstring(temporary_allocator(), path_str)); if (last_write_time != timestamp) { - goto write_cache; + return false; } } if (file_count != files.count) { - goto write_cache; + return false; } } { @@ -348,7 +358,7 @@ gb_internal bool try_cached_build(Checker *c, Array<String> const &args) { LoadedFileError file_err = load_file_32( alloc_cstring(temporary_allocator(), args_path), &loaded_file, - false + true ); if (file_err > LoadedFile_Empty) { return false; @@ -366,11 +376,11 @@ gb_internal bool try_cached_build(Checker *c, Array<String> const &args) { break; } if (args_count >= args.count) { - goto write_cache; + return false; } if (line != args[args_count]) { - goto write_cache; + return false; } } } @@ -380,7 +390,7 @@ gb_internal bool try_cached_build(Checker *c, Array<String> const &args) { LoadedFileError file_err = load_file_32( alloc_cstring(temporary_allocator(), env_path), &loaded_file, - false + true ); if (file_err > LoadedFile_Empty) { return false; @@ -398,20 +408,26 @@ gb_internal bool try_cached_build(Checker *c, Array<String> const &args) { break; } if (env_count >= envs.count) { - goto write_cache; + return false; } if (line != envs[env_count]) { - goto write_cache; + return false; } } } return try_copy_executable_from_cache(); +} + +void write_cached_build(Checker *c, Array<String> const &args) { + auto files = cache_gather_files(c); + defer (array_free(&files)); + auto envs = cache_gather_envs(); + defer (array_free(&envs)); -write_cache:; { - char const *path_c = alloc_cstring(temporary_allocator(), files_path); + char const *path_c = alloc_cstring(temporary_allocator(), build_context.build_cache_data.files_path); gb_file_remove(path_c); debugf("Cache: updating %s\n", path_c); @@ -426,7 +442,7 @@ write_cache:; } } { - char const *path_c = alloc_cstring(temporary_allocator(), args_path); + char const *path_c = alloc_cstring(temporary_allocator(), build_context.build_cache_data.args_path); gb_file_remove(path_c); debugf("Cache: updating %s\n", path_c); @@ -441,7 +457,7 @@ write_cache:; } } { - char const *path_c = alloc_cstring(temporary_allocator(), env_path); + char const *path_c = alloc_cstring(temporary_allocator(), build_context.build_cache_data.env_path); gb_file_remove(path_c); debugf("Cache: updating %s\n", path_c); @@ -454,8 +470,5 @@ write_cache:; gb_fprintf(&f, "%.*s\n", LIT(env)); } } - - - return false; } diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index 8c051cca2..c86503093 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -1533,6 +1533,10 @@ gb_internal LoadDirectiveResult check_load_directory_directive(CheckerContext *c for (FileInfo fi : list) { LoadFileCache *cache = nullptr; + if (fi.is_dir) { + continue; + } + if (cache_load_file_directive(c, call, fi.fullpath, err_on_not_found, &cache, LoadFileTier_Contents, /*use_mutex*/false)) { array_add(&file_caches, cache); } else { @@ -2060,8 +2064,8 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As bool ok = check_builtin_simd_operation(c, operand, call, id, type_hint); if (!ok) { operand->type = t_invalid; + operand->mode = Addressing_Value; } - operand->mode = Addressing_Value; operand->value = {}; operand->expr = call; return ok; @@ -3098,7 +3102,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As // Okay } else if (!is_type_ordered(type) || !(is_type_numeric(type) || is_type_string(type))) { gbString type_str = type_to_string(original_type); - error(call, "Expected a ordered numeric type to 'min', got '%s'", type_str); + error(call, "Expected an ordered numeric type to 'min', got '%s'", type_str); gb_string_free(type_str); return false; } @@ -3166,6 +3170,10 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As return false; } + if (ce->args.count <= 1) { + error(call, "Too few arguments for 'min', two or more are required"); + return false; + } bool all_constant = operand->mode == Addressing_Constant; @@ -3184,7 +3192,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As if (!is_type_ordered(b.type) || !(is_type_numeric(b.type) || is_type_string(b.type))) { gbString type_str = type_to_string(b.type); error(call, - "Expected a ordered numeric type to 'min', got '%s'", + "Expected an ordered numeric type to 'min', got '%s'", type_str); gb_string_free(type_str); return false; @@ -3267,7 +3275,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As // Okay } else if (!is_type_ordered(type) || !(is_type_numeric(type) || is_type_string(type))) { gbString type_str = type_to_string(original_type); - error(call, "Expected a ordered numeric type to 'max', got '%s'", type_str); + error(call, "Expected an ordered numeric type to 'max', got '%s'", type_str); gb_string_free(type_str); return false; } @@ -3339,6 +3347,11 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As gb_string_free(type_str); return false; } + + if (ce->args.count <= 1) { + error(call, "Too few arguments for 'max', two or more are required"); + return false; + } bool all_constant = operand->mode == Addressing_Constant; @@ -3358,7 +3371,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As if (!is_type_ordered(b.type) || !(is_type_numeric(b.type) || is_type_string(b.type))) { gbString type_str = type_to_string(b.type); error(arg, - "Expected a ordered numeric type to 'max', got '%s'", + "Expected an ordered numeric type to 'max', got '%s'", type_str); gb_string_free(type_str); return false; @@ -3488,7 +3501,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As Type *type = operand->type; if (!is_type_ordered(type) || !(is_type_numeric(type) || is_type_string(type))) { gbString type_str = type_to_string(operand->type); - error(call, "Expected a ordered numeric or string type to 'clamp', got '%s'", type_str); + error(call, "Expected an ordered numeric or string type to 'clamp', got '%s'", type_str); gb_string_free(type_str); return false; } @@ -3505,7 +3518,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As } if (!is_type_ordered(y.type) || !(is_type_numeric(y.type) || is_type_string(y.type))) { gbString type_str = type_to_string(y.type); - error(call, "Expected a ordered numeric or string type to 'clamp', got '%s'", type_str); + error(call, "Expected an ordered numeric or string type to 'clamp', got '%s'", type_str); gb_string_free(type_str); return false; } @@ -3516,7 +3529,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As } if (!is_type_ordered(z.type) || !(is_type_numeric(z.type) || is_type_string(z.type))) { gbString type_str = type_to_string(z.type); - error(call, "Expected a ordered numeric or string type to 'clamp', got '%s'", type_str); + error(call, "Expected an ordered numeric or string type to 'clamp', got '%s'", type_str); gb_string_free(type_str); return false; } diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 3b532a727..60eb030ff 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -88,11 +88,12 @@ gb_internal Type *check_init_variable(CheckerContext *ctx, Entity *e, Operand *o e->type = t_invalid; return nullptr; } else if (is_type_polymorphic(t)) { - Entity *e = entity_of_node(operand->expr); - if (e == nullptr) { + Entity *e2 = entity_of_node(operand->expr); + if (e2 == nullptr) { + e->type = t_invalid; return nullptr; } - if (e->state.load() != EntityState_Resolved) { + if (e2->state.load() != EntityState_Resolved) { gbString str = type_to_string(t); defer (gb_string_free(str)); error(e->token, "Invalid use of a polymorphic type '%s' in %.*s", str, LIT(context_name)); @@ -232,6 +233,10 @@ gb_internal bool check_override_as_type_due_to_aliasing(CheckerContext *ctx, Ent // until there is a proper delaying system to try declaration again if they // have failed. + if (e->type != nullptr && is_type_typed(e->type)) { + return false; + } + e->kind = Entity_TypeName; check_type_decl(ctx, e, init, named_type); return true; diff --git a/src/check_expr.cpp b/src/check_expr.cpp index fc1aa62e6..cb4647f33 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -285,7 +285,7 @@ gb_internal void error_operand_no_value(Operand *o) { if (o->mode == Addressing_NoValue) { Ast *x = unparen_expr(o->expr); - if (x->kind == Ast_CallExpr) { + if (x != nullptr && x->kind == Ast_CallExpr) { Ast *p = unparen_expr(x->CallExpr.proc); if (p->kind == Ast_BasicDirective) { String tag = p->BasicDirective.name.string; @@ -297,7 +297,7 @@ gb_internal void error_operand_no_value(Operand *o) { } gbString err = expr_to_string(o->expr); - if (x->kind == Ast_CallExpr) { + if (x != nullptr && x->kind == Ast_CallExpr) { error(o->expr, "'%s' call does not return a value and cannot be used as a value", err); } else { error(o->expr, "'%s' used as a value", err); @@ -897,20 +897,6 @@ gb_internal i64 check_distance_between_types(CheckerContext *c, Operand *operand } } - if (is_type_relative_pointer(dst)) { - i64 score = check_distance_between_types(c, operand, dst->RelativePointer.pointer_type, allow_array_programming); - if (score >= 0) { - return score+2; - } - } - - if (is_type_relative_multi_pointer(dst)) { - i64 score = check_distance_between_types(c, operand, dst->RelativeMultiPointer.pointer_type, allow_array_programming); - if (score >= 0) { - return score+2; - } - } - if (is_type_proc(dst)) { if (are_types_identical(src, dst)) { return 3; @@ -1052,12 +1038,6 @@ gb_internal AstPackage *get_package_of_type(Type *type) { case Type_DynamicArray: type = type->DynamicArray.elem; continue; - case Type_RelativePointer: - type = type->RelativePointer.pointer_type; - continue; - case Type_RelativeMultiPointer: - type = type->RelativeMultiPointer.pointer_type; - continue; } return nullptr; } @@ -3901,6 +3881,12 @@ gb_internal void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, Typ // IMPORTANT NOTE(bill): This uses right-left evaluation in type checking only no in check_expr(c, y, be->right); Type *rhs_type = type_deref(y->type); + if (rhs_type == nullptr) { + error(y->expr, "Cannot use '%.*s' on an expression with no value", LIT(op.string)); + x->mode = Addressing_Invalid; + x->expr = node; + return; + } if (is_type_bit_set(rhs_type)) { Type *elem = base_type(rhs_type)->BitSet.elem; @@ -5388,22 +5374,25 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod Type *t = type_deref(operand->type); if (t == nullptr) { error(operand->expr, "Cannot use a selector expression on 0-value expression"); - } else if (is_type_dynamic_array(t)) { - init_mem_allocator(c->checker); - } - sel = lookup_field(operand->type, field_name, operand->mode == Addressing_Type); - entity = sel.entity; + } else { + if (is_type_dynamic_array(t)) { + init_mem_allocator(c->checker); + } + sel = lookup_field(operand->type, field_name, operand->mode == Addressing_Type); + entity = sel.entity; - // NOTE(bill): Add type info needed for fields like 'names' - if (entity != nullptr && (entity->flags&EntityFlag_TypeField)) { - add_type_info_type(c, operand->type); - } - if (is_type_enum(operand->type)) { - add_type_info_type(c, operand->type); + // NOTE(bill): Add type info needed for fields like 'names' + if (entity != nullptr && (entity->flags&EntityFlag_TypeField)) { + add_type_info_type(c, operand->type); + } + if (is_type_enum(operand->type)) { + add_type_info_type(c, operand->type); + } } } - if (entity == nullptr && selector->kind == Ast_Ident && (is_type_array(type_deref(operand->type)) || is_type_simd_vector(type_deref(operand->type)))) { + if (entity == nullptr && selector->kind == Ast_Ident && operand->type != nullptr && + (is_type_array(type_deref(operand->type)) || is_type_simd_vector(type_deref(operand->type)))) { String field_name = selector->Ident.token.string; if (1 < field_name.len && field_name.len <= 4) { u8 swizzles_xyzw[4] = {'x', 'y', 'z', 'w'}; @@ -8002,6 +7991,7 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c pt = data.gen_entity->type; } } + pt = base_type(pt); if (pt->kind == Type_Proc && pt->Proc.calling_convention == ProcCC_Odin) { if ((c->scope->flags & ScopeFlag_ContextDefined) == 0) { @@ -8220,17 +8210,6 @@ gb_internal bool check_set_index_data(Operand *o, Type *t, bool indirection, i64 } return true; - case Type_RelativeMultiPointer: - { - Type *pointer_type = base_type(t->RelativeMultiPointer.pointer_type); - GB_ASSERT(pointer_type->kind == Type_MultiPointer); - o->type = pointer_type->MultiPointer.elem; - if (o->mode != Addressing_Constant) { - o->mode = Addressing_Variable; - } - } - return true; - case Type_DynamicArray: o->type = t->DynamicArray.elem; if (o->mode != Addressing_Constant) { @@ -8469,6 +8448,15 @@ gb_internal ExprKind check_implicit_selector_expr(CheckerContext *c, Operand *o, error(node, "Undeclared name '%.*s' for type '%s'", LIT(name), typ); check_did_you_mean_type(name, bt->Enum.fields); + } else if (is_type_bit_set(th) && is_type_enum(th->BitSet.elem)) { + ERROR_BLOCK(); + + gbString typ = type_to_string(th); + gbString str = expr_to_string(node); + error(node, "Cannot convert enum value to '%s'", typ); + error_line("\tSuggestion: Did you mean '{ %s }'?\n", str); + gb_string_free(typ); + gb_string_free(str); } else { gbString typ = type_to_string(th); gbString str = expr_to_string(node); @@ -8795,11 +8783,6 @@ gb_internal ExprKind check_ternary_if_expr(CheckerContext *c, Operand *o, Ast *n return kind; } - if (x.type == nullptr || x.type == t_invalid || - y.type == nullptr || y.type == t_invalid) { - return kind; - } - bool use_type_hint = type_hint != nullptr && (is_operand_nil(x) || is_operand_nil(y)); convert_to_typed(c, &x, use_type_hint ? type_hint : y.type); @@ -10609,8 +10592,6 @@ gb_internal ExprKind check_index_expr(CheckerContext *c, Operand *o, Ast *node, // Okay } else if (is_type_string(t)) { // Okay - } else if (is_type_relative_multi_pointer(t)) { - // Okay } else if (is_type_matrix(t)) { // Okay } else { @@ -10765,11 +10746,6 @@ gb_internal ExprKind check_slice_expr(CheckerContext *c, Operand *o, Ast *node, } break; - case Type_RelativeMultiPointer: - valid = true; - o->type = type_deref(o->type); - break; - case Type_EnumeratedArray: { gbString str = expr_to_string(o->expr); @@ -10846,16 +10822,6 @@ gb_internal ExprKind check_slice_expr(CheckerContext *c, Operand *o, Ast *node, x[i:n] -> []T */ o->type = alloc_type_slice(t->MultiPointer.elem); - } else if (t->kind == Type_RelativeMultiPointer && se->high != nullptr) { - /* - x[:] -> [^]T - x[i:] -> [^]T - x[:n] -> []T - x[i:n] -> []T - */ - Type *pointer_type = base_type(t->RelativeMultiPointer.pointer_type); - GB_ASSERT(pointer_type->kind == Type_MultiPointer); - o->type = alloc_type_slice(pointer_type->MultiPointer.elem); } @@ -11216,22 +11182,6 @@ gb_internal ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast } else if (t->kind == Type_SoaPointer) { o->mode = Addressing_SoaVariable; o->type = type_deref(t); - } else if (t->kind == Type_RelativePointer) { - if (o->mode != Addressing_Variable) { - gbString str = expr_to_string(o->expr); - gbString typ = type_to_string(o->type); - error(o->expr, "Cannot dereference relative pointer '%s' of type '%s' as it does not have a variable addressing mode", str, typ); - gb_string_free(typ); - gb_string_free(str); - } - - // NOTE(bill): This is required because when dereferencing, the original type has been lost - add_type_info_type(c, o->type); - - Type *ptr_type = base_type(t->RelativePointer.pointer_type); - GB_ASSERT(ptr_type->kind == Type_Pointer); - o->mode = Addressing_Variable; - o->type = ptr_type->Pointer.elem; } else { gbString str = expr_to_string(o->expr); gbString typ = type_to_string(o->type); diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 74a9e8825..2418fcc5c 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -2600,6 +2600,23 @@ gb_internal void check_for_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) { check_expr(ctx, &o, fs->cond); if (o.mode != Addressing_Invalid && !is_type_boolean(o.type)) { error(fs->cond, "Non-boolean condition in 'for' statement"); + } else { + Ast *cond = unparen_expr(o.expr); + if (cond && cond->kind == Ast_BinaryExpr && + cond->BinaryExpr.left && cond->BinaryExpr.right && + cond->BinaryExpr.op.kind == Token_GtEq && + is_type_unsigned(type_of_expr(cond->BinaryExpr.left)) && + cond->BinaryExpr.right->tav.value.kind == ExactValue_Integer && + is_exact_value_zero(cond->BinaryExpr.right->tav.value)) { + warning(cond, "Expression is always true since unsigned numbers are always >= 0"); + } else if (cond && cond->kind == Ast_BinaryExpr && + cond->BinaryExpr.left && cond->BinaryExpr.right && + cond->BinaryExpr.op.kind == Token_LtEq && + is_type_unsigned(type_of_expr(cond->BinaryExpr.right)) && + cond->BinaryExpr.left->tav.value.kind == ExactValue_Integer && + is_exact_value_zero(cond->BinaryExpr.left->tav.value)) { + warning(cond, "Expression is always true since unsigned numbers are always >= 0"); + } } } if (fs->post != nullptr) { diff --git a/src/check_type.cpp b/src/check_type.cpp index f0e0acb9b..84e7fb249 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -673,7 +673,7 @@ gb_internal void check_struct_type(CheckerContext *ctx, Type *struct_type, Ast * #define ST_ALIGN(_name) if (st->_name != nullptr) { \ if (st->is_packed) { \ - syntax_error(st->_name, "'#%s' cannot be applied with '#packed'", #_name); \ + error(st->_name, "'#%s' cannot be applied with '#packed'", #_name); \ return; \ } \ i64 align = 1; \ @@ -682,12 +682,31 @@ gb_internal void check_struct_type(CheckerContext *ctx, Type *struct_type, Ast * } \ } - ST_ALIGN(field_align); + ST_ALIGN(min_field_align); + ST_ALIGN(max_field_align); ST_ALIGN(align); - if (struct_type->Struct.custom_align < struct_type->Struct.custom_field_align) { - warning(st->align, "#align(%lld) is defined to be less than #field_name(%lld)", - cast(long long)struct_type->Struct.custom_align, - cast(long long)struct_type->Struct.custom_field_align); + if (struct_type->Struct.custom_align < struct_type->Struct.custom_min_field_align) { + error(st->align, "#align(%lld) is defined to be less than #min_field_align(%lld)", + cast(long long)struct_type->Struct.custom_align, + cast(long long)struct_type->Struct.custom_min_field_align); + } + if (struct_type->Struct.custom_max_field_align != 0 && + struct_type->Struct.custom_align > struct_type->Struct.custom_max_field_align) { + error(st->align, "#align(%lld) is defined to be greater than #max_field_align(%lld)", + cast(long long)struct_type->Struct.custom_align, + cast(long long)struct_type->Struct.custom_max_field_align); + } + if (struct_type->Struct.custom_max_field_align != 0 && + struct_type->Struct.custom_min_field_align > struct_type->Struct.custom_max_field_align) { + error(st->align, "#min_field_align(%lld) is defined to be greater than #max_field_align(%lld)", + cast(long long)struct_type->Struct.custom_min_field_align, + cast(long long)struct_type->Struct.custom_max_field_align); + + i64 a = gb_min(struct_type->Struct.custom_min_field_align, struct_type->Struct.custom_max_field_align); + i64 b = gb_max(struct_type->Struct.custom_min_field_align, struct_type->Struct.custom_max_field_align); + // NOTE(bill): sort them to keep code consistent + struct_type->Struct.custom_min_field_align = a; + struct_type->Struct.custom_max_field_align = b; } #undef ST_ALIGN @@ -3498,41 +3517,8 @@ gb_internal bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, T case_end; case_ast_node(rt, RelativeType, e); - GB_ASSERT(rt->tag->kind == Ast_CallExpr); - ast_node(ce, CallExpr, rt->tag); - - Type *base_integer = nullptr; - - if (ce->args.count != 1) { - error(rt->type, "#relative expected 1 type argument, got %td", ce->args.count); - } else { - base_integer = check_type(ctx, ce->args[0]); - if (!is_type_integer(base_integer)) { - error(rt->type, "#relative base types must be an integer"); - base_integer = nullptr; - } else if (type_size_of(base_integer) > 64) { - error(rt->type, "#relative base integer types be less than or equal to 64-bits"); - base_integer = nullptr; - } - } - - Type *relative_type = nullptr; - Type *base_type = check_type(ctx, rt->type); - if (!is_type_pointer(base_type) && !is_type_multi_pointer(base_type)) { - error(rt->type, "#relative types can only be a pointer or multi-pointer"); - relative_type = base_type; - } else if (base_integer == nullptr) { - relative_type = base_type; - } else { - if (is_type_pointer(base_type)) { - relative_type = alloc_type_relative_pointer(base_type, base_integer); - } else if (is_type_multi_pointer(base_type)) { - relative_type = alloc_type_relative_multi_pointer(base_type, base_integer); - } - } - GB_ASSERT(relative_type != nullptr); - - *type = relative_type; + error(e, "#relative types have been removed from the compiler. Prefer \"core:relative\"."); + *type = t_invalid; set_base_type(named_type, *type); return true; case_end; diff --git a/src/checker.cpp b/src/checker.cpp index af1e0e675..b7cf343f8 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -695,6 +695,9 @@ gb_internal void check_scope_usage_internal(Checker *c, Scope *scope, u64 vet_fl bool vet_unused = (vet_flags & VetFlag_Unused) != 0; bool vet_shadowing = (vet_flags & (VetFlag_Shadowing|VetFlag_Using)) != 0; bool vet_unused_procedures = (vet_flags & VetFlag_UnusedProcedures) != 0; + if (vet_unused_procedures && e->pkg && e->pkg->kind == Package_Runtime) { + vet_unused_procedures = false; + } VettedEntity ve_unused = {}; VettedEntity ve_shadowed = {}; @@ -2183,16 +2186,6 @@ gb_internal void add_type_info_type_internal(CheckerContext *c, Type *t) { add_type_info_type_internal(c, bt->SimdVector.elem); break; - case Type_RelativePointer: - add_type_info_type_internal(c, bt->RelativePointer.pointer_type); - add_type_info_type_internal(c, bt->RelativePointer.base_integer); - break; - - case Type_RelativeMultiPointer: - add_type_info_type_internal(c, bt->RelativeMultiPointer.pointer_type); - add_type_info_type_internal(c, bt->RelativeMultiPointer.base_integer); - break; - case Type_Matrix: add_type_info_type_internal(c, bt->Matrix.elem); break; @@ -2438,16 +2431,6 @@ gb_internal void add_min_dep_type_info(Checker *c, Type *t) { add_min_dep_type_info(c, bt->SimdVector.elem); break; - case Type_RelativePointer: - add_min_dep_type_info(c, bt->RelativePointer.pointer_type); - add_min_dep_type_info(c, bt->RelativePointer.base_integer); - break; - - case Type_RelativeMultiPointer: - add_min_dep_type_info(c, bt->RelativeMultiPointer.pointer_type); - add_min_dep_type_info(c, bt->RelativeMultiPointer.base_integer); - break; - case Type_Matrix: add_min_dep_type_info(c, bt->Matrix.elem); break; @@ -3072,8 +3055,6 @@ gb_internal void init_core_type_info(Checker *c) { t_type_info_map = find_core_type(c, str_lit("Type_Info_Map")); t_type_info_bit_set = find_core_type(c, str_lit("Type_Info_Bit_Set")); t_type_info_simd_vector = find_core_type(c, str_lit("Type_Info_Simd_Vector")); - t_type_info_relative_pointer = find_core_type(c, str_lit("Type_Info_Relative_Pointer")); - t_type_info_relative_multi_pointer = find_core_type(c, str_lit("Type_Info_Relative_Multi_Pointer")); t_type_info_matrix = find_core_type(c, str_lit("Type_Info_Matrix")); t_type_info_soa_pointer = find_core_type(c, str_lit("Type_Info_Soa_Pointer")); t_type_info_bit_field = find_core_type(c, str_lit("Type_Info_Bit_Field")); @@ -3102,8 +3083,6 @@ gb_internal void init_core_type_info(Checker *c) { t_type_info_map_ptr = alloc_type_pointer(t_type_info_map); t_type_info_bit_set_ptr = alloc_type_pointer(t_type_info_bit_set); t_type_info_simd_vector_ptr = alloc_type_pointer(t_type_info_simd_vector); - t_type_info_relative_pointer_ptr = alloc_type_pointer(t_type_info_relative_pointer); - t_type_info_relative_multi_pointer_ptr = alloc_type_pointer(t_type_info_relative_multi_pointer); t_type_info_matrix_ptr = alloc_type_pointer(t_type_info_matrix); t_type_info_soa_pointer_ptr = alloc_type_pointer(t_type_info_soa_pointer); t_type_info_bit_field_ptr = alloc_type_pointer(t_type_info_bit_field); @@ -6206,7 +6185,7 @@ gb_internal void check_deferred_procedures(Checker *c) { } if ((src_params == nullptr && dst_params != nullptr) || (src_params != nullptr && dst_params == nullptr)) { - error(src->token, "Deferred procedure '%.*s' parameters do not match the inputs of initial procedure '%.*s'", LIT(src->token.string), LIT(dst->token.string)); + error(src->token, "Deferred procedure '%.*s' parameters do not match the inputs of initial procedure '%.*s'", LIT(dst->token.string), LIT(src->token.string)); continue; } @@ -6219,8 +6198,8 @@ gb_internal void check_deferred_procedures(Checker *c) { gbString s = type_to_string(src_params); gbString d = type_to_string(dst_params); error(src->token, "Deferred procedure '%.*s' parameters do not match the inputs of initial procedure '%.*s':\n\t(%s) =/= (%s)", - LIT(src->token.string), LIT(dst->token.string), - s, d + LIT(dst->token.string), LIT(src->token.string), + d, s ); gb_string_free(d); gb_string_free(s); @@ -6236,7 +6215,7 @@ gb_internal void check_deferred_procedures(Checker *c) { } if ((src_results == nullptr && dst_params != nullptr) || (src_results != nullptr && dst_params == nullptr)) { - error(src->token, "Deferred procedure '%.*s' parameters do not match the results of initial procedure '%.*s'", LIT(src->token.string), LIT(dst->token.string)); + error(src->token, "Deferred procedure '%.*s' parameters do not match the results of initial procedure '%.*s'", LIT(dst->token.string), LIT(src->token.string)); continue; } @@ -6249,8 +6228,8 @@ gb_internal void check_deferred_procedures(Checker *c) { gbString s = type_to_string(src_results); gbString d = type_to_string(dst_params); error(src->token, "Deferred procedure '%.*s' parameters do not match the results of initial procedure '%.*s':\n\t(%s) =/= (%s)", - LIT(src->token.string), LIT(dst->token.string), - s, d + LIT(dst->token.string), LIT(src->token.string), + d, s ); gb_string_free(d); gb_string_free(s); @@ -6302,8 +6281,8 @@ gb_internal void check_deferred_procedures(Checker *c) { gbString s = type_to_string(tsrc); gbString d = type_to_string(dst_params); error(src->token, "Deferred procedure '%.*s' parameters do not match the results of initial procedure '%.*s':\n\t(%s) =/= (%s)", - LIT(src->token.string), LIT(dst->token.string), - s, d + LIT(dst->token.string), LIT(src->token.string), + d, s ); gb_string_free(d); gb_string_free(s); diff --git a/src/docs_format.cpp b/src/docs_format.cpp index ca6ecb5c2..6378971d0 100644 --- a/src/docs_format.cpp +++ b/src/docs_format.cpp @@ -79,8 +79,7 @@ enum OdinDocTypeKind : u32 { OdinDocType_SOAStructFixed = 17, OdinDocType_SOAStructSlice = 18, OdinDocType_SOAStructDynamic = 19, - OdinDocType_RelativePointer = 20, - OdinDocType_RelativeMultiPointer = 21, + OdinDocType_MultiPointer = 22, OdinDocType_Matrix = 23, OdinDocType_SoaPointer = 24, diff --git a/src/docs_writer.cpp b/src/docs_writer.cpp index 835dfdff1..341b3fa6b 100644 --- a/src/docs_writer.cpp +++ b/src/docs_writer.cpp @@ -776,24 +776,6 @@ gb_internal OdinDocTypeIndex odin_doc_type(OdinDocWriter *w, Type *type) { doc_type.types = odin_doc_type_as_slice(w, type->SimdVector.elem); // TODO(bill): break; - case Type_RelativePointer: - doc_type.kind = OdinDocType_RelativePointer; - { - OdinDocTypeIndex types[2] = {}; - types[0] = odin_doc_type(w, type->RelativePointer.pointer_type); - types[1] = odin_doc_type(w, type->RelativePointer.base_integer); - doc_type.types = odin_write_slice(w, types, gb_count_of(types)); - } - break; - case Type_RelativeMultiPointer: - doc_type.kind = OdinDocType_RelativeMultiPointer; - { - OdinDocTypeIndex types[2] = {}; - types[0] = odin_doc_type(w, type->RelativeMultiPointer.pointer_type); - types[1] = odin_doc_type(w, type->RelativeMultiPointer.base_integer); - doc_type.types = odin_write_slice(w, types, gb_count_of(types)); - } - break; case Type_Matrix: doc_type.kind = OdinDocType_Matrix; diff --git a/src/exact_value.cpp b/src/exact_value.cpp index 1a42a82a9..5d6016ecc 100644 --- a/src/exact_value.cpp +++ b/src/exact_value.cpp @@ -687,6 +687,7 @@ gb_internal void match_exact_values(ExactValue *x, ExactValue *y) { case ExactValue_String: case ExactValue_Quaternion: case ExactValue_Pointer: + case ExactValue_Compound: case ExactValue_Procedure: case ExactValue_Typeid: return; diff --git a/src/gb/gb.h b/src/gb/gb.h index 1fef4b4f5..f74026c7d 100644 --- a/src/gb/gb.h +++ b/src/gb/gb.h @@ -2541,7 +2541,11 @@ gb_inline void const *gb_pointer_add_const(void const *ptr, isize bytes) { gb_inline void const *gb_pointer_sub_const(void const *ptr, isize bytes) { return cast(void const *)(cast(u8 const *)ptr - bytes); } gb_inline isize gb_pointer_diff (void const *begin, void const *end) { return cast(isize)(cast(u8 const *)end - cast(u8 const *)begin); } -gb_inline void gb_zero_size(void *ptr, isize size) { memset(ptr, 0, size); } +gb_inline void gb_zero_size(void *ptr, isize size) { + if (size != 0) { + memset(ptr, 0, size); + } +} #if defined(_MSC_VER) && !defined(__clang__) diff --git a/src/linker.cpp b/src/linker.cpp index 500fead69..261d6e7a4 100644 --- a/src/linker.cpp +++ b/src/linker.cpp @@ -167,8 +167,10 @@ gb_internal i32 linker_stage(LinkerData *gen) { if (is_windows) { String section_name = str_lit("msvc-link"); - if (build_context.use_lld) { - section_name = str_lit("lld-link"); + switch (build_context.linker_choice) { + case Linker_Default: break; + case Linker_lld: section_name = str_lit("lld-link"); break; + case Linker_radlink: section_name = str_lit("rad-link"); break; } timings_start_section(timings, section_name); @@ -304,7 +306,48 @@ gb_internal i32 linker_stage(LinkerData *gen) { String windows_sdk_bin_path = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_Win_SDK_Bin_Path]); defer (gb_free(heap_allocator(), windows_sdk_bin_path.text)); - if (!build_context.use_lld) { // msvc + switch (build_context.linker_choice) { + case Linker_lld: + result = system_exec_command_line_app("msvc-lld-link", + "\"%.*s\\bin\\lld-link\" %s -OUT:\"%.*s\" %s " + "/nologo /incremental:no /opt:ref /subsystem:%.*s " + "%.*s " + "%.*s " + "%s " + "", + LIT(build_context.ODIN_ROOT), object_files, LIT(output_filename), + link_settings, + LIT(build_context.ODIN_WINDOWS_SUBSYSTEM), + LIT(build_context.link_flags), + LIT(build_context.extra_linker_flags), + lib_str + ); + + if (result) { + return result; + } + break; + case Linker_radlink: + result = system_exec_command_line_app("msvc-rad-link", + "\"%.*s\\bin\\radlink\" %s -OUT:\"%.*s\" %s " + "/nologo /incremental:no /opt:ref /subsystem:%.*s " + "%.*s " + "%.*s " + "%s " + "", + LIT(build_context.ODIN_ROOT), object_files, LIT(output_filename), + link_settings, + LIT(build_context.ODIN_WINDOWS_SUBSYSTEM), + LIT(build_context.link_flags), + LIT(build_context.extra_linker_flags), + lib_str + ); + + if (result) { + return result; + } + break; + default: { // msvc String res_path = quote_path(heap_allocator(), build_context.build_paths[BuildPath_RES]); String rc_path = quote_path(heap_allocator(), build_context.build_paths[BuildPath_RC]); defer (gb_free(heap_allocator(), res_path.text)); @@ -365,25 +408,8 @@ gb_internal i32 linker_stage(LinkerData *gen) { if (result) { return result; } - } else { // lld - result = system_exec_command_line_app("msvc-lld-link", - "\"%.*s\\bin\\lld-link\" %s -OUT:\"%.*s\" %s " - "/nologo /incremental:no /opt:ref /subsystem:%.*s " - "%.*s " - "%.*s " - "%s " - "", - LIT(build_context.ODIN_ROOT), object_files, LIT(output_filename), - link_settings, - LIT(build_context.ODIN_WINDOWS_SUBSYSTEM), - LIT(build_context.link_flags), - LIT(build_context.extra_linker_flags), - lib_str - ); - - if (result) { - return result; - } + break; + } } } else { timings_start_section(timings, str_lit("ld-link")); @@ -605,9 +631,18 @@ gb_internal i32 linker_stage(LinkerData *gen) { link_settings = gb_string_appendc(link_settings, "-Wl,-fini,'_odin_exit_point' "); } - } else if (build_context.metrics.os != TargetOs_openbsd && build_context.metrics.os != TargetOs_haiku && build_context.metrics.arch != TargetArch_riscv64) { - // OpenBSD and Haiku default to PIE executable. do not pass -no-pie for it. - link_settings = gb_string_appendc(link_settings, "-no-pie "); + } + + if (build_context.build_mode == BuildMode_Executable && build_context.reloc_mode == RelocMode_PIC) { + // Do not disable PIE, let the linker choose. (most likely you want it enabled) + } else if (build_context.build_mode != BuildMode_DynamicLibrary) { + if (build_context.metrics.os != TargetOs_openbsd + && build_context.metrics.os != TargetOs_haiku + && build_context.metrics.arch != TargetArch_riscv64 + ) { + // OpenBSD and Haiku default to PIE executable. do not pass -no-pie for it. + link_settings = gb_string_appendc(link_settings, "-no-pie "); + } } gbString platform_lib_str = gb_string_make(heap_allocator(), ""); @@ -670,7 +705,7 @@ gb_internal i32 linker_stage(LinkerData *gen) { link_command_line = gb_string_append_fmt(link_command_line, " %.*s ", LIT(build_context.extra_linker_flags)); link_command_line = gb_string_append_fmt(link_command_line, " %s ", link_settings); - if (build_context.use_lld) { + if (build_context.linker_choice == Linker_lld) { link_command_line = gb_string_append_fmt(link_command_line, " -fuse-ld=lld"); result = system_exec_command_line_app("lld-link", link_command_line); } else { @@ -684,7 +719,7 @@ gb_internal i32 linker_stage(LinkerData *gen) { if (is_osx && build_context.ODIN_DEBUG) { // NOTE: macOS links DWARF symbols dynamically. Dsymutil will map the stubs in the exe // to the symbols in the object file - result = system_exec_command_line_app("dsymutil", "dsymutil %.*s", LIT(output_filename)); + result = system_exec_command_line_app("dsymutil", "dsymutil \"%.*s\"", LIT(output_filename)); if (result) { return result; diff --git a/src/llvm_abi.cpp b/src/llvm_abi.cpp index 42086b09d..0b2bb7956 100644 --- a/src/llvm_abi.cpp +++ b/src/llvm_abi.cpp @@ -531,6 +531,7 @@ namespace lbAbiAmd64SysV { RegClass_SSEInt16, RegClass_SSEInt32, RegClass_SSEInt64, + RegClass_SSEInt128, RegClass_SSEUp, RegClass_X87, RegClass_X87Up, @@ -572,6 +573,15 @@ namespace lbAbiAmd64SysV { gb_internal Array<RegClass> classify(LLVMTypeRef t); gb_internal LLVMTypeRef llreg(LLVMContextRef c, Array<RegClass> const ®_classes, LLVMTypeRef type); + gb_internal LB_ABI_COMPUTE_RETURN_TYPE(compute_return_type) { + if (!return_is_defined) { + return lb_arg_type_direct(LLVMVoidTypeInContext(c)); + } + LB_ABI_MODIFY_RETURN_IF_TUPLE_MACRO(); + + return amd64_type(c, return_type, Amd64TypeAttribute_StructRect, ft->calling_convention); + } + gb_internal LB_ABI_INFO(abi_info) { LLVMContextRef c = m->ctx; lbFunctionType *ft = gb_alloc_item(permanent_allocator(), lbFunctionType); @@ -582,12 +592,7 @@ namespace lbAbiAmd64SysV { for (unsigned i = 0; i < arg_count; i++) { ft->args[i] = amd64_type(c, arg_types[i], Amd64TypeAttribute_ByVal, calling_convention); } - - if (return_is_defined) { - ft->ret = amd64_type(c, return_type, Amd64TypeAttribute_StructRect, calling_convention); - } else { - ft->ret = lb_arg_type_direct(LLVMVoidTypeInContext(c)); - } + ft->ret = compute_return_type(ft, c, return_type, return_is_defined, return_is_tuple); return ft; } @@ -616,6 +621,10 @@ namespace lbAbiAmd64SysV { } switch (kind) { case LLVMIntegerTypeKind: + if (LLVM_VERSION_MAJOR >= 18 && sz >= 16) { + return true; + } + return false; case LLVMHalfTypeKind: case LLVMFloatTypeKind: case LLVMDoubleTypeKind: diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index 68f95cb03..464efa325 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -75,7 +75,6 @@ enum lbAddrKind { lbAddr_Context, lbAddr_SoaVariable, - lbAddr_RelativePointer, lbAddr_Swizzle, lbAddr_SwizzleLarge, @@ -104,9 +103,6 @@ struct lbAddr { Ast *node; } index_set; struct { - bool deref; - } relative; - struct { Type *type; u8 count; // 2, 3, or 4 components u8 indices[4]; diff --git a/src/llvm_backend_debug.cpp b/src/llvm_backend_debug.cpp index 5cc79dcc8..464f7065c 100644 --- a/src/llvm_backend_debug.cpp +++ b/src/llvm_backend_debug.cpp @@ -920,17 +920,6 @@ gb_internal LLVMMetadataRef lb_debug_type_internal(lbModule *m, Type *type) { elem, subscripts, gb_count_of(subscripts)); } - case Type_RelativePointer: { - LLVMMetadataRef base_integer = lb_debug_type(m, type->RelativePointer.base_integer); - gbString name = type_to_string(type, temporary_allocator()); - return LLVMDIBuilderCreateTypedef(m->debug_builder, base_integer, name, gb_string_length(name), nullptr, 0, nullptr, cast(u32)(8*type_align_of(type))); - } - case Type_RelativeMultiPointer: { - LLVMMetadataRef base_integer = lb_debug_type(m, type->RelativeMultiPointer.base_integer); - gbString name = type_to_string(type, temporary_allocator()); - return LLVMDIBuilderCreateTypedef(m->debug_builder, base_integer, name, gb_string_length(name), nullptr, 0, nullptr, cast(u32)(8*type_align_of(type))); - } - case Type_Matrix: { LLVMMetadataRef subscripts[1] = {}; subscripts[0] = LLVMDIBuilderGetOrCreateSubrange(m->debug_builder, diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index 58467db2e..80c469ae6 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -130,7 +130,7 @@ gb_internal lbValue lb_emit_unary_arith(lbProcedure *p, TokenKind op, lbValue x, LLVMTypeRef vector_type = nullptr; if (op != Token_Not && lb_try_vector_cast(p->module, val, &vector_type)) { LLVMValueRef vp = LLVMBuildPointerCast(p->builder, val.value, LLVMPointerType(vector_type, 0), ""); - LLVMValueRef v = LLVMBuildLoad2(p->builder, vector_type, vp, ""); + LLVMValueRef v = OdinLLVMBuildLoad(p, vector_type, vp); LLVMValueRef opv = nullptr; switch (op) { @@ -324,8 +324,8 @@ gb_internal bool lb_try_direct_vector_arith(lbProcedure *p, TokenKind op, lbValu LLVMValueRef lhs_vp = LLVMBuildPointerCast(p->builder, lhs_ptr.value, LLVMPointerType(vector_type, 0), ""); LLVMValueRef rhs_vp = LLVMBuildPointerCast(p->builder, rhs_ptr.value, LLVMPointerType(vector_type, 0), ""); - LLVMValueRef x = LLVMBuildLoad2(p->builder, vector_type, lhs_vp, ""); - LLVMValueRef y = LLVMBuildLoad2(p->builder, vector_type, rhs_vp, ""); + LLVMValueRef x = OdinLLVMBuildLoad(p, vector_type, lhs_vp); + LLVMValueRef y = OdinLLVMBuildLoad(p, vector_type, rhs_vp); LLVMValueRef z = nullptr; if (is_type_float(integral_type)) { @@ -551,15 +551,14 @@ gb_internal LLVMValueRef lb_matrix_to_vector(lbProcedure *p, lbValue matrix) { Type *mt = base_type(matrix.type); GB_ASSERT(mt->kind == Type_Matrix); LLVMTypeRef elem_type = lb_type(p->module, mt->Matrix.elem); - + unsigned total_count = cast(unsigned)matrix_type_total_internal_elems(mt); LLVMTypeRef total_matrix_type = LLVMVectorType(elem_type, total_count); - + #if 1 LLVMValueRef ptr = lb_address_from_load_or_generate_local(p, matrix).value; LLVMValueRef matrix_vector_ptr = LLVMBuildPointerCast(p->builder, ptr, LLVMPointerType(total_matrix_type, 0), ""); - LLVMValueRef matrix_vector = LLVMBuildLoad2(p->builder, total_matrix_type, matrix_vector_ptr, ""); - LLVMSetAlignment(matrix_vector, cast(unsigned)type_align_of(mt)); + LLVMValueRef matrix_vector = OdinLLVMBuildLoadAligned(p, total_matrix_type, matrix_vector_ptr, type_align_of(mt)); return matrix_vector; #else LLVMValueRef matrix_vector = LLVMBuildBitCast(p->builder, matrix.value, total_matrix_type, ""); @@ -1225,10 +1224,10 @@ gb_internal lbValue lb_emit_arith(lbProcedure *p, TokenKind op, lbValue lhs, lbV lbValue d3 = lb_emit_struct_ep(p, res.addr, 3); if (immediate_type != ft) { - d0 = lb_emit_conv(p, d0, ft); - d1 = lb_emit_conv(p, d1, ft); - d2 = lb_emit_conv(p, d2, ft); - d3 = lb_emit_conv(p, d3, ft); + z0 = lb_emit_conv(p, z0, ft); + z1 = lb_emit_conv(p, z1, ft); + z2 = lb_emit_conv(p, z2, ft); + z3 = lb_emit_conv(p, z3, ft); } lb_emit_store(p, d0, z0); @@ -1648,7 +1647,7 @@ gb_internal lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) { lb_emit_store(p, a1, id); return lb_addr_load(p, res); } else if (dst->kind == Type_Basic) { - if (src->Basic.kind == Basic_string && dst->Basic.kind == Basic_cstring) { + if (src->kind == Type_Basic && src->Basic.kind == Basic_string && dst->Basic.kind == Basic_cstring) { String str = lb_get_const_string(m, value); lbValue res = {}; res.type = t; @@ -2364,12 +2363,23 @@ gb_internal lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) { GB_ASSERT(src->kind == Type_Matrix); lbAddr v = lb_add_local_generated(p, t, true); - if (is_matrix_square(dst) && is_matrix_square(dst)) { + if (dst->Matrix.row_count == src->Matrix.row_count && + dst->Matrix.column_count == src->Matrix.column_count) { + for (i64 j = 0; j < dst->Matrix.column_count; j++) { + for (i64 i = 0; i < dst->Matrix.row_count; i++) { + lbValue d = lb_emit_matrix_epi(p, v.addr, i, j); + lbValue s = lb_emit_matrix_ev(p, value, i, j); + s = lb_emit_conv(p, s, dst->Matrix.elem); + lb_emit_store(p, d, s); + } + } + } else if (is_matrix_square(dst) && is_matrix_square(dst)) { for (i64 j = 0; j < dst->Matrix.column_count; j++) { for (i64 i = 0; i < dst->Matrix.row_count; i++) { if (i < src->Matrix.row_count && j < src->Matrix.column_count) { lbValue d = lb_emit_matrix_epi(p, v.addr, i, j); lbValue s = lb_emit_matrix_ev(p, value, i, j); + s = lb_emit_conv(p, s, dst->Matrix.elem); lb_emit_store(p, d, s); } else if (i == j) { lbValue d = lb_emit_matrix_epi(p, v.addr, i, j); @@ -2555,17 +2565,27 @@ gb_internal lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left if (are_types_identical(a, b)) { // NOTE(bill): No need for a conversion - } else if (lb_is_const(left) || lb_is_const_nil(left)) { + } else if ((lb_is_const(left) && !is_type_array(left.type)) || lb_is_const_nil(left)) { + // NOTE(karl): !is_type_array(left.type) is there to avoid lb_emit_conv + // trying to convert a constant array into a non-array. In that case we + // want the `else` branch to happen, so it can try to convert the + // non-array into an array instead. + if (lb_is_const_nil(left)) { + if (internal_check_is_assignable_to(right.type, left.type)) { + right = lb_emit_conv(p, right, left.type); + } return lb_emit_comp_against_nil(p, op_kind, right); } left = lb_emit_conv(p, left, right.type); - } else if (lb_is_const(right) || lb_is_const_nil(right)) { + } else if ((lb_is_const(right) && !is_type_array(right.type)) || lb_is_const_nil(right)) { if (lb_is_const_nil(right)) { + if (internal_check_is_assignable_to(left.type, right.type)) { + left = lb_emit_conv(p, left, right.type); + } return lb_emit_comp_against_nil(p, op_kind, left); } right = lb_emit_conv(p, right, left.type); - } else { Type *lt = left.type; Type *rt = right.type; @@ -4180,30 +4200,6 @@ gb_internal lbAddr lb_build_addr_index_expr(lbProcedure *p, Ast *expr) { return lb_addr(v); } - case Type_RelativeMultiPointer: { - lbAddr rel_ptr_addr = {}; - if (deref) { - lbValue rel_ptr_ptr = lb_build_expr(p, ie->expr); - rel_ptr_addr = lb_addr(rel_ptr_ptr); - } else { - rel_ptr_addr = lb_build_addr(p, ie->expr); - } - lbValue rel_ptr = lb_relative_pointer_to_pointer(p, rel_ptr_addr); - - lbValue index = lb_build_expr(p, ie->index); - index = lb_emit_conv(p, index, t_int); - lbValue v = {}; - - Type *pointer_type = base_type(t->RelativeMultiPointer.pointer_type); - GB_ASSERT(pointer_type->kind == Type_MultiPointer); - Type *elem = pointer_type->MultiPointer.elem; - - LLVMValueRef indices[1] = {index.value}; - v.value = LLVMBuildGEP2(p->builder, lb_type(p->module, elem), rel_ptr.value, indices, 1, ""); - v.type = alloc_type_pointer(elem); - return lb_addr(v); - } - case Type_DynamicArray: { lbValue dynamic_array = {}; dynamic_array = lb_build_expr(p, ie->expr); @@ -4313,13 +4309,6 @@ gb_internal lbAddr lb_build_addr_slice_expr(lbProcedure *p, Ast *expr) { return slice; } - case Type_RelativePointer: - GB_PANIC("TODO(bill): Type_RelativePointer should be handled above already on the lb_addr_load"); - break; - case Type_RelativeMultiPointer: - GB_PANIC("TODO(bill): Type_RelativeMultiPointer should be handled above already on the lb_addr_load"); - break; - case Type_DynamicArray: { Type *elem_type = type->DynamicArray.elem; Type *slice_type = alloc_type_slice(elem_type); @@ -5323,11 +5312,7 @@ gb_internal lbAddr lb_build_addr_internal(lbProcedure *p, Ast *expr) { case_ast_node(de, DerefExpr, expr); Type *t = type_of_expr(de->expr); - if (is_type_relative_pointer(t)) { - lbAddr addr = lb_build_addr(p, de->expr); - addr.relative.deref = true; - return addr; - } else if (is_type_soa_pointer(t)) { + if (is_type_soa_pointer(t)) { lbValue value = lb_build_expr(p, de->expr); lbValue ptr = lb_emit_struct_ev(p, value, 0); lbValue idx = lb_emit_struct_ev(p, value, 1); diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index 842a1cbc8..9dc603993 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -409,14 +409,6 @@ gb_internal lbModule *lb_module_of_entity(lbGenerator *gen, Entity *e) { gb_internal lbAddr lb_addr(lbValue addr) { lbAddr v = {lbAddr_Default, addr}; - if (addr.type != nullptr && is_type_relative_pointer(type_deref(addr.type))) { - GB_ASSERT(is_type_pointer(addr.type)); - v.kind = lbAddr_RelativePointer; - } else if (addr.type != nullptr && is_type_relative_multi_pointer(type_deref(addr.type))) { - GB_ASSERT(is_type_pointer(addr.type) || - is_type_multi_pointer(addr.type)); - v.kind = lbAddr_RelativePointer; - } return v; } @@ -501,42 +493,6 @@ gb_internal Type *lb_addr_type(lbAddr const &addr) { return type_deref(addr.addr.type); } - -gb_internal lbValue lb_relative_pointer_to_pointer(lbProcedure *p, lbAddr const &addr) { - GB_ASSERT(addr.kind == lbAddr_RelativePointer); - - Type *t = base_type(lb_addr_type(addr)); - GB_ASSERT(is_type_relative_pointer(t) || is_type_relative_multi_pointer(t)); - - Type *pointer_type = nullptr; - Type *base_integer = nullptr; - if (t->kind == Type_RelativePointer) { - pointer_type = t->RelativePointer.pointer_type; - base_integer = t->RelativePointer.base_integer; - } else if (t->kind == Type_RelativeMultiPointer) { - pointer_type = t->RelativeMultiPointer.pointer_type; - base_integer = t->RelativeMultiPointer.base_integer; - } - - lbValue ptr = lb_emit_conv(p, addr.addr, t_uintptr); - lbValue offset = lb_emit_conv(p, ptr, alloc_type_pointer(base_integer)); - offset = lb_emit_load(p, offset); - - if (!is_type_unsigned(base_integer)) { - offset = lb_emit_conv(p, offset, t_i64); - } - offset = lb_emit_conv(p, offset, t_uintptr); - lbValue absolute_ptr = lb_emit_arith(p, Token_Add, ptr, offset, t_uintptr); - absolute_ptr = lb_emit_conv(p, absolute_ptr, pointer_type); - - lbValue cond = lb_emit_comp(p, Token_CmpEq, offset, lb_const_nil(p->module, base_integer)); - - // NOTE(bill): nil check - lbValue nil_ptr = lb_const_nil(p->module, pointer_type); - lbValue final_ptr = lb_emit_select(p, cond, nil_ptr, absolute_ptr); - return final_ptr; -} - gb_internal lbValue lb_make_soa_pointer(lbProcedure *p, Type *type, lbValue const &addr, lbValue const &index) { lbAddr v = lb_add_local_generated(p, type, false); lbValue ptr = lb_emit_struct_ep(p, v.addr, 0); @@ -557,9 +513,6 @@ gb_internal lbValue lb_addr_get_ptr(lbProcedure *p, lbAddr const &addr) { case lbAddr_Map: return lb_internal_dynamic_map_get_ptr(p, addr.addr, addr.map.key); - case lbAddr_RelativePointer: - return lb_relative_pointer_to_pointer(p, addr); - case lbAddr_SoaVariable: { Type *soa_ptr_type = alloc_type_soa_pointer(lb_addr_type(addr)); @@ -584,9 +537,6 @@ gb_internal lbValue lb_addr_get_ptr(lbProcedure *p, lbAddr const &addr) { gb_internal lbValue lb_build_addr_ptr(lbProcedure *p, Ast *expr) { lbAddr addr = lb_build_addr(p, expr); - if (addr.kind == lbAddr_RelativePointer) { - return addr.addr; - } return lb_addr_get_ptr(p, addr); } @@ -722,7 +672,10 @@ gb_internal unsigned lb_try_get_alignment(LLVMValueRef addr_ptr, unsigned defaul gb_internal bool lb_try_update_alignment(LLVMValueRef addr_ptr, unsigned alignment) { if (LLVMIsAGlobalValue(addr_ptr) || LLVMIsAAllocaInst(addr_ptr) || LLVMIsALoadInst(addr_ptr)) { if (LLVMGetAlignment(addr_ptr) < alignment) { - if (LLVMIsAAllocaInst(addr_ptr) || LLVMIsAGlobalValue(addr_ptr)) { + if (LLVMIsAAllocaInst(addr_ptr)) { + LLVMSetAlignment(addr_ptr, alignment); + } else if (LLVMIsAGlobalValue(addr_ptr) && LLVMGetLinkage(addr_ptr) != LLVMExternalLinkage) { + // NOTE(laytan): setting alignment of an external global just changes the alignment we expect it to be. LLVMSetAlignment(addr_ptr, alignment); } } @@ -755,10 +708,7 @@ gb_internal bool lb_try_vector_cast(lbModule *m, lbValue ptr, LLVMTypeRef *vecto LLVMValueRef addr_ptr = ptr.value; if (LLVMIsAAllocaInst(addr_ptr) || LLVMIsAGlobalValue(addr_ptr)) { - unsigned alignment = LLVMGetAlignment(addr_ptr); - alignment = gb_max(alignment, vector_alignment); - possible = true; - LLVMSetAlignment(addr_ptr, alignment); + possible = lb_try_update_alignment(addr_ptr, vector_alignment); } else if (LLVMIsALoadInst(addr_ptr)) { unsigned alignment = LLVMGetAlignment(addr_ptr); possible = alignment >= vector_alignment; @@ -774,6 +724,36 @@ gb_internal bool lb_try_vector_cast(lbModule *m, lbValue ptr, LLVMTypeRef *vecto return false; } +gb_internal LLVMValueRef OdinLLVMBuildLoad(lbProcedure *p, LLVMTypeRef type, LLVMValueRef value) { + LLVMValueRef result = LLVMBuildLoad2(p->builder, type, value, ""); + + // If it is not an instruction it isn't a GEP, so we don't need to track alignment in the metadata, + // which is not possible anyway (only LLVM instructions can have metadata). + if (LLVMIsAInstruction(value)) { + u64 is_packed = lb_get_metadata_custom_u64(p->module, value, ODIN_METADATA_IS_PACKED); + if (is_packed != 0) { + LLVMSetAlignment(result, 1); + } + } + + return result; +} + +gb_internal LLVMValueRef OdinLLVMBuildLoadAligned(lbProcedure *p, LLVMTypeRef type, LLVMValueRef value, i64 alignment) { + LLVMValueRef result = LLVMBuildLoad2(p->builder, type, value, ""); + + LLVMSetAlignment(result, cast(unsigned)alignment); + + if (LLVMIsAInstruction(value)) { + u64 is_packed = lb_get_metadata_custom_u64(p->module, value, ODIN_METADATA_IS_PACKED); + if (is_packed != 0) { + LLVMSetAlignment(result, 1); + } + } + + return result; +} + gb_internal void lb_addr_store(lbProcedure *p, lbAddr addr, lbValue value) { if (addr.addr.value == nullptr) { return; @@ -789,10 +769,6 @@ gb_internal void lb_addr_store(lbProcedure *p, lbAddr addr, lbValue value) { value.value = LLVMConstNull(lb_type(p->module, t)); } - if (addr.kind == lbAddr_RelativePointer && addr.relative.deref) { - addr = lb_addr(lb_address_from_load(p, lb_addr_load(p, addr))); - } - if (addr.kind == lbAddr_BitField) { lbValue dst = addr.addr; if (is_type_endian_big(addr.bitfield.type)) { @@ -830,44 +806,6 @@ gb_internal void lb_addr_store(lbProcedure *p, lbAddr addr, lbValue value) { lb_emit_runtime_call(p, "__write_bits", args); } return; - } else if (addr.kind == lbAddr_RelativePointer) { - Type *rel_ptr = base_type(lb_addr_type(addr)); - GB_ASSERT(rel_ptr->kind == Type_RelativePointer || - rel_ptr->kind == Type_RelativeMultiPointer); - Type *pointer_type = nullptr; - Type *base_integer = nullptr; - - if (rel_ptr->kind == Type_RelativePointer) { - pointer_type = rel_ptr->RelativePointer.pointer_type; - base_integer = rel_ptr->RelativePointer.base_integer; - } else if (rel_ptr->kind == Type_RelativeMultiPointer) { - pointer_type = rel_ptr->RelativeMultiPointer.pointer_type; - base_integer = rel_ptr->RelativeMultiPointer.base_integer; - } - - value = lb_emit_conv(p, value, pointer_type); - - GB_ASSERT(is_type_pointer(addr.addr.type)); - lbValue ptr = lb_emit_conv(p, addr.addr, t_uintptr); - lbValue val_ptr = lb_emit_conv(p, value, t_uintptr); - lbValue offset = {}; - offset.value = LLVMBuildSub(p->builder, val_ptr.value, ptr.value, ""); - offset.type = t_uintptr; - - if (!is_type_unsigned(base_integer)) { - offset = lb_emit_conv(p, offset, t_i64); - } - offset = lb_emit_conv(p, offset, base_integer); - - lbValue offset_ptr = lb_emit_conv(p, addr.addr, alloc_type_pointer(base_integer)); - offset = lb_emit_select(p, - lb_emit_comp(p, Token_CmpEq, val_ptr, lb_const_nil(p->module, t_uintptr)), - lb_const_nil(p->module, base_integer), - offset - ); - LLVMBuildStore(p->builder, offset.value, offset_ptr.value); - return; - } else if (addr.kind == lbAddr_Map) { lb_internal_dynamic_map_set(p, addr.addr, addr.map.type, addr.map.key, value, p->curr_stmt); return; @@ -1119,7 +1057,7 @@ gb_internal lbValue lb_emit_load(lbProcedure *p, lbValue value) { Type *vt = base_type(value.type); GB_ASSERT(vt->kind == Type_MultiPointer); Type *t = vt->MultiPointer.elem; - LLVMValueRef v = LLVMBuildLoad2(p->builder, lb_type(p->module, t), value.value, ""); + LLVMValueRef v = OdinLLVMBuildLoad(p, lb_type(p->module, t), value.value); return lbValue{v, t}; } else if (is_type_soa_pointer(value.type)) { lbValue ptr = lb_emit_struct_ev(p, value, 0); @@ -1130,16 +1068,7 @@ gb_internal lbValue lb_emit_load(lbProcedure *p, lbValue value) { GB_ASSERT_MSG(is_type_pointer(value.type), "%s", type_to_string(value.type)); Type *t = type_deref(value.type); - LLVMValueRef v = LLVMBuildLoad2(p->builder, lb_type(p->module, t), value.value, ""); - - // If it is not an instruction it isn't a GEP, so we don't need to track alignment in the metadata, - // which is not possible anyway (only LLVM instructions can have metadata). - if (LLVMIsAInstruction(value.value)) { - u64 is_packed = lb_get_metadata_custom_u64(p->module, value.value, ODIN_METADATA_IS_PACKED); - if (is_packed != 0) { - LLVMSetAlignment(v, 1); - } - } + LLVMValueRef v = OdinLLVMBuildLoad(p, lb_type(p->module, t), value.value); return lbValue{v, t}; } @@ -1225,46 +1154,6 @@ gb_internal lbValue lb_addr_load(lbProcedure *p, lbAddr const &addr) { } return r; - } else if (addr.kind == lbAddr_RelativePointer) { - Type *rel_ptr = base_type(lb_addr_type(addr)); - Type *base_integer = nullptr; - Type *pointer_type = nullptr; - GB_ASSERT(rel_ptr->kind == Type_RelativePointer || - rel_ptr->kind == Type_RelativeMultiPointer); - - if (rel_ptr->kind == Type_RelativePointer) { - base_integer = rel_ptr->RelativePointer.base_integer; - pointer_type = rel_ptr->RelativePointer.pointer_type; - } else if (rel_ptr->kind == Type_RelativeMultiPointer) { - base_integer = rel_ptr->RelativeMultiPointer.base_integer; - pointer_type = rel_ptr->RelativeMultiPointer.pointer_type; - } - - lbValue ptr = lb_emit_conv(p, addr.addr, t_uintptr); - lbValue offset = lb_emit_conv(p, ptr, alloc_type_pointer(base_integer)); - offset = lb_emit_load(p, offset); - - - if (!is_type_unsigned(base_integer)) { - offset = lb_emit_conv(p, offset, t_i64); - } - offset = lb_emit_conv(p, offset, t_uintptr); - lbValue absolute_ptr = lb_emit_arith(p, Token_Add, ptr, offset, t_uintptr); - absolute_ptr = lb_emit_conv(p, absolute_ptr, pointer_type); - - lbValue cond = lb_emit_comp(p, Token_CmpEq, offset, lb_const_nil(p->module, base_integer)); - - // NOTE(bill): nil check - lbValue nil_ptr = lb_const_nil(p->module, pointer_type); - lbValue final_ptr = {}; - final_ptr.type = absolute_ptr.type; - final_ptr.value = LLVMBuildSelect(p->builder, cond.value, nil_ptr.value, absolute_ptr.value, ""); - - if (rel_ptr->kind == Type_RelativeMultiPointer) { - return final_ptr; - } - return lb_emit_load(p, final_ptr); - } else if (addr.kind == lbAddr_Map) { Type *map_type = base_type(type_deref(addr.addr.type)); GB_ASSERT(map_type->kind == Type_Map); @@ -1413,7 +1302,7 @@ gb_internal lbValue lb_addr_load(lbProcedure *p, lbAddr const &addr) { LLVMTypeRef vector_type = nullptr; if (lb_try_vector_cast(p->module, addr.addr, &vector_type)) { LLVMValueRef vp = LLVMBuildPointerCast(p->builder, addr.addr.value, LLVMPointerType(vector_type, 0), ""); - LLVMValueRef v = LLVMBuildLoad2(p->builder, vector_type, vp, ""); + LLVMValueRef v = OdinLLVMBuildLoad(p, vector_type, vp); LLVMValueRef scalars[4] = {}; for (u8 i = 0; i < addr.swizzle.count; i++) { scalars[i] = LLVMConstInt(lb_type(p->module, t_u32), addr.swizzle.indices[i], false); @@ -2357,13 +2246,6 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) { case Type_SimdVector: return LLVMVectorType(lb_type(m, type->SimdVector.elem), cast(unsigned)type->SimdVector.count); - - case Type_RelativePointer: - return lb_type_internal(m, type->RelativePointer.base_integer); - case Type_RelativeMultiPointer: - return lb_type_internal(m, type->RelativeMultiPointer.base_integer); - - case Type_Matrix: { @@ -2710,7 +2592,7 @@ general_end:; if (LLVMIsALoadInst(val) && (src_size >= dst_size && src_align >= dst_align)) { LLVMValueRef val_ptr = LLVMGetOperand(val, 0); val_ptr = LLVMBuildPointerCast(p->builder, val_ptr, LLVMPointerType(dst_type, 0), ""); - LLVMValueRef loaded_val = LLVMBuildLoad2(p->builder, dst_type, val_ptr, ""); + LLVMValueRef loaded_val = OdinLLVMBuildLoad(p, dst_type, val_ptr); // LLVMSetAlignment(loaded_val, gb_min(src_align, dst_align)); @@ -2726,7 +2608,7 @@ general_end:; LLVMValueRef nptr = LLVMBuildPointerCast(p->builder, ptr, LLVMPointerType(src_type, 0), ""); LLVMBuildStore(p->builder, val, nptr); - return LLVMBuildLoad2(p->builder, dst_type, ptr, ""); + return OdinLLVMBuildLoad(p, dst_type, ptr); } } diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index d84599eb0..5aee5b639 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -2568,7 +2568,7 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu case BuiltinProc_atomic_load_explicit: { lbValue dst = lb_build_expr(p, ce->args[0]); - LLVMValueRef instr = LLVMBuildLoad2(p->builder, lb_type(p->module, type_deref(dst.type)), dst.value, ""); + LLVMValueRef instr = OdinLLVMBuildLoad(p, lb_type(p->module, type_deref(dst.type)), dst.value); switch (id) { case BuiltinProc_non_temporal_load: { @@ -2621,8 +2621,7 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu if (is_type_simd_vector(t)) { lbValue res = {}; res.type = t; - res.value = LLVMBuildLoad2(p->builder, lb_type(p->module, t), src.value, ""); - LLVMSetAlignment(res.value, 1); + res.value = OdinLLVMBuildLoadAligned(p, lb_type(p->module, t), src.value, 1); return res; } else { lbAddr dst = lb_add_local_generated(p, t, false); diff --git a/src/llvm_backend_stmt.cpp b/src/llvm_backend_stmt.cpp index df3d4bc03..06d66ac80 100644 --- a/src/llvm_backend_stmt.cpp +++ b/src/llvm_backend_stmt.cpp @@ -2001,7 +2001,7 @@ gb_internal void lb_build_return_stmt_internal(lbProcedure *p, lbValue res) { LLVMValueRef ptr = p->temp_callee_return_struct_memory; LLVMValueRef nptr = LLVMBuildPointerCast(p->builder, ptr, LLVMPointerType(src_type, 0), ""); LLVMBuildStore(p->builder, ret_val, nptr); - ret_val = LLVMBuildLoad2(p->builder, ret_type, ptr, ""); + ret_val = OdinLLVMBuildLoad(p, ret_type, ptr); } else { ret_val = OdinLLVMBuildTransmute(p, ret_val, ret_type); } @@ -2018,14 +2018,7 @@ gb_internal void lb_build_return_stmt_internal(lbProcedure *p, lbValue res) { gb_internal void lb_build_return_stmt(lbProcedure *p, Slice<Ast *> const &return_results) { lb_ensure_abi_function_type(p->module, p); - lbValue res = {}; - - TypeTuple *tuple = &p->type->Proc.results->Tuple; isize return_count = p->type->Proc.result_count; - isize res_count = return_results.count; - - lbFunctionType *ft = lb_get_function_type(p->module, p->type); - bool return_by_pointer = ft->ret.kind == lbArg_Indirect; if (return_count == 0) { // No return values @@ -2038,7 +2031,17 @@ gb_internal void lb_build_return_stmt(lbProcedure *p, Slice<Ast *> const &return LLVMBuildRetVoid(p->builder); } return; - } else if (return_count == 1) { + } + + lbValue res = {}; + + TypeTuple *tuple = &p->type->Proc.results->Tuple; + isize res_count = return_results.count; + + lbFunctionType *ft = lb_get_function_type(p->module, p->type); + bool return_by_pointer = ft->ret.kind == lbArg_Indirect; + + if (return_count == 1) { Entity *e = tuple->variables[0]; if (res_count == 0) { rw_mutex_shared_lock(&p->module->values_mutex); diff --git a/src/llvm_backend_type.cpp b/src/llvm_backend_type.cpp index 9d4505bb0..6c12b37be 100644 --- a/src/llvm_backend_type.cpp +++ b/src/llvm_backend_type.cpp @@ -43,6 +43,8 @@ gb_internal u64 lb_typeid_kind(lbModule *m, Type *type, u64 id=0) { if (flags & BasicFlag_Pointer) kind = Typeid_Pointer; if (flags & BasicFlag_String) kind = Typeid_String; if (flags & BasicFlag_Rune) kind = Typeid_Rune; + + if (bt->Basic.kind == Basic_typeid) kind = Typeid_Type_Id; } break; case Type_Pointer: kind = Typeid_Pointer; break; case Type_MultiPointer: kind = Typeid_Multi_Pointer; break; @@ -59,8 +61,6 @@ gb_internal u64 lb_typeid_kind(lbModule *m, Type *type, u64 id=0) { case Type_Proc: kind = Typeid_Procedure; break; case Type_BitSet: kind = Typeid_Bit_Set; break; case Type_SimdVector: kind = Typeid_Simd_Vector; break; - case Type_RelativePointer: kind = Typeid_Relative_Pointer; break; - case Type_RelativeMultiPointer: kind = Typeid_Relative_Multi_Pointer; break; case Type_SoaPointer: kind = Typeid_SoaPointer; break; case Type_BitField: kind = Typeid_Bit_Field; break; } @@ -948,30 +948,6 @@ gb_internal void lb_setup_type_info_data_giant_array(lbModule *m, i64 global_typ } break; - case Type_RelativePointer: - { - tag_type = t_type_info_relative_pointer; - LLVMValueRef vals[2] = { - get_type_info_ptr(m, t->RelativePointer.pointer_type), - get_type_info_ptr(m, t->RelativePointer.base_integer), - }; - - variant_value = llvm_const_named_struct(m, tag_type, vals, gb_count_of(vals)); - } - break; - - case Type_RelativeMultiPointer: - { - tag_type = t_type_info_relative_multi_pointer; - LLVMValueRef vals[2] = { - get_type_info_ptr(m, t->RelativeMultiPointer.pointer_type), - get_type_info_ptr(m, t->RelativeMultiPointer.base_integer), - }; - - variant_value = llvm_const_named_struct(m, tag_type, vals, gb_count_of(vals)); - } - break; - case Type_Matrix: { tag_type = t_type_info_matrix; diff --git a/src/llvm_backend_utility.cpp b/src/llvm_backend_utility.cpp index f63c42ab9..a2a0ba4cc 100644 --- a/src/llvm_backend_utility.cpp +++ b/src/llvm_backend_utility.cpp @@ -124,8 +124,16 @@ gb_internal void lb_mem_zero_ptr(lbProcedure *p, LLVMValueRef ptr, Type *type, u switch (kind) { case LLVMStructTypeKind: case LLVMArrayTypeKind: - // NOTE(bill): Enforce zeroing through memset to make sure padding is zeroed too - lb_mem_zero_ptr_internal(p, ptr, lb_const_int(p->module, t_int, sz).value, alignment, false); + if (is_type_tuple(type)) { + // NOTE(bill): even though this should be safe, to keep ASAN happy, do not zero the implicit padding at the end + GB_ASSERT(type->kind == Type_Tuple); + i64 n = type->Tuple.variables.count-1; + i64 end_offset = type->Tuple.offsets[n] + type_size_of(type->Tuple.variables[n]->type); + lb_mem_zero_ptr_internal(p, ptr, lb_const_int(p->module, t_int, end_offset).value, alignment, false); + } else { + // NOTE(bill): Enforce zeroing through memset to make sure padding is zeroed too + lb_mem_zero_ptr_internal(p, ptr, lb_const_int(p->module, t_int, sz).value, alignment, false); + } break; default: LLVMBuildStore(p->builder, LLVMConstNull(lb_type(p->module, type)), ptr); @@ -269,7 +277,7 @@ gb_internal lbValue lb_emit_transmute(lbProcedure *p, lbValue value, Type *t) { if (lb_try_update_alignment(ptr, align)) { LLVMTypeRef result_type = lb_type(p->module, t); res.value = LLVMBuildPointerCast(p->builder, ptr.value, LLVMPointerType(result_type, 0), ""); - res.value = LLVMBuildLoad2(p->builder, result_type, res.value, ""); + res.value = OdinLLVMBuildLoad(p, result_type, res.value); return res; } lbAddr addr = lb_add_local_generated(p, t, false); @@ -1123,10 +1131,6 @@ gb_internal lbValue lb_emit_struct_ep(lbProcedure *p, lbValue s, i32 index) { Type *t = base_type(type_deref(s.type)); Type *result_type = nullptr; - if (is_type_relative_pointer(t)) { - s = lb_addr_get_ptr(p, lb_addr(s)); - } - if (is_type_struct(t)) { result_type = get_struct_field_type(t, index); } else if (is_type_union(t)) { @@ -1432,8 +1436,6 @@ gb_internal lbValue lb_emit_deep_field_gep(lbProcedure *p, lbValue e, Selection e = lb_emit_array_epi(p, e, index); } else if (type->kind == Type_Map) { e = lb_emit_struct_ep(p, e, index); - } else if (type->kind == Type_RelativePointer) { - e = lb_emit_struct_ep(p, e, index); } else { GB_PANIC("un-gep-able type %s", type_to_string(type)); } diff --git a/src/main.cpp b/src/main.cpp index a969e32a9..8a5339000 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -328,6 +328,8 @@ enum BuildFlagKind { BuildFlag_NoRPath, BuildFlag_NoEntryPoint, BuildFlag_UseLLD, + BuildFlag_UseRADLink, + BuildFlag_Linker, BuildFlag_UseSeparateModules, BuildFlag_NoThreadedChecker, BuildFlag_ShowDebugMessages, @@ -539,6 +541,8 @@ gb_internal bool parse_build_flags(Array<String> args) { add_flag(&build_flags, BuildFlag_NoRPath, str_lit("no-rpath"), BuildFlagParam_None, Command__does_build); add_flag(&build_flags, BuildFlag_NoEntryPoint, str_lit("no-entry-point"), BuildFlagParam_None, Command__does_check &~ Command_test); add_flag(&build_flags, BuildFlag_UseLLD, str_lit("lld"), BuildFlagParam_None, Command__does_build); + add_flag(&build_flags, BuildFlag_UseRADLink, str_lit("radlink"), BuildFlagParam_None, Command__does_build); + add_flag(&build_flags, BuildFlag_Linker, str_lit("linker"), BuildFlagParam_String, Command__does_build); add_flag(&build_flags, BuildFlag_UseSeparateModules, str_lit("use-separate-modules"), BuildFlagParam_None, Command__does_build); add_flag(&build_flags, BuildFlag_NoThreadedChecker, str_lit("no-threaded-checker"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_ShowDebugMessages, str_lit("show-debug-messages"), BuildFlagParam_None, Command_all); @@ -1060,27 +1064,29 @@ gb_internal bool parse_build_flags(Array<String> args) { } if (!found) { - struct DistanceAndTargetIndex { - isize distance; - isize target_index; - }; + if (str != "?") { + struct DistanceAndTargetIndex { + isize distance; + isize target_index; + }; - DistanceAndTargetIndex distances[gb_count_of(named_targets)] = {}; - for (isize i = 0; i < gb_count_of(named_targets); i++) { - distances[i].target_index = i; - distances[i].distance = levenstein_distance_case_insensitive(str, named_targets[i].name); - } - gb_sort_array(distances, gb_count_of(distances), gb_isize_cmp(gb_offset_of(DistanceAndTargetIndex, distance))); + DistanceAndTargetIndex distances[gb_count_of(named_targets)] = {}; + for (isize i = 0; i < gb_count_of(named_targets); i++) { + distances[i].target_index = i; + distances[i].distance = levenstein_distance_case_insensitive(str, named_targets[i].name); + } + gb_sort_array(distances, gb_count_of(distances), gb_isize_cmp(gb_offset_of(DistanceAndTargetIndex, distance))); - gb_printf_err("Unknown target '%.*s'\n", LIT(str)); + gb_printf_err("Unknown target '%.*s'\n", LIT(str)); - if (distances[0].distance <= MAX_SMALLEST_DID_YOU_MEAN_DISTANCE) { - gb_printf_err("Did you mean:\n"); - for (isize i = 0; i < gb_count_of(named_targets); i++) { - if (distances[i].distance > MAX_SMALLEST_DID_YOU_MEAN_DISTANCE) { - break; + if (distances[0].distance <= MAX_SMALLEST_DID_YOU_MEAN_DISTANCE) { + gb_printf_err("Did you mean:\n"); + for (isize i = 0; i < gb_count_of(named_targets); i++) { + if (distances[i].distance > MAX_SMALLEST_DID_YOU_MEAN_DISTANCE) { + break; + } + gb_printf_err("\t%.*s\n", LIT(named_targets[distances[i].target_index].name)); } - gb_printf_err("\t%.*s\n", LIT(named_targets[distances[i].target_index].name)); } } gb_printf_err("All supported targets:\n"); @@ -1199,8 +1205,38 @@ gb_internal bool parse_build_flags(Array<String> args) { build_context.no_thread_local = true; break; case BuildFlag_UseLLD: - build_context.use_lld = true; + gb_printf_err("Warning: Use of -lld has been deprecated in favour of -linker:lld\n"); + build_context.linker_choice = Linker_lld; + break; + case BuildFlag_UseRADLink: + gb_printf_err("Warning: Use of -lld has been deprecated in favour of -linker:radlink\n"); + build_context.linker_choice = Linker_radlink; + break; + case BuildFlag_Linker: + { + GB_ASSERT(value.kind == ExactValue_String); + LinkerChoice linker_choice = Linker_Invalid; + + for (i32 i = 0; i < Linker_COUNT; i++) { + if (linker_choices[i] == value.value_string) { + linker_choice = cast(LinkerChoice)i; + break; + } + } + + if (linker_choice == Linker_Invalid) { + gb_printf_err("Invalid option for -linker:<string>. Expected one of the following\n"); + for (i32 i = 0; i < Linker_COUNT; i++) { + gb_printf_err("\t%.*s\n", LIT(linker_choices[i])); + } + bad_flags = true; + } else { + build_context.linker_choice = linker_choice; + } + } break; + + case BuildFlag_UseSeparateModules: build_context.use_separate_modules = true; break; @@ -1408,7 +1444,7 @@ gb_internal bool parse_build_flags(Array<String> args) { build_context.terse_errors = true; break; case BuildFlag_VerboseErrors: - gb_printf_err("-verbose-errors is not the default, -terse-errors can now disable it\n"); + gb_printf_err("-verbose-errors is now the default, -terse-errors can disable it\n"); build_context.hide_error_line = false; build_context.terse_errors = false; break; @@ -1747,7 +1783,7 @@ gb_internal void check_defines(BuildContext *bc, Checker *c) { String name = make_string_c(entry.key); ExactValue value = entry.value; GB_ASSERT(value.kind != ExactValue_Invalid); - + bool found = false; for_array(i, c->info.defineables) { Defineable *def = &c->info.defineables[i]; @@ -1776,9 +1812,9 @@ gb_internal void temp_alloc_defineable_strings(Checker *c) { gb_internal GB_COMPARE_PROC(defineables_cmp) { Defineable *x = (Defineable *)a; Defineable *y = (Defineable *)b; - + int cmp = 0; - + String x_file = get_file_path_string(x->pos.file_id); String y_file = get_file_path_string(y->pos.file_id); cmp = string_compare(x_file, y_file); @@ -1789,8 +1825,22 @@ gb_internal GB_COMPARE_PROC(defineables_cmp) { return i32_cmp(x->pos.offset, y->pos.offset); } -gb_internal void sort_defineables(Checker *c) { +gb_internal void sort_defineables_and_remove_duplicates(Checker *c) { + if (c->info.defineables.count == 0) { + return; + } gb_sort_array(c->info.defineables.data, c->info.defineables.count, defineables_cmp); + + Defineable prev = c->info.defineables[0]; + for (isize i = 1; i < c->info.defineables.count; ) { + Defineable curr = c->info.defineables[i]; + if (prev.pos == curr.pos) { + array_ordered_remove(&c->info.defineables, i); + continue; + } + prev = curr; + i++; + } } gb_internal void export_defineables(Checker *c, String path) { @@ -2125,11 +2175,12 @@ gb_internal void remove_temp_files(lbGenerator *gen) { } -gb_internal void print_show_help(String const arg0, String const &command) { +gb_internal void print_show_help(String const arg0, String const &command, String optional_flag = {}) { print_usage_line(0, "%.*s is a tool for managing Odin source code.", LIT(arg0)); print_usage_line(0, "Usage:"); print_usage_line(1, "%.*s %.*s [arguments]", LIT(arg0), LIT(command)); print_usage_line(0, ""); + defer (print_usage_line(0, "")); if (command == "build") { print_usage_line(1, "build Compiles directory of .odin files as an executable."); @@ -2175,475 +2226,560 @@ gb_internal void print_show_help(String const arg0, String const &command) { bool check_only = command == "check" || strip_semicolon; bool check = run_or_build || check_only; + if (command == "help") { + doc = true; + build = true; + run_or_build = true; + test_only = true; + strip_semicolon = true; + check_only = true; + check = true; + } + print_usage_line(0, ""); print_usage_line(1, "Flags"); print_usage_line(0, ""); - if (check) { - print_usage_line(1, "-file"); - print_usage_line(2, "Tells `%.*s %.*s` to treat the given file as a self-contained package.", LIT(arg0), LIT(command)); - print_usage_line(2, "This means that `<dir>/a.odin` won't have access to `<dir>/b.odin`'s contents."); - print_usage_line(0, ""); - } - if (doc) { - print_usage_line(1, "-short"); - print_usage_line(2, "Shows shortened documentation for the packages."); - print_usage_line(0, ""); - print_usage_line(1, "-all-packages"); - print_usage_line(2, "Generates documentation for all packages used in the current project."); + auto const print_flag = [&optional_flag](char const *flag) -> bool { + if (optional_flag.len != 0) { + String f = make_string_c(flag); + isize i = string_index_byte(f, ':'); + if (i >= 0) { + f.len = i; + } + if (optional_flag != f) { + return false; + } + } print_usage_line(0, ""); + print_usage_line(1, flag); + return true; + }; - print_usage_line(1, "-doc-format"); - print_usage_line(2, "Generates documentation as the .odin-doc format (useful for external tooling)."); - print_usage_line(0, ""); - print_usage_line(1, "-out:<filepath>"); - print_usage_line(2, "Sets the base name of the resultig .odin-doc file."); - print_usage_line(2, "The extension can be optionally included; the resulting file will always have an extension of '.odin-doc'."); - print_usage_line(2, "Example: -out:foo"); - print_usage_line(0, ""); + if (doc) { + if (print_flag("-all-packages")) { + print_usage_line(2, "Generates documentation for all packages used in the current project."); + } + } + if (test_only) { + if (print_flag("-all-packages")) { + print_usage_line(2, "Tests all packages imported into the given initial package."); + } } - if (run_or_build) { - print_usage_line(1, "-out:<filepath>"); - print_usage_line(2, "Sets the file name of the outputted executable."); - print_usage_line(2, "Example: -out:foo.exe"); - print_usage_line(0, ""); - - print_usage_line(1, "-o:<string>"); - print_usage_line(2, "Sets the optimization mode for compilation."); - print_usage_line(2, "Available options:"); - print_usage_line(3, "-o:none"); - print_usage_line(3, "-o:minimal"); - print_usage_line(3, "-o:size"); - print_usage_line(3, "-o:speed"); - if (LB_USE_NEW_PASS_SYSTEM) { - print_usage_line(3, "-o:aggressive"); - } - print_usage_line(2, "The default is -o:minimal."); - print_usage_line(0, ""); + if (build) { + if (print_flag("-build-mode:<mode>")) { + print_usage_line(2, "Sets the build mode."); + print_usage_line(2, "Available options:"); + print_usage_line(3, "-build-mode:exe Builds as an executable."); + print_usage_line(3, "-build-mode:test Builds as an executable that executes tests."); + print_usage_line(3, "-build-mode:dll Builds as a dynamically linked library."); + print_usage_line(3, "-build-mode:shared Builds as a dynamically linked library."); + print_usage_line(3, "-build-mode:dynamic Builds as a dynamically linked library."); + print_usage_line(3, "-build-mode:lib Builds as a statically linked library."); + print_usage_line(3, "-build-mode:static Builds as a statically linked library."); + print_usage_line(3, "-build-mode:obj Builds as an object file."); + print_usage_line(3, "-build-mode:object Builds as an object file."); + print_usage_line(3, "-build-mode:assembly Builds as an assembly file."); + print_usage_line(3, "-build-mode:assembler Builds as an assembly file."); + print_usage_line(3, "-build-mode:asm Builds as an assembly file."); + print_usage_line(3, "-build-mode:llvm-ir Builds as an LLVM IR file."); + print_usage_line(3, "-build-mode:llvm Builds as an LLVM IR file."); + } } if (check) { - print_usage_line(1, "-show-timings"); - print_usage_line(2, "Shows basic overview of the timings of different stages within the compiler in milliseconds."); - print_usage_line(0, ""); + if (print_flag("-collection:<name>=<filepath>")) { + print_usage_line(2, "Defines a library collection used for imports."); + print_usage_line(2, "Example: -collection:shared=dir/to/shared"); + print_usage_line(2, "Usage in Code:"); + print_usage_line(3, "import \"shared:foo\""); + } - print_usage_line(1, "-show-more-timings"); - print_usage_line(2, "Shows an advanced overview of the timings of different stages within the compiler in milliseconds."); - print_usage_line(0, ""); + if (print_flag("-custom-attribute:<string>")) { + print_usage_line(2, "Add a custom attribute which will be ignored if it is unknown."); + print_usage_line(2, "This can be used with metaprogramming tools."); + print_usage_line(2, "Examples:"); + print_usage_line(3, "-custom-attribute:my_tag"); + print_usage_line(3, "-custom-attribute:my_tag,the_other_thing"); + print_usage_line(3, "-custom-attribute:my_tag -custom-attribute:the_other_thing"); + } + } - print_usage_line(1, "-show-system-calls"); - print_usage_line(2, "Prints the whole command and arguments for calls to external tools like linker and assembler."); - print_usage_line(0, ""); + if (run_or_build) { + if (print_flag("-debug")) { + print_usage_line(2, "Enables debug information, and defines the global constant ODIN_DEBUG to be 'true'."); + } + } - print_usage_line(1, "-export-timings:<format>"); - print_usage_line(2, "Exports timings to one of a few formats. Requires `-show-timings` or `-show-more-timings`."); - print_usage_line(2, "Available options:"); - print_usage_line(3, "-export-timings:json Exports compile time stats to JSON."); - print_usage_line(3, "-export-timings:csv Exports compile time stats to CSV."); - print_usage_line(0, ""); + if (check) { + if (print_flag("-default-to-nil-allocator")) { + print_usage_line(2, "Sets the default allocator to be the nil_allocator, an allocator which does nothing."); + } - print_usage_line(1, "-export-timings-file:<filename>"); - print_usage_line(2, "Specifies the filename for `-export-timings`."); - print_usage_line(2, "Example: -export-timings-file:timings.json"); - print_usage_line(0, ""); + if (print_flag("-define:<name>=<value>")) { + print_usage_line(2, "Defines a scalar boolean, integer or string as global constant."); + print_usage_line(2, "Example: -define:SPAM=123"); + print_usage_line(2, "Usage in code:"); + print_usage_line(3, "#config(SPAM, default_value)"); + } + } - print_usage_line(1, "-export-dependencies:<format>"); - print_usage_line(2, "Exports dependencies to one of a few formats. Requires `-export-dependencies-file`."); - print_usage_line(2, "Available options:"); - print_usage_line(3, "-export-dependencies:make Exports in Makefile format"); - print_usage_line(3, "-export-dependencies:json Exports in JSON format"); - print_usage_line(0, ""); + if (run_or_build) { + if (print_flag("-disable-assert")) { + print_usage_line(2, "Disables the code generation of the built-in run-time 'assert' procedure, and defines the global constant ODIN_DISABLE_ASSERT to be 'true'."); + } - print_usage_line(1, "-export-dependencies-file:<filename>"); - print_usage_line(2, "Specifies the filename for `-export-dependencies`."); - print_usage_line(2, "Example: -export-dependencies-file:dependencies.d"); - print_usage_line(0, ""); + if (print_flag("-disable-red-zone")) { + print_usage_line(2, "Disables red zone on a supported freestanding target."); + } + } - print_usage_line(1, "-thread-count:<integer>"); - print_usage_line(2, "Overrides the number of threads the compiler will use to compile with."); - print_usage_line(2, "Example: -thread-count:2"); - print_usage_line(0, ""); + if (check) { + if (print_flag("-disallow-do")) { + print_usage_line(2, "Disallows the 'do' keyword in the project."); + } } - if (check_only) { - print_usage_line(1, "-show-unused"); - print_usage_line(2, "Shows unused package declarations within the current project."); - print_usage_line(0, ""); - print_usage_line(1, "-show-unused-with-location"); - print_usage_line(2, "Shows unused package declarations within the current project with the declarations source location."); - print_usage_line(0, ""); + if (doc) { + if (print_flag("-doc-format")) { + print_usage_line(2, "Generates documentation as the .odin-doc format (useful for external tooling)."); + } } if (run_or_build) { - print_usage_line(1, "-keep-temp-files"); - print_usage_line(2, "Keeps the temporary files generated during compilation."); - print_usage_line(0, ""); - } else if (strip_semicolon) { - print_usage_line(1, "-keep-temp-files"); - print_usage_line(2, "Keeps the temporary files generated during stripping the unneeded semicolons from files."); - print_usage_line(0, ""); + if (print_flag("-dynamic-map-calls")) { + print_usage_line(2, "Uses dynamic map calls to minimize code generation at the cost of runtime execution."); + } } if (check) { - print_usage_line(1, "-collection:<name>=<filepath>"); - print_usage_line(2, "Defines a library collection used for imports."); - print_usage_line(2, "Example: -collection:shared=dir/to/shared"); - print_usage_line(2, "Usage in Code:"); - print_usage_line(3, "import \"shared:foo\""); - print_usage_line(0, ""); + if (print_flag("-error-pos-style:<string>")) { + print_usage_line(2, "Available options:"); + print_usage_line(3, "-error-pos-style:unix file/path:45:3:"); + print_usage_line(3, "-error-pos-style:odin file/path(45:3)"); + print_usage_line(3, "-error-pos-style:default (Defaults to 'odin'.)"); + } - print_usage_line(1, "-define:<name>=<value>"); - print_usage_line(2, "Defines a scalar boolean, integer or string as global constant."); - print_usage_line(2, "Example: -define:SPAM=123"); - print_usage_line(2, "Usage in code:"); - print_usage_line(3, "#config(SPAM, default_value)"); - print_usage_line(0, ""); + if (print_flag("-export-defineables:<filename>")) { + print_usage_line(2, "Exports an overview of all the #config/#defined usages in CSV format to the given file path."); + print_usage_line(2, "Example: -export-defineables:defineables.csv"); + } - print_usage_line(1, "-show-defineables"); - print_usage_line(2, "Shows an overview of all the #config/#defined usages in the project."); - print_usage_line(0, ""); + if (print_flag("-export-dependencies:<format>")) { + print_usage_line(2, "Exports dependencies to one of a few formats. Requires `-export-dependencies-file`."); + print_usage_line(2, "Available options:"); + print_usage_line(3, "-export-dependencies:make Exports in Makefile format"); + print_usage_line(3, "-export-dependencies:json Exports in JSON format"); + } - print_usage_line(1, "-export-defineables:<filename>"); - print_usage_line(2, "Exports an overview of all the #config/#defined usages in CSV format to the given file path."); - print_usage_line(2, "Example: -export-defineables:defineables.csv"); - print_usage_line(0, ""); - } + if (print_flag("-export-dependencies-file:<filename>")) { + print_usage_line(2, "Specifies the filename for `-export-dependencies`."); + print_usage_line(2, "Example: -export-dependencies-file:dependencies.d"); + } - if (build) { - print_usage_line(1, "-build-mode:<mode>"); - print_usage_line(2, "Sets the build mode."); - print_usage_line(2, "Available options:"); - print_usage_line(3, "-build-mode:exe Builds as an executable."); - print_usage_line(3, "-build-mode:test Builds as an executable that executes tests."); - print_usage_line(3, "-build-mode:dll Builds as a dynamically linked library."); - print_usage_line(3, "-build-mode:shared Builds as a dynamically linked library."); - print_usage_line(3, "-build-mode:lib Builds as a statically linked library."); - print_usage_line(3, "-build-mode:static Builds as a statically linked library."); - print_usage_line(3, "-build-mode:obj Builds as an object file."); - print_usage_line(3, "-build-mode:object Builds as an object file."); - print_usage_line(3, "-build-mode:assembly Builds as an assembly file."); - print_usage_line(3, "-build-mode:assembler Builds as an assembly file."); - print_usage_line(3, "-build-mode:asm Builds as an assembly file."); - print_usage_line(3, "-build-mode:llvm-ir Builds as an LLVM IR file."); - print_usage_line(3, "-build-mode:llvm Builds as an LLVM IR file."); - print_usage_line(0, ""); - } + if (print_flag("-export-timings:<format>")) { + print_usage_line(2, "Exports timings to one of a few formats. Requires `-show-timings` or `-show-more-timings`."); + print_usage_line(2, "Available options:"); + print_usage_line(3, "-export-timings:json Exports compile time stats to JSON."); + print_usage_line(3, "-export-timings:csv Exports compile time stats to CSV."); + } - if (check) { - print_usage_line(1, "-target:<string>"); - print_usage_line(2, "Sets the target for the executable to be built in."); - print_usage_line(0, ""); + if (print_flag("-export-timings-file:<filename>")) { + print_usage_line(2, "Specifies the filename for `-export-timings`."); + print_usage_line(2, "Example: -export-timings-file:timings.json"); + } } if (run_or_build) { - print_usage_line(1, "-debug"); - print_usage_line(2, "Enables debug information, and defines the global constant ODIN_DEBUG to be 'true'."); - print_usage_line(0, ""); + if (print_flag("-extra-assembler-flags:<string>")) { + print_usage_line(2, "Adds extra assembler specific flags in a string."); + } - print_usage_line(1, "-disable-assert"); - print_usage_line(2, "Disables the code generation of the built-in run-time 'assert' procedure, and defines the global constant ODIN_DISABLE_ASSERT to be 'true'."); - print_usage_line(0, ""); + if (print_flag("-extra-linker-flags:<string>")) { + print_usage_line(2, "Adds extra linker specific flags in a string."); + } + } - print_usage_line(1, "-no-bounds-check"); - print_usage_line(2, "Disables bounds checking program wide."); - print_usage_line(0, ""); + if (check) { + if (print_flag("-file")) { + print_usage_line(2, "Tells `%.*s %.*s` to treat the given file as a self-contained package.", LIT(arg0), LIT(command)); + print_usage_line(2, "This means that `<dir>/a.odin` won't have access to `<dir>/b.odin`'s contents."); + } - print_usage_line(1, "-no-type-assert"); - print_usage_line(2, "Disables type assertion checking program wide."); - print_usage_line(0, ""); + if (print_flag("-foreign-error-procedures")) { + print_usage_line(2, "States that the error procedures used in the runtime are defined in a separate translation unit."); + } - print_usage_line(1, "-no-crt"); - print_usage_line(2, "Disables automatic linking with the C Run Time."); - print_usage_line(0, ""); + if (print_flag("-ignore-unknown-attributes")) { + print_usage_line(2, "Ignores unknown attributes."); + print_usage_line(2, "This can be used with metaprogramming tools."); + } + } - print_usage_line(1, "-no-rpath"); - print_usage_line(2, "Disables automatic addition of an rpath linked to the executable directory."); - print_usage_line(0, ""); + if (run_or_build) { + #if defined(GB_SYSTEM_WINDOWS) + if (print_flag("-ignore-vs-search")) { + print_usage_line(2, "[Windows only]"); + print_usage_line(2, "Ignores the Visual Studio search for library paths."); + } + #endif + } - print_usage_line(1, "-no-thread-local"); - print_usage_line(2, "Ignores @thread_local attribute, effectively treating the program as if it is single-threaded."); - print_usage_line(0, ""); + if (check) { + if (print_flag("-ignore-warnings")) { + print_usage_line(2, "Ignores warning messages."); + } - print_usage_line(1, "-lld"); - print_usage_line(2, "Uses the LLD linker rather than the default."); - print_usage_line(0, ""); + if (print_flag("-json-errors")) { + print_usage_line(2, "Prints the error messages as json to stderr."); + } + } - print_usage_line(1, "-use-separate-modules"); - print_usage_line(2, "The backend generates multiple build units which are then linked together."); - print_usage_line(2, "Normally, a single build unit is generated for a standard project."); - print_usage_line(2, "This is the default behaviour on Windows for '-o:none' and '-o:minimal' builds."); - print_usage_line(0, ""); + if (run_or_build) { + if (print_flag("-keep-temp-files")) { + print_usage_line(2, "Keeps the temporary files generated during compilation."); + } + } else if (strip_semicolon) { + if (print_flag("-keep-temp-files")) { + print_usage_line(2, "Keeps the temporary files generated during stripping the unneeded semicolons from files."); + } + } + if (run_or_build) { + if (print_flag("-linker:<string>")) { + print_usage_line(2, "Specify the linker to use."); + print_usage_line(2, "Choices:"); + for (i32 i = 0; i < Linker_COUNT; i++) { + print_usage_line(3, "%.*s", LIT(linker_choices[i])); + } + } + + if (print_flag("-lld")) { + print_usage_line(2, "Uses the LLD linker rather than the default."); + } } if (check) { - print_usage_line(1, "-no-threaded-checker"); - print_usage_line(2, "Disables multithreading in the semantic checker stage."); - print_usage_line(0, ""); + if (print_flag("-max-error-count:<integer>")) { + print_usage_line(2, "Sets the maximum number of errors that can be displayed before the compiler terminates."); + print_usage_line(2, "Must be an integer >0."); + print_usage_line(2, "If not set, the default max error count is %d.", DEFAULT_MAX_ERROR_COLLECTOR_COUNT); + } + } + + if (run_or_build) { + if (print_flag("-microarch:<string>")) { + print_usage_line(2, "Specifies the specific micro-architecture for the build in a string."); + print_usage_line(2, "Examples:"); + print_usage_line(3, "-microarch:sandybridge"); + print_usage_line(3, "-microarch:native"); + print_usage_line(3, "-microarch:\"?\" for a list"); + } } if (check) { - print_usage_line(1, "-vet"); - print_usage_line(2, "Does extra checks on the code."); - print_usage_line(2, "Extra checks include:"); - print_usage_line(3, "-vet-unused"); - print_usage_line(3, "-vet-unused-variables"); - print_usage_line(3, "-vet-unused-imports"); - print_usage_line(3, "-vet-shadowing"); - print_usage_line(3, "-vet-using-stmt"); - print_usage_line(0, ""); + if (print_flag("-min-link-libs")) { + print_usage_line(2, "If set, the number of linked libraries will be minimized to prevent duplications."); + print_usage_line(2, "This is useful for so called \"dumb\" linkers compared to \"smart\" linkers."); + } + } - print_usage_line(1, "-vet-unused"); - print_usage_line(2, "Checks for unused declarations (variables and imports)."); - print_usage_line(0, ""); + if (run_or_build) { + if (print_flag("-minimum-os-version:<string>")) { + print_usage_line(2, "Sets the minimum OS version targeted by the application."); + print_usage_line(2, "Default: -minimum-os-version:11.0.0"); + print_usage_line(2, "Only used when target is Darwin, if given, linking mismatched versions will emit a warning."); + } - print_usage_line(1, "-vet-unused-variables"); - print_usage_line(2, "Checks for unused variable declarations."); - print_usage_line(0, ""); + if (print_flag("-no-bounds-check")) { + print_usage_line(2, "Disables bounds checking program wide."); + } - print_usage_line(1, "-vet-unused-imports"); - print_usage_line(2, "Checks for unused import declarations."); - print_usage_line(0, ""); + if (print_flag("-no-crt")) { + print_usage_line(2, "Disables automatic linking with the C Run Time."); + } + } - print_usage_line(1, "-vet-shadowing"); - print_usage_line(2, "Checks for variable shadowing within procedures."); - print_usage_line(0, ""); + if (check && command != "test") { + if (print_flag("-no-entry-point")) { + print_usage_line(2, "Removes default requirement of an entry point (e.g. main procedure)."); + } + } - print_usage_line(1, "-vet-using-stmt"); - print_usage_line(2, "Checks for the use of 'using' as a statement."); - print_usage_line(2, "'using' is considered bad practice outside of immediate refactoring."); - print_usage_line(0, ""); + if (run_or_build) { + if (print_flag("-no-rpath")) { + print_usage_line(2, "Disables automatic addition of an rpath linked to the executable directory."); + } - print_usage_line(1, "-vet-using-param"); - print_usage_line(2, "Checks for the use of 'using' on procedure parameters."); - print_usage_line(2, "'using' is considered bad practice outside of immediate refactoring."); - print_usage_line(0, ""); + if (print_flag("-no-thread-local")) { + print_usage_line(2, "Ignores @thread_local attribute, effectively treating the program as if it is single-threaded."); + } - print_usage_line(1, "-vet-style"); - print_usage_line(2, "Errs on missing trailing commas followed by a newline."); - print_usage_line(2, "Errs on deprecated syntax."); - print_usage_line(2, "Does not err on unneeded tokens (unlike -strict-style)."); - print_usage_line(0, ""); + if (print_flag("-no-threaded-checker")) { + print_usage_line(2, "Disables multithreading in the semantic checker stage."); + } - print_usage_line(1, "-vet-semicolon"); - print_usage_line(2, "Errs on unneeded semicolons."); - print_usage_line(0, ""); + if (print_flag("-no-type-assert")) { + print_usage_line(2, "Disables type assertion checking program wide."); + } + } - print_usage_line(1, "-vet-cast"); - print_usage_line(2, "Errs on casting a value to its own type or using `transmute` rather than `cast`."); - print_usage_line(0, ""); + if (run_or_build) { + if (print_flag("-o:<string>")) { + print_usage_line(2, "Sets the optimization mode for compilation."); + print_usage_line(2, "Available options:"); + print_usage_line(3, "-o:none"); + print_usage_line(3, "-o:minimal"); + print_usage_line(3, "-o:size"); + print_usage_line(3, "-o:speed"); + if (LB_USE_NEW_PASS_SYSTEM) { + print_usage_line(3, "-o:aggressive (use this with caution)"); + } + print_usage_line(2, "The default is -o:minimal."); + } - print_usage_line(1, "-vet-tabs"); - print_usage_line(2, "Errs when the use of tabs has not been used for indentation."); - print_usage_line(0, ""); - print_usage_line(1, "-vet-packages:<comma-separated-strings>"); - print_usage_line(2, "Sets which packages by name will be vetted."); - print_usage_line(2, "Files with specific +vet tags will not be ignored if they are not in the packages set."); - print_usage_line(0, ""); + if (print_flag("-obfuscate-source-code-locations")) { + print_usage_line(2, "Obfuscate the file and procedure strings, and line and column numbers, stored with a 'runtime.Source_Code_Location' value."); + } - print_usage_line(1, "-vet-unused-procedures"); - print_usage_line(2, "Checks for unused procedures."); - print_usage_line(2, "Must be used with -vet-packages or specified on a per file with +vet tags."); - print_usage_line(0, ""); - } - if (check) { - print_usage_line(1, "-custom-attribute:<string>"); - print_usage_line(2, "Add a custom attribute which will be ignored if it is unknown."); - print_usage_line(2, "This can be used with metaprogramming tools."); - print_usage_line(2, "Examples:"); - print_usage_line(3, "-custom-attribute:my_tag"); - print_usage_line(3, "-custom-attribute:my_tag,the_other_thing"); - print_usage_line(3, "-custom-attribute:my_tag -custom-attribute:the_other_thing"); - print_usage_line(0, ""); + if (print_flag("-out:<filepath>")) { + print_usage_line(2, "Sets the file name of the outputted executable."); + print_usage_line(2, "Example: -out:foo.exe"); + } + } - print_usage_line(1, "-ignore-unknown-attributes"); - print_usage_line(2, "Ignores unknown attributes."); - print_usage_line(2, "This can be used with metaprogramming tools."); - print_usage_line(0, ""); + if (doc) { + if (print_flag("-out:<filepath>")) { + print_usage_line(2, "Sets the base name of the resultig .odin-doc file."); + print_usage_line(2, "The extension can be optionally included; the resulting file will always have an extension of '.odin-doc'."); + print_usage_line(2, "Example: -out:foo"); + } + } - if (command != "test") { - print_usage_line(1, "-no-entry-point"); - print_usage_line(2, "Removes default requirement of an entry point (e.g. main procedure)."); - print_usage_line(0, ""); + if (run_or_build) { + #if defined(GB_SYSTEM_WINDOWS) + if (print_flag("-pdb-name:<filepath>")) { + print_usage_line(2, "[Windows only]"); + print_usage_line(2, "Defines the generated PDB name when -debug is enabled."); + print_usage_line(2, "Example: -pdb-name:different.pdb"); } + #endif } - if (test_only) { - print_usage_line(1, "-all-packages"); - print_usage_line(2, "Tests all packages imported into the given initial package."); - print_usage_line(0, ""); + if (build) { + if (print_flag("-print-linker-flags")) { + print_usage_line(2, "Prints the all of the flags/arguments that will be passed to the linker."); + } } if (run_or_build) { - print_usage_line(1, "-minimum-os-version:<string>"); - print_usage_line(2, "Sets the minimum OS version targeted by the application."); - print_usage_line(2, "Default: -minimum-os-version:11.0.0"); - print_usage_line(2, "Only used when target is Darwin, if given, linking mismatched versions will emit a warning."); - print_usage_line(0, ""); + if (print_flag("-radlink")) { + print_usage_line(2, "Uses the RAD linker rather than the default."); + } - print_usage_line(1, "-extra-linker-flags:<string>"); - print_usage_line(2, "Adds extra linker specific flags in a string."); - print_usage_line(0, ""); + if (print_flag("-reloc-mode:<string>")) { + print_usage_line(2, "Specifies the reloc mode."); + print_usage_line(2, "Available options:"); + print_usage_line(3, "-reloc-mode:default"); + print_usage_line(3, "-reloc-mode:static"); + print_usage_line(3, "-reloc-mode:pic"); + print_usage_line(3, "-reloc-mode:dynamic-no-pic"); + } - print_usage_line(1, "-extra-assembler-flags:<string>"); - print_usage_line(2, "Adds extra assembler specific flags in a string."); - print_usage_line(0, ""); + #if defined(GB_SYSTEM_WINDOWS) + if (print_flag("-resource:<filepath>")) { + print_usage_line(2, "[Windows only]"); + print_usage_line(2, "Defines the resource file for the executable."); + print_usage_line(2, "Example: -resource:path/to/file.rc"); + print_usage_line(2, "or: -resource:path/to/file.res for a precompiled one."); + } + #endif - print_usage_line(1, "-microarch:<string>"); - print_usage_line(2, "Specifies the specific micro-architecture for the build in a string."); - print_usage_line(2, "Examples:"); - print_usage_line(3, "-microarch:sandybridge"); - print_usage_line(3, "-microarch:native"); - print_usage_line(3, "-microarch:\"?\" for a list"); - print_usage_line(0, ""); + if (print_flag("-sanitize:<string>")) { + print_usage_line(2, "Enables sanitization analysis."); + print_usage_line(2, "Available options:"); + print_usage_line(3, "-sanitize:address"); + print_usage_line(3, "-sanitize:memory"); + print_usage_line(3, "-sanitize:thread"); + print_usage_line(2, "NOTE: This flag can be used multiple times."); + } + } - print_usage_line(1, "-target-features:<string>"); - print_usage_line(2, "Specifies CPU features to enable on top of the enabled features implied by -microarch."); - print_usage_line(2, "Examples:"); - print_usage_line(3, "-target-features:atomics"); - print_usage_line(3, "-target-features:\"sse2,aes\""); - print_usage_line(3, "-target-features:\"?\" for a list"); - print_usage_line(0, ""); + if (doc) { + if (print_flag("-short")) { + print_usage_line(2, "Shows shortened documentation for the packages."); + } + } - print_usage_line(1, "-strict-target-features"); - print_usage_line(2, "Makes @(enable_target_features=\"...\") behave the same way as @(require_target_features=\"...\")."); - print_usage_line(2, "This enforces that all generated code uses features supported by the combination of -target, -microarch, and -target-features."); - print_usage_line(0, ""); + if (check) { + if (print_flag("-show-defineables")) { + print_usage_line(2, "Shows an overview of all the #config/#defined usages in the project."); + } - print_usage_line(1, "-reloc-mode:<string>"); - print_usage_line(2, "Specifies the reloc mode."); - print_usage_line(2, "Available options:"); - print_usage_line(3, "-reloc-mode:default"); - print_usage_line(3, "-reloc-mode:static"); - print_usage_line(3, "-reloc-mode:pic"); - print_usage_line(3, "-reloc-mode:dynamic-no-pic"); - print_usage_line(0, ""); + if (print_flag("-show-system-calls")) { + print_usage_line(2, "Prints the whole command and arguments for calls to external tools like linker and assembler."); + } - print_usage_line(1, "-disable-red-zone"); - print_usage_line(2, "Disables red zone on a supported freestanding target."); - print_usage_line(0, ""); + if (print_flag("-show-timings")) { + print_usage_line(2, "Shows basic overview of the timings of different stages within the compiler in milliseconds."); + } - print_usage_line(1, "-dynamic-map-calls"); - print_usage_line(2, "Uses dynamic map calls to minimize code generation at the cost of runtime execution."); - print_usage_line(0, ""); + if (print_flag("-show-more-timings")) { + print_usage_line(2, "Shows an advanced overview of the timings of different stages within the compiler in milliseconds."); + } } - if (build) { - print_usage_line(1, "-print-linker-flags"); - print_usage_line(2, "Prints the all of the flags/arguments that will be passed to the linker."); - print_usage_line(0, ""); + if (check_only) { + if (print_flag("-show-unused")) { + print_usage_line(2, "Shows unused package declarations within the current project."); + } + if (print_flag("-show-unused-with-location")) { + print_usage_line(2, "Shows unused package declarations within the current project with the declarations source location."); + } } if (check) { - print_usage_line(1, "-disallow-do"); - print_usage_line(2, "Disallows the 'do' keyword in the project."); - print_usage_line(0, ""); + if (print_flag("-strict-style")) { + print_usage_line(2, "This enforces parts of same style as the Odin compiler, prefer '-vet-style -vet-semicolon' if you do not want to match it exactly."); + print_usage_line(2, ""); + print_usage_line(2, "Errs on unneeded tokens, such as unneeded semicolons."); + print_usage_line(2, "Errs on missing trailing commas followed by a newline."); + print_usage_line(2, "Errs on deprecated syntax."); + print_usage_line(2, "Errs when the attached-brace style in not adhered to (also known as 1TBS)."); + print_usage_line(2, "Errs when 'case' labels are not in the same column as the associated 'switch' token."); + } + } - print_usage_line(1, "-default-to-nil-allocator"); - print_usage_line(2, "Sets the default allocator to be the nil_allocator, an allocator which does nothing."); - print_usage_line(0, ""); + if (run_or_build) { + if (print_flag("-strict-target-features")) { + print_usage_line(2, "Makes @(enable_target_features=\"...\") behave the same way as @(require_target_features=\"...\")."); + print_usage_line(2, "This enforces that all generated code uses features supported by the combination of -target, -microarch, and -target-features."); + } - print_usage_line(1, "-strict-style"); - print_usage_line(2, "This enforces parts of same style as the Odin compiler, prefer '-vet-style -vet-semicolon' if you do not want to match it exactly."); - print_usage_line(2, ""); - print_usage_line(2, "Errs on unneeded tokens, such as unneeded semicolons."); - print_usage_line(2, "Errs on missing trailing commas followed by a newline."); - print_usage_line(2, "Errs on deprecated syntax."); - print_usage_line(2, "Errs when the attached-brace style in not adhered to (also known as 1TBS)."); - print_usage_line(2, "Errs when 'case' labels are not in the same column as the associated 'switch' token."); - print_usage_line(0, ""); + #if defined(GB_SYSTEM_WINDOWS) + if (print_flag("-subsystem:<option>")) { + print_usage_line(2, "[Windows only]"); + print_usage_line(2, "Defines the subsystem for the application."); + print_usage_line(2, "Available options:"); + print_usage_line(3, "-subsystem:console"); + print_usage_line(3, "-subsystem:windows"); + } + #endif - print_usage_line(1, "-ignore-warnings"); - print_usage_line(2, "Ignores warning messages."); - print_usage_line(0, ""); + if (print_flag("-target-features:<string>")) { + print_usage_line(2, "Specifies CPU features to enable on top of the enabled features implied by -microarch."); + print_usage_line(2, "Examples:"); + print_usage_line(3, "-target-features:atomics"); + print_usage_line(3, "-target-features:\"sse2,aes\""); + print_usage_line(3, "-target-features:\"?\" for a list"); + } + } - print_usage_line(1, "-warnings-as-errors"); - print_usage_line(2, "Treats warning messages as error messages."); - print_usage_line(0, ""); + if (check) { + if (print_flag("-target:<string>")) { + print_usage_line(2, "Sets the target for the executable to be built in."); + } - print_usage_line(1, "-terse-errors"); - print_usage_line(2, "Prints a terse error message without showing the code on that line and the location in that line."); - print_usage_line(0, ""); + if (print_flag("-terse-errors")) { + print_usage_line(2, "Prints a terse error message without showing the code on that line and the location in that line."); + } - print_usage_line(1, "-json-errors"); - print_usage_line(2, "Prints the error messages as json to stderr."); - print_usage_line(0, ""); + if (print_flag("-thread-count:<integer>")) { + print_usage_line(2, "Overrides the number of threads the compiler will use to compile with."); + print_usage_line(2, "Example: -thread-count:2"); + } + } - print_usage_line(1, "-error-pos-style:<string>"); - print_usage_line(2, "Available options:"); - print_usage_line(3, "-error-pos-style:unix file/path:45:3:"); - print_usage_line(3, "-error-pos-style:odin file/path(45:3)"); - print_usage_line(3, "-error-pos-style:default (Defaults to 'odin'.)"); - print_usage_line(0, ""); + if (run_or_build) { + if (print_flag("-use-separate-modules")) { + print_usage_line(2, "The backend generates multiple build units which are then linked together."); + print_usage_line(2, "Normally, a single build unit is generated for a standard project."); + print_usage_line(2, "This is the default behaviour on Windows for '-o:none' and '-o:minimal' builds."); + } - print_usage_line(1, "-max-error-count:<integer>"); - print_usage_line(2, "Sets the maximum number of errors that can be displayed before the compiler terminates."); - print_usage_line(2, "Must be an integer >0."); - print_usage_line(2, "If not set, the default max error count is %d.", DEFAULT_MAX_ERROR_COLLECTOR_COUNT); - print_usage_line(0, ""); + } - print_usage_line(1, "-min-link-libs"); - print_usage_line(2, "If set, the number of linked libraries will be minimized to prevent duplications."); - print_usage_line(2, "This is useful for so called \"dumb\" linkers compared to \"smart\" linkers."); - print_usage_line(0, ""); - print_usage_line(1, "-foreign-error-procedures"); - print_usage_line(2, "States that the error procedures used in the runtime are defined in a separate translation unit."); - print_usage_line(0, ""); + if (check) { + if (print_flag("-vet")) { + print_usage_line(2, "Does extra checks on the code."); + print_usage_line(2, "Extra checks include:"); + print_usage_line(3, "-vet-unused"); + print_usage_line(3, "-vet-unused-variables"); + print_usage_line(3, "-vet-unused-imports"); + print_usage_line(3, "-vet-shadowing"); + print_usage_line(3, "-vet-using-stmt"); + } - } + if (print_flag("-vet-cast")) { + print_usage_line(2, "Errs on casting a value to its own type or using `transmute` rather than `cast`."); + } - if (run_or_build) { - print_usage_line(1, "-obfuscate-source-code-locations"); - print_usage_line(2, "Obfuscate the file and procedure strings, and line and column numbers, stored with a 'runtime.Source_Code_Location' value."); - print_usage_line(0, ""); + if (print_flag("-vet-packages:<comma-separated-strings>")) { + print_usage_line(2, "Sets which packages by name will be vetted."); + print_usage_line(2, "Files with specific +vet tags will not be ignored if they are not in the packages set."); + } - print_usage_line(1, "-sanitize:<string>"); - print_usage_line(2, "Enables sanitization analysis."); - print_usage_line(2, "Available options:"); - print_usage_line(3, "-sanitize:address"); - print_usage_line(3, "-sanitize:memory"); - print_usage_line(3, "-sanitize:thread"); - print_usage_line(2, "NOTE: This flag can be used multiple times."); - print_usage_line(0, ""); + if (print_flag("-vet-semicolon")) { + print_usage_line(2, "Errs on unneeded semicolons."); + } - } - if (run_or_build) { - #if defined(GB_SYSTEM_WINDOWS) - print_usage_line(1, "-ignore-vs-search"); - print_usage_line(2, "[Windows only]"); - print_usage_line(2, "Ignores the Visual Studio search for library paths."); - print_usage_line(0, ""); + if (print_flag("-vet-shadowing")) { + print_usage_line(2, "Checks for variable shadowing within procedures."); + } - print_usage_line(1, "-resource:<filepath>"); - print_usage_line(2, "[Windows only]"); - print_usage_line(2, "Defines the resource file for the executable."); - print_usage_line(2, "Example: -resource:path/to/file.rc"); - print_usage_line(2, "or: -resource:path/to/file.res for a precompiled one."); - print_usage_line(0, ""); + if (print_flag("-vet-style")) { + print_usage_line(2, "Errs on missing trailing commas followed by a newline."); + print_usage_line(2, "Errs on deprecated syntax."); + print_usage_line(2, "Does not err on unneeded tokens (unlike -strict-style)."); + } - print_usage_line(1, "-pdb-name:<filepath>"); - print_usage_line(2, "[Windows only]"); - print_usage_line(2, "Defines the generated PDB name when -debug is enabled."); - print_usage_line(2, "Example: -pdb-name:different.pdb"); - print_usage_line(0, ""); - print_usage_line(1, "-subsystem:<option>"); - print_usage_line(2, "[Windows only]"); - print_usage_line(2, "Defines the subsystem for the application."); - print_usage_line(2, "Available options:"); - print_usage_line(3, "-subsystem:console"); - print_usage_line(3, "-subsystem:windows"); - print_usage_line(0, ""); + if (print_flag("-vet-tabs")) { + print_usage_line(2, "Errs when the use of tabs has not been used for indentation."); + } - #endif + + if (print_flag("-vet-unused")) { + print_usage_line(2, "Checks for unused declarations (variables and imports)."); + } + + if (print_flag("-vet-unused-imports")) { + print_usage_line(2, "Checks for unused import declarations."); + } + + if (print_flag("-vet-unused-procedures")) { + print_usage_line(2, "Checks for unused procedures."); + print_usage_line(2, "Must be used with -vet-packages or specified on a per file with +vet tags."); + } + + if (print_flag("-vet-unused-variables")) { + print_usage_line(2, "Checks for unused variable declarations."); + } + + + if (print_flag("-vet-using-param")) { + print_usage_line(2, "Checks for the use of 'using' on procedure parameters."); + print_usage_line(2, "'using' is considered bad practice outside of immediate refactoring."); + } + + if (print_flag("-vet-using-stmt")) { + print_usage_line(2, "Checks for the use of 'using' as a statement."); + print_usage_line(2, "'using' is considered bad practice outside of immediate refactoring."); + } + } + + if (check) { + if (print_flag("-warnings-as-errors")) { + print_usage_line(2, "Treats warning messages as error messages."); + } } } @@ -3132,7 +3268,7 @@ int main(int arg_count, char const **arg_ptr) { usage(args[0]); return 1; } else { - print_show_help(args[0], args[2]); + print_show_help(args[0], args[1], args[2]); return 0; } } else if (command == "root") { @@ -3276,6 +3412,19 @@ int main(int arg_count, char const **arg_ptr) { } } + #if defined(GB_CPU_X86) + // We've detected that the CPU doesn't support popcnt, or another reason to use `-microarch:native`, + // and that no custom microarch was chosen. + if (should_use_march_native() && march == get_default_microarchitecture()) { + if (command == "run" || command == "test") { + gb_printf_err("Error: Try using '-microarch:native' as Odin defaults to %.*s (close to Nehalem), and your CPU seems to be older.\n", LIT(march)); + gb_exit(1); + } else if (command == "build") { + gb_printf("Suggestion: Try using '-microarch:native' as Odin defaults to %.*s (close to Nehalem), and your CPU seems to be older.\n", LIT(march)); + } + } + #endif + if (build_context.target_features_string.len != 0) { String_Iterator target_it = {build_context.target_features_string, 0}; for (;;) { @@ -3325,7 +3474,7 @@ int main(int arg_count, char const **arg_ptr) { if (LLVM_VERSION_MAJOR < 17) { gb_printf_err("Invalid LLVM version %s, RISC-V targets require at least LLVM 17\n", LLVM_VERSION_STRING); gb_exit(1); - } + } } if (build_context.show_debug_messages) { @@ -3347,6 +3496,7 @@ int main(int arg_count, char const **arg_ptr) { Parser *parser = gb_alloc_item(permanent_allocator(), Parser); Checker *checker = gb_alloc_item(permanent_allocator(), Checker); + bool failed_to_cache_parsing = false; MAIN_TIME_SECTION("parse files"); @@ -3366,10 +3516,6 @@ int main(int arg_count, char const **arg_ptr) { print_all_errors(); return 1; } - if (any_warnings()) { - print_all_errors(); - } - checker->parser = parser; init_checker(checker); @@ -3396,7 +3542,7 @@ int main(int arg_count, char const **arg_ptr) { if (build_context.show_defineables || build_context.export_defineables_file != "") { TEMPORARY_ALLOCATOR_GUARD(); temp_alloc_defineable_strings(checker); - sort_defineables(checker); + sort_defineables_and_remove_duplicates(checker); if (build_context.show_defineables) { show_defineables(checker); @@ -3440,6 +3586,7 @@ int main(int arg_count, char const **arg_ptr) { if (try_cached_build(checker, args)) { goto end_of_code_gen; } + failed_to_cache_parsing = true; } #if ALLOW_TILDE @@ -3505,18 +3652,23 @@ int main(int arg_count, char const **arg_ptr) { end_of_code_gen:; - if (build_context.show_timings) { - show_timings(checker, &global_timings); - } - if (build_context.export_dependencies_format != DependenciesExportUnspecified) { export_dependencies(checker); } + if (build_context.cached) { + MAIN_TIME_SECTION("write cached build"); + if (!build_context.build_cache_data.copy_already_done) { + try_copy_executable_to_cache(); + } + + if (failed_to_cache_parsing) { + write_cached_build(checker, args); + } + } - if (!build_context.build_cache_data.copy_already_done && - build_context.cached) { - try_copy_executable_to_cache(); + if (build_context.show_timings) { + show_timings(checker, &global_timings); } if (run_output) { diff --git a/src/parser.cpp b/src/parser.cpp index 520a23c5a..aa90651d3 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -448,7 +448,8 @@ gb_internal Ast *clone_ast(Ast *node, AstFile *f) { n->StructType.fields = clone_ast_array(n->StructType.fields, f); n->StructType.polymorphic_params = clone_ast(n->StructType.polymorphic_params, f); n->StructType.align = clone_ast(n->StructType.align, f); - n->StructType.field_align = clone_ast(n->StructType.field_align, f); + n->StructType.min_field_align = clone_ast(n->StructType.min_field_align, f); + n->StructType.max_field_align = clone_ast(n->StructType.max_field_align, f); n->StructType.where_clauses = clone_ast_array(n->StructType.where_clauses, f); break; case Ast_UnionType: @@ -1217,7 +1218,7 @@ gb_internal Ast *ast_dynamic_array_type(AstFile *f, Token token, Ast *elem) { gb_internal Ast *ast_struct_type(AstFile *f, Token token, Slice<Ast *> fields, isize field_count, Ast *polymorphic_params, bool is_packed, bool is_raw_union, bool is_no_copy, - Ast *align, Ast *field_align, + Ast *align, Ast *min_field_align, Ast *max_field_align, Token where_token, Array<Ast *> const &where_clauses) { Ast *result = alloc_ast_node(f, Ast_StructType); result->StructType.token = token; @@ -1228,7 +1229,8 @@ gb_internal Ast *ast_struct_type(AstFile *f, Token token, Slice<Ast *> fields, i result->StructType.is_raw_union = is_raw_union; result->StructType.is_no_copy = is_no_copy; result->StructType.align = align; - result->StructType.field_align = field_align; + result->StructType.min_field_align = min_field_align; + result->StructType.max_field_align = max_field_align; result->StructType.where_token = where_token; result->StructType.where_clauses = slice_from_array(where_clauses); return result; @@ -2486,6 +2488,7 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) { tag = parse_call_expr(f, tag); } Ast *type = parse_type(f); + syntax_error(tag, "#relative types have now been removed in favour of \"core:relative\""); return ast_relative_type(f, tag, type); } else if (name.string == "force_inline" || name.string == "force_no_inline") { @@ -2757,7 +2760,8 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) { bool is_raw_union = false; bool no_copy = false; Ast *align = nullptr; - Ast *field_align = nullptr; + Ast *min_field_align = nullptr; + Ast *max_field_align = nullptr; if (allow_token(f, Token_OpenParen)) { isize param_count = 0; @@ -2795,18 +2799,43 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) { gb_string_free(s); } } else if (tag.string == "field_align") { - if (field_align) { + if (min_field_align) { syntax_error(tag, "Duplicate struct tag '#%.*s'", LIT(tag.string)); } - field_align = parse_expr(f, true); - if (field_align && field_align->kind != Ast_ParenExpr) { + syntax_warning(tag, "#field_align has been deprecated in favour of #min_field_align"); + min_field_align = parse_expr(f, true); + if (min_field_align && min_field_align->kind != Ast_ParenExpr) { ERROR_BLOCK(); - gbString s = expr_to_string(field_align); + gbString s = expr_to_string(min_field_align); syntax_warning(tag, "#field_align requires parentheses around the expression"); - error_line("\tSuggestion: #field_align(%s)", s); + error_line("\tSuggestion: #min_field_align(%s)", s); gb_string_free(s); } - } else if (tag.string == "raw_union") { + } else if (tag.string == "min_field_align") { + if (min_field_align) { + syntax_error(tag, "Duplicate struct tag '#%.*s'", LIT(tag.string)); + } + min_field_align = parse_expr(f, true); + if (min_field_align && min_field_align->kind != Ast_ParenExpr) { + ERROR_BLOCK(); + gbString s = expr_to_string(min_field_align); + syntax_warning(tag, "#min_field_align requires parentheses around the expression"); + error_line("\tSuggestion: #min_field_align(%s)", s); + gb_string_free(s); + } + } else if (tag.string == "max_field_align") { + if (max_field_align) { + syntax_error(tag, "Duplicate struct tag '#%.*s'", LIT(tag.string)); + } + max_field_align = parse_expr(f, true); + if (max_field_align && max_field_align->kind != Ast_ParenExpr) { + ERROR_BLOCK(); + gbString s = expr_to_string(max_field_align); + syntax_warning(tag, "#max_field_align requires parentheses around the expression"); + error_line("\tSuggestion: #max_field_align(%s)", s); + gb_string_free(s); + } + }else if (tag.string == "raw_union") { if (is_raw_union) { syntax_error(tag, "Duplicate struct tag '#%.*s'", LIT(tag.string)); } @@ -2856,7 +2885,7 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) { parser_check_polymorphic_record_parameters(f, polymorphic_params); - return ast_struct_type(f, token, decls, name_count, polymorphic_params, is_packed, is_raw_union, no_copy, align, field_align, where_token, where_clauses); + return ast_struct_type(f, token, decls, name_count, polymorphic_params, is_packed, is_raw_union, no_copy, align, min_field_align, max_field_align, where_token, where_clauses); } break; case Token_union: { @@ -4234,8 +4263,6 @@ gb_internal bool allow_field_separator(AstFile *f) { gb_internal Ast *parse_struct_field_list(AstFile *f, isize *name_count_) { Token start_token = f->curr_token; - auto decls = array_make<Ast *>(ast_allocator(f)); - isize total_name_count = 0; Ast *params = parse_field_list(f, &total_name_count, FieldFlag_Struct, Token_CloseBrace, false, false); @@ -4377,10 +4404,14 @@ gb_internal Ast *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_fl } } - allow_field_separator(f); + bool more_fields = allow_field_separator(f); Ast *param = ast_field(f, names, type, default_value, set_flags, tag, docs, f->line_comment); array_add(¶ms, param); + if (!more_fields) { + if (name_count_) *name_count_ = total_name_count; + return ast_field_list(f, start_token, params); + } while (f->curr_token.kind != follow && f->curr_token.kind != Token_EOF && diff --git a/src/parser.hpp b/src/parser.hpp index d5117a31e..e332fed50 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -736,7 +736,8 @@ AST_KIND(_TypeBegin, "", bool) \ isize field_count; \ Ast *polymorphic_params; \ Ast *align; \ - Ast *field_align; \ + Ast *min_field_align; \ + Ast *max_field_align; \ Token where_token; \ Slice<Ast *> where_clauses; \ bool is_packed; \ diff --git a/src/string.cpp b/src/string.cpp index 3c7d96934..f8ee6c53e 100644 --- a/src/string.cpp +++ b/src/string.cpp @@ -156,6 +156,7 @@ gb_internal isize string_index_byte(String const &s, u8 x) { gb_internal gb_inline bool str_eq(String const &a, String const &b) { if (a.len != b.len) return false; + if (a.len == 0) return true; return memcmp(a.text, b.text, a.len) == 0; } gb_internal gb_inline bool str_ne(String const &a, String const &b) { return !str_eq(a, b); } @@ -411,6 +412,32 @@ gb_internal String concatenate4_strings(gbAllocator a, String const &x, String c return make_string(data, len); } +#if defined(GB_SYSTEM_WINDOWS) +gb_internal String escape_char(gbAllocator a, String s, char cte) { + isize buf_len = s.len; + isize cte_count = 0; + for (isize j = 0; j < s.len; j++) { + if (s.text[j] == cte) { + cte_count++; + } + } + + u8 *buf = gb_alloc_array(a, u8, buf_len+cte_count); + isize i = 0; + for (isize j = 0; j < s.len; j++) { + u8 c = s.text[j]; + + if (c == cte) { + buf[i++] = '\\'; + buf[i++] = c; + } else { + buf[i++] = c; + } + } + return make_string(buf, i); +} +#endif + gb_internal String string_join_and_quote(gbAllocator a, Array<String> strings) { if (!strings.count) { return make_string(nullptr, 0); @@ -426,7 +453,11 @@ gb_internal String string_join_and_quote(gbAllocator a, Array<String> strings) { if (i > 0) { s = gb_string_append_fmt(s, " "); } +#if defined(GB_SYSTEM_WINDOWS) + s = gb_string_append_fmt(s, "\"%.*s\" ", LIT(escape_char(a, strings[i], '\\'))); +#else s = gb_string_append_fmt(s, "\"%.*s\" ", LIT(strings[i])); +#endif } return make_string(cast(u8 *) s, gb_string_length(s)); diff --git a/src/types.cpp b/src/types.cpp index a9a7d6dda..c51df7261 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -137,7 +137,8 @@ struct TypeStruct { Scope * scope; i64 custom_align; - i64 custom_field_align; + i64 custom_min_field_align; + i64 custom_max_field_align; Type * polymorphic_params; // Type_Tuple Type * polymorphic_parent; Wait_Signal polymorphic_wait_signal; @@ -271,14 +272,6 @@ struct TypeProc { Type *elem; \ Type *generic_count; \ }) \ - TYPE_KIND(RelativePointer, struct { \ - Type *pointer_type; \ - Type *base_integer; \ - }) \ - TYPE_KIND(RelativeMultiPointer, struct { \ - Type *pointer_type; \ - Type *base_integer; \ - }) \ TYPE_KIND(Matrix, struct { \ Type *elem; \ i64 row_count; \ @@ -366,8 +359,6 @@ enum Typeid_Kind : u8 { Typeid_Map, Typeid_Bit_Set, Typeid_Simd_Vector, - Typeid_Relative_Pointer, - Typeid_Relative_Multi_Pointer, Typeid_Matrix, Typeid_SoaPointer, Typeid_Bit_Field, @@ -677,8 +668,6 @@ gb_global Type *t_type_info_enum = nullptr; gb_global Type *t_type_info_map = nullptr; gb_global Type *t_type_info_bit_set = nullptr; gb_global Type *t_type_info_simd_vector = nullptr; -gb_global Type *t_type_info_relative_pointer = nullptr; -gb_global Type *t_type_info_relative_multi_pointer = nullptr; gb_global Type *t_type_info_matrix = nullptr; gb_global Type *t_type_info_soa_pointer = nullptr; gb_global Type *t_type_info_bit_field = nullptr; @@ -707,8 +696,6 @@ gb_global Type *t_type_info_enum_ptr = nullptr; gb_global Type *t_type_info_map_ptr = nullptr; gb_global Type *t_type_info_bit_set_ptr = nullptr; gb_global Type *t_type_info_simd_vector_ptr = nullptr; -gb_global Type *t_type_info_relative_pointer_ptr = nullptr; -gb_global Type *t_type_info_relative_multi_pointer_ptr = nullptr; gb_global Type *t_type_info_matrix_ptr = nullptr; gb_global Type *t_type_info_soa_pointer_ptr = nullptr; gb_global Type *t_type_info_bit_field_ptr = nullptr; @@ -1117,24 +1104,6 @@ gb_internal Type *alloc_type_bit_field() { return t; } -gb_internal Type *alloc_type_relative_pointer(Type *pointer_type, Type *base_integer) { - GB_ASSERT(is_type_pointer(pointer_type)); - GB_ASSERT(is_type_integer(base_integer)); - Type *t = alloc_type(Type_RelativePointer); - t->RelativePointer.pointer_type = pointer_type; - t->RelativePointer.base_integer = base_integer; - return t; -} - -gb_internal Type *alloc_type_relative_multi_pointer(Type *pointer_type, Type *base_integer) { - GB_ASSERT(is_type_multi_pointer(pointer_type)); - GB_ASSERT(is_type_integer(base_integer)); - Type *t = alloc_type(Type_RelativeMultiPointer); - t->RelativeMultiPointer.pointer_type = pointer_type; - t->RelativeMultiPointer.base_integer = base_integer; - return t; -} - gb_internal Type *alloc_type_named(String name, Type *base, Entity *type_name) { Type *t = alloc_type(Type_Named); t->Named.name = name; @@ -1226,8 +1195,6 @@ gb_internal Type *type_deref(Type *t, bool allow_multi_pointer) { switch (bt->kind) { case Type_Pointer: return bt->Pointer.elem; - case Type_RelativePointer: - return type_deref(bt->RelativePointer.pointer_type); case Type_SoaPointer: { Type *elem = base_type(bt->SoaPointer.elem); @@ -1666,15 +1633,6 @@ gb_internal bool is_type_generic(Type *t) { return t->kind == Type_Generic; } -gb_internal bool is_type_relative_pointer(Type *t) { - t = base_type(t); - return t->kind == Type_RelativePointer; -} -gb_internal bool is_type_relative_multi_pointer(Type *t) { - t = base_type(t); - return t->kind == Type_RelativeMultiPointer; -} - gb_internal bool is_type_u8_slice(Type *t) { t = base_type(t); if (t->kind == Type_Slice) { @@ -2117,8 +2075,6 @@ gb_internal bool is_type_indexable(Type *t) { return true; case Type_EnumeratedArray: return true; - case Type_RelativeMultiPointer: - return true; case Type_Matrix: return true; } @@ -2136,8 +2092,6 @@ gb_internal bool is_type_sliceable(Type *t) { return true; case Type_EnumeratedArray: return false; - case Type_RelativeMultiPointer: - return true; case Type_Matrix: return false; } @@ -2344,27 +2298,7 @@ gb_internal bool is_type_polymorphic(Type *t, bool or_specialized=false) { return true; } break; - - case Type_RelativeMultiPointer: - if (is_type_polymorphic(t->RelativeMultiPointer.pointer_type, or_specialized)) { - return true; - } - if (t->RelativeMultiPointer.base_integer != nullptr && - is_type_polymorphic(t->RelativeMultiPointer.base_integer, or_specialized)) { - return true; - } - break; - case Type_RelativePointer: - if (is_type_polymorphic(t->RelativePointer.pointer_type, or_specialized)) { - return true; - } - if (t->RelativePointer.base_integer != nullptr && - is_type_polymorphic(t->RelativePointer.base_integer, or_specialized)) { - return true; - } - break; } - return false; } @@ -2406,10 +2340,6 @@ gb_internal bool type_has_nil(Type *t) { } } return false; - - case Type_RelativePointer: - case Type_RelativeMultiPointer: - return true; } return false; } @@ -2578,10 +2508,6 @@ gb_internal bool is_type_load_safe(Type *type) { } return true; - case Type_RelativePointer: - case Type_RelativeMultiPointer: - return true; - case Type_Pointer: case Type_MultiPointer: case Type_Slice: @@ -3912,6 +3838,14 @@ gb_internal i64 type_align_of_internal(Type *t, TypePath *path) { max = align; } } + + if (t->Struct.custom_min_field_align > 0) { + max = gb_max(max, t->Struct.custom_min_field_align); + } + if (t->Struct.custom_max_field_align != 0 && + t->Struct.custom_max_field_align > t->Struct.custom_min_field_align) { + max = gb_min(max, t->Struct.custom_max_field_align); + } return max; } break; @@ -3936,11 +3870,6 @@ gb_internal i64 type_align_of_internal(Type *t, TypePath *path) { case Type_Matrix: return matrix_align_of(t, path); - case Type_RelativePointer: - return type_align_of_internal(t->RelativePointer.base_integer, path); - case Type_RelativeMultiPointer: - return type_align_of_internal(t->RelativeMultiPointer.base_integer, path); - case Type_SoaPointer: return build_context.int_size; } @@ -3950,7 +3879,7 @@ gb_internal i64 type_align_of_internal(Type *t, TypePath *path) { return gb_clamp(next_pow2(type_size_of_internal(t, path)), 1, build_context.max_align); } -gb_internal i64 *type_set_offsets_of(Slice<Entity *> const &fields, bool is_packed, bool is_raw_union, i64 min_field_align) { +gb_internal i64 *type_set_offsets_of(Slice<Entity *> const &fields, bool is_packed, bool is_raw_union, i64 min_field_align, i64 max_field_align) { gbAllocator a = permanent_allocator(); auto offsets = gb_alloc_array(a, i64, fields.count); i64 curr_offset = 0; @@ -3980,6 +3909,9 @@ gb_internal i64 *type_set_offsets_of(Slice<Entity *> const &fields, bool is_pack } else { Type *t = fields[i]->type; i64 align = gb_max(type_align_of(t), min_field_align); + if (max_field_align > min_field_align) { + align = gb_min(align, max_field_align); + } i64 size = gb_max(type_size_of( t), 0); curr_offset = align_formula(curr_offset, align); offsets[i] = curr_offset; @@ -3996,7 +3928,7 @@ gb_internal bool type_set_offsets(Type *t) { MUTEX_GUARD(&t->Struct.offset_mutex); if (!t->Struct.are_offsets_set) { t->Struct.are_offsets_being_processed = true; - t->Struct.offsets = type_set_offsets_of(t->Struct.fields, t->Struct.is_packed, t->Struct.is_raw_union, t->Struct.custom_field_align); + t->Struct.offsets = type_set_offsets_of(t->Struct.fields, t->Struct.is_packed, t->Struct.is_raw_union, t->Struct.custom_min_field_align, t->Struct.custom_max_field_align); t->Struct.are_offsets_being_processed = false; t->Struct.are_offsets_set = true; return true; @@ -4005,7 +3937,7 @@ gb_internal bool type_set_offsets(Type *t) { MUTEX_GUARD(&t->Tuple.mutex); if (!t->Tuple.are_offsets_set) { t->Tuple.are_offsets_being_processed = true; - t->Tuple.offsets = type_set_offsets_of(t->Tuple.variables, t->Tuple.is_packed, false, 1); + t->Tuple.offsets = type_set_offsets_of(t->Tuple.variables, t->Tuple.is_packed, false, 1, 0); t->Tuple.are_offsets_being_processed = false; t->Tuple.are_offsets_set = true; return true; @@ -4230,11 +4162,6 @@ gb_internal i64 type_size_of_internal(Type *t, TypePath *path) { case Type_BitField: return type_size_of_internal(t->BitField.backing_type, path); - - case Type_RelativePointer: - return type_size_of_internal(t->RelativePointer.base_integer, path); - case Type_RelativeMultiPointer: - return type_size_of_internal(t->RelativeMultiPointer.base_integer, path); } // Catch all @@ -4860,19 +4787,6 @@ gb_internal gbString write_type_to_string(gbString str, Type *type, bool shortha str = gb_string_append_fmt(str, "#simd[%d]", cast(int)type->SimdVector.count); str = write_type_to_string(str, type->SimdVector.elem); break; - - case Type_RelativePointer: - str = gb_string_append_fmt(str, "#relative("); - str = write_type_to_string(str, type->RelativePointer.base_integer); - str = gb_string_append_fmt(str, ") "); - str = write_type_to_string(str, type->RelativePointer.pointer_type); - break; - case Type_RelativeMultiPointer: - str = gb_string_append_fmt(str, "#relative("); - str = write_type_to_string(str, type->RelativePointer.base_integer); - str = gb_string_append_fmt(str, ") "); - str = write_type_to_string(str, type->RelativePointer.pointer_type); - break; case Type_Matrix: if (type->Matrix.is_row_major) { diff --git a/src/unicode.cpp b/src/unicode.cpp index 665d5b182..cb9fb12ab 100644 --- a/src/unicode.cpp +++ b/src/unicode.cpp @@ -1,10 +1,15 @@ -#pragma warning(push) -#pragma warning(disable: 4245) +#if defined(GB_SYSTEM_WINDOWS) + #pragma warning(push) + #pragma warning(disable: 4245) +#endif extern "C" { #include "utf8proc/utf8proc.c" } -#pragma warning(pop) + +#if defined(GB_SYSTEM_WINDOWS) + #pragma warning(pop) +#endif gb_internal bool rune_is_letter(Rune r) { @@ -109,7 +114,7 @@ gb_internal isize utf8_decode(u8 const *str, isize str_len, Rune *codepoint_out) u8 b1, b2, b3; Utf8AcceptRange accept; if (x >= 0xf0) { - Rune mask = (cast(Rune)x << 31) >> 31; + Rune mask = -cast(Rune)(x & 1); codepoint = (cast(Rune)s0 & (~mask)) | (GB_RUNE_INVALID & mask); width = 1; goto end; |