diff options
| author | korvahkh <92224397+korvahkh@users.noreply.github.com> | 2024-06-13 01:27:44 +0000 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-06-13 01:27:44 +0000 |
| commit | 104ca2ce22c269b71df08edb00cb26bee4daf59d (patch) | |
| tree | ee0a3275d3b42ae9aa85d09bf01f278d3965cc31 /src | |
| parent | a7a6ff8c693be92929327660fd446dfc0af62e01 (diff) | |
| parent | a67df0739245d85e7aa773e7271a64121ca534c5 (diff) | |
Merge branch 'odin-lang:master' into fix-omitempty-comma
Diffstat (limited to 'src')
| -rw-r--r-- | src/bug_report.cpp | 1 | ||||
| -rw-r--r-- | src/build_settings.cpp | 64 | ||||
| -rw-r--r-- | src/check_builtin.cpp | 343 | ||||
| -rw-r--r-- | src/check_decl.cpp | 67 | ||||
| -rw-r--r-- | src/check_expr.cpp | 65 | ||||
| -rw-r--r-- | src/check_stmt.cpp | 30 | ||||
| -rw-r--r-- | src/check_type.cpp | 28 | ||||
| -rw-r--r-- | src/checker.cpp | 252 | ||||
| -rw-r--r-- | src/checker.hpp | 24 | ||||
| -rw-r--r-- | src/checker_builtin_procs.hpp | 4 | ||||
| -rw-r--r-- | src/entity.cpp | 5 | ||||
| -rw-r--r-- | src/error.cpp | 69 | ||||
| -rw-r--r-- | src/gb/gb.h | 3 | ||||
| -rw-r--r-- | src/linker.cpp | 34 | ||||
| -rw-r--r-- | src/llvm_abi.cpp | 10 | ||||
| -rw-r--r-- | src/llvm_backend.cpp | 264 | ||||
| -rw-r--r-- | src/llvm_backend.hpp | 3 | ||||
| -rw-r--r-- | src/llvm_backend_debug.cpp | 53 | ||||
| -rw-r--r-- | src/llvm_backend_expr.cpp | 55 | ||||
| -rw-r--r-- | src/llvm_backend_general.cpp | 7 | ||||
| -rw-r--r-- | src/llvm_backend_proc.cpp | 15 | ||||
| -rw-r--r-- | src/llvm_backend_stmt.cpp | 4 | ||||
| -rw-r--r-- | src/llvm_backend_utility.cpp | 2 | ||||
| -rw-r--r-- | src/main.cpp | 187 | ||||
| -rw-r--r-- | src/parser.cpp | 94 | ||||
| -rw-r--r-- | src/parser.hpp | 4 | ||||
| -rw-r--r-- | src/parser_pos.cpp | 2 | ||||
| -rw-r--r-- | src/threading.cpp | 2 | ||||
| -rw-r--r-- | src/types.cpp | 19 |
29 files changed, 1226 insertions, 484 deletions
diff --git a/src/bug_report.cpp b/src/bug_report.cpp index 1f754ce7c..dab8c4391 100644 --- a/src/bug_report.cpp +++ b/src/bug_report.cpp @@ -910,6 +910,7 @@ gb_internal void report_os_info() { {"23D60", {23, 3, 0}, "macOS", {"Sonoma", {14, 3, 1}}}, {"23E214", {23, 4, 0}, "macOS", {"Sonoma", {14, 4, 0}}}, {"23E224", {23, 4, 0}, "macOS", {"Sonoma", {14, 4, 1}}}, + {"23F79", {23, 5, 0}, "macOS", {"Sonoma", {14, 5, 0}}}, }; diff --git a/src/build_settings.cpp b/src/build_settings.cpp index d9454ba9b..dc11a5fd2 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -23,6 +23,7 @@ enum TargetOsKind : u16 { TargetOs_wasi, TargetOs_js, + TargetOs_orca, TargetOs_freestanding, @@ -90,6 +91,7 @@ gb_global String target_os_names[TargetOs_COUNT] = { str_lit("wasi"), str_lit("js"), + str_lit("orca"), str_lit("freestanding"), }; @@ -694,6 +696,12 @@ enum TimingsExportFormat : i32 { TimingsExportCSV = 2, }; +enum DependenciesExportFormat : i32 { + DependenciesExportUnspecified = 0, + DependenciesExportMake = 1, + DependenciesExportJson = 2, +}; + enum ErrorPosStyle { ErrorPosStyle_Default, // path(line:column) msg ErrorPosStyle_Unix, // path:line:column: msg @@ -831,6 +839,8 @@ struct BuildContext { bool show_timings; TimingsExportFormat export_timings_format; String export_timings_file; + DependenciesExportFormat export_dependencies_format; + String export_dependencies_file; bool show_unused; bool show_unused_with_location; bool show_more_timings; @@ -891,7 +901,6 @@ struct BuildContext { u32 cmd_doc_flags; Array<String> extra_packages; - StringSet test_names; bool test_all_packages; gbAffinity affinity; @@ -1030,6 +1039,13 @@ gb_global TargetMetrics target_netbsd_amd64 = { str_lit("x86_64-unknown-netbsd-elf"), }; +gb_global TargetMetrics target_netbsd_arm64 = { + TargetOs_netbsd, + TargetArch_arm64, + 8, 8, 16, 16, + str_lit("aarch64-unknown-netbsd-elf"), +}; + gb_global TargetMetrics target_haiku_amd64 = { TargetOs_haiku, TargetArch_amd64, @@ -1067,6 +1083,14 @@ gb_global TargetMetrics target_wasi_wasm32 = { }; +gb_global TargetMetrics target_orca_wasm32 = { + TargetOs_orca, + TargetArch_wasm32, + 4, 4, 8, 16, + str_lit("wasm32-wasi-js"), +}; + + gb_global TargetMetrics target_freestanding_wasm64p32 = { TargetOs_freestanding, TargetArch_wasm64p32, @@ -1113,6 +1137,14 @@ gb_global TargetMetrics target_freestanding_arm64 = { str_lit("aarch64-none-elf"), }; +gb_global TargetMetrics target_freestanding_arm32 = { + TargetOs_freestanding, + TargetArch_arm32, + 4, 4, 4, 8, + str_lit("arm-unknown-unknown-gnueabihf"), +}; + + struct NamedTargetMetrics { String name; TargetMetrics *metrics; @@ -1136,13 +1168,16 @@ gb_global NamedTargetMetrics named_targets[] = { { str_lit("freebsd_amd64"), &target_freebsd_amd64 }, { str_lit("freebsd_arm64"), &target_freebsd_arm64 }, - { str_lit("openbsd_amd64"), &target_openbsd_amd64 }, { str_lit("netbsd_amd64"), &target_netbsd_amd64 }, + { str_lit("netbsd_arm64"), &target_netbsd_arm64 }, + + { str_lit("openbsd_amd64"), &target_openbsd_amd64 }, { str_lit("haiku_amd64"), &target_haiku_amd64 }, { str_lit("freestanding_wasm32"), &target_freestanding_wasm32 }, { str_lit("wasi_wasm32"), &target_wasi_wasm32 }, { str_lit("js_wasm32"), &target_js_wasm32 }, + { str_lit("orca_wasm32"), &target_orca_wasm32 }, { str_lit("freestanding_wasm64p32"), &target_freestanding_wasm64p32 }, { str_lit("js_wasm64p32"), &target_js_wasm64p32 }, @@ -1152,6 +1187,7 @@ gb_global NamedTargetMetrics named_targets[] = { { str_lit("freestanding_amd64_win64"), &target_freestanding_amd64_win64 }, { str_lit("freestanding_arm64"), &target_freestanding_arm64 }, + { str_lit("freestanding_arm32"), &target_freestanding_arm32 }, }; gb_global NamedTargetMetrics *selected_target_metrics; @@ -1898,7 +1934,11 @@ gb_internal void init_build_context(TargetMetrics *cross_target, Subtarget subta #elif defined(GB_SYSTEM_OPENBSD) metrics = &target_openbsd_amd64; #elif defined(GB_SYSTEM_NETBSD) - metrics = &target_netbsd_amd64; + #if defined(GB_CPU_ARM) + metrics = &target_netbsd_arm64; + #else + metrics = &target_netbsd_amd64; + #endif #elif defined(GB_SYSTEM_HAIKU) metrics = &target_haiku_amd64; #elif defined(GB_CPU_ARM) @@ -2004,6 +2044,9 @@ gb_internal void init_build_context(TargetMetrics *cross_target, Subtarget subta bc->link_flags = str_lit("/machine:x86 "); break; } + } else if (bc->metrics.os == TargetOs_darwin) { + bc->link_flags = concatenate3_strings(permanent_allocator(), + str_lit("-target "), bc->metrics.target_triplet, str_lit(" ")); } else if (is_arch_wasm()) { gbString link_flags = gb_string_make(heap_allocator(), " "); // link_flags = gb_string_appendc(link_flags, "--export-all "); @@ -2012,17 +2055,22 @@ gb_internal void init_build_context(TargetMetrics *cross_target, Subtarget subta // if (bc->metrics.arch == TargetArch_wasm64) { // link_flags = gb_string_appendc(link_flags, "-mwasm64 "); // } - if (bc->no_entry_point) { + if (bc->no_entry_point || bc->metrics.os == TargetOs_orca) { link_flags = gb_string_appendc(link_flags, "--no-entry "); } - + bc->link_flags = make_string_c(link_flags); - + // Disallow on wasm bc->use_separate_modules = false; } else { - bc->link_flags = concatenate3_strings(permanent_allocator(), - str_lit("-target "), bc->metrics.target_triplet, str_lit(" ")); + // NOTE: for targets other than darwin, we don't specify a `-target` link flag. + // This is because we don't support cross-linking and clang is better at figuring + // out what the actual target for linking is, + // for example, on x86/alpine/musl it HAS to be `x86_64-alpine-linux-musl` to link correctly. + // + // Note that codegen will still target the triplet we specify, but the intricate details of + // a target shouldn't matter as much to codegen (if it does at all) as it does to linking. } // NOTE: needs to be done after adding the -target flag to the linker flags so the linker diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index d85e94db3..3aee804df 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -1079,7 +1079,7 @@ gb_internal bool check_builtin_simd_operation(CheckerContext *c, Operand *operan return false; } -gb_internal bool cache_load_file_directive(CheckerContext *c, Ast *call, String const &original_string, bool err_on_not_found, LoadFileCache **cache_) { +gb_internal bool cache_load_file_directive(CheckerContext *c, Ast *call, String const &original_string, bool err_on_not_found, LoadFileCache **cache_, LoadFileTier tier) { ast_node(ce, CallExpr, call); ast_node(bd, BasicDirective, ce->proc); String builtin_name = bd->name.string; @@ -1105,12 +1105,16 @@ gb_internal bool cache_load_file_directive(CheckerContext *c, Ast *call, String gbFileError file_error = gbFileError_None; String data = {}; + bool exists = false; + LoadFileTier cache_tier = LoadFileTier_Invalid; LoadFileCache **cache_ptr = string_map_get(&c->info->load_file_cache, path); LoadFileCache *cache = cache_ptr ? *cache_ptr : nullptr; if (cache) { file_error = cache->file_error; data = cache->data; + exists = cache->exists; + cache_tier = cache->tier; } defer ({ if (cache == nullptr) { @@ -1118,60 +1122,78 @@ gb_internal bool cache_load_file_directive(CheckerContext *c, Ast *call, String new_cache->path = path; new_cache->data = data; new_cache->file_error = file_error; + new_cache->exists = exists; + new_cache->tier = cache_tier; string_map_init(&new_cache->hashes, 32); string_map_set(&c->info->load_file_cache, path, new_cache); if (cache_) *cache_ = new_cache; } else { cache->data = data; cache->file_error = file_error; + cache->exists = exists; + cache->tier = cache_tier; if (cache_) *cache_ = cache; } }); - TEMPORARY_ALLOCATOR_GUARD(); - char *c_str = alloc_cstring(temporary_allocator(), path); + if (tier > cache_tier) { + cache_tier = tier; - gbFile f = {}; - if (cache == nullptr) { + TEMPORARY_ALLOCATOR_GUARD(); + char *c_str = alloc_cstring(temporary_allocator(), path); + + gbFile f = {}; file_error = gb_file_open(&f, c_str); + defer (gb_file_close(&f)); + + if (file_error == gbFileError_None) { + exists = true; + + switch(tier) { + case LoadFileTier_Exists: + // Nothing to do. + break; + case LoadFileTier_Contents: { + isize file_size = cast(isize)gb_file_size(&f); + if (file_size > 0) { + u8 *ptr = cast(u8 *)gb_alloc(permanent_allocator(), file_size+1); + gb_file_read_at(&f, ptr, file_size, 0); + ptr[file_size] = '\0'; + data.text = ptr; + data.len = file_size; + } + break; + } + default: + GB_PANIC("Unhandled LoadFileTier"); + }; + } } - defer (gb_file_close(&f)); switch (file_error) { default: case gbFileError_Invalid: if (err_on_not_found) { - error(ce->proc, "Failed to `#%.*s` file: %s; invalid file or cannot be found", LIT(builtin_name), c_str); + error(ce->proc, "Failed to `#%.*s` file: %.*s; invalid file or cannot be found", LIT(builtin_name), LIT(path)); } call->state_flags |= StateFlag_DirectiveWasFalse; return false; case gbFileError_NotExists: if (err_on_not_found) { - error(ce->proc, "Failed to `#%.*s` file: %s; file cannot be found", LIT(builtin_name), c_str); + error(ce->proc, "Failed to `#%.*s` file: %.*s; file cannot be found", LIT(builtin_name), LIT(path)); } call->state_flags |= StateFlag_DirectiveWasFalse; return false; case gbFileError_Permission: if (err_on_not_found) { - error(ce->proc, "Failed to `#%.*s` file: %s; file permissions problem", LIT(builtin_name), c_str); + error(ce->proc, "Failed to `#%.*s` file: %.*s; file permissions problem", LIT(builtin_name), LIT(path)); } call->state_flags |= StateFlag_DirectiveWasFalse; return false; case gbFileError_None: // Okay break; - } - - if (cache == nullptr) { - isize file_size = cast(isize)gb_file_size(&f); - if (file_size > 0) { - u8 *ptr = cast(u8 *)gb_alloc(permanent_allocator(), file_size+1); - gb_file_read_at(&f, ptr, file_size, 0); - ptr[file_size] = '\0'; - data.text = ptr; - data.len = file_size; - } - } + }; return true; } @@ -1263,7 +1285,7 @@ gb_internal LoadDirectiveResult check_load_directive(CheckerContext *c, Operand operand->mode = Addressing_Constant; LoadFileCache *cache = nullptr; - if (cache_load_file_directive(c, call, o.value.value_string, err_on_not_found, &cache)) { + if (cache_load_file_directive(c, call, o.value.value_string, err_on_not_found, &cache, LoadFileTier_Contents)) { operand->value = exact_value_string(cache->data); return LoadDirective_Success; } @@ -1345,6 +1367,8 @@ gb_internal LoadDirectiveResult check_load_directory_directive(CheckerContext *c map_set(&c->info->load_directory_map, call, new_cache); } else { cache->file_error = file_error; + + map_set(&c->info->load_directory_map, call, cache); } }); @@ -1389,7 +1413,7 @@ gb_internal LoadDirectiveResult check_load_directory_directive(CheckerContext *c for (FileInfo fi : list) { LoadFileCache *cache = nullptr; - if (cache_load_file_directive(c, call, fi.fullpath, err_on_not_found, &cache)) { + if (cache_load_file_directive(c, call, fi.fullpath, err_on_not_found, &cache, LoadFileTier_Contents)) { array_add(&file_caches, cache); } else { result = LoadDirective_Error; @@ -1403,6 +1427,65 @@ gb_internal LoadDirectiveResult check_load_directory_directive(CheckerContext *c return result; } +gb_internal bool check_hash_kind(CheckerContext *c, Ast *call, String const &hash_kind, u8 const *data, isize data_size, u64 *hash_value) { + ast_node(ce, CallExpr, call); + ast_node(bd, BasicDirective, ce->proc); + String name = bd->name.string; + GB_ASSERT(name == "load_hash" || name == "hash"); + + String supported_hashes[] = { + str_lit("adler32"), + str_lit("crc32"), + str_lit("crc64"), + str_lit("fnv32"), + str_lit("fnv64"), + str_lit("fnv32a"), + str_lit("fnv64a"), + str_lit("murmur32"), + str_lit("murmur64"), + }; + + bool hash_found = false; + for (isize i = 0; i < gb_count_of(supported_hashes); i++) { + if (supported_hashes[i] == hash_kind) { + hash_found = true; + break; + } + } + if (!hash_found) { + ERROR_BLOCK(); + error(ce->proc, "Invalid hash kind passed to `#%.*s`, got: %.*s", LIT(name), LIT(hash_kind)); + error_line("\tAvailable hash kinds:\n"); + for (isize i = 0; i < gb_count_of(supported_hashes); i++) { + error_line("\t%.*s\n", LIT(supported_hashes[i])); + } + return false; + } + + if (hash_kind == "adler32") { + *hash_value = gb_adler32(data, data_size); + } else if (hash_kind == "crc32") { + *hash_value = gb_crc32(data, data_size); + } else if (hash_kind == "crc64") { + *hash_value = gb_crc64(data, data_size); + } else if (hash_kind == "fnv32") { + *hash_value = gb_fnv32(data, data_size); + } else if (hash_kind == "fnv64") { + *hash_value = gb_fnv64(data, data_size); + } else if (hash_kind == "fnv32a") { + *hash_value = fnv32a(data, data_size); + } else if (hash_kind == "fnv64a") { + *hash_value = fnv64a(data, data_size); + } else if (hash_kind == "murmur32") { + *hash_value = gb_murmur32(data, data_size); + } else if (hash_kind == "murmur64") { + *hash_value = gb_murmur64(data, data_size); + } else { + compiler_error("unhandled hash kind: %.*s", LIT(hash_kind)); + } + return true; +} + gb_internal bool check_builtin_procedure_directive(CheckerContext *c, Operand *operand, Ast *call, Type *type_hint) { @@ -1429,6 +1512,30 @@ gb_internal bool check_builtin_procedure_directive(CheckerContext *c, Operand *o operand->type = t_source_code_location; operand->mode = Addressing_Value; + } else if (name == "exists") { + if (ce->args.count != 1) { + error(ce->close, "'#exists' expects 1 argument, got %td", ce->args.count); + return false; + } + + Operand o = {}; + check_expr(c, &o, ce->args[0]); + if (o.mode != Addressing_Constant || !is_type_string(o.type)) { + error(ce->args[0], "'#exists' expected a constant string argument"); + return false; + } + + operand->type = t_untyped_bool; + operand->mode = Addressing_Constant; + + String original_string = o.value.value_string; + LoadFileCache *cache = nullptr; + if (cache_load_file_directive(c, call, original_string, /* err_on_not_found=*/ false, &cache, LoadFileTier_Exists)) { + operand->value = exact_value_bool(cache->exists); + } else { + operand->value = exact_value_bool(false); + } + } else if (name == "load") { return check_load_directive(c, operand, call, type_hint, true) == LoadDirective_Success; } else if (name == "load_directory") { @@ -1480,37 +1587,8 @@ gb_internal bool check_builtin_procedure_directive(CheckerContext *c, Operand *o String original_string = o.value.value_string; String hash_kind = o_hash.value.value_string; - String supported_hashes[] = { - str_lit("adler32"), - str_lit("crc32"), - str_lit("crc64"), - str_lit("fnv32"), - str_lit("fnv64"), - str_lit("fnv32a"), - str_lit("fnv64a"), - str_lit("murmur32"), - str_lit("murmur64"), - }; - - bool hash_found = false; - for (isize i = 0; i < gb_count_of(supported_hashes); i++) { - if (supported_hashes[i] == hash_kind) { - hash_found = true; - break; - } - } - if (!hash_found) { - ERROR_BLOCK(); - error(ce->proc, "Invalid hash kind passed to `#load_hash`, got: %.*s", LIT(hash_kind)); - error_line("\tAvailable hash kinds:\n"); - for (isize i = 0; i < gb_count_of(supported_hashes); i++) { - error_line("\t%.*s\n", LIT(supported_hashes[i])); - } - return false; - } - LoadFileCache *cache = nullptr; - if (cache_load_file_directive(c, call, original_string, true, &cache)) { + if (cache_load_file_directive(c, call, original_string, true, &cache, LoadFileTier_Contents)) { MUTEX_GUARD(&c->info->load_file_mutex); // TODO(bill): make these procedures fast :P u64 hash_value = 0; @@ -1520,26 +1598,9 @@ gb_internal bool check_builtin_procedure_directive(CheckerContext *c, Operand *o } else { u8 *data = cache->data.text; isize file_size = cache->data.len; - if (hash_kind == "adler32") { - hash_value = gb_adler32(data, file_size); - } else if (hash_kind == "crc32") { - hash_value = gb_crc32(data, file_size); - } else if (hash_kind == "crc64") { - hash_value = gb_crc64(data, file_size); - } else if (hash_kind == "fnv32") { - hash_value = gb_fnv32(data, file_size); - } else if (hash_kind == "fnv64") { - hash_value = gb_fnv64(data, file_size); - } else if (hash_kind == "fnv32a") { - hash_value = fnv32a(data, file_size); - } else if (hash_kind == "fnv64a") { - hash_value = fnv64a(data, file_size); - } else if (hash_kind == "murmur32") { - hash_value = gb_murmur32(data, file_size); - } else if (hash_kind == "murmur64") { - hash_value = gb_murmur64(data, file_size); - } else { - compiler_error("unhandled hash kind: %.*s", LIT(hash_kind)); + + if (!check_hash_kind(c, call, hash_kind, data, file_size, &hash_value)) { + return false; } string_map_set(&cache->hashes, hash_kind, hash_value); } @@ -1550,6 +1611,62 @@ gb_internal bool check_builtin_procedure_directive(CheckerContext *c, Operand *o return true; } return false; + } else if (name == "hash") { + if (ce->args.count != 2) { + if (ce->args.count == 0) { + error(ce->close, "'#hash' expects 2 argument, got 0"); + } else { + error(ce->args[0], "'#hash' expects 2 argument, got %td", ce->args.count); + } + return false; + } + + Ast *arg0 = ce->args[0]; + Ast *arg1 = ce->args[1]; + Operand o = {}; + check_expr(c, &o, arg0); + if (o.mode != Addressing_Constant) { + error(arg0, "'#hash' expected a constant string argument"); + return false; + } + + if (!is_type_string(o.type)) { + gbString str = type_to_string(o.type); + error(arg0, "'#hash' expected a constant string, got %s", str); + gb_string_free(str); + return false; + } + + Operand o_hash = {}; + check_expr(c, &o_hash, arg1); + if (o_hash.mode != Addressing_Constant) { + error(arg1, "'#hash' expected a constant string argument"); + return false; + } + + if (!is_type_string(o_hash.type)) { + gbString str = type_to_string(o.type); + error(arg1, "'#hash' expected a constant string, got %s", str); + gb_string_free(str); + return false; + } + gbAllocator a = heap_allocator(); + + GB_ASSERT(o.value.kind == ExactValue_String); + GB_ASSERT(o_hash.value.kind == ExactValue_String); + + String original_string = o.value.value_string; + String hash_kind = o_hash.value.value_string; + + // TODO: Cache hash values based off of string constant and hash kind? + u64 hash_value = 0; + if (check_hash_kind(c, call, hash_kind, original_string.text, original_string.len, &hash_value)) { + operand->type = t_untyped_integer; + operand->mode = Addressing_Constant; + operand->value = exact_value_u64(hash_value); + return true; + } + return false; } else if (name == "assert") { if (ce->args.count != 1 && ce->args.count != 2) { error(call, "'#assert' expects either 1 or 2 arguments, got %td", ce->args.count); @@ -1726,6 +1843,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As case BuiltinProc_objc_register_class: case BuiltinProc_atomic_type_is_lock_free: case BuiltinProc_has_target_feature: + case BuiltinProc_procedure_of: // NOTE(bill): The first arg may be a Type, this will be checked case by case break; @@ -2302,6 +2420,9 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As if (arg_count > max_count) { error(call, "Too many 'swizzle' indices, %td > %td", arg_count, max_count); return false; + } else if (arg_count < 2) { + error(call, "Not enough 'swizzle' indices, %td < 2", arg_count); + return false; } if (type->kind == Type_Array) { @@ -5795,15 +5916,9 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As if (operand->mode != Addressing_Type) { error(operand->expr, "Expected a record type for '%.*s'", LIT(builtin_name)); } else { - Type *bt = base_type(operand->type); - if (bt->kind == Type_Struct) { - if (bt->Struct.polymorphic_params != nullptr) { - operand->value = exact_value_i64(bt->Struct.polymorphic_params->Tuple.variables.count); - } - } else if (bt->kind == Type_Union) { - if (bt->Union.polymorphic_params != nullptr) { - operand->value = exact_value_i64(bt->Union.polymorphic_params->Tuple.variables.count); - } + TypeTuple *tuple = get_record_polymorphic_params(operand->type); + if (tuple) { + operand->value = exact_value_i64(tuple->variables.count); } else { error(operand->expr, "Expected a record type for '%.*s'", LIT(builtin_name)); } @@ -5835,20 +5950,11 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As Entity *param = nullptr; i64 count = 0; - Type *bt = base_type(operand->type); - if (bt->kind == Type_Struct) { - if (bt->Struct.polymorphic_params != nullptr) { - count = bt->Struct.polymorphic_params->Tuple.variables.count; - if (index < count) { - param = bt->Struct.polymorphic_params->Tuple.variables[cast(isize)index]; - } - } - } else if (bt->kind == Type_Union) { - if (bt->Union.polymorphic_params != nullptr) { - count = bt->Union.polymorphic_params->Tuple.variables.count; - if (index < count) { - param = bt->Union.polymorphic_params->Tuple.variables[cast(isize)index]; - } + TypeTuple *tuple = get_record_polymorphic_params(operand->type); + if (tuple) { + count = tuple->variables.count; + if (index < count) { + param = tuple->variables[cast(isize)index]; } } else { error(operand->expr, "Expected a specialized polymorphic record type for '%.*s'", LIT(builtin_name)); @@ -6052,6 +6158,51 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As break; } + case BuiltinProc_procedure_of: + { + Ast *call_expr = unparen_expr(ce->args[0]); + Operand op = {}; + check_expr_base(c, &op, ce->args[0], nullptr); + if (op.mode != Addressing_Value && !(call_expr && call_expr->kind == Ast_CallExpr)) { + error(ce->args[0], "Expected a call expression for '%.*s'", LIT(builtin_name)); + return false; + } + + Ast *proc = call_expr->CallExpr.proc; + Entity *e = entity_of_node(proc); + + if (e == nullptr) { + error(ce->args[0], "Invalid procedure value, expected a regular/specialized procedure"); + return false; + } + + TypeAndValue tav = proc->tav; + + + operand->type = e->type; + operand->mode = Addressing_Value; + operand->value = tav.value; + operand->builtin_id = BuiltinProc_Invalid; + operand->proc_group = nullptr; + + if (tav.mode == Addressing_Builtin) { + operand->mode = tav.mode; + operand->builtin_id = cast(BuiltinProcId)e->Builtin.id; + break; + } + + if (!is_type_proc(e->type)) { + gbString s = type_to_string(e->type); + error(ce->args[0], "Expected a procedure value, got '%s'", s); + gb_string_free(s); + return false; + } + + + ce->entity_procedure_of = e; + break; + } + case BuiltinProc_constant_utf16_cstring: { String value = {}; diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 1ec366ae7..02445cbc6 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -88,11 +88,17 @@ gb_internal Type *check_init_variable(CheckerContext *ctx, Entity *e, Operand *o e->type = t_invalid; return nullptr; } else if (is_type_polymorphic(t)) { - 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)); - e->type = t_invalid; - return nullptr; + Entity *e = entity_of_node(operand->expr); + if (e == nullptr) { + return nullptr; + } + if (e->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)); + e->type = t_invalid; + return nullptr; + } } else if (is_type_empty_union(t)) { gbString str = type_to_string(t); defer (gb_string_free(str)); @@ -479,6 +485,9 @@ gb_internal void check_const_decl(CheckerContext *ctx, Entity *e, Ast *type_expr entity = check_selector(ctx, &operand, init, e->type); } else { check_expr_or_type(ctx, &operand, init, e->type); + if (init->kind == Ast_CallExpr) { + entity = init->CallExpr.entity_procedure_of; + } } switch (operand.mode) { @@ -526,6 +535,7 @@ gb_internal void check_const_decl(CheckerContext *ctx, Entity *e, Ast *type_expr return; } + if (entity != nullptr) { if (e->type != nullptr) { Operand x = {}; @@ -724,9 +734,10 @@ gb_internal Entity *init_entity_foreign_library(CheckerContext *ctx, Entity *e) return nullptr; } -gb_internal String handle_link_name(CheckerContext *ctx, Token token, String link_name, String link_prefix) { +gb_internal String handle_link_name(CheckerContext *ctx, Token token, String link_name, String link_prefix, String link_suffix) { + String original_link_name = link_name; if (link_prefix.len > 0) { - if (link_name.len > 0) { + if (original_link_name.len > 0) { error(token, "'link_name' and 'link_prefix' cannot be used together"); } else { isize len = link_prefix.len + token.string.len; @@ -738,9 +749,28 @@ gb_internal String handle_link_name(CheckerContext *ctx, Token token, String lin link_name = make_string(name, len); } } + + if (link_suffix.len > 0) { + if (original_link_name.len > 0) { + error(token, "'link_name' and 'link_suffix' cannot be used together"); + } else { + String new_name = token.string; + if (link_name != original_link_name) { + new_name = link_name; + } + + isize len = new_name.len + link_suffix.len; + u8 *name = gb_alloc_array(permanent_allocator(), u8, len+1); + gb_memmove(name, &new_name[0], new_name.len); + gb_memmove(name+new_name.len, &link_suffix[0], link_suffix.len); + name[len] = 0; + link_name = make_string(name, len); + } + } return link_name; } + gb_internal void check_objc_methods(CheckerContext *ctx, Entity *e, AttributeContext const &ac) { if (!(ac.objc_name.len || ac.objc_is_class_method || ac.objc_type)) { return; @@ -862,7 +892,7 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) { } TypeProc *pt = &proc_type->Proc; - AttributeContext ac = make_attribute_context(e->Procedure.link_prefix); + AttributeContext ac = make_attribute_context(e->Procedure.link_prefix, e->Procedure.link_suffix); if (d != nullptr) { check_decl_attributes(ctx, d->attributes, proc_decl_attribute, &ac); @@ -1015,7 +1045,7 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) { e->deprecated_message = ac.deprecated_message; e->warning_message = ac.warning_message; - ac.link_name = handle_link_name(ctx, e->token, ac.link_name, ac.link_prefix); + ac.link_name = handle_link_name(ctx, e->token, ac.link_name, ac.link_prefix,ac.link_suffix); if (ac.has_disabled_proc) { if (ac.disabled_proc) { e->flags |= EntityFlag_Disabled; @@ -1115,7 +1145,14 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) { } if (ac.link_name.len > 0) { - e->Procedure.link_name = ac.link_name; + String ln = ac.link_name; + e->Procedure.link_name = ln; + if (ln == "memcpy" || + ln == "memmove" || + ln == "mem_copy" || + ln == "mem_copy_non_overlapping") { + e->Procedure.is_memcpy_like = true; + } } if (ac.deferred_procedure.entity != nullptr) { @@ -1223,7 +1260,7 @@ gb_internal void check_global_variable_decl(CheckerContext *ctx, Entity *&e, Ast } e->flags |= EntityFlag_Visited; - AttributeContext ac = make_attribute_context(e->Variable.link_prefix); + AttributeContext ac = make_attribute_context(e->Variable.link_prefix, e->Variable.link_suffix); ac.init_expr_list_count = init_expr != nullptr ? 1 : 0; DeclInfo *decl = decl_info_of_entity(e); @@ -1244,7 +1281,10 @@ gb_internal void check_global_variable_decl(CheckerContext *ctx, Entity *&e, Ast if (ac.is_static) { error(e->token, "@(static) is not supported for global variables, nor required"); } - ac.link_name = handle_link_name(ctx, e->token, ac.link_name, ac.link_prefix); + if (ac.rodata) { + e->Variable.is_rodata = true; + } + ac.link_name = handle_link_name(ctx, e->token, ac.link_name, ac.link_prefix, ac.link_suffix); if (is_arch_wasm() && e->Variable.thread_local_model.len != 0) { e->Variable.thread_local_model.len = 0; @@ -1330,6 +1370,9 @@ gb_internal void check_global_variable_decl(CheckerContext *ctx, Entity *&e, Ast Operand o = {}; check_expr_with_type_hint(ctx, &o, init_expr, e->type); check_init_variable(ctx, e, &o, str_lit("variable declaration")); + if (e->Variable.is_rodata && o.mode != Addressing_Constant) { + error(o.expr, "Variables declared with @(rodata) must have constant initialization"); + } check_rtti_type_disallowed(e->token, e->type, "A variable declaration is using a type, %s, which has been disallowed"); } diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 8672941c1..359b30276 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -125,6 +125,8 @@ gb_internal Entity *find_polymorphic_record_entity(GenTypesData *found_gen_types gb_internal bool complete_soa_type(Checker *checker, Type *t, bool wait_to_finish); +gb_internal bool check_is_castable_to(CheckerContext *c, Operand *operand, Type *y); + enum LoadDirectiveResult { LoadDirective_Success = 0, LoadDirective_Error = 1, @@ -279,8 +281,20 @@ gb_internal void error_operand_not_expression(Operand *o) { gb_internal void error_operand_no_value(Operand *o) { if (o->mode == Addressing_NoValue) { - gbString err = expr_to_string(o->expr); Ast *x = unparen_expr(o->expr); + + if (x->kind == Ast_CallExpr) { + Ast *p = unparen_expr(x->CallExpr.proc); + if (p->kind == Ast_BasicDirective) { + String tag = p->BasicDirective.name.string; + if (tag == "panic" || + tag == "assert") { + return; + } + } + } + + gbString err = expr_to_string(o->expr); if (x->kind == Ast_CallExpr) { error(o->expr, "'%s' call does not return a value and cannot be used as a value", err); } else { @@ -564,6 +578,7 @@ gb_internal bool find_or_generate_polymorphic_procedure(CheckerContext *old_c, E d->defer_use_checked = false; Entity *entity = alloc_entity_procedure(nullptr, token, final_proc_type, tags); + entity->state.store(EntityState_Resolved); entity->identifier = ident; add_entity_and_decl_info(&nctx, ident, entity, d); @@ -2252,6 +2267,17 @@ gb_internal bool check_representable_as_constant(CheckerContext *c, ExactValue i gb_internal bool check_integer_exceed_suggestion(CheckerContext *c, Operand *o, Type *type, i64 max_bit_size=0) { if (is_type_integer(type) && o->value.kind == ExactValue_Integer) { gbString b = type_to_string(type); + defer (gb_string_free(b)); + + if (is_type_enum(o->type)) { + if (check_is_castable_to(c, o, type)) { + gbString ot = type_to_string(o->type); + error_line("\tSuggestion: Try casting the '%s' expression to '%s'", ot, b); + gb_string_free(ot); + } + return true; + } + i64 sz = type_size_of(type); i64 bit_size = 8*sz; @@ -2301,7 +2327,6 @@ gb_internal bool check_integer_exceed_suggestion(CheckerContext *c, Operand *o, } } - gb_string_free(b); return true; } @@ -2525,7 +2550,7 @@ gb_internal void check_unary_expr(CheckerContext *c, Operand *o, Token op, Ast * error_line("\tSuggestion: Did you want to pass the iterable value to the for statement by pointer to get addressable semantics?\n"); } - if (is_type_map(parent_type)) { + if (parent_type != nullptr && is_type_map(parent_type)) { error_line("\t Prefer doing 'for key, &%.*s in ...'\n", LIT(e->token.string)); } else { error_line("\t Prefer doing 'for &%.*s in ...'\n", LIT(e->token.string)); @@ -3326,6 +3351,9 @@ gb_internal void check_cast(CheckerContext *c, Operand *x, Type *type) { if (is_type_untyped(x->type)) { Type *final_type = type; if (is_const_expr && !is_type_constant_type(type)) { + if (is_type_union(type)) { + convert_to_typed(c, x, type); + } final_type = default_type(x->type); } update_untyped_expr_type(c, x->expr, final_type, true); @@ -3536,6 +3564,9 @@ gb_internal void check_binary_matrix(CheckerContext *c, Token const &op, Operand x->mode = Addressing_Value; if (are_types_identical(xt, yt)) { + if (are_types_identical(x->type, y->type)) { + return; + } if (!is_type_named(x->type) && is_type_named(y->type)) { // prefer the named type x->type = y->type; @@ -4274,7 +4305,8 @@ gb_internal void convert_to_typed(CheckerContext *c, Operand *operand, Type *tar } else { switch (operand->type->Basic.kind) { case Basic_UntypedBool: - if (!is_type_boolean(target_type)) { + if (!is_type_boolean(target_type) && + !is_type_integer(target_type)) { operand->mode = Addressing_Invalid; convert_untyped_error(c, operand, target_type); return; @@ -7319,14 +7351,9 @@ gb_internal CallArgumentError check_polymorphic_record_type(CheckerContext *c, O gbString s = gb_string_make_reserve(heap_allocator(), e->token.string.len+3); s = gb_string_append_fmt(s, "%.*s(", LIT(e->token.string)); - Type *params = nullptr; - switch (bt->kind) { - case Type_Struct: params = bt->Struct.polymorphic_params; break; - case Type_Union: params = bt->Union.polymorphic_params; break; - } - - if (params != nullptr) for_array(i, params->Tuple.variables) { - Entity *v = params->Tuple.variables[i]; + TypeTuple *tuple = get_record_polymorphic_params(e->type); + if (tuple != nullptr) for_array(i, tuple->variables) { + Entity *v = tuple->variables[i]; String name = v->token.string; if (i > 0) { s = gb_string_append_fmt(s, ", "); @@ -7408,13 +7435,15 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c String name = bd->name.string; if ( name == "location" || + name == "exists" || name == "assert" || name == "panic" || name == "defined" || name == "config" || name == "load" || name == "load_directory" || - name == "load_hash" + name == "load_hash" || + name == "hash" ) { operand->mode = Addressing_Builtin; operand->builtin_id = BuiltinProc_DIRECTIVE; @@ -7647,7 +7676,7 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c if (decl->proc_lit) { ast_node(pl, ProcLit, decl->proc_lit); if (pl->inlining == ProcInlining_no_inline) { - error(call, "'#force_inline' cannot be applied to a procedure that has be marked as '#force_no_inline'"); + error(call, "'#force_inline' cannot be applied to a procedure that has been marked as '#force_no_inline'"); } } } @@ -8328,6 +8357,7 @@ gb_internal ExprKind check_basic_directive_expr(CheckerContext *c, Operand *o, A name == "assert" || name == "defined" || name == "config" || + name == "exists" || name == "load" || name == "load_hash" || name == "load_directory" || @@ -8851,6 +8881,10 @@ gb_internal void check_compound_literal_field_values(CheckerContext *c, Slice<As case Type_Array: ft = bt->Array.elem; break; + case Type_BitField: + is_constant = false; + ft = bt->BitField.fields[index]->type; + break; default: GB_PANIC("invalid type: %s", type_to_string(ft)); break; @@ -8877,6 +8911,9 @@ gb_internal void check_compound_literal_field_values(CheckerContext *c, Slice<As case Type_Array: nested_ft = bt->Array.elem; break; + case Type_BitField: + nested_ft = bt->BitField.fields[index]->type; + break; default: GB_PANIC("invalid type %s", type_to_string(nested_ft)); break; diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 866cdb5a1..f2e3b0242 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -501,6 +501,9 @@ gb_internal Type *check_assignment_variable(CheckerContext *ctx, Operand *lhs, O return nullptr; case Addressing_Variable: + if (e && e->kind == Entity_Variable && e->Variable.is_rodata) { + error(lhs->expr, "Assignment to variable '%.*s' marked as @(rodata) is not allowed", LIT(e->token.string)); + } break; case Addressing_MapIndex: { @@ -1252,8 +1255,6 @@ gb_internal void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags error_line("\t%.*s\n", LIT(f->token.string)); } } - error_line("\n"); - error_line("\tSuggestion: Was '#partial switch' wanted?\n"); } } @@ -2020,7 +2021,7 @@ gb_internal void check_value_decl_stmt(CheckerContext *ctx, Ast *node, u32 mod_f // TODO NOTE(bill): This technically checks things multple times - AttributeContext ac = make_attribute_context(ctx->foreign_context.link_prefix); + AttributeContext ac = make_attribute_context(ctx->foreign_context.link_prefix, ctx->foreign_context.link_suffix); check_decl_attributes(ctx, vd->attributes, var_decl_attribute, &ac); for (isize i = 0; i < entity_count; i++) { @@ -2037,7 +2038,7 @@ gb_internal void check_value_decl_stmt(CheckerContext *ctx, Ast *node, u32 mod_f e->type = init_type; e->state = EntityState_Resolved; } - ac.link_name = handle_link_name(ctx, e->token, ac.link_name, ac.link_prefix); + ac.link_name = handle_link_name(ctx, e->token, ac.link_name, ac.link_prefix, ac.link_suffix); if (ac.link_name.len > 0) { e->Variable.link_name = ac.link_name; @@ -2055,6 +2056,13 @@ gb_internal void check_value_decl_stmt(CheckerContext *ctx, Ast *node, u32 mod_f } } } + if (ac.rodata) { + if (ac.is_static) { + e->Variable.is_rodata = true; + } else { + error(e->token, "Only global or @(static) variables can have @(rodata) applied"); + } + } if (ac.thread_local_model != "") { String name = e->token.string; if (name == "_") { @@ -2216,8 +2224,16 @@ gb_internal void check_expr_stmt(CheckerContext *ctx, Ast *node) { } if (do_require) { gbString expr_str = expr_to_string(ce->proc); + defer (gb_string_free(expr_str)); + if (builtin_id) { + String real_name = builtin_procs[builtin_id].name; + if (real_name != make_string(cast(u8 const *)expr_str, gb_string_length(expr_str))) { + error(node, "'%s' ('%.*s.%.*s') requires that its results must be handled", expr_str, + LIT(builtin_proc_pkg_name[builtin_procs[builtin_id].pkg]), LIT(real_name)); + return; + } + } error(node, "'%s' requires that its results must be handled", expr_str); - gb_string_free(expr_str); } return; } else if (expr && expr->kind == Ast_SelectorCallExpr) { @@ -2493,6 +2509,10 @@ gb_internal void check_return_stmt(CheckerContext *ctx, Ast *node) { unsafe_return_error(o, "the address of an indexed variable", f->type); } } + } else if (o.mode == Addressing_Constant && is_type_slice(o.type)) { + ERROR_BLOCK(); + unsafe_return_error(o, "a compound literal of a slice"); + error_line("\tNote: A constant slice value will use the memory of the current stack frame\n"); } } diff --git a/src/check_type.cpp b/src/check_type.cpp index 7ed657bee..c56c8a739 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -564,19 +564,7 @@ gb_internal bool check_record_poly_operand_specialization(CheckerContext *ctx, T gb_internal Entity *find_polymorphic_record_entity(GenTypesData *found_gen_types, isize param_count, Array<Operand> const &ordered_operands) { for (Entity *e : found_gen_types->types) { Type *t = base_type(e->type); - TypeTuple *tuple = nullptr; - switch (t->kind) { - case Type_Struct: - if (t->Struct.polymorphic_params) { - tuple = &t->Struct.polymorphic_params->Tuple; - } - break; - case Type_Union: - if (t->Union.polymorphic_params) { - tuple = &t->Union.polymorphic_params->Tuple; - } - break; - } + TypeTuple *tuple = get_record_polymorphic_params(t); GB_ASSERT_MSG(tuple != nullptr, "%s :: %s", type_to_string(e->type), type_to_string(t)); GB_ASSERT(param_count == tuple->variables.count); @@ -663,6 +651,8 @@ gb_internal void check_struct_type(CheckerContext *ctx, Type *struct_type, Ast * &struct_type->Struct.is_polymorphic, node, poly_operands ); + wait_signal_set(&struct_type->Struct.polymorphic_wait_signal); + struct_type->Struct.is_poly_specialized = check_record_poly_operand_specialization(ctx, struct_type, poly_operands, &struct_type->Struct.is_polymorphic); if (original_type_for_poly) { GB_ASSERT(named_type != nullptr); @@ -712,6 +702,8 @@ gb_internal void check_union_type(CheckerContext *ctx, Type *union_type, Ast *no &union_type->Union.is_polymorphic, node, poly_operands ); + wait_signal_set(&union_type->Union.polymorphic_wait_signal); + union_type->Union.is_poly_specialized = check_record_poly_operand_specialization(ctx, union_type, poly_operands, &union_type->Union.is_polymorphic); if (original_type_for_poly) { GB_ASSERT(named_type != nullptr); @@ -784,7 +776,7 @@ gb_internal void check_union_type(CheckerContext *ctx, Type *union_type, Ast *no } } if (variants.count < 2) { - error(ut->align, "A union with #no_nil must have at least 2 variants"); + error(node, "A union with #no_nil must have at least 2 variants"); } break; } @@ -1457,8 +1449,8 @@ gb_internal bool check_type_specialization_to(CheckerContext *ctx, Type *special s->Struct.polymorphic_params != nullptr && t->Struct.polymorphic_params != nullptr) { - TypeTuple *s_tuple = &s->Struct.polymorphic_params->Tuple; - TypeTuple *t_tuple = &t->Struct.polymorphic_params->Tuple; + TypeTuple *s_tuple = get_record_polymorphic_params(s); + TypeTuple *t_tuple = get_record_polymorphic_params(t); GB_ASSERT(t_tuple->variables.count == s_tuple->variables.count); for_array(i, s_tuple->variables) { Entity *s_e = s_tuple->variables[i]; @@ -1510,8 +1502,8 @@ gb_internal bool check_type_specialization_to(CheckerContext *ctx, Type *special s->Union.polymorphic_params != nullptr && t->Union.polymorphic_params != nullptr) { - TypeTuple *s_tuple = &s->Union.polymorphic_params->Tuple; - TypeTuple *t_tuple = &t->Union.polymorphic_params->Tuple; + TypeTuple *s_tuple = get_record_polymorphic_params(s); + TypeTuple *t_tuple = get_record_polymorphic_params(t); GB_ASSERT(t_tuple->variables.count == s_tuple->variables.count); for_array(i, s_tuple->variables) { Entity *s_e = s_tuple->variables[i]; diff --git a/src/checker.cpp b/src/checker.cpp index 9d44c34dc..852fb89bb 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -3,6 +3,8 @@ #include "entity.cpp" #include "types.cpp" +String get_final_microarchitecture(); + gb_internal void check_expr(CheckerContext *c, Operand *operand, Ast *expression); gb_internal void check_expr_or_type(CheckerContext *c, Operand *operand, Ast *expression, Type *type_hint=nullptr); gb_internal void add_comparison_procedures_for_fields(CheckerContext *c, Type *t); @@ -1016,6 +1018,7 @@ gb_internal void init_universal(void) { {"NetBSD", TargetOs_netbsd}, {"WASI", TargetOs_wasi}, {"JS", TargetOs_js}, + {"Orca", TargetOs_orca}, {"Freestanding", TargetOs_freestanding}, }; @@ -1039,6 +1042,8 @@ gb_internal void init_universal(void) { add_global_enum_constant(fields, "ODIN_ARCH", bc->metrics.arch); add_global_string_constant("ODIN_ARCH_STRING", target_arch_names[bc->metrics.arch]); } + + add_global_string_constant("ODIN_MICROARCH_STRING", get_final_microarchitecture()); { GlobalEnumValue values[BuildMode_COUNT] = { @@ -1130,6 +1135,17 @@ gb_internal void init_universal(void) { add_global_constant("ODIN_COMPILE_TIMESTAMP", t_untyped_integer, exact_value_i64(odin_compile_timestamp())); { + String version = {}; + + #ifdef GIT_SHA + version.text = cast(u8 *)GIT_SHA; + version.len = gb_strlen(GIT_SHA); + #endif + + add_global_string_constant("ODIN_VERSION_HASH", version); + } + + { bool f16_supported = lb_use_new_pass_system(); if (is_arch_wasm()) { f16_supported = false; @@ -1166,6 +1182,18 @@ gb_internal void init_universal(void) { add_global_constant("ODIN_SANITIZER_FLAGS", named_type, exact_value_u64(bc->sanitizer_flags)); } + { + GlobalEnumValue values[5] = { + {"None", -1}, + {"Minimal", 0}, + {"Size", 1}, + {"Speed", 2}, + {"Aggressive", 3}, + }; + + auto fields = add_global_enum_type(str_lit("Odin_Optimization_Mode"), values, gb_count_of(values)); + add_global_enum_constant(fields, "ODIN_OPTIMIZATION_MODE", bc->optimization_level); + } // Builtin Procedures @@ -1283,6 +1311,7 @@ gb_internal void init_checker_info(CheckerInfo *i) { mpsc_init(&i->definition_queue, a); //); // 1<<20); mpsc_init(&i->required_global_variable_queue, a); // 1<<10); mpsc_init(&i->required_foreign_imports_through_force_queue, a); // 1<<10); + mpsc_init(&i->foreign_imports_to_check_fullpaths, a); // 1<<10); mpsc_init(&i->intrinsics_entry_point_usage, a); // 1<<10); // just waste some memory here, even if it probably never used string_map_init(&i->load_directory_cache); @@ -1307,6 +1336,7 @@ gb_internal void destroy_checker_info(CheckerInfo *i) { mpsc_destroy(&i->definition_queue); mpsc_destroy(&i->required_global_variable_queue); mpsc_destroy(&i->required_foreign_imports_through_force_queue); + mpsc_destroy(&i->foreign_imports_to_check_fullpaths); map_destroy(&i->objc_msgSend_types); string_map_destroy(&i->load_file_cache); @@ -1449,6 +1479,10 @@ gb_internal Entity *entity_of_node(Ast *expr) { case_ast_node(cc, CaseClause, expr); return cc->implicit_entity; case_end; + + case_ast_node(ce, CallExpr, expr); + return ce->entity_procedure_of; + case_end; } return nullptr; } @@ -3125,6 +3159,18 @@ gb_internal DECL_ATTRIBUTE_PROC(foreign_block_decl_attribute) { error(elem, "Expected a string value for '%.*s'", LIT(name)); } return true; + } else if (name == "link_suffix") { + if (ev.kind == ExactValue_String) { + String link_suffix = ev.value_string; + if (!is_foreign_name_valid(link_suffix)) { + error(elem, "Invalid link suffix: '%.*s'", LIT(link_suffix)); + } else { + c->foreign_context.link_suffix = link_suffix; + } + } else { + error(elem, "Expected a string value for '%.*s'", LIT(name)); + } + return true; } else if (name == "private") { EntityVisiblityKind kind = EntityVisiblity_PrivateToPackage; if (ev.kind == ExactValue_Invalid) { @@ -3419,6 +3465,18 @@ gb_internal DECL_ATTRIBUTE_PROC(proc_decl_attribute) { error(elem, "Expected a string value for '%.*s'", LIT(name)); } return true; + } else if (name == "link_suffix") { + ExactValue ev = check_decl_attribute_value(c, value); + + if (ev.kind == ExactValue_String) { + ac->link_suffix = ev.value_string; + if (!is_foreign_name_valid(ac->link_suffix)) { + error(elem, "Invalid link suffix: %.*s", LIT(ac->link_suffix)); + } + } else { + error(elem, "Expected a string value for '%.*s'", LIT(name)); + } + return true; } else if (name == "deprecated") { ExactValue ev = check_decl_attribute_value(c, value); @@ -3601,6 +3659,12 @@ gb_internal DECL_ATTRIBUTE_PROC(var_decl_attribute) { } ac->is_static = true; return true; + } else if (name == "rodata") { + if (value != nullptr) { + error(elem, "'rodata' does not have any parameters"); + } + ac->rodata = true; + return true; } else if (name == "thread_local") { ExactValue ev = check_decl_attribute_value(c, value); if (ac->init_expr_list_count > 0) { @@ -3700,6 +3764,17 @@ gb_internal DECL_ATTRIBUTE_PROC(var_decl_attribute) { error(elem, "Expected a string value for '%.*s'", LIT(name)); } return true; + } else if (name == "link_suffix") { + ExactValue ev = check_decl_attribute_value(c, value); + if (ev.kind == ExactValue_String) { + ac->link_suffix = ev.value_string; + if (!is_foreign_name_valid(ac->link_suffix)) { + error(elem, "Invalid link suffix: %.*s", LIT(ac->link_suffix)); + } + } else { + error(elem, "Expected a string value for '%.*s'", LIT(name)); + } + return true; } else if (name == "link_section") { ExactValue ev = check_decl_attribute_value(c, value); if (ev.kind == ExactValue_String) { @@ -3731,6 +3806,7 @@ gb_internal DECL_ATTRIBUTE_PROC(const_decl_attribute) { name == "linkage" || name == "link_name" || name == "link_prefix" || + name == "link_suffix" || false) { error(elem, "@(%.*s) is not supported for compile time constant value declarations", LIT(name)); return true; @@ -3773,8 +3849,10 @@ gb_internal void check_decl_attributes(CheckerContext *c, Array<Ast *> const &at if (attributes.count == 0) return; String original_link_prefix = {}; + String original_link_suffix = {}; if (ac) { original_link_prefix = ac->link_prefix; + original_link_suffix = ac->link_suffix; } StringSet set = {}; @@ -3849,6 +3927,12 @@ gb_internal void check_decl_attributes(CheckerContext *c, Array<Ast *> const &at ac->link_prefix.len = 0; } } + if (ac->link_suffix.text == original_link_suffix.text) { + if (ac->link_name.len > 0) { + ac->link_suffix.text = nullptr; + ac->link_suffix.len = 0; + } + } } } @@ -4143,6 +4227,7 @@ gb_internal void check_collect_value_decl(CheckerContext *c, Ast *decl) { e->Variable.foreign_library_ident = fl; e->Variable.link_prefix = c->foreign_context.link_prefix; + e->Variable.link_suffix = c->foreign_context.link_suffix; } Ast *init_expr = value; @@ -4217,6 +4302,7 @@ gb_internal void check_collect_value_decl(CheckerContext *c, Ast *decl) { } } e->Procedure.link_prefix = c->foreign_context.link_prefix; + e->Procedure.link_suffix = c->foreign_context.link_suffix; GB_ASSERT(cc != ProcCC_Invalid); pl->type->ProcType.calling_convention = cc; @@ -4874,6 +4960,83 @@ gb_internal DECL_ATTRIBUTE_PROC(foreign_import_decl_attribute) { return false; } +gb_internal void check_foreign_import_fullpaths(Checker *c) { + CheckerContext ctx = make_checker_context(c); + + UntypedExprInfoMap untyped = {}; + defer (map_destroy(&untyped)); + + for (Entity *e = nullptr; mpsc_dequeue(&c->info.foreign_imports_to_check_fullpaths, &e); /**/) { + GB_ASSERT(e != nullptr); + GB_ASSERT(e->kind == Entity_LibraryName); + Ast *decl = e->LibraryName.decl; + ast_node(fl, ForeignImportDecl, decl); + + AstFile *f = decl->file(); + + reset_checker_context(&ctx, f, &untyped); + ctx.collect_delayed_decls = false; + + GB_ASSERT(ctx.scope == e->scope); + + if (fl->fullpaths.count == 0) { + String base_dir = dir_from_path(decl->file()->fullpath); + + auto fullpaths = array_make<String>(permanent_allocator(), 0, fl->filepaths.count); + + for (Ast *fp_node : fl->filepaths) { + Operand op = {}; + check_expr(&ctx, &op, fp_node); + if (op.mode != Addressing_Constant && op.value.kind != ExactValue_String) { + gbString s = expr_to_string(op.expr); + error(fp_node, "Expected a constant string value, got '%s'", s); + gb_string_free(s); + continue; + } + if (!is_type_string(op.type)) { + gbString s = type_to_string(op.type); + error(fp_node, "Expected a constant string value, got value of type '%s'", s); + gb_string_free(s); + continue; + } + + String file_str = op.value.value_string; + file_str = string_trim_whitespace(file_str); + + String fullpath = file_str; + if (allow_check_foreign_filepath()) { + String foreign_path = {}; + bool ok = determine_path_from_string(nullptr, decl, base_dir, file_str, &foreign_path, /*use error not syntax_error*/true); + if (ok) { + fullpath = foreign_path; + } + } + array_add(&fullpaths, fullpath); + } + fl->fullpaths = slice_from_array(fullpaths); + } + + for (String const &path : fl->fullpaths) { + String ext = path_extension(path); + if (str_eq_ignore_case(ext, ".c") || + str_eq_ignore_case(ext, ".cpp") || + str_eq_ignore_case(ext, ".cxx") || + str_eq_ignore_case(ext, ".h") || + str_eq_ignore_case(ext, ".hpp") || + str_eq_ignore_case(ext, ".hxx") || + false + ) { + error(fl->token, "With 'foreign import', you cannot import a %.*s file/directory, you must precompile the library and link against that", LIT(ext)); + break; + } + } + + add_untyped_expressions(ctx.info, &untyped); + + e->LibraryName.paths = fl->fullpaths; + } +} + gb_internal void check_add_foreign_import_decl(CheckerContext *ctx, Ast *decl) { if (decl->state_flags & StateFlag_BeenHandled) return; decl->state_flags |= StateFlag_BeenHandled; @@ -4883,59 +5046,26 @@ gb_internal void check_add_foreign_import_decl(CheckerContext *ctx, Ast *decl) { Scope *parent_scope = ctx->scope; GB_ASSERT(parent_scope->flags&ScopeFlag_File); - GB_ASSERT(fl->fullpaths.count > 0); - String fullpath = fl->fullpaths[0]; - String library_name = path_to_entity_name(fl->library_name.string, fullpath); - if (is_blank_ident(library_name)) { - error(fl->token, "File name, %.*s, cannot be as a library name as it is not a valid identifier", LIT(fl->library_name.string)); - return; + String library_name = fl->library_name.string; + if (library_name.len == 0 && fl->fullpaths.count != 0) { + String fullpath = fl->fullpaths[0]; + library_name = path_to_entity_name(fl->library_name.string, fullpath); } - - for (String const &path : fl->fullpaths) { - String ext = path_extension(path); - if (str_eq_ignore_case(ext, ".c") || - str_eq_ignore_case(ext, ".cpp") || - str_eq_ignore_case(ext, ".cxx") || - str_eq_ignore_case(ext, ".h") || - str_eq_ignore_case(ext, ".hpp") || - str_eq_ignore_case(ext, ".hxx") || - false - ) { - error(fl->token, "With 'foreign import', you cannot import a %.*s file directory, you must precompile the library and link against that", LIT(ext)); - break; - } + if (library_name.len == 0 || is_blank_ident(library_name)) { + error(fl->token, "File name, '%.*s', cannot be as a library name as it is not a valid identifier", LIT(library_name)); + return; } - // if (fl->collection_name != "system") { - // char *c_str = gb_alloc_array(heap_allocator(), char, fullpath.len+1); - // defer (gb_free(heap_allocator(), c_str)); - // gb_memmove(c_str, fullpath.text, fullpath.len); - // c_str[fullpath.len] = '\0'; - - // gbFile f = {}; - // gbFileError file_err = gb_file_open(&f, c_str); - // defer (gb_file_close(&f)); - - // switch (file_err) { - // case gbFileError_Invalid: - // error(decl, "Invalid file or cannot be found ('%.*s')", LIT(fullpath)); - // return; - // case gbFileError_NotExists: - // error(decl, "File cannot be found ('%.*s')", LIT(fullpath)); - // return; - // } - // } - GB_ASSERT(fl->library_name.pos.line != 0); fl->library_name.string = library_name; Entity *e = alloc_entity_library_name(parent_scope, fl->library_name, t_invalid, fl->fullpaths, library_name); + e->LibraryName.decl = decl; add_entity_flags_from_file(ctx, e, parent_scope); add_entity(ctx, parent_scope, nullptr, e); - AttributeContext ac = {}; check_decl_attributes(ctx, fl->attributes, foreign_import_decl_attribute, &ac); if (ac.require_declaration) { @@ -4950,12 +5080,8 @@ gb_internal void check_add_foreign_import_decl(CheckerContext *ctx, Ast *decl) { e->LibraryName.extra_linker_flags = extra_linker_flags; } - if (has_asm_extension(fullpath)) { - if (build_context.metrics.arch != TargetArch_amd64 && build_context.metrics.os != TargetOs_darwin) { - error(decl, "Assembly files are not yet supported on this platform: %.*s_%.*s", - LIT(target_os_names[build_context.metrics.os]), LIT(target_arch_names[build_context.metrics.arch])); - } - } + mpsc_enqueue(&ctx->info->foreign_imports_to_check_fullpaths, e); + } // Returns true if a new package is present @@ -5763,35 +5889,6 @@ gb_internal void remove_neighbouring_duplicate_entires_from_sorted_array(Array<E gb_internal void check_test_procedures(Checker *c) { array_sort(c->info.testing_procedures, init_procedures_cmp); remove_neighbouring_duplicate_entires_from_sorted_array(&c->info.testing_procedures); - - if (build_context.test_names.entries.count == 0) { - return; - } - - AstPackage *pkg = c->info.init_package; - Scope *s = pkg->scope; - - for (String const &name : build_context.test_names) { - Entity *e = scope_lookup(s, name); - if (e == nullptr) { - Token tok = {}; - if (pkg->files.count != 0) { - tok = pkg->files[0]->tokens[0]; - } - error(tok, "Unable to find the test '%.*s' in 'package %.*s' ", LIT(name), LIT(pkg->name)); - } - } - - for (isize i = 0; i < c->info.testing_procedures.count; /**/) { - Entity *e = c->info.testing_procedures[i]; - String name = e->token.string; - if (!string_set_exists(&build_context.test_names, name)) { - array_ordered_remove(&c->info.testing_procedures, i); - } else { - i += 1; - } - } - } @@ -6317,6 +6414,9 @@ gb_internal void check_parsed_files(Checker *c) { TIME_SECTION("check procedure bodies"); check_procedure_bodies(c); + TIME_SECTION("check foreign import fullpaths"); + check_foreign_import_fullpaths(c); + TIME_SECTION("add entities from procedure bodies"); check_merge_queues_into_arrays(c); diff --git a/src/checker.hpp b/src/checker.hpp index 2ade9312e..492a64fb6 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -51,6 +51,12 @@ enum StmtFlag { enum BuiltinProcPkg { BuiltinProcPkg_builtin, BuiltinProcPkg_intrinsics, + BuiltinProcPkg_COUNT +}; + +String builtin_proc_pkg_name[BuiltinProcPkg_COUNT] = { + str_lit("builtin"), + str_lit("intrinsics"), }; struct BuiltinProc { @@ -112,6 +118,7 @@ enum InstrumentationFlag : i32 { struct AttributeContext { String link_name; String link_prefix; + String link_suffix; String link_section; String linkage; isize init_expr_list_count; @@ -132,6 +139,7 @@ struct AttributeContext { bool entry_point_only : 1; bool instrumentation_enter : 1; bool instrumentation_exit : 1; + bool rodata : 1; u32 optimization_mode; // ProcedureOptimizationMode i64 foreign_import_priority_index; String extra_linker_flags; @@ -146,9 +154,10 @@ struct AttributeContext { String enable_target_feature; // will be enabled for the procedure only }; -gb_internal gb_inline AttributeContext make_attribute_context(String link_prefix) { +gb_internal gb_inline AttributeContext make_attribute_context(String link_prefix, String link_suffix) { AttributeContext ac = {}; ac.link_prefix = link_prefix; + ac.link_suffix = link_suffix; return ac; } @@ -302,6 +311,7 @@ struct ForeignContext { Ast * curr_library; ProcCallingConvention default_cc; String link_prefix; + String link_suffix; EntityVisiblityKind visibility_kind; }; @@ -333,7 +343,16 @@ struct ObjcMsgData { ObjcMsgKind kind; Type *proc_type; }; + +enum LoadFileTier { + LoadFileTier_Invalid, + LoadFileTier_Exists, + LoadFileTier_Contents, +}; + struct LoadFileCache { + LoadFileTier tier; + bool exists; String path; gbFileError file_error; String data; @@ -414,6 +433,7 @@ struct CheckerInfo { MPSCQueue<Entity *> entity_queue; MPSCQueue<Entity *> required_global_variable_queue; MPSCQueue<Entity *> required_foreign_imports_through_force_queue; + MPSCQueue<Entity *> foreign_imports_to_check_fullpaths; MPSCQueue<Ast *> intrinsics_entry_point_usage; @@ -434,6 +454,8 @@ struct CheckerInfo { BlockingMutex load_directory_mutex; StringMap<LoadDirectoryCache *> load_directory_cache; PtrMap<Ast *, LoadDirectoryCache *> load_directory_map; // Key: Ast_CallExpr * + + }; struct CheckerContext { diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp index 5f98bb7b3..35acad42f 100644 --- a/src/checker_builtin_procs.hpp +++ b/src/checker_builtin_procs.hpp @@ -299,6 +299,8 @@ BuiltinProc__type_simple_boolean_end, BuiltinProc__type_end, + BuiltinProc_procedure_of, + BuiltinProc___entry_point, BuiltinProc_objc_send, @@ -614,6 +616,8 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, + {STR_LIT("procedure_of"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("__entry_point"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, {STR_LIT("objc_send"), 3, true, Expr_Expr, BuiltinProcPkg_intrinsics, false, true}, diff --git a/src/entity.cpp b/src/entity.cpp index 8a7417006..8f55c1faf 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -223,12 +223,14 @@ struct Entity { Ast * foreign_library_ident; String link_name; String link_prefix; + String link_suffix; String link_section; CommentGroup *docs; CommentGroup *comment; bool is_foreign; bool is_export; bool is_global; + bool is_rodata; } Variable; struct { Type * type_parameter_specialization; @@ -243,6 +245,7 @@ struct Entity { Ast * foreign_library_ident; String link_name; String link_prefix; + String link_suffix; DeferredProcedure deferred_procedure; struct GenProcsData *gen_procs; @@ -253,6 +256,7 @@ struct Entity { bool generated_from_polymorphic : 1; bool entry_point_only : 1; bool has_instrumentation : 1; + bool is_memcpy_like : 1; } Procedure; struct { Array<Entity *> entities; @@ -266,6 +270,7 @@ struct Entity { Scope *scope; } ImportName; struct { + Ast *decl; Slice<String> paths; String name; i64 priority_index; diff --git a/src/error.cpp b/src/error.cpp index da444e998..03d96219b 100644 --- a/src/error.cpp +++ b/src/error.cpp @@ -390,8 +390,6 @@ gb_internal void error_va(TokenPos const &pos, TokenPos end, char const *fmt, va error_out_empty(); } else { error_out_pos(pos); - } - if (has_ansi_terminal_colours()) { error_out_coloured("Error: ", TerminalStyle_Normal, TerminalColour_Red); } error_out_va(fmt, va); @@ -407,29 +405,31 @@ gb_internal void warning_va(TokenPos const &pos, TokenPos end, char const *fmt, error_va(pos, end, fmt, va); return; } + if (global_ignore_warnings()) { + return; + } + global_error_collector.warning_count.fetch_add(1); mutex_lock(&global_error_collector.mutex); push_error_value(pos, ErrorValue_Warning); - if (!global_ignore_warnings()) { - if (pos.line == 0) { + if (pos.line == 0) { + error_out_empty(); + error_out_coloured("Warning: ", TerminalStyle_Normal, TerminalColour_Yellow); + error_out_va(fmt, va); + error_out("\n"); + } else { + // global_error_collector.prev = pos; + if (json_errors()) { error_out_empty(); - error_out_coloured("Warning: ", TerminalStyle_Normal, TerminalColour_Yellow); - error_out_va(fmt, va); - error_out("\n"); } else { - // global_error_collector.prev = pos; - if (json_errors()) { - error_out_empty(); - } else { - error_out_pos(pos); - } + error_out_pos(pos); error_out_coloured("Warning: ", TerminalStyle_Normal, TerminalColour_Yellow); - error_out_va(fmt, va); - error_out("\n"); - show_error_on_line(pos, end); } + error_out_va(fmt, va); + error_out("\n"); + show_error_on_line(pos, end); } try_pop_error_value(); mutex_unlock(&global_error_collector.mutex); @@ -516,7 +516,7 @@ gb_internal void syntax_error_with_verbose_va(TokenPos const &pos, TokenPos end, if (pos.line == 0) { error_out_empty(); - error_out_coloured("Syntax_Error: ", TerminalStyle_Normal, TerminalColour_Red); + error_out_coloured("Syntax Error: ", TerminalStyle_Normal, TerminalColour_Red); error_out_va(fmt, va); error_out("\n"); } else { @@ -527,7 +527,7 @@ gb_internal void syntax_error_with_verbose_va(TokenPos const &pos, TokenPos end, error_out_pos(pos); } if (has_ansi_terminal_colours()) { - error_out_coloured("Syntax_Error: ", TerminalStyle_Normal, TerminalColour_Red); + error_out_coloured("Syntax Error: ", TerminalStyle_Normal, TerminalColour_Red); } error_out_va(fmt, va); error_out("\n"); @@ -544,30 +544,31 @@ gb_internal void syntax_warning_va(TokenPos const &pos, TokenPos end, char const syntax_error_va(pos, end, fmt, va); return; } + if (global_ignore_warnings()) { + return; + } mutex_lock(&global_error_collector.mutex); global_error_collector.warning_count++; push_error_value(pos, ErrorValue_Warning); - if (!global_ignore_warnings()) { - if (pos.line == 0) { + if (pos.line == 0) { + error_out_empty(); + error_out_coloured("Syntax Warning: ", TerminalStyle_Normal, TerminalColour_Yellow); + error_out_va(fmt, va); + error_out("\n"); + } else { + // global_error_collector.prev = pos; + if (json_errors()) { error_out_empty(); - error_out_coloured("Syntax Warning: ", TerminalStyle_Normal, TerminalColour_Yellow); - error_out_va(fmt, va); - error_out("\n"); } else { - // global_error_collector.prev = pos; - if (json_errors()) { - error_out_empty(); - } else { - error_out_pos(pos); - } - error_out_coloured("Syntax Warning: ", TerminalStyle_Normal, TerminalColour_Yellow); - error_out_va(fmt, va); - error_out("\n"); - // show_error_on_line(pos, end); + error_out_pos(pos); } + error_out_coloured("Syntax Warning: ", TerminalStyle_Normal, TerminalColour_Yellow); + error_out_va(fmt, va); + error_out("\n"); + // show_error_on_line(pos, end); } try_pop_error_value(); @@ -838,4 +839,4 @@ gb_internal void print_all_errors(void) { gb_file_write(f, res, gb_string_length(res)); errors_already_printed = true; -}
\ No newline at end of file +} diff --git a/src/gb/gb.h b/src/gb/gb.h index 17d5e97d1..22a30a04b 100644 --- a/src/gb/gb.h +++ b/src/gb/gb.h @@ -256,6 +256,7 @@ extern "C" { #if defined(GB_SYSTEM_NETBSD) #include <stdio.h> + #include <lwp.h> #define lseek64 lseek #endif @@ -3027,6 +3028,8 @@ gb_inline u32 gb_thread_current_id(void) { thread_id = find_thread(NULL); #elif defined(GB_SYSTEM_FREEBSD) thread_id = pthread_getthreadid_np(); +#elif defined(GB_SYSTEM_NETBSD) + thread_id = (u32)_lwp_self(); #else #error Unsupported architecture for gb_thread_current_id() #endif diff --git a/src/linker.cpp b/src/linker.cpp index c41f10593..25c54a6ab 100644 --- a/src/linker.cpp +++ b/src/linker.cpp @@ -13,6 +13,7 @@ struct LinkerData { }; gb_internal i32 system_exec_command_line_app(char const *name, char const *fmt, ...); +gb_internal bool system_exec_command_line_app_output(char const *command, gbString *output); #if defined(GB_SYSTEM_OSX) gb_internal void linker_enable_system_library_linking(LinkerData *ld) { @@ -69,15 +70,40 @@ gb_internal i32 linker_stage(LinkerData *gen) { if (is_arch_wasm()) { timings_start_section(timings, str_lit("wasm-ld")); + gbString extra_orca_flags = gb_string_make(temporary_allocator(), ""); + + gbString inputs = gb_string_make(temporary_allocator(), ""); + inputs = gb_string_append_fmt(inputs, "\"%.*s.o\"", LIT(output_filename)); + + if (build_context.metrics.os == TargetOs_orca) { + gbString orca_sdk_path = gb_string_make(temporary_allocator(), ""); + if (!system_exec_command_line_app_output("orca sdk-path", &orca_sdk_path)) { + gb_printf_err("executing `orca sdk-path` failed, make sure Orca is installed and added to your path\n"); + return 1; + } + if (gb_string_length(orca_sdk_path) == 0) { + gb_printf_err("executing `orca sdk-path` did not produce output\n"); + return 1; + } + inputs = gb_string_append_fmt(inputs, " \"%s/orca-libc/lib/crt1.o\" \"%s/orca-libc/lib/libc.o\"", orca_sdk_path, orca_sdk_path); + + extra_orca_flags = gb_string_append_fmt(extra_orca_flags, " -L \"%s/bin\" -lorca_wasm --export-dynamic", orca_sdk_path); + } + + #if defined(GB_SYSTEM_WINDOWS) result = system_exec_command_line_app("wasm-ld", - "\"%.*s\\bin\\wasm-ld\" \"%.*s.o\" -o \"%.*s\" %.*s %.*s", + "\"%.*s\\bin\\wasm-ld\" %s -o \"%.*s\" %.*s %.*s %s", LIT(build_context.ODIN_ROOT), - LIT(output_filename), LIT(output_filename), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags)); + inputs, LIT(output_filename), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags), + extra_orca_flags); #else result = system_exec_command_line_app("wasm-ld", - "wasm-ld \"%.*s.o\" -o \"%.*s\" %.*s %.*s", - LIT(output_filename), LIT(output_filename), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags)); + "wasm-ld %s -o \"%.*s\" %.*s %.*s %s", + inputs, LIT(output_filename), + LIT(build_context.link_flags), + LIT(build_context.extra_linker_flags), + extra_orca_flags); #endif return result; } diff --git a/src/llvm_abi.cpp b/src/llvm_abi.cpp index 85a16d321..1f7a39447 100644 --- a/src/llvm_abi.cpp +++ b/src/llvm_abi.cpp @@ -900,7 +900,15 @@ namespace lbAbiAmd64SysV { } switch (LLVMGetTypeKind(t)) { - case LLVMIntegerTypeKind: + case LLVMIntegerTypeKind: { + i64 s = t_size; + while (s > 0) { + unify(cls, ix + off/8, RegClass_Int); + off += 8; + s -= 8; + } + break; + } case LLVMPointerTypeKind: case LLVMHalfTypeKind: unify(cls, ix + off/8, RegClass_Int); diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 03c17a8bb..04c4ce244 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -9,6 +9,11 @@ #endif +#ifndef LLVM_IGNORE_VERIFICATION +#define LLVM_IGNORE_VERIFICATION 0 +#endif + + #include "llvm_backend.hpp" #include "llvm_abi.cpp" #include "llvm_backend_opt.cpp" @@ -1125,6 +1130,53 @@ gb_internal void lb_finalize_objc_names(lbProcedure *p) { lb_end_procedure_body(p); } +gb_internal void lb_verify_function(lbModule *m, lbProcedure *p, bool dump_ll=false) { + if (LLVM_IGNORE_VERIFICATION) { + return; + } + + if (!m->debug_builder && LLVMVerifyFunction(p->value, LLVMReturnStatusAction)) { + char *llvm_error = nullptr; + + gb_printf_err("LLVM CODE GEN FAILED FOR PROCEDURE: %.*s\n", LIT(p->name)); + LLVMDumpValue(p->value); + gb_printf_err("\n"); + if (dump_ll) { + gb_printf_err("\n\n\n"); + String filepath_ll = lb_filepath_ll_for_module(m); + if (LLVMPrintModuleToFile(m->mod, cast(char const *)filepath_ll.text, &llvm_error)) { + gb_printf_err("LLVM Error: %s\n", llvm_error); + } + } + LLVMVerifyFunction(p->value, LLVMPrintMessageAction); + exit_with_errors(); + } +} + +gb_internal WORKER_TASK_PROC(lb_llvm_module_verification_worker_proc) { + char *llvm_error = nullptr; + defer (LLVMDisposeMessage(llvm_error)); + lbModule *m = cast(lbModule *)data; + + if (LLVMVerifyModule(m->mod, LLVMReturnStatusAction, &llvm_error)) { + gb_printf_err("LLVM Error:\n%s\n", llvm_error); + if (build_context.keep_temp_files) { + TIME_SECTION("LLVM Print Module to File"); + String filepath_ll = lb_filepath_ll_for_module(m); + if (LLVMPrintModuleToFile(m->mod, cast(char const *)filepath_ll.text, &llvm_error)) { + gb_printf_err("LLVM Error: %s\n", llvm_error); + exit_with_errors(); + return false; + } + } + exit_with_errors(); + return 1; + } + return 0; +} + + + gb_internal lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProcedure *objc_names, Array<lbGlobalVariable> &global_variables) { // Startup Runtime Type *proc_type = alloc_type_proc(nullptr, nullptr, 0, nullptr, 0, false, ProcCC_Odin); @@ -1160,6 +1212,10 @@ gb_internal lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProc if (is_type_untyped_nil(init.type)) { LLVMSetInitializer(var.var.value, LLVMConstNull(global_type)); var.is_initialized = true; + + if (e->Variable.is_rodata) { + LLVMSetGlobalConstant(var.var.value, true); + } continue; } GB_PANIC("Invalid init value, got %s", expr_to_string(init_expr)); @@ -1174,6 +1230,10 @@ gb_internal lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProc } LLVMSetInitializer(var.var.value, init.value); var.is_initialized = true; + + if (e->Variable.is_rodata) { + LLVMSetGlobalConstant(var.var.value, true); + } continue; } } else { @@ -1206,8 +1266,9 @@ gb_internal lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProc var.is_initialized = true; } + + } - CheckerInfo *info = main_module->gen->info; for (Entity *e : info->init_procedures) { @@ -1218,13 +1279,7 @@ gb_internal lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProc lb_end_procedure_body(p); - if (!main_module->debug_builder && LLVMVerifyFunction(p->value, LLVMReturnStatusAction)) { - gb_printf_err("LLVM CODE GEN FAILED FOR PROCEDURE: %s\n", "main"); - LLVMDumpValue(p->value); - gb_printf_err("\n\n\n\n"); - LLVMVerifyFunction(p->value, LLVMAbortProcessAction); - } - + lb_verify_function(main_module, p); return p; } @@ -1247,31 +1302,21 @@ gb_internal lbProcedure *lb_create_cleanup_runtime(lbModule *main_module) { // C lb_end_procedure_body(p); - if (!main_module->debug_builder && LLVMVerifyFunction(p->value, LLVMReturnStatusAction)) { - gb_printf_err("LLVM CODE GEN FAILED FOR PROCEDURE: %s\n", "main"); - LLVMDumpValue(p->value); - gb_printf_err("\n\n\n\n"); - LLVMVerifyFunction(p->value, LLVMAbortProcessAction); - } - + lb_verify_function(main_module, p); return p; } gb_internal WORKER_TASK_PROC(lb_generate_procedures_and_types_per_module) { lbModule *m = cast(lbModule *)data; - for (Entity *e : m->global_procedures_and_types_to_create) { - if (e->kind == Entity_TypeName) { - (void)lb_get_entity_name(m, e); - lb_type(m, e->type); - } + for (Entity *e : m->global_types_to_create) { + (void)lb_get_entity_name(m, e); + (void)lb_type(m, e->type); } - for (Entity *e : m->global_procedures_and_types_to_create) { - if (e->kind == Entity_Procedure) { - (void)lb_get_entity_name(m, e); - array_add(&m->procedures_to_generate, lb_create_procedure(m, e)); - } + for (Entity *e : m->global_procedures_to_create) { + (void)lb_get_entity_name(m, e); + array_add(&m->procedures_to_generate, lb_create_procedure(m, e)); } return 0; } @@ -1325,16 +1370,24 @@ gb_internal void lb_create_global_procedures_and_types(lbGenerator *gen, Checker m = lb_module_of_entity(gen, e); } - array_add(&m->global_procedures_and_types_to_create, e); + if (e->kind == Entity_Procedure) { + array_add(&m->global_procedures_to_create, e); + } else if (e->kind == Entity_TypeName) { + array_add(&m->global_types_to_create, e); + } } - for (auto const &entry : gen->modules) { - lbModule *m = entry.value; - if (do_threading) { + if (do_threading) { + for (auto const &entry : gen->modules) { + lbModule *m = entry.value; thread_pool_add_task(lb_generate_procedures_and_types_per_module, m); - } else { + } + } else { + for (auto const &entry : gen->modules) { + lbModule *m = entry.value; lb_generate_procedures_and_types_per_module(m); } + } thread_pool_wait(); @@ -2365,16 +2418,19 @@ gb_internal WORKER_TASK_PROC(lb_generate_procedures_worker_proc) { } gb_internal void lb_generate_procedures(lbGenerator *gen, bool do_threading) { - for (auto const &entry : gen->modules) { - lbModule *m = entry.value; - if (do_threading) { + if (do_threading) { + for (auto const &entry : gen->modules) { + lbModule *m = entry.value; thread_pool_add_task(lb_generate_procedures_worker_proc, m); - } else { + } + + thread_pool_wait(); + } else { + for (auto const &entry : gen->modules) { + lbModule *m = entry.value; lb_generate_procedures_worker_proc(m); } } - - thread_pool_wait(); } gb_internal WORKER_TASK_PROC(lb_generate_missing_procedures_to_check_worker_proc) { @@ -2388,17 +2444,20 @@ gb_internal WORKER_TASK_PROC(lb_generate_missing_procedures_to_check_worker_proc } gb_internal void lb_generate_missing_procedures(lbGenerator *gen, bool do_threading) { - for (auto const &entry : gen->modules) { - lbModule *m = entry.value; - // NOTE(bill): procedures may be added during generation - if (do_threading) { + if (do_threading) { + for (auto const &entry : gen->modules) { + lbModule *m = entry.value; + // NOTE(bill): procedures may be added during generation thread_pool_add_task(lb_generate_missing_procedures_to_check_worker_proc, m); - } else { + } + thread_pool_wait(); + } else { + for (auto const &entry : gen->modules) { + lbModule *m = entry.value; + // NOTE(bill): procedures may be added during generation lb_generate_missing_procedures_to_check_worker_proc(m); } } - - thread_pool_wait(); } gb_internal void lb_debug_info_complete_types_and_finalize(lbGenerator *gen) { @@ -2411,32 +2470,45 @@ gb_internal void lb_debug_info_complete_types_and_finalize(lbGenerator *gen) { } gb_internal void lb_llvm_function_passes(lbGenerator *gen, bool do_threading) { - for (auto const &entry : gen->modules) { - lbModule *m = entry.value; - if (do_threading) { + if (do_threading) { + for (auto const &entry : gen->modules) { + lbModule *m = entry.value; thread_pool_add_task(lb_llvm_function_pass_per_module, m); - } else { + } + thread_pool_wait(); + } else { + for (auto const &entry : gen->modules) { + lbModule *m = entry.value; lb_llvm_function_pass_per_module(m); } } - thread_pool_wait(); } gb_internal void lb_llvm_module_passes(lbGenerator *gen, bool do_threading) { - for (auto const &entry : gen->modules) { - lbModule *m = entry.value; - auto wd = gb_alloc_item(permanent_allocator(), lbLLVMModulePassWorkerData); - wd->m = m; - wd->target_machine = m->target_machine; + if (do_threading) { + for (auto const &entry : gen->modules) { + lbModule *m = entry.value; + auto wd = gb_alloc_item(permanent_allocator(), lbLLVMModulePassWorkerData); + wd->m = m; + wd->target_machine = m->target_machine; - if (do_threading) { - thread_pool_add_task(lb_llvm_module_pass_worker_proc, wd); - } else { + if (do_threading) { + thread_pool_add_task(lb_llvm_module_pass_worker_proc, wd); + } else { + lb_llvm_module_pass_worker_proc(wd); + } + } + thread_pool_wait(); + } else { + for (auto const &entry : gen->modules) { + lbModule *m = entry.value; + auto wd = gb_alloc_item(permanent_allocator(), lbLLVMModulePassWorkerData); + wd->m = m; + wd->target_machine = m->target_machine; lb_llvm_module_pass_worker_proc(wd); } } - thread_pool_wait(); } gb_internal String lb_filepath_ll_for_module(lbModule *m) { @@ -2514,40 +2586,27 @@ gb_internal String lb_filepath_obj_for_module(lbModule *m) { return concatenate_strings(permanent_allocator(), path, ext); } -gb_internal WORKER_TASK_PROC(lb_llvm_module_verification_worker_proc) { - char *llvm_error = nullptr; - defer (LLVMDisposeMessage(llvm_error)); - lbModule *m = cast(lbModule *)data; - if (LLVMVerifyModule(m->mod, LLVMReturnStatusAction, &llvm_error)) { - gb_printf_err("LLVM Error:\n%s\n", llvm_error); - if (build_context.keep_temp_files) { - TIME_SECTION("LLVM Print Module to File"); - String filepath_ll = lb_filepath_ll_for_module(m); - if (LLVMPrintModuleToFile(m->mod, cast(char const *)filepath_ll.text, &llvm_error)) { - gb_printf_err("LLVM Error: %s\n", llvm_error); - exit_with_errors(); - return false; - } - } - exit_with_errors(); - return 1; - } - return 0; -} - gb_internal bool lb_llvm_module_verification(lbGenerator *gen, bool do_threading) { - for (auto const &entry : gen->modules) { - lbModule *m = entry.value; - if (do_threading) { + if (LLVM_IGNORE_VERIFICATION) { + return true; + } + + if (do_threading) { + for (auto const &entry : gen->modules) { + lbModule *m = entry.value; thread_pool_add_task(lb_llvm_module_verification_worker_proc, m); - } else { + } + thread_pool_wait(); + + } else { + for (auto const &entry : gen->modules) { + lbModule *m = entry.value; if (lb_llvm_module_verification_worker_proc(m)) { return false; } } } - thread_pool_wait(); return true; } @@ -2768,12 +2827,7 @@ gb_internal lbProcedure *lb_create_main_procedure(lbModule *m, lbProcedure *star } - if (!m->debug_builder && LLVMVerifyFunction(p->value, LLVMReturnStatusAction)) { - gb_printf_err("LLVM CODE GEN FAILED FOR PROCEDURE: %s\n", "main"); - LLVMDumpValue(p->value); - gb_printf_err("\n\n\n\n"); - LLVMVerifyFunction(p->value, LLVMAbortProcessAction); - } + lb_verify_function(m, p); lb_run_function_pass_manager(default_function_pass_manager, p, lbFunctionPassManager_default); return p; @@ -2794,28 +2848,11 @@ gb_internal void lb_generate_procedure(lbModule *m, lbProcedure *p) { lb_end_procedure(p); // Add Flags - if (p->body != nullptr) { - if (p->name == "memcpy" || p->name == "memmove" || - p->name == "runtime.mem_copy" || p->name == "mem_copy_non_overlapping" || - string_starts_with(p->name, str_lit("llvm.memcpy")) || - string_starts_with(p->name, str_lit("llvm.memmove"))) { - p->flags |= lbProcedureFlag_WithoutMemcpyPass; - } + if (p->entity && p->entity->kind == Entity_Procedure && p->entity->Procedure.is_memcpy_like) { + p->flags |= lbProcedureFlag_WithoutMemcpyPass; } - if (!m->debug_builder && LLVMVerifyFunction(p->value, LLVMReturnStatusAction)) { - char *llvm_error = nullptr; - - gb_printf_err("LLVM CODE GEN FAILED FOR PROCEDURE: %.*s\n", LIT(p->name)); - LLVMDumpValue(p->value); - gb_printf_err("\n\n\n\n"); - String filepath_ll = lb_filepath_ll_for_module(m); - if (LLVMPrintModuleToFile(m->mod, cast(char const *)filepath_ll.text, &llvm_error)) { - gb_printf_err("LLVM Error: %s\n", llvm_error); - } - LLVMVerifyFunction(p->value, LLVMPrintMessageAction); - exit_with_errors(); - } + lb_verify_function(m, p, true); } @@ -3210,14 +3247,21 @@ gb_internal bool lb_generate_code(lbGenerator *gen) { lbValue init = lb_const_value(m, tav.type, v); LLVMSetInitializer(g.value, init.value); var.is_initialized = true; + if (e->kind == Entity_Variable && e->Variable.is_rodata) { + LLVMSetGlobalConstant(g.value, true); + } } } } if (!var.is_initialized && is_type_untyped_nil(tav.type)) { var.is_initialized = true; + if (e->kind == Entity_Variable && e->Variable.is_rodata) { + LLVMSetGlobalConstant(g.value, true); + } } + } else if (e->kind == Entity_Variable && e->Variable.is_rodata) { + LLVMSetGlobalConstant(g.value, true); } - array_add(&global_variables, var); lb_add_entity(m, e, g); diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index 9f7bc8843..447e93d42 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -181,7 +181,8 @@ struct lbModule { std::atomic<u32> nested_type_name_guid; Array<lbProcedure *> procedures_to_generate; - Array<Entity *> global_procedures_and_types_to_create; + Array<Entity *> global_procedures_to_create; + Array<Entity *> global_types_to_create; lbProcedure *curr_procedure; diff --git a/src/llvm_backend_debug.cpp b/src/llvm_backend_debug.cpp index 2654a1d28..f1ace5f06 100644 --- a/src/llvm_backend_debug.cpp +++ b/src/llvm_backend_debug.cpp @@ -46,6 +46,15 @@ gb_internal LLVMMetadataRef lb_debug_end_location_from_ast(lbProcedure *p, Ast * return lb_debug_location_from_token_pos(p, ast_end_token(node).pos); } +gb_internal void lb_debug_file_line(lbModule *m, Ast *node, LLVMMetadataRef *file, unsigned *line) { + if (*file == nullptr) { + if (node) { + *file = lb_get_llvm_metadata(m, node->file()); + *line = cast(unsigned)ast_token(node).pos.line; + } + } +} + gb_internal LLVMMetadataRef lb_debug_type_internal_proc(lbModule *m, Type *type) { i64 size = type_size_of(type); // Check size gb_unused(size); @@ -117,6 +126,8 @@ gb_internal LLVMMetadataRef lb_debug_basic_struct(lbModule *m, String const &nam gb_internal LLVMMetadataRef lb_debug_struct(lbModule *m, Type *type, Type *bt, String name, LLVMMetadataRef scope, LLVMMetadataRef file, unsigned line) { GB_ASSERT(bt->kind == Type_Struct); + lb_debug_file_line(m, bt->Struct.node, &file, &line); + unsigned tag = DW_TAG_structure_type; if (is_type_raw_union(bt)) { tag = DW_TAG_union_type; @@ -336,6 +347,8 @@ gb_internal LLVMMetadataRef lb_debug_union(lbModule *m, Type *type, String name, Type *bt = base_type(type); GB_ASSERT(bt->kind == Type_Union); + lb_debug_file_line(m, bt->Union.node, &file, &line); + u64 size_in_bits = 8*type_size_of(bt); u32 align_in_bits = 8*cast(u32)type_align_of(bt); @@ -415,6 +428,8 @@ gb_internal LLVMMetadataRef lb_debug_bitset(lbModule *m, Type *type, String name Type *bt = base_type(type); GB_ASSERT(bt->kind == Type_BitSet); + lb_debug_file_line(m, bt->BitSet.node, &file, &line); + u64 size_in_bits = 8*type_size_of(bt); u32 align_in_bits = 8*cast(u32)type_align_of(bt); @@ -494,6 +509,8 @@ gb_internal LLVMMetadataRef lb_debug_enum(lbModule *m, Type *type, String name, Type *bt = base_type(type); GB_ASSERT(bt->kind == Type_Enum); + lb_debug_file_line(m, bt->Enum.node, &file, &line); + u64 size_in_bits = 8*type_size_of(bt); u32 align_in_bits = 8*cast(u32)type_align_of(bt); @@ -609,50 +626,50 @@ gb_internal LLVMMetadataRef lb_debug_type_internal(lbModule *m, Type *type) { case Basic_complex32: { LLVMMetadataRef elements[2] = {}; - elements[0] = lb_debug_struct_field(m, str_lit("real"), t_f16, 0); - elements[1] = lb_debug_struct_field(m, str_lit("imag"), t_f16, 4); + elements[0] = lb_debug_struct_field(m, str_lit("real"), t_f16, 0*16); + elements[1] = lb_debug_struct_field(m, str_lit("imag"), t_f16, 1*16); return lb_debug_basic_struct(m, str_lit("complex32"), 64, 32, elements, gb_count_of(elements)); } case Basic_complex64: { LLVMMetadataRef elements[2] = {}; - elements[0] = lb_debug_struct_field(m, str_lit("real"), t_f32, 0); - elements[1] = lb_debug_struct_field(m, str_lit("imag"), t_f32, 4); + elements[0] = lb_debug_struct_field(m, str_lit("real"), t_f32, 0*32); + elements[1] = lb_debug_struct_field(m, str_lit("imag"), t_f32, 2*32); return lb_debug_basic_struct(m, str_lit("complex64"), 64, 32, elements, gb_count_of(elements)); } case Basic_complex128: { LLVMMetadataRef elements[2] = {}; - elements[0] = lb_debug_struct_field(m, str_lit("real"), t_f64, 0); - elements[1] = lb_debug_struct_field(m, str_lit("imag"), t_f64, 8); + elements[0] = lb_debug_struct_field(m, str_lit("real"), t_f64, 0*64); + elements[1] = lb_debug_struct_field(m, str_lit("imag"), t_f64, 1*64); return lb_debug_basic_struct(m, str_lit("complex128"), 128, 64, elements, gb_count_of(elements)); } case Basic_quaternion64: { LLVMMetadataRef elements[4] = {}; - elements[0] = lb_debug_struct_field(m, str_lit("imag"), t_f16, 0); - elements[1] = lb_debug_struct_field(m, str_lit("jmag"), t_f16, 4); - elements[2] = lb_debug_struct_field(m, str_lit("kmag"), t_f16, 8); - elements[3] = lb_debug_struct_field(m, str_lit("real"), t_f16, 12); + elements[0] = lb_debug_struct_field(m, str_lit("imag"), t_f16, 0*16); + elements[1] = lb_debug_struct_field(m, str_lit("jmag"), t_f16, 1*16); + elements[2] = lb_debug_struct_field(m, str_lit("kmag"), t_f16, 2*16); + elements[3] = lb_debug_struct_field(m, str_lit("real"), t_f16, 3*16); return lb_debug_basic_struct(m, str_lit("quaternion64"), 128, 32, elements, gb_count_of(elements)); } case Basic_quaternion128: { LLVMMetadataRef elements[4] = {}; - elements[0] = lb_debug_struct_field(m, str_lit("imag"), t_f32, 0); - elements[1] = lb_debug_struct_field(m, str_lit("jmag"), t_f32, 4); - elements[2] = lb_debug_struct_field(m, str_lit("kmag"), t_f32, 8); - elements[3] = lb_debug_struct_field(m, str_lit("real"), t_f32, 12); + elements[0] = lb_debug_struct_field(m, str_lit("imag"), t_f32, 0*32); + elements[1] = lb_debug_struct_field(m, str_lit("jmag"), t_f32, 1*32); + elements[2] = lb_debug_struct_field(m, str_lit("kmag"), t_f32, 2*32); + elements[3] = lb_debug_struct_field(m, str_lit("real"), t_f32, 3*32); return lb_debug_basic_struct(m, str_lit("quaternion128"), 128, 32, elements, gb_count_of(elements)); } case Basic_quaternion256: { LLVMMetadataRef elements[4] = {}; - elements[0] = lb_debug_struct_field(m, str_lit("imag"), t_f64, 0); - elements[1] = lb_debug_struct_field(m, str_lit("jmag"), t_f64, 8); - elements[2] = lb_debug_struct_field(m, str_lit("kmag"), t_f64, 16); - elements[3] = lb_debug_struct_field(m, str_lit("real"), t_f64, 24); + elements[0] = lb_debug_struct_field(m, str_lit("imag"), t_f64, 0*64); + elements[1] = lb_debug_struct_field(m, str_lit("jmag"), t_f64, 1*64); + elements[2] = lb_debug_struct_field(m, str_lit("kmag"), t_f64, 2*64); + elements[3] = lb_debug_struct_field(m, str_lit("real"), t_f64, 3*64); return lb_debug_basic_struct(m, str_lit("quaternion256"), 256, 32, elements, gb_count_of(elements)); } diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index c12489598..a23f8cfbe 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -504,6 +504,10 @@ gb_internal bool lb_is_matrix_simdable(Type *t) { if ((mt->Matrix.row_count & 1) ^ (mt->Matrix.column_count & 1)) { return false; } + if (mt->Matrix.is_row_major) { + // TODO(bill): make #row_major matrices work with SIMD + return false; + } if (elem->kind == Type_Basic) { switch (elem->Basic.kind) { @@ -1869,13 +1873,40 @@ gb_internal lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) { lbValue res_i128 = lb_emit_runtime_call(p, call, args); return lb_emit_conv(p, res_i128, t); } + i64 sz = type_size_of(src); lbValue res = {}; res.type = t; if (is_type_unsigned(dst)) { - res.value = LLVMBuildFPToUI(p->builder, value.value, lb_type(m, t), ""); + switch (sz) { + case 2: + case 4: + res.value = LLVMBuildFPToUI(p->builder, value.value, lb_type(m, t_u32), ""); + res.value = LLVMBuildIntCast2(p->builder, res.value, lb_type(m, t), false, ""); + break; + case 8: + res.value = LLVMBuildFPToUI(p->builder, value.value, lb_type(m, t_u64), ""); + res.value = LLVMBuildIntCast2(p->builder, res.value, lb_type(m, t), false, ""); + break; + default: + GB_PANIC("Unhandled float type"); + break; + } } else { - res.value = LLVMBuildFPToSI(p->builder, value.value, lb_type(m, t), ""); + switch (sz) { + case 2: + case 4: + res.value = LLVMBuildFPToSI(p->builder, value.value, lb_type(m, t_i32), ""); + res.value = LLVMBuildIntCast2(p->builder, res.value, lb_type(m, t), true, ""); + break; + case 8: + res.value = LLVMBuildFPToSI(p->builder, value.value, lb_type(m, t_i64), ""); + res.value = LLVMBuildIntCast2(p->builder, res.value, lb_type(m, t), true, ""); + break; + default: + GB_PANIC("Unhandled float type"); + break; + } } return res; } @@ -4533,10 +4564,26 @@ gb_internal lbAddr lb_build_addr_compound_lit(lbProcedure *p, Ast *expr) { if (lb_is_nested_possibly_constant(type, sel, elem)) { continue; } - lbValue dst = lb_emit_deep_field_gep(p, comp_lit_ptr, sel); field_expr = lb_build_expr(p, elem); field_expr = lb_emit_conv(p, field_expr, sel.entity->type); - lb_emit_store(p, dst, field_expr); + if (sel.is_bit_field) { + Selection sub_sel = trim_selection(sel); + lbValue trimmed_dst = lb_emit_deep_field_gep(p, comp_lit_ptr, sub_sel); + Type *bf = base_type(type_deref(trimmed_dst.type)); + if (is_type_pointer(bf)) { + trimmed_dst = lb_emit_load(p, trimmed_dst); + bf = base_type(type_deref(trimmed_dst.type)); + } + GB_ASSERT(bf->kind == Type_BitField); + + isize idx = sel.index[sel.index.count-1]; + lbAddr dst = lb_addr_bit_field(trimmed_dst, bf->BitField.fields[idx]->type, bf->BitField.bit_offsets[idx], bf->BitField.bit_sizes[idx]); + lb_addr_store(p, dst, field_expr); + + } else { + lbValue dst = lb_emit_deep_field_gep(p, comp_lit_ptr, sel); + lb_emit_store(p, dst, field_expr); + } continue; } diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index e8183027f..03d0f8b32 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -78,7 +78,8 @@ gb_internal void lb_init_module(lbModule *m, Checker *c) { array_init(&m->procedures_to_generate, a, 0, c->info.all_procedures.count); map_init(&m->procedure_values, c->info.all_procedures.count*2); } - array_init(&m->global_procedures_and_types_to_create, a, 0, 1024); + array_init(&m->global_procedures_to_create, a, 0, 1024); + array_init(&m->global_types_to_create, a, 0, 1024); array_init(&m->missing_procedures_to_check, a, 0, 16); map_init(&m->debug_values); @@ -1383,8 +1384,6 @@ 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)) { - LLVMSetAlignment(res.addr.value, cast(unsigned)lb_alignof(vector_type)); - LLVMValueRef vp = LLVMBuildPointerCast(p->builder, addr.addr.value, LLVMPointerType(vector_type, 0), ""); LLVMValueRef v = LLVMBuildLoad2(p->builder, vector_type, vp, ""); LLVMValueRef scalars[4] = {}; @@ -1394,6 +1393,8 @@ gb_internal lbValue lb_addr_load(lbProcedure *p, lbAddr const &addr) { LLVMValueRef mask = LLVMConstVector(scalars, addr.swizzle.count); LLVMValueRef sv = llvm_basic_shuffle(p, v, mask); + LLVMSetAlignment(res.addr.value, cast(unsigned)lb_alignof(LLVMTypeOf(sv))); + LLVMValueRef dst = LLVMBuildPointerCast(p->builder, ptr.value, LLVMPointerType(LLVMTypeOf(sv), 0), ""); LLVMBuildStore(p->builder, sv, dst); } else { diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index 3b9b1be05..87f75fb1d 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -710,13 +710,12 @@ gb_internal void lb_begin_procedure_body(lbProcedure *p) { lb_set_debug_position_to_procedure_begin(p); if (p->debug_info != nullptr) { if (p->context_stack.count != 0) { + lbBlock *prev_block = p->curr_block; p->curr_block = p->decl_block; lb_add_debug_context_variable(p, lb_find_or_generate_context_ptr(p)); + p->curr_block = prev_block; } - } - - lb_start_block(p, p->entry_block); } gb_internal void lb_end_procedure_body(lbProcedure *p) { @@ -1097,15 +1096,7 @@ gb_internal lbValue lb_emit_call(lbProcedure *p, lbValue value, Array<lbValue> c ptr = lb_address_from_load_or_generate_local(p, x); } } else { - if (LLVMIsConstant(x.value)) { - // NOTE(bill): if the value is already constant, then just it as a global variable - // and pass it by pointer - lbAddr addr = lb_add_global_generated(p->module, original_type, x); - lb_make_global_private_const(addr); - ptr = addr.addr; - } else { - ptr = lb_copy_value_to_ptr(p, x, original_type, 16); - } + ptr = lb_copy_value_to_ptr(p, x, original_type, 16); } array_add(&processed_args, ptr); } diff --git a/src/llvm_backend_stmt.cpp b/src/llvm_backend_stmt.cpp index b18db4e45..9f28e45e0 100644 --- a/src/llvm_backend_stmt.cpp +++ b/src/llvm_backend_stmt.cpp @@ -1850,7 +1850,9 @@ gb_internal void lb_build_static_variables(lbProcedure *p, AstValueDecl *vd) { LLVMSetInitializer(global, LLVMConstNull(lb_type(p->module, e->type))); if (value.value != nullptr) { LLVMSetInitializer(global, value.value); - } else { + } + if (e->Variable.is_rodata) { + LLVMSetGlobalConstant(global, true); } if (e->Variable.thread_local_model != "") { LLVMSetThreadLocal(global, true); diff --git a/src/llvm_backend_utility.cpp b/src/llvm_backend_utility.cpp index f7674a8bc..94153e233 100644 --- a/src/llvm_backend_utility.cpp +++ b/src/llvm_backend_utility.cpp @@ -1365,6 +1365,8 @@ gb_internal lbValue lb_emit_deep_field_gep(lbProcedure *p, lbValue e, Selection } else { e = lb_emit_ptr_offset(p, lb_emit_load(p, arr), index); } + e.type = alloc_type_multi_pointer_to_pointer(e.type); + } else if (is_type_quaternion(type)) { e = lb_emit_struct_ep(p, e, index); } else if (is_type_raw_union(type)) { diff --git a/src/main.cpp b/src/main.cpp index 4df6f97d5..70def5802 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -155,6 +155,38 @@ gb_internal i32 system_exec_command_line_app(char const *name, char const *fmt, return exit_code; } +#if defined(GB_SYSTEM_WINDOWS) +#define popen _popen +#define pclose _pclose +#endif + +gb_internal bool system_exec_command_line_app_output(char const *command, gbString *output) { + GB_ASSERT(output); + + u8 buffer[256]; + FILE *stream; + stream = popen(command, "r"); + if (!stream) { + return false; + } + defer (pclose(stream)); + + while (!feof(stream)) { + size_t n = fread(buffer, 1, 255, stream); + *output = gb_string_append_length(*output, buffer, n); + + if (ferror(stream)) { + return false; + } + } + + if (build_context.show_system_calls) { + gb_printf_err("[SYSTEM CALL OUTPUT] %s -> %s\n", command, *output); + } + + return true; +} + gb_internal Array<String> setup_args(int argc, char const **argv) { gbAllocator a = heap_allocator(); @@ -234,6 +266,8 @@ enum BuildFlagKind { BuildFlag_ShowMoreTimings, BuildFlag_ExportTimings, BuildFlag_ExportTimingsFile, + BuildFlag_ExportDependencies, + BuildFlag_ExportDependenciesFile, BuildFlag_ShowSystemCalls, BuildFlag_ThreadCount, BuildFlag_KeepTempFiles, @@ -276,8 +310,6 @@ enum BuildFlagKind { BuildFlag_RelocMode, BuildFlag_DisableRedZone, - BuildFlag_TestName, - BuildFlag_DisallowDo, BuildFlag_DefaultToNilAllocator, BuildFlag_DefaultToPanicAllocator, @@ -320,7 +352,6 @@ enum BuildFlagKind { BuildFlag_Subsystem, #endif - BuildFlag_COUNT, }; @@ -427,6 +458,8 @@ gb_internal bool parse_build_flags(Array<String> args) { add_flag(&build_flags, BuildFlag_ShowMoreTimings, str_lit("show-more-timings"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_ExportTimings, str_lit("export-timings"), BuildFlagParam_String, Command__does_check); add_flag(&build_flags, BuildFlag_ExportTimingsFile, str_lit("export-timings-file"), BuildFlagParam_String, Command__does_check); + add_flag(&build_flags, BuildFlag_ExportDependencies, str_lit("export-dependencies"), BuildFlagParam_String, Command__does_build); + add_flag(&build_flags, BuildFlag_ExportDependenciesFile, str_lit("export-dependencies-file"), BuildFlagParam_String, Command__does_build); add_flag(&build_flags, BuildFlag_ShowUnused, str_lit("show-unused"), BuildFlagParam_None, Command_check); add_flag(&build_flags, BuildFlag_ShowUnusedWithLocation, str_lit("show-unused-with-location"), BuildFlagParam_None, Command_check); add_flag(&build_flags, BuildFlag_ShowSystemCalls, str_lit("show-system-calls"), BuildFlagParam_None, Command_all); @@ -471,8 +504,6 @@ gb_internal bool parse_build_flags(Array<String> args) { add_flag(&build_flags, BuildFlag_RelocMode, str_lit("reloc-mode"), BuildFlagParam_String, Command__does_build); add_flag(&build_flags, BuildFlag_DisableRedZone, str_lit("disable-red-zone"), BuildFlagParam_None, Command__does_build); - add_flag(&build_flags, BuildFlag_TestName, str_lit("test-name"), BuildFlagParam_String, Command_test); - add_flag(&build_flags, BuildFlag_DisallowDo, str_lit("disallow-do"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_DefaultToNilAllocator, str_lit("default-to-nil-allocator"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_DefaultToPanicAllocator, str_lit("default-to-panic-allocator"),BuildFlagParam_None, Command__does_check); @@ -753,6 +784,36 @@ gb_internal bool parse_build_flags(Array<String> args) { break; } + case BuildFlag_ExportDependencies: { + GB_ASSERT(value.kind == ExactValue_String); + + if (value.value_string == "make") { + build_context.export_dependencies_format = DependenciesExportMake; + } else if (value.value_string == "json") { + build_context.export_dependencies_format = DependenciesExportJson; + } else { + gb_printf_err("Invalid export format for -export-dependencies:<string>, got %.*s\n", LIT(value.value_string)); + gb_printf_err("Valid export formats:\n"); + gb_printf_err("\tmake\n"); + gb_printf_err("\tjson\n"); + bad_flags = true; + } + + break; + } + case BuildFlag_ExportDependenciesFile: { + GB_ASSERT(value.kind == ExactValue_String); + + String export_path = string_trim_whitespace(value.value_string); + if (is_build_flag_path_valid(export_path)) { + build_context.export_dependencies_file = path_to_full_path(heap_allocator(), export_path); + } else { + gb_printf_err("Invalid -export-dependencies path, got %.*s\n", LIT(export_path)); + bad_flags = true; + } + + break; + } case BuildFlag_ShowSystemCalls: { GB_ASSERT(value.kind == ExactValue_Invalid); build_context.show_system_calls = true; @@ -1119,21 +1180,6 @@ gb_internal bool parse_build_flags(Array<String> args) { case BuildFlag_DisableRedZone: build_context.disable_red_zone = true; break; - case BuildFlag_TestName: { - GB_ASSERT(value.kind == ExactValue_String); - { - String name = value.value_string; - if (!string_is_valid_identifier(name)) { - gb_printf_err("Test name '%.*s' must be a valid identifier\n", LIT(name)); - bad_flags = true; - break; - } - string_set_add(&build_context.test_names, name); - - // NOTE(bill): Allow for multiple -test-name - continue; - } - } case BuildFlag_DisallowDo: build_context.disallow_do = true; break; @@ -1635,6 +1681,74 @@ gb_internal void show_timings(Checker *c, Timings *t) { } } +gb_internal void export_dependencies(Parser *p) { + GB_ASSERT(build_context.export_dependencies_format != DependenciesExportUnspecified); + + if (build_context.export_dependencies_file.len <= 0) { + gb_printf_err("No dependency file specified with `-export-dependencies-file`\n"); + exit_with_errors(); + return; + } + + gbFile f = {}; + char * fileName = (char *)build_context.export_dependencies_file.text; + gbFileError err = gb_file_open_mode(&f, gbFileMode_Write, fileName); + if (err != gbFileError_None) { + gb_printf_err("Failed to export dependencies to: %s\n", fileName); + exit_with_errors(); + return; + } + defer (gb_file_close(&f)); + + if (build_context.export_dependencies_format == DependenciesExportMake) { + String exe_name = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_Output]); + defer (gb_free(heap_allocator(), exe_name.text)); + + gb_fprintf(&f, "%.*s:", LIT(exe_name)); + + isize current_line_length = exe_name.len + 1; + + for(AstPackage *pkg : p->packages) { + for(AstFile *file : pkg->files) { + /* Arbitrary line break value. Maybe make this better? */ + if (current_line_length >= 80-2) { + gb_file_write(&f, " \\\n ", 4); + current_line_length = 1; + } + + gb_file_write(&f, " ", 1); + current_line_length++; + + for (isize k = 0; k < file->fullpath.len; k++) { + char part = file->fullpath.text[k]; + if (part == ' ') { + gb_file_write(&f, "\\", 1); + current_line_length++; + } + gb_file_write(&f, &part, 1); + current_line_length++; + } + } + } + + gb_fprintf(&f, "\n"); + } else if (build_context.export_dependencies_format == DependenciesExportJson) { + gb_fprintf(&f, "{\n"); + + gb_fprintf(&f, "\t\"source_files\": [\n"); + + for(AstPackage *pkg : p->packages) { + for(AstFile *file : pkg->files) { + gb_fprintf(&f, "\t\t\"%.*s\",\n", LIT(file->fullpath)); + } + } + + gb_fprintf(&f, "\t],\n"); + + gb_fprintf(&f, "}\n"); + } +} + gb_internal void remove_temp_files(lbGenerator *gen) { if (build_context.keep_temp_files) return; @@ -1790,6 +1904,18 @@ gb_internal void print_show_help(String const arg0, String const &command) { print_usage_line(2, "Example: -export-timings-file:timings.json"); print_usage_line(0, ""); + 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, ""); + + 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, ""); + 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"); @@ -1962,10 +2088,6 @@ gb_internal void print_show_help(String const arg0, String const &command) { } if (test_only) { - print_usage_line(1, "-test-name:<string>"); - print_usage_line(2, "Runs specific test only by name."); - print_usage_line(0, ""); - print_usage_line(1, "-all-packages"); print_usage_line(2, "Tests all packages imported into the given initial package."); print_usage_line(0, ""); @@ -2489,7 +2611,6 @@ int main(int arg_count, char const **arg_ptr) { TIME_SECTION("init args"); map_init(&build_context.defined_values); build_context.extra_packages.allocator = heap_allocator(); - string_set_init(&build_context.test_names); Array<String> args = setup_args(arg_count, arg_ptr); @@ -2657,6 +2778,10 @@ int main(int arg_count, char const **arg_ptr) { gb_printf_err("Expected either a directory or a .odin file, got '%.*s'\n", LIT(init_filename)); return 1; } + if (!gb_file_exists(cast(const char*)init_filename.text)) { + gb_printf_err("The file '%.*s' was not found.\n", LIT(init_filename)); + return 1; + } } } } @@ -2881,6 +3006,10 @@ int main(int arg_count, char const **arg_ptr) { if (build_context.show_timings) { show_timings(checker, &global_timings); } + + if (build_context.export_dependencies_format != DependenciesExportUnspecified) { + export_dependencies(parser); + } return result; } break; @@ -2903,6 +3032,10 @@ int main(int arg_count, char const **arg_ptr) { if (build_context.show_timings) { show_timings(checker, &global_timings); } + + if (build_context.export_dependencies_format != DependenciesExportUnspecified) { + export_dependencies(parser); + } return result; } break; @@ -2916,6 +3049,10 @@ int main(int arg_count, char const **arg_ptr) { show_timings(checker, &global_timings); } + if (build_context.export_dependencies_format != DependenciesExportUnspecified) { + export_dependencies(parser); + } + if (run_output) { String exe_name = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_Output]); defer (gb_free(heap_allocator(), exe_name.text)); diff --git a/src/parser.cpp b/src/parser.cpp index 5aa11b5d0..0cd96f5b5 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -555,7 +555,7 @@ gb_internal Ast *ast_unary_expr(AstFile *f, Token op, Ast *expr) { syntax_error_with_verbose(expr, "'or_return' within an unary expression not wrapped in parentheses (...)"); break; case Ast_OrBranchExpr: - syntax_error_with_verbose(expr, "'or_%.*s' within an unary expression not wrapped in parentheses (...)", LIT(expr->OrBranchExpr.token.string)); + syntax_error_with_verbose(expr, "'%.*s' within an unary expression not wrapped in parentheses (...)", LIT(expr->OrBranchExpr.token.string)); break; } @@ -583,7 +583,7 @@ gb_internal Ast *ast_binary_expr(AstFile *f, Token op, Ast *left, Ast *right) { syntax_error_with_verbose(left, "'or_return' within a binary expression not wrapped in parentheses (...)"); break; case Ast_OrBranchExpr: - syntax_error_with_verbose(left, "'or_%.*s' within a binary expression not wrapped in parentheses (...)", LIT(left->OrBranchExpr.token.string)); + syntax_error_with_verbose(left, "'%.*s' within a binary expression not wrapped in parentheses (...)", LIT(left->OrBranchExpr.token.string)); break; } if (right) switch (right->kind) { @@ -591,7 +591,7 @@ gb_internal Ast *ast_binary_expr(AstFile *f, Token op, Ast *left, Ast *right) { syntax_error_with_verbose(right, "'or_return' within a binary expression not wrapped in parentheses (...)"); break; case Ast_OrBranchExpr: - syntax_error_with_verbose(right, "'or_%.*s' within a binary expression not wrapped in parentheses (...)", LIT(right->OrBranchExpr.token.string)); + syntax_error_with_verbose(right, "'%.*s' within a binary expression not wrapped in parentheses (...)", LIT(right->OrBranchExpr.token.string)); break; } @@ -1284,14 +1284,16 @@ gb_internal Ast *ast_import_decl(AstFile *f, Token token, Token relpath, Token i return result; } -gb_internal Ast *ast_foreign_import_decl(AstFile *f, Token token, Array<Token> filepaths, Token library_name, - CommentGroup *docs, CommentGroup *comment) { +gb_internal Ast *ast_foreign_import_decl(AstFile *f, Token token, Array<Ast *> filepaths, Token library_name, + bool multiple_filepaths, + CommentGroup *docs, CommentGroup *comment) { Ast *result = alloc_ast_node(f, Ast_ForeignImportDecl); result->ForeignImportDecl.token = token; result->ForeignImportDecl.filepaths = slice_from_array(filepaths); result->ForeignImportDecl.library_name = library_name; result->ForeignImportDecl.docs = docs; result->ForeignImportDecl.comment = comment; + result->ForeignImportDecl.multiple_filepaths = multiple_filepaths; result->ForeignImportDecl.attributes.allocator = ast_allocator(f); return result; @@ -2089,6 +2091,9 @@ gb_internal bool ast_on_same_line(Token const &x, Ast *yp) { gb_internal Ast *parse_force_inlining_operand(AstFile *f, Token token) { Ast *expr = parse_unary_expr(f, false); Ast *e = strip_or_return_expr(expr); + if (e == nullptr) { + return expr; + } if (e->kind != Ast_ProcLit && e->kind != Ast_CallExpr) { syntax_error(expr, "%.*s must be followed by a procedure literal or call, got %.*s", LIT(token.string), LIT(ast_strings[expr->kind])); return ast_bad_expr(f, token, f->curr_token); @@ -3100,7 +3105,7 @@ gb_internal void parse_check_or_return(Ast *operand, char const *msg) { syntax_error_with_verbose(operand, "'or_return' use within %s is not wrapped in parentheses (...)", msg); break; case Ast_OrBranchExpr: - syntax_error_with_verbose(operand, "'or_%.*s' use within %s is not wrapped in parentheses (...)", msg, LIT(operand->OrBranchExpr.token.string)); + syntax_error_with_verbose(operand, "'%.*s' use within %s is not wrapped in parentheses (...)", msg, LIT(operand->OrBranchExpr.token.string)); break; } } @@ -3745,8 +3750,10 @@ gb_internal Ast *parse_simple_stmt(AstFile *f, u32 flags) { case Ast_TypeSwitchStmt: stmt->TypeSwitchStmt.partial = true; break; + default: + syntax_error(partial_token, "Incorrect use of directive, use '%.*s: #partial switch'", LIT(ast_token(name).string)); + break; } - syntax_error(partial_token, "Incorrect use of directive, use '#partial %.*s: switch'", LIT(ast_token(name).string)); } else if (is_reverse) { switch (stmt->kind) { case Ast_RangeStmt: @@ -4882,14 +4889,17 @@ gb_internal Ast *parse_foreign_decl(AstFile *f) { if (is_blank_ident(lib_name)) { syntax_error(lib_name, "Illegal foreign import name: '_'"); } - Array<Token> filepaths = {}; + bool multiple_filepaths = false; + + Array<Ast *> filepaths = {}; if (allow_token(f, Token_OpenBrace)) { + multiple_filepaths = true; array_init(&filepaths, ast_allocator(f)); while (f->curr_token.kind != Token_CloseBrace && f->curr_token.kind != Token_EOF) { - Token path = expect_token(f, Token_String); + Ast *path = parse_expr(f, false); array_add(&filepaths, path); if (!allow_field_separator(f)) { @@ -4898,9 +4908,10 @@ gb_internal Ast *parse_foreign_decl(AstFile *f) { } expect_closing_brace_of_field_list(f); } else { - filepaths = array_make<Token>(ast_allocator(f), 0, 1); + filepaths = array_make<Ast *>(ast_allocator(f), 0, 1); Token path = expect_token(f, Token_String); - array_add(&filepaths, path); + Ast *lit = ast_basic_lit(f, path); + array_add(&filepaths, lit); } Ast *s = nullptr; @@ -4909,9 +4920,9 @@ gb_internal Ast *parse_foreign_decl(AstFile *f) { s = ast_bad_decl(f, lib_name, f->curr_token); } else if (f->curr_proc != nullptr) { syntax_error(lib_name, "You cannot use foreign import within a procedure. This must be done at the file scope"); - s = ast_bad_decl(f, lib_name, filepaths[0]); + s = ast_bad_decl(f, lib_name, ast_token(filepaths[0])); } else { - s = ast_foreign_import_decl(f, token, filepaths, lib_name, docs, f->line_comment); + s = ast_foreign_import_decl(f, token, filepaths, lib_name, multiple_filepaths, docs, f->line_comment); } expect_semicolon(f); return s; @@ -5170,7 +5181,7 @@ gb_internal Ast *parse_stmt(AstFile *f) { } else if (tag == "unroll") { return parse_unrolled_for_loop(f, name); } else if (tag == "reverse") { - Ast *for_stmt = parse_for_stmt(f); + Ast *for_stmt = parse_stmt(f); if (for_stmt->kind == Ast_RangeStmt) { if (for_stmt->RangeStmt.reverse) { syntax_error(token, "#reverse already applied to a 'for in' statement"); @@ -5648,9 +5659,19 @@ gb_internal bool is_package_name_reserved(String const &name) { } -gb_internal bool determine_path_from_string(BlockingMutex *file_mutex, Ast *node, String base_dir, String const &original_string, String *path) { +gb_internal bool determine_path_from_string(BlockingMutex *file_mutex, Ast *node, String base_dir, String const &original_string, String *path, bool use_check_errors=false) { GB_ASSERT(path != nullptr); + void (*do_error)(Ast *, char const *, ...); + void (*do_warning)(Token const &, char const *, ...); + + do_error = &syntax_error; + do_warning = &syntax_warning; + if (use_check_errors) { + do_error = &error; + do_error = &warning; + } + // NOTE(bill): if file_mutex == nullptr, this means that the code is used within the semantics stage String collection_name = {}; @@ -5677,7 +5698,7 @@ gb_internal bool determine_path_from_string(BlockingMutex *file_mutex, Ast *node String file_str = {}; if (colon_pos == 0) { - syntax_error(node, "Expected a collection name"); + do_error(node, "Expected a collection name"); return false; } @@ -5692,11 +5713,11 @@ gb_internal bool determine_path_from_string(BlockingMutex *file_mutex, Ast *node if (has_windows_drive) { String sub_file_path = substring(file_str, 3, file_str.len); if (!is_import_path_valid(sub_file_path)) { - syntax_error(node, "Invalid import path: '%.*s'", LIT(file_str)); + do_error(node, "Invalid import path: '%.*s'", LIT(file_str)); return false; } } else if (!is_import_path_valid(file_str)) { - syntax_error(node, "Invalid import path: '%.*s'", LIT(file_str)); + do_error(node, "Invalid import path: '%.*s'", LIT(file_str)); return false; } @@ -5718,16 +5739,16 @@ gb_internal bool determine_path_from_string(BlockingMutex *file_mutex, Ast *node } if (replace_with_base) { if (ast_file_vet_deprecated(node->file())) { - syntax_error(node, "import \"core:%.*s\" has been deprecated in favour of \"base:%.*s\"", LIT(file_str), LIT(file_str)); + do_error(node, "import \"core:%.*s\" has been deprecated in favour of \"base:%.*s\"", LIT(file_str), LIT(file_str)); } else { - syntax_warning(ast_token(node), "import \"core:%.*s\" has been deprecated in favour of \"base:%.*s\"", LIT(file_str), LIT(file_str)); + do_warning(ast_token(node), "import \"core:%.*s\" has been deprecated in favour of \"base:%.*s\"", LIT(file_str), LIT(file_str)); } } } if (collection_name == "system") { if (node->kind != Ast_ForeignImportDecl) { - syntax_error(node, "The library collection 'system' is restrict for 'foreign_library'"); + do_error(node, "The library collection 'system' is restrict for 'foreign import'"); return false; } else { *path = file_str; @@ -5735,7 +5756,7 @@ gb_internal bool determine_path_from_string(BlockingMutex *file_mutex, Ast *node } } else if (!find_library_collection_path(collection_name, &base_dir)) { // NOTE(bill): It's a naughty name - syntax_error(node, "Unknown library collection: '%.*s'", LIT(collection_name)); + do_error(node, "Unknown library collection: '%.*s'", LIT(collection_name)); return false; } } else { @@ -5759,7 +5780,7 @@ gb_internal bool determine_path_from_string(BlockingMutex *file_mutex, Ast *node if (collection_name == "core" || collection_name == "base") { return true; } else { - syntax_error(node, "The package '%.*s' must be imported with the 'base' library collection: 'base:%.*s'", LIT(file_str), LIT(file_str)); + do_error(node, "The package '%.*s' must be imported with the 'base' library collection: 'base:%.*s'", LIT(file_str), LIT(file_str)); return false; } } @@ -5844,30 +5865,29 @@ gb_internal void parse_setup_file_decls(Parser *p, AstFile *f, String const &bas } else if (node->kind == Ast_ForeignImportDecl) { ast_node(fl, ForeignImportDecl, node); - auto fullpaths = array_make<String>(permanent_allocator(), 0, fl->filepaths.count); - - for (Token const &fp : fl->filepaths) { - String file_str = string_trim_whitespace(string_value_from_token(f, fp)); + if (fl->filepaths.count == 0) { + syntax_error(decls[i], "No foreign paths found"); + decls[i] = ast_bad_decl(f, ast_token(fl->filepaths[0]), ast_end_token(fl->filepaths[fl->filepaths.count-1])); + goto end; + } else if (!fl->multiple_filepaths && + fl->filepaths.count == 1) { + Ast *fp = fl->filepaths[0]; + GB_ASSERT(fp->kind == Ast_BasicLit); + Token fp_token = fp->BasicLit.token; + String file_str = string_trim_whitespace(string_value_from_token(f, fp_token)); String fullpath = file_str; if (allow_check_foreign_filepath()) { String foreign_path = {}; bool ok = determine_path_from_string(&p->file_decl_mutex, node, base_dir, file_str, &foreign_path); if (!ok) { - decls[i] = ast_bad_decl(f, fp, fl->filepaths[fl->filepaths.count-1]); + decls[i] = ast_bad_decl(f, fp_token, fp_token); goto end; } fullpath = foreign_path; } - array_add(&fullpaths, fullpath); + fl->fullpaths = slice_make<String>(permanent_allocator(), 1); + fl->fullpaths[0] = fullpath; } - if (fullpaths.count == 0) { - syntax_error(decls[i], "No foreign paths found"); - decls[i] = ast_bad_decl(f, fl->filepaths[0], fl->filepaths[fl->filepaths.count-1]); - goto end; - } - - fl->fullpaths = slice_from_array(fullpaths); - } else if (node->kind == Ast_WhenStmt) { ast_node(ws, WhenStmt, node); diff --git a/src/parser.hpp b/src/parser.hpp index 5820275c8..02f2af28d 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -458,6 +458,7 @@ AST_KIND(_ExprBegin, "", bool) \ bool optional_ok_one; \ bool was_selector; \ AstSplitArgs *split_args; \ + Entity *entity_procedure_of; \ }) \ AST_KIND(FieldValue, "field value", struct { Token eq; Ast *field, *value; }) \ AST_KIND(EnumFieldValue, "enum field value", struct { \ @@ -631,7 +632,8 @@ AST_KIND(_DeclBegin, "", bool) \ }) \ AST_KIND(ForeignImportDecl, "foreign import declaration", struct { \ Token token; \ - Slice<Token> filepaths; \ + Slice<Ast *> filepaths; \ + bool multiple_filepaths; \ Token library_name; \ String collection_name; \ Slice<String> fullpaths; \ diff --git a/src/parser_pos.cpp b/src/parser_pos.cpp index b2e12999b..1ffd3a82f 100644 --- a/src/parser_pos.cpp +++ b/src/parser_pos.cpp @@ -278,7 +278,7 @@ Token ast_end_token(Ast *node) { case Ast_ImportDecl: return node->ImportDecl.relpath; case Ast_ForeignImportDecl: if (node->ForeignImportDecl.filepaths.count > 0) { - return node->ForeignImportDecl.filepaths[node->ForeignImportDecl.filepaths.count-1]; + return ast_end_token(node->ForeignImportDecl.filepaths[node->ForeignImportDecl.filepaths.count-1]); } if (node->ForeignImportDecl.library_name.kind != Token_Invalid) { return node->ForeignImportDecl.library_name; diff --git a/src/threading.cpp b/src/threading.cpp index 48c58e8f4..717dcb874 100644 --- a/src/threading.cpp +++ b/src/threading.cpp @@ -494,6 +494,8 @@ gb_internal u32 thread_current_id(void) { thread_id = find_thread(NULL); #elif defined(GB_SYSTEM_FREEBSD) thread_id = pthread_getthreadid_np(); +#elif defined(GB_SYSTEM_NETBSD) + thread_id = (u32)_lwp_self(); #else #error Unsupported architecture for thread_current_id() #endif diff --git a/src/types.cpp b/src/types.cpp index e568d2af2..97e8267a3 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -140,6 +140,7 @@ struct TypeStruct { i64 custom_field_align; Type * polymorphic_params; // Type_Tuple Type * polymorphic_parent; + Wait_Signal polymorphic_wait_signal; Type * soa_elem; i32 soa_count; @@ -167,6 +168,7 @@ struct TypeUnion { i64 custom_align; Type * polymorphic_params; // Type_Tuple Type * polymorphic_parent; + Wait_Signal polymorphic_wait_signal; i16 tag_size; bool is_polymorphic; @@ -457,6 +459,15 @@ gb_internal Selection sub_selection(Selection const &sel, isize offset) { return res; } +gb_internal Selection trim_selection(Selection const &sel) { + Selection res = {}; + res.index.data = sel.index.data; + res.index.count = gb_max(sel.index.count - 1, 0); + res.index.capacity = res.index.count; + return res; +} + + gb_global Type basic_types[] = { {Type_Basic, {Basic_Invalid, 0, 0, STR_LIT("invalid type")}}, @@ -1084,6 +1095,7 @@ gb_internal Type *alloc_type_struct() { gb_internal Type *alloc_type_struct_complete() { Type *t = alloc_type(Type_Struct); wait_signal_set(&t->Struct.fields_wait_signal); + wait_signal_set(&t->Struct.polymorphic_wait_signal); return t; } @@ -1482,10 +1494,10 @@ gb_internal i64 matrix_align_of(Type *t, struct TypePath *tp) { i64 total_expected_size = row_count*t->Matrix.column_count*elem_size; // i64 min_alignment = prev_pow2(elem_align * row_count); i64 min_alignment = prev_pow2(total_expected_size); - while ((total_expected_size % min_alignment) != 0) { + while (total_expected_size != 0 && (total_expected_size % min_alignment) != 0) { min_alignment >>= 1; } - GB_ASSERT(min_alignment >= elem_align); + min_alignment = gb_max(min_alignment, elem_align); i64 align = gb_min(min_alignment, build_context.max_simd_align); return align; @@ -2127,15 +2139,18 @@ gb_internal bool is_type_polymorphic_record_unspecialized(Type *t) { return false; } + gb_internal TypeTuple *get_record_polymorphic_params(Type *t) { t = base_type(t); switch (t->kind) { case Type_Struct: + wait_signal_until_available(&t->Struct.polymorphic_wait_signal); if (t->Struct.polymorphic_params) { return &t->Struct.polymorphic_params->Tuple; } break; case Type_Union: + wait_signal_until_available(&t->Union.polymorphic_wait_signal); if (t->Union.polymorphic_params) { return &t->Union.polymorphic_params->Tuple; } |