diff options
| author | FourteenBrush <naessensarthur2@protonmail.com> | 2024-01-25 10:15:25 +0100 |
|---|---|---|
| committer | FourteenBrush <naessensarthur2@protonmail.com> | 2024-01-25 10:15:25 +0100 |
| commit | 967ccfc7ccc0f76c653ff4bb625bac60e89a7904 (patch) | |
| tree | f989a0f99337c7a0c93ee84249d73ec0c33e306d /src | |
| parent | 712ae1c5ac73493498aa8e5076d91a6558337117 (diff) | |
| parent | 9cfd4a953e2a7d19237891993b643b2d1477f8b3 (diff) | |
Merge branch 'master' of https://github.com/FourteenBrush/Odin
Diffstat (limited to 'src')
40 files changed, 1907 insertions, 751 deletions
diff --git a/src/bug_report.cpp b/src/bug_report.cpp index fbf616efb..ac3805919 100644 --- a/src/bug_report.cpp +++ b/src/bug_report.cpp @@ -824,6 +824,17 @@ gb_internal void report_os_info() { {"20G624", {20, 6, 0}, "macOS", {"Big Sur", {11, 6, 6}}},
{"20G630", {20, 6, 0}, "macOS", {"Big Sur", {11, 6, 7}}},
{"20G730", {20, 6, 0}, "macOS", {"Big Sur", {11, 6, 8}}},
+ {"20G817", {20, 6, 0}, "macOS", {"Big Sur", {11, 7, 0}}},
+ {"20G918", {20, 6, 0}, "macOS", {"Big Sur", {11, 7, 1}}},
+ {"20G1020", {20, 6, 0}, "macOS", {"Big Sur", {11, 7, 2}}},
+ {"20G1116", {20, 6, 0}, "macOS", {"Big Sur", {11, 7, 3}}},
+ {"20G1120", {20, 6, 0}, "macOS", {"Big Sur", {11, 7, 4}}},
+ {"20G1225", {20, 6, 0}, "macOS", {"Big Sur", {11, 7, 5}}},
+ {"20G1231", {20, 6, 0}, "macOS", {"Big Sur", {11, 7, 6}}},
+ {"20G1345", {20, 6, 0}, "macOS", {"Big Sur", {11, 7, 7}}},
+ {"20G1351", {20, 6, 0}, "macOS", {"Big Sur", {11, 7, 8}}},
+ {"20G1426", {20, 6, 0}, "macOS", {"Big Sur", {11, 7, 9}}},
+ {"20G1427", {20, 6, 0}, "macOS", {"Big Sur", {11, 7, 10}}},
{"21A344", {21, 0, 1}, "macOS", {"Monterey", {12, 0, 0}}},
{"21A559", {21, 1, 0}, "macOS", {"Monterey", {12, 0, 1}}},
{"21C52", {21, 2, 0}, "macOS", {"Monterey", {12, 1, 0}}},
@@ -836,6 +847,42 @@ gb_internal void report_os_info() { {"21F2092", {21, 5, 0}, "macOS", {"Monterey", {12, 4, 0}}},
{"21G72", {21, 6, 0}, "macOS", {"Monterey", {12, 5, 0}}},
{"21G83", {21, 6, 0}, "macOS", {"Monterey", {12, 5, 1}}},
+ {"21G115", {21, 6, 0}, "macOS", {"Monterey", {12, 6, 0}}},
+ {"21G217", {21, 6, 0}, "macOS", {"Monterey", {12, 6, 1}}},
+ {"21G320", {21, 6, 0}, "macOS", {"Monterey", {12, 6, 2}}},
+ {"21G419", {21, 6, 0}, "macOS", {"Monterey", {12, 6, 3}}},
+ {"21G526", {21, 6, 0}, "macOS", {"Monterey", {12, 6, 4}}},
+ {"21G531", {21, 6, 0}, "macOS", {"Monterey", {12, 6, 5}}},
+ {"21G646", {21, 6, 0}, "macOS", {"Monterey", {12, 6, 6}}},
+ {"21G651", {21, 6, 0}, "macOS", {"Monterey", {12, 6, 7}}},
+ {"21G725", {21, 6, 0}, "macOS", {"Monterey", {12, 6, 8}}},
+ {"21G726", {21, 6, 0}, "macOS", {"Monterey", {12, 6, 9}}},
+ {"21G816", {21, 6, 0}, "macOS", {"Monterey", {12, 7, 0}}},
+ {"21G920", {21, 6, 0}, "macOS", {"Monterey", {12, 7, 1}}},
+ {"21G1974", {21, 6, 0}, "macOS", {"Monterey", {12, 7, 2}}},
+ {"22A380", {13, 0, 0}, "macOS", {"Ventura", {22, 1, 0}}},
+ {"22A400", {13, 0, 1}, "macOS", {"Ventura", {22, 1, 0}}},
+ {"22C65", {13, 1, 0}, "macOS", {"Ventura", {22, 2, 0}}},
+ {"22D49", {13, 2, 0}, "macOS", {"Ventura", {22, 3, 0}}},
+ {"22D68", {13, 2, 1}, "macOS", {"Ventura", {22, 3, 0}}},
+ {"22E252", {13, 3, 0}, "macOS", {"Ventura", {22, 4, 0}}},
+ {"22E261", {13, 3, 1}, "macOS", {"Ventura", {22, 4, 0}}},
+ {"22F66", {13, 4, 0}, "macOS", {"Ventura", {22, 5, 0}}},
+ {"22F82", {13, 4, 1}, "macOS", {"Ventura", {22, 5, 0}}},
+ {"22E772610a", {13, 4, 1}, "macOS", {"Ventura", {22, 5, 0}}},
+ {"22F770820d", {13, 4, 1}, "macOS", {"Ventura", {22, 5, 0}}},
+ {"22G74", {13, 5, 0}, "macOS", {"Ventura", {22, 6, 0}}},
+ {"22G90", {13, 5, 1}, "macOS", {"Ventura", {22, 6, 0}}},
+ {"22G91", {13, 5, 2}, "macOS", {"Ventura", {22, 6, 0}}},
+ {"22G120", {13, 6, 0}, "macOS", {"Ventura", {22, 6, 0}}},
+ {"22G313", {13, 6, 1}, "macOS", {"Ventura", {22, 6, 0}}},
+ {"22G320", {13, 6, 2}, "macOS", {"Ventura", {22, 6, 0}}},
+ {"23A344", {23, 0, 0}, "macOS", {"Sonoma", {14, 0, 0}}},
+ {"23B74", {23, 1, 0}, "macOS", {"Sonoma", {14, 1, 0}}},
+ {"23B81", {23, 1, 0}, "macOS", {"Sonoma", {14, 1, 1}}},
+ {"23B92", {23, 1, 0}, "macOS", {"Sonoma", {14, 1, 2}}},
+ {"23C64", {23, 2, 0}, "macOS", {"Sonoma", {14, 2, 0}}},
+ {"23C71", {23, 2, 0}, "macOS", {"Sonoma", {14, 2, 1}}},
};
@@ -867,37 +914,44 @@ gb_internal void report_os_info() { // Scan table for match on BUILD
int macos_release_count = sizeof(macos_release_map) / sizeof(macos_release_map[0]);
- Darwin_To_Release match = {};
-
+ Darwin_To_Release build_match = {};
+ Darwin_To_Release kernel_match = {};
+
for (int build = 0; build < macos_release_count; build++) {
Darwin_To_Release rel = macos_release_map[build];
-
+
// Do we have an exact match on the BUILD?
if (gb_strcmp(rel.build, (const char *)build_buffer) == 0) {
- match = rel;
+ build_match = rel;
break;
}
-
+
// Do we have an exact Darwin match?
if (rel.darwin[0] == major && rel.darwin[1] == minor && rel.darwin[2] == patch) {
- match = rel;
- break;
+ kernel_match = rel;
}
-
+
// Major kernel version needs to match exactly,
if (rel.darwin[0] == major) {
// No major version match yet.
- if (!match.os_name) {
- match = rel;
+ if (!kernel_match.os_name) {
+ kernel_match = rel;
}
if (minor >= rel.darwin[1]) {
- match = rel;
+ kernel_match = rel;
if (patch >= rel.darwin[2]) {
- match = rel;
+ kernel_match = rel;
}
}
}
}
+
+ Darwin_To_Release match = {};
+ if(!build_match.build) {
+ match = kernel_match;
+ } else {
+ match = build_match;
+ }
if (match.os_name) {
gb_printf("%s %s %d", match.os_name, match.release.name, match.release.version[0]);
diff --git a/src/build_settings.cpp b/src/build_settings.cpp index ffb276d1e..af518bcb4 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -56,6 +56,19 @@ enum TargetABIKind : u16 { TargetABI_COUNT, }; +enum Windows_Subsystem : u8 { + Windows_Subsystem_BOOT_APPLICATION, + Windows_Subsystem_CONSOLE, // Default, + Windows_Subsystem_EFI_APPLICATION, + Windows_Subsystem_EFI_BOOT_SERVICE_DRIVER, + Windows_Subsystem_EFI_ROM, + Windows_Subsystem_EFI_RUNTIME_DRIVER, + Windows_Subsystem_NATIVE, + Windows_Subsystem_POSIX, + Windows_Subsystem_WINDOWS, + Windows_Subsystem_WINDOWSCE, + Windows_Subsystem_COUNT, +}; gb_global String target_os_names[TargetOs_COUNT] = { str_lit(""), @@ -82,6 +95,23 @@ gb_global String target_arch_names[TargetArch_COUNT] = { str_lit("wasm64p32"), }; +gb_global String target_microarch_list[TargetArch_COUNT] = { + // TargetArch_Invalid, + str_lit("Invalid!"), + // TargetArch_amd64, + str_lit("alderlake,amdfam10,athlon-fx,athlon64,athlon64-sse3,atom_sse4_2,atom_sse4_2_movbe,barcelona,bdver1,bdver2,bdver3,bdver4,broadwell,btver1,btver2,cannonlake,cascadelake,cooperlake,core-avx-i,core-avx2,core2,core_2_duo_sse4_1,core_2_duo_ssse3,core_2nd_gen_avx,core_3rd_gen_avx,core_4th_gen_avx,core_4th_gen_avx_tsx,core_5th_gen_avx,core_5th_gen_avx_tsx,core_aes_pclmulqdq,core_i7_sse4_2,corei7,corei7-avx,generic,goldmont,goldmont-plus,goldmont_plus,grandridge,graniterapids,graniterapids-d,graniterapids_d,haswell,icelake-client,icelake-server,icelake_client,icelake_server,ivybridge,k8,k8-sse3,knl,knm,meteorlake,mic_avx512,native,nehalem,nocona,opteron,opteron-sse3,penryn,raptorlake,rocketlake,sandybridge,sapphirerapids,sierraforest,silvermont,skx,skylake,skylake-avx512,skylake_avx512,slm,tigerlake,tremont,westmere,x86-64,x86-64-v2,x86-64-v3,x86-64-v4,znver1,znver2,znver3,znver4"), + // TargetArch_i386, + str_lit("athlon,athlon-4,athlon-mp,athlon-tbird,athlon-xp,atom,bonnell,c3,c3-2,generic,geode,i386,i486,i586,i686,k6,k6-2,k6-3,lakemont,native,pentium,pentium-m,pentium-mmx,pentium2,pentium3,pentium3m,pentium4,pentium4m,pentium_4,pentium_4_sse3,pentium_ii,pentium_iii,pentium_iii_no_xmm_regs,pentium_m,pentium_mmx,pentium_pro,pentiumpro,prescott,winchip-c6,winchip2,yonah"), + // TargetArch_arm32, + str_lit("arm1020e,arm1020t,arm1022e,arm10e,arm10tdmi,arm1136j-s,arm1136jf-s,arm1156t2-s,arm1156t2f-s,arm1176jz-s,arm1176jzf-s,arm710t,arm720t,arm7tdmi,arm7tdmi-s,arm8,arm810,arm9,arm920,arm920t,arm922t,arm926ej-s,arm940t,arm946e-s,arm966e-s,arm968e-s,arm9e,arm9tdmi,cortex-a12,cortex-a15,cortex-a17,cortex-a32,cortex-a35,cortex-a5,cortex-a53,cortex-a55,cortex-a57,cortex-a7,cortex-a710,cortex-a72,cortex-a73,cortex-a75,cortex-a76,cortex-a76ae,cortex-a77,cortex-a78,cortex-a78c,cortex-a8,cortex-a9,cortex-m0,cortex-m0plus,cortex-m1,cortex-m23,cortex-m3,cortex-m33,cortex-m35p,cortex-m4,cortex-m55,cortex-m7,cortex-m85,cortex-r4,cortex-r4f,cortex-r5,cortex-r52,cortex-r7,cortex-r8,cortex-x1,cortex-x1c,cyclone,ep9312,exynos-m3,exynos-m4,exynos-m5,generic,iwmmxt,krait,kryo,mpcore,mpcorenovfp,native,neoverse-n1,neoverse-n2,neoverse-v1,sc000,sc300,strongarm,strongarm110,strongarm1100,strongarm1110,swift,xscale"), + // TargetArch_arm64, + str_lit("a64fx,ampere1,ampere1a,apple-a10,apple-a11,apple-a12,apple-a13,apple-a14,apple-a15,apple-a16,apple-a7,apple-a8,apple-a9,apple-latest,apple-m1,apple-m2,apple-s4,apple-s5,carmel,cortex-a34,cortex-a35,cortex-a510,cortex-a53,cortex-a55,cortex-a57,cortex-a65,cortex-a65ae,cortex-a710,cortex-a715,cortex-a72,cortex-a73,cortex-a75,cortex-a76,cortex-a76ae,cortex-a77,cortex-a78,cortex-a78c,cortex-r82,cortex-x1,cortex-x1c,cortex-x2,cortex-x3,cyclone,exynos-m3,exynos-m4,exynos-m5,falkor,generic,kryo,native,neoverse-512tvb,neoverse-e1,neoverse-n1,neoverse-n2,neoverse-v1,neoverse-v2,saphira,thunderx,thunderx2t99,thunderx3t110,thunderxt81,thunderxt83,thunderxt88,tsv110"), + // TargetArch_wasm32, + str_lit("generic"), + // TargetArch_wasm64p32, + str_lit("generic"), +}; + gb_global String target_endian_names[TargetEndian_COUNT] = { str_lit("little"), str_lit("big"), @@ -103,14 +133,25 @@ gb_global TargetEndianKind target_endians[TargetArch_COUNT] = { TargetEndian_Little, }; +gb_global String windows_subsystem_names[Windows_Subsystem_COUNT] = { + str_lit("BOOT_APPLICATION"), + str_lit("CONSOLE"), // Default + str_lit("EFI_APPLICATION"), + str_lit("EFI_BOOT_SERVICE_DRIVER"), + str_lit("EFI_ROM"), + str_lit("EFI_RUNTIME_DRIVER"), + str_lit("NATIVE"), + str_lit("POSIX"), + str_lit("WINDOWS"), + str_lit("WINDOWSCE"), +}; + #ifndef ODIN_VERSION_RAW #define ODIN_VERSION_RAW "dev-unknown-unknown" #endif gb_global String const ODIN_VERSION = str_lit(ODIN_VERSION_RAW); - - struct TargetMetrics { TargetOsKind os; TargetArchKind arch; @@ -272,14 +313,15 @@ enum SanitizerFlags : u32 { // This stores the information for the specify architecture of this build struct BuildContext { // Constants - String ODIN_OS; // target operating system - String ODIN_ARCH; // target architecture - String ODIN_VENDOR; // compiler vendor - String ODIN_VERSION; // compiler version - String ODIN_ROOT; // Odin ROOT - String ODIN_BUILD_PROJECT_NAME; // Odin main/initial package's directory name - bool ODIN_DEBUG; // Odin in debug mode - bool ODIN_DISABLE_ASSERT; // Whether the default 'assert' et al is disabled in code or not + String ODIN_OS; // Target operating system + String ODIN_ARCH; // Target architecture + String ODIN_VENDOR; // Compiler vendor + String ODIN_VERSION; // Compiler version + String ODIN_ROOT; // Odin ROOT + String ODIN_BUILD_PROJECT_NAME; // Odin main/initial package's directory name + String ODIN_WINDOWS_SUBSYSTEM; // Empty string for non-Windows targets + bool ODIN_DEBUG; // Odin in debug mode + bool ODIN_DISABLE_ASSERT; // Whether the default 'assert' et al is disabled in code or not bool ODIN_DEFAULT_TO_NIL_ALLOCATOR; // Whether the default allocator is a "nil" allocator or not (i.e. it does nothing) bool ODIN_FOREIGN_ERROR_PROCEDURES; bool ODIN_VALGRIND_SUPPORT; @@ -346,12 +388,12 @@ struct BuildContext { bool ignore_warnings; bool warnings_as_errors; bool hide_error_line; + bool terse_errors; bool has_ansi_terminal_colours; bool ignore_lazy; bool ignore_llvm_build; - bool use_subsystem_windows; bool ignore_microsoft_magic; bool linker_map_file; @@ -366,6 +408,8 @@ struct BuildContext { bool dynamic_map_calls; + bool obfuscate_source_code_locations; + RelocMode reloc_mode; bool disable_red_zone; @@ -564,7 +608,13 @@ gb_global TargetMetrics target_freestanding_amd64_sysv = { TargetABI_SysV, }; - +gb_global TargetMetrics target_freestanding_arm64 = { + TargetOs_freestanding, + TargetArch_arm64, + 8, 8, 8, 16, + str_lit("aarch64-none-elf"), + str_lit("e-m:o-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"), +}; struct NamedTargetMetrics { String name; @@ -599,6 +649,7 @@ gb_global NamedTargetMetrics named_targets[] = { { str_lit("wasi_wasm64p32"), &target_wasi_wasm64p32 }, { str_lit("freestanding_amd64_sysv"), &target_freestanding_amd64_sysv }, + { str_lit("freestanding_arm64"), &target_freestanding_arm64 }, }; gb_global NamedTargetMetrics *selected_target_metrics; @@ -623,7 +674,6 @@ gb_internal TargetArchKind get_target_arch_from_string(String str) { return TargetArch_Invalid; } - gb_internal bool is_excluded_target_filename(String name) { String original_name = name; name = remove_extension_from_path(name); @@ -1258,8 +1308,6 @@ gb_internal void init_build_context(TargetMetrics *cross_target, Subtarget subta GB_ASSERT(metrics->int_size == 2*metrics->ptr_size); } - - bc->metrics = *metrics; switch (subtarget) { case Subtarget_Default: @@ -1276,14 +1324,14 @@ gb_internal void init_build_context(TargetMetrics *cross_target, Subtarget subta break; } - bc->ODIN_OS = target_os_names[metrics->os]; - bc->ODIN_ARCH = target_arch_names[metrics->arch]; - bc->endian_kind = target_endians[metrics->arch]; - bc->ptr_size = metrics->ptr_size; - bc->int_size = metrics->int_size; - bc->max_align = metrics->max_align; - bc->max_simd_align = metrics->max_simd_align; - bc->link_flags = str_lit(" "); + bc->ODIN_OS = target_os_names[metrics->os]; + bc->ODIN_ARCH = target_arch_names[metrics->arch]; + bc->endian_kind = target_endians[metrics->arch]; + bc->ptr_size = metrics->ptr_size; + bc->int_size = metrics->int_size; + bc->max_align = metrics->max_align; + bc->max_simd_align = metrics->max_simd_align; + bc->link_flags = str_lit(" "); #if defined(DEFAULT_TO_THREADED_CHECKER) bc->threaded_checker = true; @@ -1305,6 +1353,11 @@ gb_internal void init_build_context(TargetMetrics *cross_target, Subtarget subta } } + // Default to subsystem:CONSOLE on Windows targets + if (bc->ODIN_WINDOWS_SUBSYSTEM == "" && bc->metrics.os == TargetOs_windows) { + bc->ODIN_WINDOWS_SUBSYSTEM = windows_subsystem_names[Windows_Subsystem_CONSOLE]; + } + // NOTE(zangent): The linker flags to set the build architecture are different // across OSs. It doesn't make sense to allocate extra data on the heap // here, so I just #defined the linker flags to keep things concise. @@ -1476,7 +1529,7 @@ gb_internal void enable_target_feature(TokenPos pos, String const &target_featur } -gb_internal char const *target_features_set_to_cstring(gbAllocator allocator, bool with_quotes) { +gb_internal char const *target_features_set_to_cstring(gbAllocator allocator, bool with_quotes, bool with_plus) { isize len = 0; isize i = 0; for (String const &feature : build_context.target_features_set) { @@ -1485,6 +1538,7 @@ gb_internal char const *target_features_set_to_cstring(gbAllocator allocator, bo } len += feature.len; if (with_quotes) len += 2; + if (with_plus) len += 1; i += 1; } char *features = gb_alloc_array(allocator, char, len+1); @@ -1496,6 +1550,7 @@ gb_internal char const *target_features_set_to_cstring(gbAllocator allocator, bo } if (with_quotes) features[len++] = '"'; + if (with_plus) features[len++] = '+'; gb_memmove(features + len, feature.text, feature.len); len += feature.len; if (with_quotes) features[len++] = '"'; diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index 77ba6b435..09ca0bc23 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -1083,13 +1083,16 @@ gb_internal bool cache_load_file_directive(CheckerContext *c, Ast *call, String ast_node(bd, BasicDirective, ce->proc); String builtin_name = bd->name.string; - String base_dir = dir_from_path(get_file_path_string(call->file_id)); - - BlockingMutex *ignore_mutex = nullptr; - String path = {}; - bool ok = determine_path_from_string(ignore_mutex, call, base_dir, original_string, &path); - gb_unused(ok); + String path; + if (gb_path_is_absolute((char*)original_string.text)) { + path = original_string; + } else { + String base_dir = dir_from_path(get_file_path_string(call->file_id)); + BlockingMutex *ignore_mutex = nullptr; + bool ok = determine_path_from_string(ignore_mutex, call, base_dir, original_string, &path); + gb_unused(ok); + } MUTEX_GUARD(&c->info->load_file_mutex); @@ -1663,7 +1666,12 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As if (ce->args.count > 0) { if (ce->args[0]->kind == Ast_FieldValue) { - if (id != BuiltinProc_soa_zip) { + switch (id) { + case BuiltinProc_soa_zip: + case BuiltinProc_quaternion: + // okay + break; + default: error(call, "'field = value' calling is not allowed on built-in procedures"); return false; } @@ -2088,6 +2096,8 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As t = default_type(t); add_type_info_type(c, t); + GB_ASSERT(t_type_info_ptr != nullptr); + add_type_info_type(c, t_type_info_ptr); if (is_operand_value(o) && is_type_typeid(t)) { add_package_dependency(c, "runtime", "__type_info_of"); @@ -2294,61 +2304,150 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As } case BuiltinProc_quaternion: { - // quaternion :: proc(real, imag, jmag, kmag: float_type) -> complex_type - Operand x = *operand; - Operand y = {}; - Operand z = {}; - Operand w = {}; + bool first_is_field_value = (ce->args[0]->kind == Ast_FieldValue); + + bool fail = false; + for (Ast *arg : ce->args) { + bool mix = false; + if (first_is_field_value) { + mix = arg->kind != Ast_FieldValue; + } else { + mix = arg->kind == Ast_FieldValue; + } + if (mix) { + error(arg, "Mixture of 'field = value' and value elements in the procedure call '%.*s' is not allowed", LIT(builtin_name)); + fail = true; + break; + } + } + + if (fail) { + operand->type = t_untyped_quaternion; + operand->mode = Addressing_Constant; + operand->value = exact_value_quaternion(0.0, 0.0, 0.0, 0.0); + break; + } + + // quaternion :: proc(imag, jmag, kmag, real: float_type) -> complex_type + Operand xyzw[4] = {}; + + u32 first_index = 0; // NOTE(bill): Invalid will be the default till fixed operand->type = t_invalid; operand->mode = Addressing_Invalid; - check_expr(c, &y, ce->args[1]); - if (y.mode == Addressing_Invalid) { - return false; - } - check_expr(c, &z, ce->args[2]); - if (y.mode == Addressing_Invalid) { - return false; - } - check_expr(c, &w, ce->args[3]); - if (y.mode == Addressing_Invalid) { - return false; - } + if (first_is_field_value) { + u32 fields_set[4] = {}; // 0 unset, 1 xyzw, 2 real/etc - convert_to_typed(c, &x, y.type); if (x.mode == Addressing_Invalid) return false; - convert_to_typed(c, &y, x.type); if (y.mode == Addressing_Invalid) return false; - convert_to_typed(c, &z, x.type); if (z.mode == Addressing_Invalid) return false; - convert_to_typed(c, &w, x.type); if (w.mode == Addressing_Invalid) return false; - if (x.mode == Addressing_Constant && - y.mode == Addressing_Constant && - z.mode == Addressing_Constant && - w.mode == Addressing_Constant) { - x.value = exact_value_to_float(x.value); - y.value = exact_value_to_float(y.value); - z.value = exact_value_to_float(z.value); - w.value = exact_value_to_float(w.value); - if (is_type_numeric(x.type) && x.value.kind == ExactValue_Float) { - x.type = t_untyped_float; + auto const check_field = [&fields_set, &builtin_name](CheckerContext *c, Operand *o, Ast *arg, i32 *index) -> bool { + *index = -1; + + ast_node(field, FieldValue, arg); + String name = {}; + if (field->field->kind == Ast_Ident) { + name = field->field->Ident.token.string; + } else { + error(field->field, "Expected an identifier for field argument"); + return false; + } + + u32 style = 0; + + if (name == "x") { + *index = 0; style = 1; + } else if (name == "y") { + *index = 1; style = 1; + } else if (name == "z") { + *index = 2; style = 1; + } else if (name == "w") { + *index = 3; style = 1; + } else if (name == "imag") { + *index = 0; style = 2; + } else if (name == "jmag") { + *index = 1; style = 2; + } else if (name == "kmag") { + *index = 2; style = 2; + } else if (name == "real") { + *index = 3; style = 2; + } else { + error(field->field, "Unknown name for '%.*s', expected (w, x, y, z; or real, imag, jmag, kmag), got '%.*s'", LIT(builtin_name), LIT(name)); + return false; + } + + if (fields_set[*index]) { + error(field->field, "Previously assigned field: '%.*s'", LIT(name)); + } + fields_set[*index] = style; + + check_expr(c, o, field->value); + return o->mode != Addressing_Invalid; + }; + + Operand *refs[4] = {&xyzw[0], &xyzw[1], &xyzw[2], &xyzw[3]}; + + for (i32 i = 0; i < 4; i++) { + i32 index = -1; + Operand o = {}; + bool ok = check_field(c, &o, ce->args[i], &index); + if (!ok || index < 0) { + return false; + } + first_index = cast(u32)index; + *refs[index] = o; } - if (is_type_numeric(y.type) && y.value.kind == ExactValue_Float) { - y.type = t_untyped_float; + + for (i32 i = 0; i < 4; i++) { + GB_ASSERT(fields_set[i]); } - if (is_type_numeric(z.type) && z.value.kind == ExactValue_Float) { - z.type = t_untyped_float; + for (i32 i = 1; i < 4; i++) { + if (fields_set[i] != fields_set[i-1]) { + error(call, "Mixture of xyzw and real/etc is not allowed with '%.*s'", LIT(builtin_name)); + break; + } } - if (is_type_numeric(w.type) && w.value.kind == ExactValue_Float) { - w.type = t_untyped_float; + } else { + error(call, "'%.*s' requires that all arguments are named (w, x, y, z; or real, imag, jmag, kmag)", LIT(builtin_name)); + + for (i32 i = 0; i < 4; i++) { + check_expr(c, &xyzw[i], ce->args[i]); + if (xyzw[i].mode == Addressing_Invalid) { + return false; + } } } - if (!(are_types_identical(x.type, y.type) && are_types_identical(x.type, z.type) && are_types_identical(x.type, w.type))) { - gbString tx = type_to_string(x.type); - gbString ty = type_to_string(y.type); - gbString tz = type_to_string(z.type); - gbString tw = type_to_string(w.type); - error(call, "Mismatched types to 'quaternion', '%s' vs '%s' vs '%s' vs '%s'", tx, ty, tz, tw); + + for (u32 i = 0; i < 4; i++ ){ + u32 j = (i + first_index) % 4; + if (j == first_index) { + convert_to_typed(c, &xyzw[j], xyzw[(first_index+1)%4].type); if (xyzw[j].mode == Addressing_Invalid) return false; + } else { + convert_to_typed(c, &xyzw[j], xyzw[first_index].type); if (xyzw[j].mode == Addressing_Invalid) return false; + } + } + if (xyzw[0].mode == Addressing_Constant && + xyzw[1].mode == Addressing_Constant && + xyzw[2].mode == Addressing_Constant && + xyzw[3].mode == Addressing_Constant) { + for (i32 i = 0; i < 4; i++) { + xyzw[i].value = exact_value_to_float(xyzw[i].value); + } + for (i32 i = 0; i < 4; i++) { + if (is_type_numeric(xyzw[i].type) && xyzw[i].value.kind == ExactValue_Float) { + xyzw[i].type = t_untyped_float; + } + } + } + + if (!(are_types_identical(xyzw[0].type, xyzw[1].type) && + are_types_identical(xyzw[0].type, xyzw[2].type) && + are_types_identical(xyzw[0].type, xyzw[3].type))) { + gbString tx = type_to_string(xyzw[0].type); + gbString ty = type_to_string(xyzw[1].type); + gbString tz = type_to_string(xyzw[2].type); + gbString tw = type_to_string(xyzw[3].type); + error(call, "Mismatched types to 'quaternion', 'x=%s' vs 'y=%s' vs 'z=%s' vs 'w=%s'", tx, ty, tz, tw); gb_string_free(tw); gb_string_free(tz); gb_string_free(ty); @@ -2356,31 +2455,35 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As return false; } - if (!is_type_float(x.type)) { - gbString s = type_to_string(x.type); + if (!is_type_float(xyzw[0].type)) { + gbString s = type_to_string(xyzw[0].type); error(call, "Arguments have type '%s', expected a floating point", s); gb_string_free(s); return false; } - if (is_type_endian_specific(x.type)) { - gbString s = type_to_string(x.type); + if (is_type_endian_specific(xyzw[0].type)) { + gbString s = type_to_string(xyzw[0].type); error(call, "Arguments with a specified endian are not allow, expected a normal floating point, got '%s'", s); gb_string_free(s); return false; } - if (x.mode == Addressing_Constant && y.mode == Addressing_Constant && z.mode == Addressing_Constant && w.mode == Addressing_Constant) { - f64 r = exact_value_to_float(x.value).value_float; - f64 i = exact_value_to_float(y.value).value_float; - f64 j = exact_value_to_float(z.value).value_float; - f64 k = exact_value_to_float(w.value).value_float; + + operand->mode = Addressing_Value; + + if (xyzw[0].mode == Addressing_Constant && + xyzw[1].mode == Addressing_Constant && + xyzw[2].mode == Addressing_Constant && + xyzw[3].mode == Addressing_Constant) { + f64 r = exact_value_to_float(xyzw[3].value).value_float; + f64 i = exact_value_to_float(xyzw[0].value).value_float; + f64 j = exact_value_to_float(xyzw[1].value).value_float; + f64 k = exact_value_to_float(xyzw[2].value).value_float; operand->value = exact_value_quaternion(r, i, j, k); operand->mode = Addressing_Constant; - } else { - operand->mode = Addressing_Value; } - BasicKind kind = core_type(x.type)->Basic.kind; + BasicKind kind = core_type(xyzw[first_index].type)->Basic.kind; switch (kind) { case Basic_f16: operand->type = t_quaternion64; break; case Basic_f32: operand->type = t_quaternion128; break; @@ -3087,7 +3190,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As mix = arg->kind == Ast_FieldValue; } if (mix) { - error(arg, "Mixture of 'field = value' and value elements in the procedure call 'soa_zip' is not allowed"); + error(arg, "Mixture of 'field = value' and value elements in the procedure call '%.*s' is not allowed", LIT(builtin_name)); fail = true; break; } @@ -5117,6 +5220,202 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As } break; + case BuiltinProc_type_union_tag_type: + { + if (operand->mode != Addressing_Type) { + error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name)); + operand->mode = Addressing_Invalid; + operand->type = t_invalid; + return false; + } + + Type *u = operand->type; + + if (!is_type_union(u)) { + error(operand->expr, "Expected a union type for '%.*s'", LIT(builtin_name)); + operand->mode = Addressing_Invalid; + operand->type = t_invalid; + return false; + } + + u = base_type(u); + GB_ASSERT(u->kind == Type_Union); + + operand->mode = Addressing_Type; + operand->type = union_tag_type(u); + } + break; + + case BuiltinProc_type_union_tag_offset: + { + if (operand->mode != Addressing_Type) { + error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name)); + operand->mode = Addressing_Invalid; + operand->type = t_invalid; + return false; + } + + Type *u = operand->type; + + if (!is_type_union(u)) { + error(operand->expr, "Expected a union type for '%.*s'", LIT(builtin_name)); + operand->mode = Addressing_Invalid; + operand->type = t_invalid; + return false; + } + + u = base_type(u); + GB_ASSERT(u->kind == Type_Union); + + // NOTE(jakubtomsu): forces calculation of variant_block_size + type_size_of(u); + i64 tag_offset = u->Union.variant_block_size; + GB_ASSERT(tag_offset > 0); + + operand->mode = Addressing_Constant; + operand->type = t_untyped_integer; + operand->value = exact_value_i64(tag_offset); + } + break; + + case BuiltinProc_type_union_base_tag_value: + { + if (operand->mode != Addressing_Type) { + error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name)); + operand->mode = Addressing_Invalid; + operand->type = t_invalid; + return false; + } + + Type *u = operand->type; + + if (!is_type_union(u)) { + error(operand->expr, "Expected a union type for '%.*s'", LIT(builtin_name)); + operand->mode = Addressing_Invalid; + operand->type = t_invalid; + return false; + } + + u = base_type(u); + GB_ASSERT(u->kind == Type_Union); + + operand->mode = Addressing_Constant; + operand->type = t_untyped_integer; + operand->value = exact_value_i64(u->Union.kind == UnionType_no_nil ? 0 : 1); + } break; + + case BuiltinProc_type_union_variant_count: + { + if (operand->mode != Addressing_Type) { + error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name)); + operand->mode = Addressing_Invalid; + operand->type = t_invalid; + return false; + } + + Type *u = operand->type; + + if (!is_type_union(u)) { + error(operand->expr, "Expected a union type for '%.*s'", LIT(builtin_name)); + operand->mode = Addressing_Invalid; + operand->type = t_invalid; + return false; + } + + u = base_type(u); + GB_ASSERT(u->kind == Type_Union); + + operand->mode = Addressing_Constant; + operand->type = t_untyped_integer; + operand->value = exact_value_i64(u->Union.variants.count); + } break; + + case BuiltinProc_type_variant_type_of: + { + if (operand->mode != Addressing_Type) { + error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name)); + operand->mode = Addressing_Invalid; + operand->type = t_invalid; + return false; + } + + Type *u = operand->type; + + if (!is_type_union(u)) { + error(operand->expr, "Expected a union type for '%.*s'", LIT(builtin_name)); + operand->mode = Addressing_Invalid; + operand->type = t_invalid; + return false; + } + + u = base_type(u); + GB_ASSERT(u->kind == Type_Union); + Operand x = {}; + check_expr_or_type(c, &x, ce->args[1]); + if (!is_type_integer(x.type) || x.mode != Addressing_Constant) { + error(call, "Expected a constant integer for '%.*s", LIT(builtin_name)); + operand->mode = Addressing_Type; + operand->type = t_invalid; + return false; + } + + i64 index = big_int_to_i64(&x.value.value_integer); + if (index < 0 || index >= u->Union.variants.count) { + error(call, "Variant tag out of bounds index for '%.*s", LIT(builtin_name)); + operand->mode = Addressing_Type; + operand->type = t_invalid; + return false; + } + + operand->mode = Addressing_Type; + operand->type = u->Union.variants[index]; + } + break; + + case BuiltinProc_type_variant_index_of: + { + if (operand->mode != Addressing_Type) { + error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name)); + operand->mode = Addressing_Invalid; + operand->type = t_invalid; + return false; + } + + Type *u = operand->type; + + if (!is_type_union(u)) { + error(operand->expr, "Expected a union type for '%.*s'", LIT(builtin_name)); + operand->mode = Addressing_Invalid; + operand->type = t_invalid; + return false; + } + + Type *v = check_type(c, ce->args[1]); + u = base_type(u); + GB_ASSERT(u->kind == Type_Union); + + i64 index = -1; + for_array(i, u->Union.variants) { + Type *vt = u->Union.variants[i]; + if (union_variant_index_types_equal(v, vt)) { + index = i64(i); + break; + } + } + + if (index < 0) { + error(operand->expr, "Expected a variant type for '%.*s'", LIT(builtin_name)); + operand->mode = Addressing_Invalid; + operand->type = t_invalid; + return false; + } + + operand->mode = Addressing_Constant; + operand->type = t_untyped_integer; + operand->value = exact_value_i64(index); + } + break; + case BuiltinProc_type_struct_field_count: operand->value = exact_value_i64(0); if (operand->mode != Addressing_Type) { diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 71b897a84..ed3a109c2 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -138,11 +138,10 @@ gb_internal void check_init_variables(CheckerContext *ctx, Entity **lhs, isize l } if (o->type && is_type_no_copy(o->type)) { - begin_error_block(); + ERROR_BLOCK(); if (check_no_copy_assignment(*o, str_lit("initialization"))) { error_line("\tInitialization of a #no_copy type must be either implicitly zero, a constant literal, or a return value from a call expression"); } - end_error_block(); } } if (rhs_count > 0 && lhs_count != rhs_count) { @@ -908,7 +907,91 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) { break; } + e->Procedure.entry_point_only = ac.entry_point_only; e->Procedure.is_export = ac.is_export; + + bool has_instrumentation = false; + if (pl->body == nullptr) { + has_instrumentation = false; + if (ac.no_instrumentation != Instrumentation_Default) { + error(e->token, "@(no_instrumentation) is not allowed on foreign procedures"); + } + } else { + AstFile *file = e->token.pos.file_id ? global_files[e->token.pos.file_id] : nullptr; + if (file) { + has_instrumentation = (file->flags & AstFile_NoInstrumentation) == 0; + } + + switch (ac.no_instrumentation) { + case Instrumentation_Enabled: has_instrumentation = true; break; + case Instrumentation_Default: break; + case Instrumentation_Disabled: has_instrumentation = false; break; + } + } + + auto const is_valid_instrumentation_call = [](Type *type) -> bool { + if (type == nullptr || type->kind != Type_Proc) { + return false; + } + if (type->Proc.calling_convention != ProcCC_Contextless) { + return false; + } + if (type->Proc.result_count != 0) { + return false; + } + if (type->Proc.param_count != 3) { + return false; + } + Type *p0 = type->Proc.params->Tuple.variables[0]->type; + Type *p1 = type->Proc.params->Tuple.variables[1]->type; + Type *p3 = type->Proc.params->Tuple.variables[2]->type; + return is_type_rawptr(p0) && is_type_rawptr(p1) && are_types_identical(p3, t_source_code_location); + }; + + static char const *instrumentation_proc_type_str = "proc \"contextless\" (proc_address: rawptr, call_site_return_address: rawptr, loc: runtime.Source_Code_Location)"; + + if (ac.instrumentation_enter && ac.instrumentation_exit) { + error(e->token, "A procedure cannot be marked with both @(instrumentation_enter) and @(instrumentation_exit)"); + + has_instrumentation = false; + e->flags |= EntityFlag_Require; + } else if (ac.instrumentation_enter) { + if (!is_valid_instrumentation_call(e->type)) { + init_core_source_code_location(ctx->checker); + gbString s = type_to_string(e->type); + error(e->token, "@(instrumentation_enter) procedures must have the type '%s', got %s", instrumentation_proc_type_str, s); + gb_string_free(s); + } + MUTEX_GUARD(&ctx->info->instrumentation_mutex); + if (ctx->info->instrumentation_enter_entity != nullptr) { + error(e->token, "@(instrumentation_enter) has already been set"); + } else { + ctx->info->instrumentation_enter_entity = e; + } + + has_instrumentation = false; + e->flags |= EntityFlag_Require; + } else if (ac.instrumentation_exit) { + init_core_source_code_location(ctx->checker); + if (!is_valid_instrumentation_call(e->type)) { + gbString s = type_to_string(e->type); + error(e->token, "@(instrumentation_exit) procedures must have the type '%s', got %s", instrumentation_proc_type_str, s); + gb_string_free(s); + } + MUTEX_GUARD(&ctx->info->instrumentation_mutex); + if (ctx->info->instrumentation_exit_entity != nullptr) { + error(e->token, "@(instrumentation_exit) has already been set"); + } else { + ctx->info->instrumentation_exit_entity = e; + } + + has_instrumentation = false; + e->flags |= EntityFlag_Require; + } + + e->Procedure.has_instrumentation = has_instrumentation; + + 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); @@ -1300,8 +1383,8 @@ gb_internal void check_proc_group_decl(CheckerContext *ctx, Entity *pg_entity, D continue; } - begin_error_block(); - defer (end_error_block()); + + ERROR_BLOCK(); ProcTypeOverloadKind kind = are_proc_types_overload_safe(p->type, q->type); bool both_have_where_clauses = false; diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 5cc548739..bc7ff1bbb 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -184,6 +184,8 @@ gb_internal void populate_check_did_you_mean_objc_entity(StringSet *set, Entity gb_internal void check_did_you_mean_objc_entity(String const &name, Entity *e, bool is_type, char const *prefix = "") { + if (build_context.terse_errors) { return; } + ERROR_BLOCK(); GB_ASSERT(e->kind == Entity_TypeName); GB_ASSERT(e->TypeName.objc_metadata != nullptr); @@ -204,6 +206,8 @@ gb_internal void check_did_you_mean_objc_entity(String const &name, Entity *e, b } gb_internal void check_did_you_mean_type(String const &name, Array<Entity *> const &fields, char const *prefix = "") { + if (build_context.terse_errors) { return; } + ERROR_BLOCK(); DidYouMeanAnswers d = did_you_mean_make(heap_allocator(), fields.count, name); @@ -217,6 +221,8 @@ gb_internal void check_did_you_mean_type(String const &name, Array<Entity *> con gb_internal void check_did_you_mean_type(String const &name, Slice<Entity *> const &fields, char const *prefix = "") { + if (build_context.terse_errors) { return; } + ERROR_BLOCK(); DidYouMeanAnswers d = did_you_mean_make(heap_allocator(), fields.count, name); @@ -229,6 +235,8 @@ gb_internal void check_did_you_mean_type(String const &name, Slice<Entity *> con } gb_internal void check_did_you_mean_scope(String const &name, Scope *scope, char const *prefix = "") { + if (build_context.terse_errors) { return; } + ERROR_BLOCK(); DidYouMeanAnswers d = did_you_mean_make(heap_allocator(), scope->elements.count, name); @@ -2203,7 +2211,6 @@ gb_internal bool check_is_expressible(CheckerContext *ctx, Operand *o, Type *typ ERROR_BLOCK(); - if (is_type_numeric(o->type) && is_type_numeric(type)) { if (!is_type_integer(o->type) && is_type_integer(type)) { error(o->expr, "'%s' truncated to '%s', got %s", a, b, s); @@ -2264,8 +2271,7 @@ gb_internal void check_old_for_or_switch_value_usage(Ast *expr) { if (e != nullptr && (e->flags & EntityFlag_OldForOrSwitchValue) != 0) { GB_ASSERT(e->kind == Entity_Variable); - begin_error_block(); - defer (end_error_block()); + ERROR_BLOCK(); if ((e->flags & EntityFlag_ForValue) != 0) { Type *parent_type = type_deref(e->Variable.for_loop_parent_type); @@ -2309,8 +2315,7 @@ gb_internal void check_unary_expr(CheckerContext *c, Operand *o, Token op, Ast * break; default: { - begin_error_block(); - defer (end_error_block()); + ERROR_BLOCK(); error(op, "Cannot take the pointer address of '%s'", str); if (e != nullptr && (e->flags & EntityFlag_ForValue) != 0) { Type *parent_type = type_deref(e->Variable.for_loop_parent_type); @@ -2339,7 +2344,7 @@ gb_internal void check_unary_expr(CheckerContext *c, Operand *o, Token op, Ast * ast_node(ue, UnaryExpr, node); if (ast_node_expect(ue->expr, Ast_IndexExpr)) { ast_node(ie, IndexExpr, ue->expr); - Type *soa_type = type_of_expr(ie->expr); + Type *soa_type = type_deref(type_of_expr(ie->expr)); GB_ASSERT(is_type_soa_struct(soa_type)); o->type = alloc_type_soa_pointer(soa_type); } else { @@ -3071,7 +3076,7 @@ gb_internal void check_cast(CheckerContext *c, Operand *x, Type *type) { x->mode = Addressing_Invalid; - begin_error_block(); + ERROR_BLOCK(); error(x->expr, "Cannot cast '%s' as '%s' from '%s'", expr_str, to_type, from_type); if (is_const_expr) { gbString val_str = exact_value_to_string(x->value); @@ -3094,8 +3099,6 @@ gb_internal void check_cast(CheckerContext *c, Operand *x, Type *type) { } check_cast_error_suggestion(c, x, type); - end_error_block(); - return; } @@ -4047,8 +4050,7 @@ gb_internal void convert_to_typed(CheckerContext *c, Operand *operand, Type *tar if (check_is_assignable_to(c, operand, elem)) { if (t->Matrix.row_count != t->Matrix.column_count) { operand->mode = Addressing_Invalid; - begin_error_block(); - defer (end_error_block()); + ERROR_BLOCK(); convert_untyped_error(c, operand, target_type); error_line("\tNote: Only a square matrix types can be initialized with a scalar value\n"); @@ -4109,8 +4111,7 @@ gb_internal void convert_to_typed(CheckerContext *c, Operand *operand, Type *tar target_type = t->Union.variants[first_success_index]; break; } else if (valid_count > 1) { - begin_error_block(); - defer (end_error_block()); + ERROR_BLOCK(); GB_ASSERT(first_success_index >= 0); operand->mode = Addressing_Invalid; @@ -4136,8 +4137,7 @@ gb_internal void convert_to_typed(CheckerContext *c, Operand *operand, Type *tar } else if (is_type_untyped_uninit(operand->type)) { target_type = t_untyped_uninit; } else if (!is_type_untyped_nil(operand->type) || !type_has_nil(target_type)) { - begin_error_block(); - defer (end_error_block()); + ERROR_BLOCK(); operand->mode = Addressing_Invalid; convert_untyped_error(c, operand, target_type); @@ -4714,6 +4714,7 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod entity = scope_lookup_current(import_scope, entity_name); bool allow_builtin = false; if (!is_entity_declared_for_selector(entity, import_scope, &allow_builtin)) { + ERROR_BLOCK(); error(node, "'%.*s' is not declared by '%.*s'", LIT(entity_name), LIT(import_name)); operand->mode = Addressing_Invalid; operand->expr = node; @@ -4914,6 +4915,8 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod error(op_expr, "Type '%s' has no field '%s'", op_str, sel_str); } } else { + ERROR_BLOCK(); + error(op_expr, "'%s' of type '%s' has no field '%s'", op_str, type_str, sel_str); if (operand->type != nullptr && selector->kind == Ast_Ident) { @@ -5606,9 +5609,6 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A for (isize i = 0; i < pt->param_count; i++) { if (!visited[i]) { Entity *e = pt->params->Tuple.variables[i]; - if (is_blank_ident(e->token)) { - continue; - } if (e->kind == Entity_Variable) { if (e->Variable.param_value.kind != ParameterValue_Invalid) { ordered_operands[i].mode = Addressing_Value; @@ -5652,6 +5652,14 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A } else { if (show_error) { check_assignment(c, o, param_type, str_lit("procedure argument")); + + Type *src = base_type(o->type); + Type *dst = base_type(param_type); + if (is_type_slice(src) && are_types_identical(src->Slice.elem, dst)) { + gbString a = expr_to_string(o->expr); + error_line("\tSuggestion: Did you mean to pass the slice into the variadic parameter with ..%s?\n\n", a); + gb_string_free(a); + } } err = CallArgumentError_WrongTypes; } @@ -6333,8 +6341,7 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c, }; if (valids.count == 0) { - begin_error_block(); - defer (end_error_block()); + ERROR_BLOCK(); error(operand->expr, "No procedures or ambiguous call for procedure group '%s' that match with the given arguments", expr_name); if (positional_operands.count == 0 && named_operands.count == 0) { @@ -6424,8 +6431,7 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c, data.result_type = t_invalid; } else if (valids.count > 1) { - begin_error_block(); - defer (end_error_block()); + ERROR_BLOCK(); error(operand->expr, "Ambiguous procedure group call '%s' that match with the given arguments", expr_name); print_argument_types(); @@ -7190,6 +7196,14 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c } } add_entity_use(c, operand->expr, initial_entity); + + if (initial_entity->Procedure.entry_point_only) { + if (c->curr_proc_decl && c->curr_proc_decl->entity == c->info->entry_point) { + // Okay + } else { + error(operand->expr, "Procedures with the attribute '@(entry_point_only)' can only be called directly from the user-level entry point procedure"); + } + } } if (operand->mode != Addressing_ProcGroup) { @@ -7420,7 +7434,7 @@ gb_internal bool check_set_index_data(Operand *o, Type *t, bool indirection, i64 *max_count = t->Struct.soa_count; } o->type = t->Struct.soa_elem; - if (o->mode == Addressing_SoaVariable || o->mode == Addressing_Variable) { + if (o->mode == Addressing_SoaVariable || o->mode == Addressing_Variable || indirection) { o->mode = Addressing_SoaVariable; } else { o->mode = Addressing_Value; @@ -7636,6 +7650,8 @@ gb_internal ExprKind check_implicit_selector_expr(CheckerContext *c, Operand *o, String name = ise->selector->Ident.token.string; if (is_type_enum(th)) { + ERROR_BLOCK(); + Type *bt = base_type(th); GB_ASSERT(bt->kind == Type_Enum); @@ -7879,7 +7895,7 @@ gb_internal ExprKind check_basic_directive_expr(CheckerContext *c, Operand *o, A } else { if (name == "location") { init_core_source_code_location(c->checker); - error(node, "'#%.*s' must be used in a call expression", LIT(name)); + error(node, "'#location' must be used as a call, i.e. #location(proc), where #location() defaults to the procedure in which it was used."); o->type = t_source_code_location; o->mode = Addressing_Value; } else if ( @@ -9037,8 +9053,7 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast * } if (unhandled.count > 0) { - begin_error_block(); - defer (end_error_block()); + ERROR_BLOCK(); if (unhandled.count == 1) { error_no_newline(node, "Unhandled enumerated array case: %.*s", LIT(unhandled[0]->token.string)); @@ -9049,9 +9064,11 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast * error_line("\t%.*s\n", LIT(f->token.string)); } } - error_line("\n"); - error_line("\tSuggestion: Was '#partial %s{...}' wanted?\n", type_to_string(type)); + if (!build_context.terse_errors) { + error_line("\n"); + error_line("\tSuggestion: Was '#partial %s{...}' wanted?\n", type_to_string(type)); + } } } @@ -9675,7 +9692,9 @@ gb_internal ExprKind check_index_expr(CheckerContext *c, Operand *o, Ast *node, if (index < 0) { gbString str = expr_to_string(o->expr); error(o->expr, "Cannot index a constant '%s'", str); - error_line("\tSuggestion: store the constant into a variable in order to index it with a variable index\n"); + if (!build_context.terse_errors) { + error_line("\tSuggestion: store the constant into a variable in order to index it with a variable index\n"); + } gb_string_free(str); o->mode = Addressing_Invalid; o->expr = node; @@ -9689,7 +9708,9 @@ gb_internal ExprKind check_index_expr(CheckerContext *c, Operand *o, Ast *node, if (!success) { gbString str = expr_to_string(o->expr); error(o->expr, "Cannot index a constant '%s' with index %lld", str, cast(long long)index); - error_line("\tSuggestion: store the constant into a variable in order to index it with a variable index\n"); + if (!build_context.terse_errors) { + error_line("\tSuggestion: store the constant into a variable in order to index it with a variable index\n"); + } gb_string_free(str); o->mode = Addressing_Invalid; o->expr = node; @@ -9877,7 +9898,9 @@ gb_internal ExprKind check_slice_expr(CheckerContext *c, Operand *o, Ast *node, if (!all_constant) { gbString str = expr_to_string(o->expr); error(o->expr, "Cannot slice '%s' with non-constant indices", str); - error_line("\tSuggestion: store the constant into a variable in order to index it with a variable index\n"); + if (!build_context.terse_errors) { + error_line("\tSuggestion: store the constant into a variable in order to index it with a variable index\n"); + } gb_string_free(str); o->mode = Addressing_Value; // NOTE(bill): Keep subsequent values going without erring o->expr = node; @@ -10233,15 +10256,15 @@ gb_internal ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast } else { gbString str = expr_to_string(o->expr); gbString typ = type_to_string(o->type); - begin_error_block(); + ERROR_BLOCK(); error(o->expr, "Cannot dereference '%s' of type '%s'", str, typ); if (o->type && is_type_multi_pointer(o->type)) { - error_line("\tDid you mean '%s[0]'?\n", str); + if (!build_context.terse_errors) { + error_line("\tDid you mean '%s[0]'?\n", str); + } } - end_error_block(); - gb_string_free(typ); gb_string_free(str); o->mode = Addressing_Invalid; diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index b93be734e..d56e5e212 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -1085,8 +1085,7 @@ gb_internal void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags } if (unhandled.count > 0) { - begin_error_block(); - defer (end_error_block()); + ERROR_BLOCK(); if (unhandled.count == 1) { error_no_newline(node, "Unhandled switch case: %.*s", LIT(unhandled[0]->token.string)); @@ -1813,7 +1812,7 @@ gb_internal void check_value_decl_stmt(CheckerContext *ctx, Ast *node, u32 mod_f } if (new_name_count == 0) { - begin_error_block(); + ERROR_BLOCK(); error(node, "No new declarations on the left hand side"); bool all_underscore = true; for (Ast *name : vd->names) { @@ -1831,7 +1830,6 @@ gb_internal void check_value_decl_stmt(CheckerContext *ctx, Ast *node, u32 mod_f error_line("\tSuggestion: Try changing the declaration (:=) to an assignment (=)\n"); } - end_error_block(); } Type *init_type = nullptr; diff --git a/src/check_type.cpp b/src/check_type.cpp index d66b196bc..a95026711 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -2070,33 +2070,33 @@ gb_internal bool check_procedure_type(CheckerContext *ctx, Type *type, Ast *proc type->Proc.diverging = pt->diverging; type->Proc.optional_ok = optional_ok; - if (param_count > 0) { - Entity *end = params->Tuple.variables[param_count-1]; - if (end->flags&EntityFlag_CVarArg) { + bool is_polymorphic = false; + for (isize i = 0; i < param_count; i++) { + Entity *e = params->Tuple.variables[i]; + + if (e->kind != Entity_Variable) { + is_polymorphic = true; + } else if (is_type_polymorphic(e->type)) { + is_polymorphic = true; + } + + if (e->flags&EntityFlag_CVarArg) { + if (i != param_count - 1) { + error(e->token, "#c_vararg can only be applied to the last parameter"); + continue; + } + switch (cc) { default: type->Proc.c_vararg = true; break; case ProcCC_Odin: case ProcCC_Contextless: - error(end->token, "Calling convention does not support #c_vararg"); + error(e->token, "Calling convention does not support #c_vararg"); break; } } } - - - bool is_polymorphic = false; - for (isize i = 0; i < param_count; i++) { - Entity *e = params->Tuple.variables[i]; - if (e->kind != Entity_Variable) { - is_polymorphic = true; - break; - } else if (is_type_polymorphic(e->type)) { - is_polymorphic = true; - break; - } - } for (isize i = 0; i < result_count; i++) { Entity *e = results->Tuple.variables[i]; if (e->kind != Entity_Variable) { @@ -2702,14 +2702,13 @@ gb_internal bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, T check_expr_or_type(&c, &o, pt->type); if (o.mode != Addressing_Invalid && o.mode != Addressing_Type) { // NOTE(bill): call check_type_expr again to get a consistent error message - begin_error_block(); + ERROR_BLOCK(); elem = check_type_expr(&c, pt->type, nullptr); if (o.mode == Addressing_Variable) { gbString s = expr_to_string(pt->type); error_line("\tSuggestion: ^ is used for pointer types, did you mean '&%s'?\n", s); gb_string_free(s); } - end_error_block(); } else { elem = o.type; } diff --git a/src/checker.cpp b/src/checker.cpp index 29f22bd9c..4d7514d0b 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -968,10 +968,11 @@ gb_internal void init_universal(void) { add_global_bool_constant("true", true); add_global_bool_constant("false", false); - add_global_string_constant("ODIN_VENDOR", bc->ODIN_VENDOR); - add_global_string_constant("ODIN_VERSION", bc->ODIN_VERSION); - add_global_string_constant("ODIN_ROOT", bc->ODIN_ROOT); + add_global_string_constant("ODIN_VENDOR", bc->ODIN_VENDOR); + add_global_string_constant("ODIN_VERSION", bc->ODIN_VERSION); + add_global_string_constant("ODIN_ROOT", bc->ODIN_ROOT); add_global_string_constant("ODIN_BUILD_PROJECT_NAME", bc->ODIN_BUILD_PROJECT_NAME); + add_global_string_constant("ODIN_WINDOWS_SUBSYSTEM", bc->ODIN_WINDOWS_SUBSYSTEM); { GlobalEnumValue values[TargetOs_COUNT] = { @@ -1084,7 +1085,7 @@ gb_internal void init_universal(void) { add_global_constant("ODIN_COMPILE_TIMESTAMP", t_untyped_integer, exact_value_i64(odin_compile_timestamp())); - add_global_bool_constant("__ODIN_LLVM_F16_SUPPORTED", lb_use_new_pass_system()); + add_global_bool_constant("__ODIN_LLVM_F16_SUPPORTED", lb_use_new_pass_system() && !is_arch_wasm()); { GlobalEnumValue values[3] = { @@ -2211,9 +2212,14 @@ gb_internal void add_min_dep_type_info(Checker *c, Type *t) { Entity *e = entry.value; switch (bt->Struct.soa_kind) { case StructSoa_Dynamic: + add_min_dep_type_info(c, t_type_info_ptr); // append_soa + add_min_dep_type_info(c, t_allocator); /*fallthrough*/ case StructSoa_Slice: + add_min_dep_type_info(c, t_int); + add_min_dep_type_info(c, t_uint); + /*fallthrough*/ case StructSoa_Fixed: add_min_dep_type_info(c, alloc_type_pointer(e->type)); break; @@ -2289,7 +2295,6 @@ gb_internal void add_dependency_to_set(Checker *c, Entity *entity) { if (entity->type != nullptr && is_type_polymorphic(entity->type)) { - DeclInfo *decl = decl_info_of_entity(entity); if (decl != nullptr && decl->gen_proc_type == nullptr) { return; @@ -2341,98 +2346,7 @@ gb_internal void force_add_dependency_entity(Checker *c, Scope *scope, String co } - -gb_internal void generate_minimum_dependency_set(Checker *c, Entity *start) { - isize entity_count = c->info.entities.count; - isize min_dep_set_cap = next_pow2_isize(entity_count*4); // empirically determined factor - - ptr_set_init(&c->info.minimum_dependency_set, min_dep_set_cap); - map_init(&c->info.minimum_dependency_type_info_set); - -#define FORCE_ADD_RUNTIME_ENTITIES(condition, ...) do { \ - if (condition) { \ - String entities[] = {__VA_ARGS__}; \ - for (isize i = 0; i < gb_count_of(entities); i++) { \ - force_add_dependency_entity(c, c->info.runtime_package->scope, entities[i]); \ - } \ - } \ -} while (0) - - // required runtime entities - FORCE_ADD_RUNTIME_ENTITIES(true, - // Odin types - str_lit("Source_Code_Location"), - str_lit("Context"), - str_lit("Allocator"), - str_lit("Logger"), - - // Odin internal procedures - str_lit("__init_context"), - str_lit("cstring_to_string"), - str_lit("_cleanup_runtime"), - - // Pseudo-CRT required procedures - str_lit("memset"), - str_lit("memcpy"), - str_lit("memmove"), - - // Utility procedures - str_lit("memory_equal"), - str_lit("memory_compare"), - str_lit("memory_compare_zero"), - ); - - FORCE_ADD_RUNTIME_ENTITIES(!build_context.tilde_backend, - // Extended data type internal procedures - str_lit("umodti3"), - str_lit("udivti3"), - str_lit("modti3"), - str_lit("divti3"), - str_lit("fixdfti"), - str_lit("fixunsdfti"), - str_lit("fixunsdfdi"), - str_lit("floattidf"), - str_lit("floattidf_unsigned"), - str_lit("truncsfhf2"), - str_lit("truncdfhf2"), - str_lit("gnu_h2f_ieee"), - str_lit("gnu_f2h_ieee"), - str_lit("extendhfsf2"), - - // WASM Specific - str_lit("__ashlti3"), - str_lit("__multi3"), - ); - - FORCE_ADD_RUNTIME_ENTITIES(!build_context.no_rtti, - // Odin types - str_lit("Type_Info"), - - // Global variables - str_lit("type_table"), - str_lit("__type_info_of"), - ); - - FORCE_ADD_RUNTIME_ENTITIES(!build_context.no_entry_point, - // Global variables - str_lit("args__"), - ); - - FORCE_ADD_RUNTIME_ENTITIES((build_context.no_crt && !is_arch_wasm()), - // NOTE(bill): Only if these exist - str_lit("_tls_index"), - str_lit("_fltused"), - ); - - FORCE_ADD_RUNTIME_ENTITIES(!build_context.no_bounds_check, - // Bounds checking related procedures - str_lit("bounds_check_error"), - str_lit("matrix_bounds_check_error"), - str_lit("slice_expr_error_hi"), - str_lit("slice_expr_error_lo_hi"), - str_lit("multi_pointer_slice_expr_error"), - ); - +gb_internal void generate_minimum_dependency_set_internal(Checker *c, Entity *start) { for_array(i, c->info.definitions) { Entity *e = c->info.definitions[i]; if (e->scope == builtin_pkg->scope) { @@ -2575,6 +2489,104 @@ gb_internal void generate_minimum_dependency_set(Checker *c, Entity *start) { start->flags |= EntityFlag_Used; add_dependency_to_set(c, start); } +} + +gb_internal void generate_minimum_dependency_set(Checker *c, Entity *start) { + isize entity_count = c->info.entities.count; + isize min_dep_set_cap = next_pow2_isize(entity_count*4); // empirically determined factor + + ptr_set_init(&c->info.minimum_dependency_set, min_dep_set_cap); + map_init(&c->info.minimum_dependency_type_info_set); + +#define FORCE_ADD_RUNTIME_ENTITIES(condition, ...) do { \ + if (condition) { \ + String entities[] = {__VA_ARGS__}; \ + for (isize i = 0; i < gb_count_of(entities); i++) { \ + force_add_dependency_entity(c, c->info.runtime_package->scope, entities[i]); \ + } \ + } \ +} while (0) + + // required runtime entities + FORCE_ADD_RUNTIME_ENTITIES(true, + // Odin types + str_lit("Source_Code_Location"), + str_lit("Context"), + str_lit("Allocator"), + str_lit("Logger"), + + // Odin internal procedures + str_lit("__init_context"), + str_lit("cstring_to_string"), + str_lit("_cleanup_runtime"), + + // Pseudo-CRT required procedures + str_lit("memset"), + str_lit("memcpy"), + str_lit("memmove"), + + // Utility procedures + str_lit("memory_equal"), + str_lit("memory_compare"), + str_lit("memory_compare_zero"), + ); + + FORCE_ADD_RUNTIME_ENTITIES(!build_context.tilde_backend, + // Extended data type internal procedures + str_lit("umodti3"), + str_lit("udivti3"), + str_lit("modti3"), + str_lit("divti3"), + str_lit("fixdfti"), + str_lit("fixunsdfti"), + str_lit("fixunsdfdi"), + str_lit("floattidf"), + str_lit("floattidf_unsigned"), + str_lit("truncsfhf2"), + str_lit("truncdfhf2"), + str_lit("gnu_h2f_ieee"), + str_lit("gnu_f2h_ieee"), + str_lit("extendhfsf2"), + + // WASM Specific + str_lit("__ashlti3"), + str_lit("__multi3"), + ); + + FORCE_ADD_RUNTIME_ENTITIES(!build_context.no_rtti, + // Odin types + str_lit("Type_Info"), + + // Global variables + str_lit("type_table"), + str_lit("__type_info_of"), + ); + + FORCE_ADD_RUNTIME_ENTITIES(!build_context.no_entry_point, + // Global variables + str_lit("args__"), + ); + + FORCE_ADD_RUNTIME_ENTITIES((build_context.no_crt && !is_arch_wasm()), + // NOTE(bill): Only if these exist + str_lit("_tls_index"), + str_lit("_fltused"), + ); + + FORCE_ADD_RUNTIME_ENTITIES(!build_context.no_bounds_check, + // Bounds checking related procedures + str_lit("bounds_check_error"), + str_lit("matrix_bounds_check_error"), + str_lit("slice_expr_error_hi"), + str_lit("slice_expr_error_lo_hi"), + str_lit("multi_pointer_slice_expr_error"), + ); + + add_dependency_to_set(c, c->info.instrumentation_enter_entity); + add_dependency_to_set(c, c->info.instrumentation_exit_entity); + + generate_minimum_dependency_set_internal(c, start); + #undef FORCE_ADD_RUNTIME_ENTITIES } @@ -3405,6 +3417,39 @@ gb_internal DECL_ATTRIBUTE_PROC(proc_decl_attribute) { error(elem, "Expected a string value for '%.*s'", LIT(name)); } return true; + } else if (name == "entry_point_only") { + if (value != nullptr) { + error(value, "'%.*s' expects no parameter", LIT(name)); + } + ac->entry_point_only = true; + return true; + } else if (name == "no_instrumentation") { + ExactValue ev = check_decl_attribute_value(c, value); + if (ev.kind == ExactValue_Invalid) { + ac->no_instrumentation = Instrumentation_Disabled; + } else if (ev.kind == ExactValue_Bool) { + if (ev.value_bool) { + ac->no_instrumentation = Instrumentation_Disabled; + } else { + ac->no_instrumentation = Instrumentation_Enabled; + } + } else { + error(value, "Expected either a boolean or no parameter for '%.*s'", LIT(name)); + return false; + } + return true; + } else if (name == "instrumentation_enter") { + if (value != nullptr) { + error(value, "'%.*s' expects no parameter", LIT(name)); + } + ac->instrumentation_enter = true; + return true; + } else if (name == "instrumentation_exit") { + if (value != nullptr) { + error(value, "'%.*s' expects no parameter", LIT(name)); + } + ac->instrumentation_exit = true; + return true; } return false; } @@ -4014,12 +4059,11 @@ gb_internal void check_collect_value_decl(CheckerContext *c, Ast *decl) { if (c->foreign_context.default_cc > 0) { cc = c->foreign_context.default_cc; } else if (is_arch_wasm()) { - begin_error_block(); + ERROR_BLOCK(); error(init, "For wasm related targets, it is required that you either define the" " @(default_calling_convention=<string>) on the foreign block or" " explicitly assign it on the procedure signature"); error_line("\tSuggestion: when dealing with normal Odin code (e.g. js_wasm32), use \"contextless\"; when dealing with Emscripten like code, use \"c\"\n"); - end_error_block(); } } e->Procedure.link_prefix = c->foreign_context.link_prefix; @@ -4066,8 +4110,7 @@ gb_internal void check_collect_value_decl(CheckerContext *c, Ast *decl) { if (e->kind != Entity_Procedure) { if (fl != nullptr) { - begin_error_block(); - defer (end_error_block()); + ERROR_BLOCK(); AstKind kind = init->kind; error(name, "Only procedures and variables are allowed to be in a foreign block, got %.*s", LIT(ast_strings[kind])); @@ -4733,7 +4776,7 @@ gb_internal void check_add_foreign_import_decl(CheckerContext *ctx, Ast *decl) { } if (has_asm_extension(fullpath)) { - if (build_context.metrics.arch != TargetArch_amd64) { + 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])); } @@ -5304,6 +5347,44 @@ gb_internal void calculate_global_init_order(Checker *c) { } } +gb_internal void check_procedure_later_from_entity(Checker *c, Entity *e, char const *from_msg) { + if (e == nullptr || e->kind != Entity_Procedure) { + return; + } + if (e->Procedure.is_foreign) { + return; + } + if ((e->flags & EntityFlag_ProcBodyChecked) != 0) { + return; + } + Type *type = base_type(e->type); + GB_ASSERT(type->kind == Type_Proc); + + if (is_type_polymorphic(type) && !type->Proc.is_poly_specialized) { + return; + } + + GB_ASSERT(e->decl_info != nullptr); + + ProcInfo *pi = gb_alloc_item(permanent_allocator(), ProcInfo); + pi->file = e->file; + pi->token = e->token; + pi->decl = e->decl_info; + pi->type = e->type; + + Ast *pl = e->decl_info->proc_lit; + GB_ASSERT(pl != nullptr); + pi->body = pl->ProcLit.body; + pi->tags = pl->ProcLit.tags; + if (pi->body == nullptr) { + return; + } + if (from_msg != nullptr) { + debugf("CHECK PROCEDURE LATER [FROM %s]! %.*s :: %s {...}\n", from_msg, LIT(e->token.string), type_to_string(e->type)); + } + check_procedure_later(c, pi); +} + gb_internal bool check_proc_info(Checker *c, ProcInfo *pi, UntypedExprInfoMap *untyped) { if (pi == nullptr) { @@ -5410,6 +5491,15 @@ gb_internal bool check_proc_info(Checker *c, ProcInfo *pi, UntypedExprInfoMap *u add_untyped_expressions(&c->info, ctx.untyped); + rw_mutex_shared_lock(&ctx.decl->deps_mutex); + for (Entity *dep : ctx.decl->deps) { + if (dep && dep->kind == Entity_Procedure && + (dep->flags & EntityFlag_ProcBodyChecked) == 0) { + check_procedure_later_from_entity(c, dep, NULL); + } + } + rw_mutex_shared_unlock(&ctx.decl->deps_mutex); + return true; } @@ -5432,30 +5522,7 @@ gb_internal void check_unchecked_bodies(Checker *c) { global_procedure_body_in_worker_queue = false; for (Entity *e : c->info.minimum_dependency_set) { - if (e == nullptr || e->kind != Entity_Procedure) { - continue; - } - if (e->Procedure.is_foreign) { - continue; - } - if ((e->flags & EntityFlag_ProcBodyChecked) == 0) { - GB_ASSERT(e->decl_info != nullptr); - - ProcInfo *pi = gb_alloc_item(permanent_allocator(), ProcInfo); - pi->file = e->file; - pi->token = e->token; - pi->decl = e->decl_info; - pi->type = e->type; - - Ast *pl = e->decl_info->proc_lit; - GB_ASSERT(pl != nullptr); - pi->body = pl->ProcLit.body; - pi->tags = pl->ProcLit.tags; - if (pi->body == nullptr) { - continue; - } - check_procedure_later(c, pi); - } + check_procedure_later_from_entity(c, e, "check_unchecked_bodies"); } if (!global_procedure_body_in_worker_queue) { @@ -5497,7 +5564,28 @@ gb_internal void check_safety_all_procedures_for_unchecked(Checker *c) { } } +gb_internal GB_COMPARE_PROC(init_procedures_cmp); +gb_internal GB_COMPARE_PROC(fini_procedures_cmp); + +gb_internal void remove_neighbouring_duplicate_entires_from_sorted_array(Array<Entity *> *array) { + Entity *prev = nullptr; + + for (isize i = 0; i < array->count; /**/) { + Entity *curr = array->data[i]; + if (prev == curr) { + array_ordered_remove(array, i); + } else { + prev = curr; + i += 1; + } + } +} + + gb_internal void check_test_procedures(Checker *c) { + gb_sort_array(c->info.testing_procedures.data, c->info.testing_procedures.count, init_procedures_cmp); + remove_neighbouring_duplicate_entires_from_sorted_array(&c->info.testing_procedures); + if (build_context.test_names.entries.count == 0) { return; } @@ -5943,10 +6031,14 @@ gb_internal GB_COMPARE_PROC(fini_procedures_cmp) { return init_procedures_cmp(b, a); } - gb_internal void check_sort_init_and_fini_procedures(Checker *c) { gb_sort_array(c->info.init_procedures.data, c->info.init_procedures.count, init_procedures_cmp); gb_sort_array(c->info.fini_procedures.data, c->info.fini_procedures.count, fini_procedures_cmp); + + // NOTE(bill): remove possible duplicates from the init/fini lists + // NOTE(bill): because the arrays are sorted, you only need to check the previous element + remove_neighbouring_duplicate_entires_from_sorted_array(&c->info.init_procedures); + remove_neighbouring_duplicate_entires_from_sorted_array(&c->info.fini_procedures); } gb_internal void add_type_info_for_type_definitions(Checker *c) { @@ -6091,9 +6183,6 @@ gb_internal void check_parsed_files(Checker *c) { TIME_SECTION("calculate global init order"); calculate_global_init_order(c); - TIME_SECTION("check test procedures"); - check_test_procedures(c); - TIME_SECTION("add type info for type definitions"); add_type_info_for_type_definitions(c); check_merge_queues_into_arrays(c); @@ -6108,7 +6197,18 @@ gb_internal void check_parsed_files(Checker *c) { check_unchecked_bodies(c); check_merge_queues_into_arrays(c); + thread_pool_wait(); + + TIME_SECTION("update minimum dependency set"); + generate_minimum_dependency_set_internal(c, c->info.entry_point); + + // NOTE(laytan): has to be ran after generate_minimum_dependency_set, + // because that collects the test procedures. + TIME_SECTION("check test procedures"); + check_test_procedures(c); + check_merge_queues_into_arrays(c); + thread_pool_wait(); TIME_SECTION("check entry point"); if (build_context.build_mode == BuildMode_Executable && !build_context.no_entry_point && build_context.command_kind != Command_test) { @@ -6150,6 +6250,17 @@ gb_internal void check_parsed_files(Checker *c) { GB_ASSERT(c->info.entity_queue.count.load(std::memory_order_relaxed) == 0); GB_ASSERT(c->info.definition_queue.count.load(std::memory_order_relaxed) == 0); + TIME_SECTION("check instrumentation calls"); + { + if ((c->info.instrumentation_enter_entity != nullptr) ^ + (c->info.instrumentation_exit_entity != nullptr)) { + Entity *e = c->info.instrumentation_enter_entity; + if (!e) e = c->info.instrumentation_exit_entity; + error(e->token, "Both @(instrumentation_enter) and @(instrumentation_exit) must be defined"); + } + } + + TIME_SECTION("add untyped expression values"); // Add untyped expression values for (UntypedExprInfo u = {}; mpsc_dequeue(&c->global_untyped_queue, &u); /**/) { diff --git a/src/checker.hpp b/src/checker.hpp index a6a5f6788..9da0f2950 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -103,6 +103,12 @@ struct DeferredProcedure { }; +enum InstrumentationFlag : i32 { + Instrumentation_Enabled = -1, + Instrumentation_Default = 0, + Instrumentation_Disabled = +1, +}; + struct AttributeContext { String link_name; String link_prefix; @@ -113,19 +119,23 @@ struct AttributeContext { String deprecated_message; String warning_message; DeferredProcedure deferred_procedure; - bool is_export : 1; - bool is_static : 1; - bool require_results : 1; - bool require_declaration : 1; - bool has_disabled_proc : 1; - bool disabled_proc : 1; - bool test : 1; - bool init : 1; - bool fini : 1; - bool set_cold : 1; + bool is_export : 1; + bool is_static : 1; + bool require_results : 1; + bool require_declaration : 1; + bool has_disabled_proc : 1; + bool disabled_proc : 1; + bool test : 1; + bool init : 1; + bool fini : 1; + bool set_cold : 1; + bool entry_point_only : 1; + bool instrumentation_enter : 1; + bool instrumentation_exit : 1; u32 optimization_mode; // ProcedureOptimizationMode i64 foreign_import_priority_index; String extra_linker_flags; + InstrumentationFlag no_instrumentation; String objc_class; String objc_name; @@ -402,6 +412,10 @@ struct CheckerInfo { BlockingMutex all_procedures_mutex; Array<ProcInfo *> all_procedures; + + BlockingMutex instrumentation_mutex; + Entity *instrumentation_enter_entity; + Entity *instrumentation_exit_entity; }; struct CheckerContext { diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp index c89ab2429..3bab16293 100644 --- a/src/checker_builtin_procs.hpp +++ b/src/checker_builtin_procs.hpp @@ -260,6 +260,12 @@ BuiltinProc__type_simple_boolean_end, BuiltinProc_type_is_specialization_of, BuiltinProc_type_is_variant_of, + BuiltinProc_type_union_tag_type, + BuiltinProc_type_union_tag_offset, + BuiltinProc_type_union_base_tag_value, + BuiltinProc_type_union_variant_count, + BuiltinProc_type_variant_type_of, + BuiltinProc_type_variant_index_of, BuiltinProc_type_struct_field_count, @@ -557,7 +563,13 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT("type_is_specialization_of"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("type_is_variant_of"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_variant_of"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_union_tag_type"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_union_tag_offset"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_union_base_tag_value"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_union_variant_count"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_variant_type_of"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_variant_index_of"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("type_struct_field_count"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, diff --git a/src/entity.cpp b/src/entity.cpp index ce27da3f2..e6c46d37e 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -250,6 +250,8 @@ struct Entity { bool is_export : 1; bool generated_from_polymorphic : 1; bool target_feature_disabled : 1; + bool entry_point_only : 1; + bool has_instrumentation : 1; String target_feature; } Procedure; struct { diff --git a/src/exact_value.cpp b/src/exact_value.cpp index cd499272f..b744d2db0 100644 --- a/src/exact_value.cpp +++ b/src/exact_value.cpp @@ -174,7 +174,7 @@ gb_internal ExactValue exact_value_integer_from_string(String const &string) { -gb_internal f64 float_from_string(String const &string) { +gb_internal f64 float_from_string(String const &string, bool *success = nullptr) { if (string.len < 128) { char buf[128] = {}; isize n = 0; @@ -187,7 +187,13 @@ gb_internal f64 float_from_string(String const &string) { buf[n++] = cast(char)c; } buf[n] = 0; - return atof(buf); + + char *end_ptr; + f64 f = strtod(buf, &end_ptr); + if (success != nullptr) { + *success = *end_ptr == '\0'; + } + return f; } else { TEMPORARY_ALLOCATOR_GUARD(); char *buf = gb_alloc_array(temporary_allocator(), char, string.len+1); @@ -201,7 +207,13 @@ gb_internal f64 float_from_string(String const &string) { buf[n++] = cast(char)c; } buf[n] = 0; - return atof(buf); + + char *end_ptr; + f64 f = strtod(buf, &end_ptr); + if (success != nullptr) { + *success = *end_ptr == '\0'; + } + return f; } /* isize i = 0; @@ -313,7 +325,11 @@ gb_internal ExactValue exact_value_float_from_string(String string) { return exact_value_integer_from_string(string); } - f64 f = float_from_string(string); + bool success; + f64 f = float_from_string(string, &success); + if (!success) { + return {ExactValue_Invalid}; + } return exact_value_float(f); } diff --git a/src/gb/gb.h b/src/gb/gb.h index 5dae7a5c4..93d250f21 100644 --- a/src/gb/gb.h +++ b/src/gb/gb.h @@ -448,7 +448,7 @@ typedef i32 b32; // NOTE(bill): Prefer this!!! #define gb_inline __forceinline #endif #else - #define gb_inline __attribute__ ((__always_inline__)) + #define gb_inline inline __attribute__ ((__always_inline__)) #endif #endif diff --git a/src/linker.cpp b/src/linker.cpp index eb3687ae2..c0952d0e0 100644 --- a/src/linker.cpp +++ b/src/linker.cpp @@ -7,10 +7,19 @@ struct LinkerData { Array<String> output_temp_paths; String output_base; String output_name; +#if defined(GB_SYSTEM_OSX) + b8 needs_system_library_linked; +#endif }; gb_internal i32 system_exec_command_line_app(char const *name, char const *fmt, ...); +#if defined(GB_SYSTEM_OSX) +gb_internal void linker_enable_system_library_linking(LinkerData *ld) { + ld->needs_system_library_linked = 1; +} +#endif + gb_internal void linker_data_init(LinkerData *ld, CheckerInfo *info, String const &init_fullpath) { gbAllocator ha = heap_allocator(); array_init(&ld->output_object_paths, ha); @@ -18,6 +27,10 @@ gb_internal void linker_data_init(LinkerData *ld, CheckerInfo *info, String cons array_init(&ld->foreign_libraries, ha, 0, 1024); ptr_set_init(&ld->foreign_libraries_set, 1024); +#if defined(GB_SYSTEM_OSX) + ld->needs_system_library_linked = 0; +#endif + if (build_context.out_filepath.len == 0) { ld->output_name = remove_directory_from_path(init_fullpath); ld->output_name = remove_extension_from_path(ld->output_name); @@ -195,7 +208,7 @@ gb_internal i32 linker_stage(LinkerData *gen) { if (build_context.pdb_filepath != "") { String pdb_path = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_PDB]); - link_settings = gb_string_append_fmt(link_settings, " /PDB:%.*s", LIT(pdb_path)); + link_settings = gb_string_append_fmt(link_settings, " /PDB:\"%.*s\"", LIT(pdb_path)); } if (build_context.no_crt) { @@ -220,7 +233,6 @@ gb_internal i32 linker_stage(LinkerData *gen) { String windows_sdk_bin_path = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_Win_SDK_Bin_Path]); defer (gb_free(heap_allocator(), windows_sdk_bin_path.text)); - char const *subsystem_str = build_context.use_subsystem_windows ? "WINDOWS" : "CONSOLE"; if (!build_context.use_lld) { // msvc String res_path = {}; defer (gb_free(heap_allocator(), res_path.text)); @@ -252,14 +264,14 @@ gb_internal i32 linker_stage(LinkerData *gen) { result = system_exec_command_line_app("msvc-link", "\"%.*slink.exe\" %s %.*s -OUT:\"%.*s\" %s " - "/nologo /incremental:no /opt:ref /subsystem:%s " + "/nologo /incremental:no /opt:ref /subsystem:%.*s " "%.*s " "%.*s " "%s " "", LIT(vs_exe_path), object_files, LIT(res_path), LIT(output_filename), link_settings, - subsystem_str, + LIT(build_context.ODIN_WINDOWS_SUBSYSTEM), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags), lib_str @@ -270,14 +282,14 @@ gb_internal i32 linker_stage(LinkerData *gen) { } else { // lld result = system_exec_command_line_app("msvc-lld-link", "\"%.*s\\bin\\lld-link\" %s -OUT:\"%.*s\" %s " - "/nologo /incremental:no /opt:ref /subsystem:%s " + "/nologo /incremental:no /opt:ref /subsystem:%.*s " "%.*s " "%.*s " "%s " "", LIT(build_context.ODIN_ROOT), object_files, LIT(output_filename), link_settings, - subsystem_str, + LIT(build_context.ODIN_WINDOWS_SUBSYSTEM), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags), lib_str @@ -337,20 +349,34 @@ gb_internal i32 linker_stage(LinkerData *gen) { obj_format = str_lit("elf32"); } #endif // GB_ARCH_*_BIT - // Note(bumbread): I'm assuming nasm is installed on the host machine. - // Shipping binaries on unix-likes gets into the weird territorry of - // "which version of glibc" is it linked with. - result = system_exec_command_line_app("nasm", - "nasm \"%.*s\" " - "-f \"%.*s\" " - "-o \"%.*s\" " - "%.*s " - "", - LIT(asm_file), - LIT(obj_format), - LIT(obj_file), - LIT(build_context.extra_assembler_flags) - ); + + if (is_osx) { + // `as` comes with MacOS. + result = system_exec_command_line_app("as", + "as \"%.*s\" " + "-o \"%.*s\" " + "%.*s " + "", + LIT(asm_file), + LIT(obj_file), + LIT(build_context.extra_assembler_flags) + ); + } else { + // Note(bumbread): I'm assuming nasm is installed on the host machine. + // Shipping binaries on unix-likes gets into the weird territorry of + // "which version of glibc" is it linked with. + result = system_exec_command_line_app("nasm", + "nasm \"%.*s\" " + "-f \"%.*s\" " + "-o \"%.*s\" " + "%.*s " + "", + LIT(asm_file), + LIT(obj_format), + LIT(obj_file), + LIT(build_context.extra_assembler_flags) + ); + } array_add(&gen->output_object_paths, obj_file); } else { if (string_set_update(&libs, lib)) { @@ -456,7 +482,26 @@ gb_internal i32 linker_stage(LinkerData *gen) { gbString platform_lib_str = gb_string_make(heap_allocator(), ""); defer (gb_string_free(platform_lib_str)); if (build_context.metrics.os == TargetOs_darwin) { - platform_lib_str = gb_string_appendc(platform_lib_str, "-lSystem -lm -Wl,-syslibroot /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk -L/usr/local/lib"); + platform_lib_str = gb_string_appendc(platform_lib_str, "-Wl,-syslibroot /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk -L/usr/local/lib"); + + // Homebrew's default library path, checking if it exists to avoid linking warnings. + if (gb_file_exists("/opt/homebrew/lib")) { + platform_lib_str = gb_string_appendc(platform_lib_str, " -L/opt/homebrew/lib"); + } + + // MacPort's default library path, checking if it exists to avoid linking warnings. + if (gb_file_exists("/opt/local/lib")) { + platform_lib_str = gb_string_appendc(platform_lib_str, " -L/opt/local/lib"); + } + + #if defined(GB_SYSTEM_OSX) + if(!build_context.no_crt) { + platform_lib_str = gb_string_appendc(platform_lib_str, " -lm "); + if(gen->needs_system_library_linked == 1) { + platform_lib_str = gb_string_appendc(platform_lib_str, " -lSystem "); + } + } + #endif } else { platform_lib_str = gb_string_appendc(platform_lib_str, "-lc -lm"); } @@ -465,10 +510,6 @@ gb_internal i32 linker_stage(LinkerData *gen) { // This sets a requirement of Mountain Lion and up, but the compiler doesn't work without this limit. if (build_context.minimum_os_version_string.len) { link_settings = gb_string_append_fmt(link_settings, " -mmacosx-version-min=%.*s ", LIT(build_context.minimum_os_version_string)); - } else if (build_context.metrics.arch == TargetArch_arm64) { - link_settings = gb_string_appendc(link_settings, " -mmacosx-version-min=12.0.0 "); - } else { - link_settings = gb_string_appendc(link_settings, " -mmacosx-version-min=10.12.0 "); } // This points the linker to where the entry point is link_settings = gb_string_appendc(link_settings, " -e _main "); diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 276abc2d4..003424e0a 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -21,6 +21,25 @@ #include "llvm_backend_stmt.cpp" #include "llvm_backend_proc.cpp" +String get_default_microarchitecture() { + String default_march = str_lit("generic"); + if (build_context.metrics.arch == TargetArch_amd64) { + // NOTE(bill): x86-64-v2 is more than enough for everyone + // + // x86-64: CMOV, CMPXCHG8B, FPU, FXSR, MMX, FXSR, SCE, SSE, SSE2 + // x86-64-v2: (close to Nehalem) CMPXCHG16B, LAHF-SAHF, POPCNT, SSE3, SSE4.1, SSE4.2, SSSE3 + // x86-64-v3: (close to Haswell) AVX, AVX2, BMI1, BMI2, F16C, FMA, LZCNT, MOVBE, XSAVE + // x86-64-v4: AVX512F, AVX512BW, AVX512CD, AVX512DQ, AVX512VL + if (ODIN_LLVM_MINIMUM_VERSION_12) { + if (build_context.metrics.os == TargetOs_freestanding) { + default_march = str_lit("x86-64"); + } else { + default_march = str_lit("x86-64-v2"); + } + } + } + return default_march; +} gb_internal void lb_add_foreign_library_path(lbModule *m, Entity *e) { if (e == nullptr) { @@ -1478,8 +1497,6 @@ gb_internal WORKER_TASK_PROC(lb_llvm_module_pass_worker_proc) { auto passes = array_make<char const *>(heap_allocator(), 0, 64); defer (array_free(&passes)); - - LLVMPassBuilderOptionsRef pb_options = LLVMCreatePassBuilderOptions(); defer (LLVMDisposePassBuilderOptions(pb_options)); @@ -2486,40 +2503,72 @@ gb_internal bool lb_generate_code(lbGenerator *gen) { LLVMCodeModel code_mode = LLVMCodeModelDefault; if (is_arch_wasm()) { code_mode = LLVMCodeModelJITDefault; - } else if (build_context.metrics.os == TargetOs_freestanding) { + } else if (is_arch_x86() && build_context.metrics.os == TargetOs_freestanding) { code_mode = LLVMCodeModelKernel; } - char const *host_cpu_name = LLVMGetHostCPUName(); - char const *llvm_cpu = "generic"; + String host_cpu_name = copy_string(permanent_allocator(), make_string_c(LLVMGetHostCPUName())); + String llvm_cpu = get_default_microarchitecture(); char const *llvm_features = ""; if (build_context.microarch.len != 0) { if (build_context.microarch == "native") { llvm_cpu = host_cpu_name; } else { - llvm_cpu = alloc_cstring(permanent_allocator(), build_context.microarch); + llvm_cpu = copy_string(permanent_allocator(), build_context.microarch); } - if (gb_strcmp(llvm_cpu, host_cpu_name) == 0) { + if (llvm_cpu == host_cpu_name) { llvm_features = LLVMGetHostCPUFeatures(); } - } else if (build_context.metrics.arch == TargetArch_amd64) { - // NOTE(bill): x86-64-v2 is more than enough for everyone - // - // x86-64: CMOV, CMPXCHG8B, FPU, FXSR, MMX, FXSR, SCE, SSE, SSE2 - // x86-64-v2: (close to Nehalem) CMPXCHG16B, LAHF-SAHF, POPCNT, SSE3, SSE4.1, SSE4.2, SSSE3 - // x86-64-v3: (close to Haswell) AVX, AVX2, BMI1, BMI2, F16C, FMA, LZCNT, MOVBE, XSAVE - // x86-64-v4: AVX512F, AVX512BW, AVX512CD, AVX512DQ, AVX512VL - if (ODIN_LLVM_MINIMUM_VERSION_12) { - if (build_context.metrics.os == TargetOs_freestanding) { - llvm_cpu = "x86-64"; - } else { - llvm_cpu = "x86-64-v2"; - } - } } + // NOTE(Jeroen): Uncomment to get the list of supported microarchitectures. + /* + if (build_context.microarch == "?") { + string_set_add(&build_context.target_features_set, str_lit("+cpuhelp")); + } + */ + if (build_context.target_features_set.entries.count != 0) { - llvm_features = target_features_set_to_cstring(permanent_allocator(), false); + // Prefix all of the features with a `+`, because we are + // enabling additional features. + char const *additional_features = target_features_set_to_cstring(permanent_allocator(), false, true); + + String f_string = make_string_c(llvm_features); + String a_string = make_string_c(additional_features); + isize f_len = f_string.len; + + if (f_len == 0) { + // The common case is that llvm_features is empty, so + // the target_features_set additions can be used as is. + llvm_features = additional_features; + } else { + // The user probably specified `-microarch:native`, so + // llvm_features is populated by LLVM's idea of what + // the host CPU supports. + // + // As far as I can tell, (which is barely better than + // wild guessing), a bitset is formed by parsing the + // string left to right. + // + // So, llvm_features + ',' + additonal_features, will + // makes the target_features_set override llvm_features. + + char *tmp = gb_alloc_array(permanent_allocator(), char, f_len + 1 + a_string.len + 1); + isize len = 0; + + // tmp = f_string + gb_memmove(tmp, f_string.text, f_string.len); + len += f_string.len; + // tmp += ',' + tmp[len++] = ','; + // tmp += a_string + gb_memmove(tmp + len, a_string.text, a_string.len); + len += a_string.len; + // tmp += NUL + tmp[len++] = 0; + + llvm_features = tmp; + } } // GB_ASSERT_MSG(LLVMTargetHasAsmBackend(target)); @@ -2566,7 +2615,7 @@ gb_internal bool lb_generate_code(lbGenerator *gen) { for (auto const &entry : gen->modules) { LLVMTargetMachineRef target_machine = LLVMCreateTargetMachine( - target, target_triple, llvm_cpu, + target, target_triple, (const char *)llvm_cpu.text, llvm_features, code_gen_level, reloc_mode, @@ -2685,64 +2734,22 @@ gb_internal bool lb_generate_code(lbGenerator *gen) { } } - { - char const *name = LB_TYPE_INFO_TYPES_NAME; - Type *t = alloc_type_array(t_type_info_ptr, count); - LLVMValueRef g = LLVMAddGlobal(m->mod, lb_type(m, t), name); - LLVMSetInitializer(g, LLVMConstNull(lb_type(m, t))); - LLVMSetLinkage(g, LLVMInternalLinkage); - if (LB_USE_GIANT_PACKED_STRUCT) { - lb_make_global_private_const(g); - } - lb_global_type_info_member_types = lb_addr({g, alloc_type_pointer(t)}); - - } - { - char const *name = LB_TYPE_INFO_NAMES_NAME; - Type *t = alloc_type_array(t_string, count); - LLVMValueRef g = LLVMAddGlobal(m->mod, lb_type(m, t), name); - LLVMSetInitializer(g, LLVMConstNull(lb_type(m, t))); - LLVMSetLinkage(g, LLVMInternalLinkage); - if (LB_USE_GIANT_PACKED_STRUCT) { - lb_make_global_private_const(g); - } - lb_global_type_info_member_names = lb_addr({g, alloc_type_pointer(t)}); - } - { - char const *name = LB_TYPE_INFO_OFFSETS_NAME; - Type *t = alloc_type_array(t_uintptr, count); - LLVMValueRef g = LLVMAddGlobal(m->mod, lb_type(m, t), name); - LLVMSetInitializer(g, LLVMConstNull(lb_type(m, t))); - LLVMSetLinkage(g, LLVMInternalLinkage); - if (LB_USE_GIANT_PACKED_STRUCT) { - lb_make_global_private_const(g); - } - lb_global_type_info_member_offsets = lb_addr({g, alloc_type_pointer(t)}); - } - - { - char const *name = LB_TYPE_INFO_USINGS_NAME; - Type *t = alloc_type_array(t_bool, count); + auto const global_type_info_make = [](lbModule *m, char const *name, Type *elem_type, i64 count) -> lbAddr { + Type *t = alloc_type_array(elem_type, count); LLVMValueRef g = LLVMAddGlobal(m->mod, lb_type(m, t), name); LLVMSetInitializer(g, LLVMConstNull(lb_type(m, t))); LLVMSetLinkage(g, LLVMInternalLinkage); if (LB_USE_GIANT_PACKED_STRUCT) { lb_make_global_private_const(g); } - lb_global_type_info_member_usings = lb_addr({g, alloc_type_pointer(t)}); - } - - { - char const *name = LB_TYPE_INFO_TAGS_NAME; - Type *t = alloc_type_array(t_string, count); - LLVMValueRef g = LLVMAddGlobal(m->mod, lb_type(m, t), name); - LLVMSetInitializer(g, LLVMConstNull(lb_type(m, t))); - LLVMSetLinkage(g, LLVMInternalLinkage); - if (LB_USE_GIANT_PACKED_STRUCT) { - lb_make_global_private_const(g); - } - lb_global_type_info_member_tags = lb_addr({g, alloc_type_pointer(t)}); - } + return lb_addr({g, alloc_type_pointer(t)}); + }; + + lb_global_type_info_member_types = global_type_info_make(m, LB_TYPE_INFO_TYPES_NAME, t_type_info_ptr, count); + lb_global_type_info_member_names = global_type_info_make(m, LB_TYPE_INFO_NAMES_NAME, t_string, count); + lb_global_type_info_member_offsets = global_type_info_make(m, LB_TYPE_INFO_OFFSETS_NAME, t_uintptr, count); + lb_global_type_info_member_usings = global_type_info_make(m, LB_TYPE_INFO_USINGS_NAME, t_bool, count); + lb_global_type_info_member_tags = global_type_info_make(m, LB_TYPE_INFO_TAGS_NAME, t_string, count); } } diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index 5894dd38a..fe2c2deba 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -339,6 +339,8 @@ struct lbProcedure { bool in_multi_assignment; Array<LLVMValueRef> raw_input_parameters; + LLVMValueRef temp_callee_return_struct_memory; + Ast *curr_stmt; Array<Scope *> scope_stack; @@ -550,6 +552,7 @@ gb_internal LLVMValueRef lb_call_intrinsic(lbProcedure *p, const char *name, LLV gb_internal void lb_mem_copy_overlapping(lbProcedure *p, lbValue dst, lbValue src, lbValue len, bool is_volatile=false); gb_internal void lb_mem_copy_non_overlapping(lbProcedure *p, lbValue dst, lbValue src, lbValue len, bool is_volatile=false); gb_internal LLVMValueRef lb_mem_zero_ptr_internal(lbProcedure *p, LLVMValueRef ptr, LLVMValueRef len, unsigned alignment, bool is_volatile); +gb_internal LLVMValueRef lb_mem_zero_ptr_internal(lbProcedure *p, LLVMValueRef ptr, usize len, unsigned alignment, bool is_volatile); gb_internal gb_inline i64 lb_max_zero_init_size(void) { return cast(i64)(4*build_context.int_size); @@ -560,7 +563,9 @@ gb_internal LLVMTypeRef OdinLLVMGetVectorElementType(LLVMTypeRef type); gb_internal String lb_filepath_ll_for_module(lbModule *m); +gb_internal LLVMTypeRef lb_type_internal_for_procedures_raw(lbModule *m, Type *type); +gb_internal lbValue lb_emit_source_code_location_as_global_ptr(lbProcedure *p, String const &procedure, TokenPos const &pos); gb_internal LLVMTypeRef llvm_array_type(LLVMTypeRef ElementType, uint64_t ElementCount) { #if LB_USE_NEW_PASS_SYSTEM diff --git a/src/llvm_backend_const.cpp b/src/llvm_backend_const.cpp index 7584df3ee..2291f24ac 100644 --- a/src/llvm_backend_const.cpp +++ b/src/llvm_backend_const.cpp @@ -287,11 +287,44 @@ gb_internal lbValue lb_expr_untyped_const_to_typed(lbModule *m, Ast *expr, Type return lb_const_value(m, t, tv.value); } -gb_internal lbValue lb_const_source_code_location_const(lbModule *m, String const &procedure, TokenPos const &pos) { +gb_internal String lb_obfuscate_string(String const &s, char const *prefix) { + if (s.len == 0) { + return {}; + } + GB_ASSERT(prefix != nullptr); + u64 hash = gb_fnv64a(s.text, s.len); + gbString res = gb_string_make(temporary_allocator(), prefix); + res = gb_string_append_fmt(res, "x%llx", cast(long long unsigned)hash); + return make_string_c(res); +} + +gb_internal i32 lb_obfuscate_i32(i32 i) { + i32 x = cast(i32)gb_fnv64a(&i, sizeof(i)); + if (x < 0) { + x = 1-x; + } + return cast(i32)x; +} + +gb_internal lbValue lb_const_source_code_location_const(lbModule *m, String const &procedure_, TokenPos const &pos) { + String file = get_file_path_string(pos.file_id); + String procedure = procedure_; + + i32 line = pos.line; + i32 column = pos.column; + + if (build_context.obfuscate_source_code_locations) { + file = lb_obfuscate_string(file, "F"); + procedure = lb_obfuscate_string(procedure, "P"); + + line = lb_obfuscate_i32(line); + column = lb_obfuscate_i32(column); + } + LLVMValueRef fields[4] = {}; - fields[0]/*file*/ = lb_find_or_add_entity_string(m, get_file_path_string(pos.file_id)).value; - fields[1]/*line*/ = lb_const_int(m, t_i32, pos.line).value; - fields[2]/*column*/ = lb_const_int(m, t_i32, pos.column).value; + fields[0]/*file*/ = lb_find_or_add_entity_string(m, file).value; + fields[1]/*line*/ = lb_const_int(m, t_i32, line).value; + fields[2]/*column*/ = lb_const_int(m, t_i32, column).value; fields[3]/*procedure*/ = lb_find_or_add_entity_string(m, procedure).value; lbValue res = {}; diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index 8678a125c..4675e203b 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -2939,6 +2939,12 @@ gb_internal lbValue lb_build_unary_and(lbProcedure *p, Ast *expr) { } else if (is_type_soa_pointer(tv.type)) { ast_node(ie, IndexExpr, ue_expr); lbValue addr = lb_build_addr_ptr(p, ie->expr); + + if (is_type_pointer(type_deref(addr.type))) { + addr = lb_emit_load(p, addr); + } + GB_ASSERT(is_type_pointer(addr.type)); + lbValue index = lb_build_expr(p, ie->index); if (!build_context.no_bounds_check) { @@ -4099,7 +4105,7 @@ gb_internal lbAddr lb_build_addr_slice_expr(lbProcedure *p, Ast *expr) { } case Type_Basic: { - GB_ASSERT_MSG(type == t_string, "got %s", type_to_string(type)); + GB_ASSERT_MSG(are_types_identical(type, t_string), "got %s", type_to_string(type)); lbValue len = lb_string_len(p, base); if (high.value == nullptr) high = len; diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index fdcf94f29..f0f5327c6 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -107,6 +107,10 @@ gb_internal bool lb_init_generator(lbGenerator *gen, Checker *c) { String init_fullpath = c->parser->init_fullpath; linker_data_init(gen, &c->info, init_fullpath); + #if defined(GB_SYSTEM_OSX) && (LLVM_VERSION_MAJOR < 14) + linker_enable_system_library_linking(gen); + #endif + gen->info = &c->info; map_init(&gen->modules, gen->info->packages.count*2); @@ -1332,6 +1336,8 @@ gb_internal void lb_emit_store_union_variant(lbProcedure *p, lbValue parent, lbV Type *pt = base_type(type_deref(parent.type)); GB_ASSERT(pt->kind == Type_Union); if (pt->Union.kind == UnionType_shared_nil) { + GB_ASSERT(type_size_of(variant_type)); + lbBlock *if_nil = lb_create_block(p, "shared_nil.if_nil"); lbBlock *if_not_nil = lb_create_block(p, "shared_nil.if_not_nil"); lbBlock *done = lb_create_block(p, "shared_nil.done"); @@ -1353,9 +1359,13 @@ gb_internal void lb_emit_store_union_variant(lbProcedure *p, lbValue parent, lbV } else { - lbValue underlying = lb_emit_conv(p, parent, alloc_type_pointer(variant_type)); - - lb_emit_store(p, underlying, variant); + if (type_size_of(variant_type) == 0) { + unsigned alignment = 1; + lb_mem_zero_ptr_internal(p, parent.value, pt->Union.variant_block_size, alignment, false); + } else { + lbValue underlying = lb_emit_conv(p, parent, alloc_type_pointer(variant_type)); + lb_emit_store(p, underlying, variant); + } lb_emit_store_union_variant_tag(p, parent, variant_type); } } @@ -2338,6 +2348,15 @@ gb_internal LLVMAttributeRef lb_create_enum_attribute(LLVMContextRef ctx, char c return LLVMCreateEnumAttribute(ctx, kind, value); } +gb_internal LLVMAttributeRef lb_create_string_attribute(LLVMContextRef ctx, String const &key, String const &value) { + LLVMAttributeRef attr = LLVMCreateStringAttribute( + ctx, + cast(char const *)key.text, cast(unsigned)key.len, + cast(char const *)value.text, cast(unsigned)value.len); + return attr; +} + + gb_internal void lb_add_proc_attribute_at_index(lbProcedure *p, isize index, char const *name, u64 value) { LLVMAttributeRef attr = lb_create_enum_attribute(p->module->ctx, name, value); GB_ASSERT(attr != nullptr); @@ -2351,6 +2370,10 @@ gb_internal void lb_add_proc_attribute_at_index(lbProcedure *p, isize index, cha gb_internal void lb_add_attribute_to_proc(lbModule *m, LLVMValueRef proc_value, char const *name, u64 value=0) { LLVMAddAttributeAtIndex(proc_value, LLVMAttributeIndex_FunctionIndex, lb_create_enum_attribute(m->ctx, name, value)); } +gb_internal void lb_add_attribute_to_proc_with_string(lbModule *m, LLVMValueRef proc_value, String const &name, String const &value) { + LLVMAttributeRef attr = lb_create_string_attribute(m->ctx, name, value); + LLVMAddAttributeAtIndex(proc_value, LLVMAttributeIndex_FunctionIndex, attr); +} diff --git a/src/llvm_backend_opt.cpp b/src/llvm_backend_opt.cpp index 2e03b7974..b57e74799 100644 --- a/src/llvm_backend_opt.cpp +++ b/src/llvm_backend_opt.cpp @@ -380,6 +380,86 @@ gb_internal void lb_run_remove_dead_instruction_pass(lbProcedure *p) { } } +gb_internal LLVMValueRef lb_run_instrumentation_pass_insert_call(lbProcedure *p, Entity *entity, LLVMBuilderRef dummy_builder) { + lbModule *m = p->module; + + lbValue cc = lb_find_procedure_value_from_entity(m, entity); + + LLVMValueRef args[3] = {}; + args[0] = p->value; + + LLVMValueRef returnaddress_args[1] = {}; + + returnaddress_args[0] = LLVMConstInt(LLVMInt32TypeInContext(m->ctx), 0, false); + + char const *instrinsic_name = "llvm.returnaddress"; + unsigned id = LLVMLookupIntrinsicID(instrinsic_name, gb_strlen(instrinsic_name)); + GB_ASSERT_MSG(id != 0, "Unable to find %s", instrinsic_name); + LLVMValueRef ip = LLVMGetIntrinsicDeclaration(m->mod, id, nullptr, 0); + LLVMTypeRef call_type = LLVMIntrinsicGetType(m->ctx, id, nullptr, 0); + args[1] = LLVMBuildCall2(dummy_builder, call_type, ip, returnaddress_args, gb_count_of(returnaddress_args), ""); + + Token name = {}; + if (p->entity) { + name = p->entity->token; + } + args[2] = lb_emit_source_code_location_as_global_ptr(p, name.string, name.pos).value; + + LLVMTypeRef fnp = lb_type_internal_for_procedures_raw(p->module, entity->type); + return LLVMBuildCall2(dummy_builder, fnp, cc.value, args, gb_count_of(args), ""); +} + + +gb_internal void lb_run_instrumentation_pass(lbProcedure *p) { + lbModule *m = p->module; + Entity *enter = m->info->instrumentation_enter_entity; + Entity *exit = m->info->instrumentation_exit_entity; + if (enter == nullptr || exit == nullptr) { + return; + } + if (!(p->entity && + p->entity->kind == Entity_Procedure && + p->entity->Procedure.has_instrumentation)) { + return; + } + +#define LLVM_V_NAME(x) x, cast(unsigned)(gb_count_of(x)-1) + + LLVMBuilderRef dummy_builder = LLVMCreateBuilderInContext(m->ctx); + defer (LLVMDisposeBuilder(dummy_builder)); + + LLVMBasicBlockRef entry_bb = p->entry_block->block; + LLVMPositionBuilder(dummy_builder, entry_bb, LLVMGetFirstInstruction(entry_bb)); + lb_run_instrumentation_pass_insert_call(p, enter, dummy_builder); + LLVMRemoveStringAttributeAtIndex(p->value, LLVMAttributeIndex_FunctionIndex, LLVM_V_NAME("instrument-function-entry")); + + unsigned bb_count = LLVMCountBasicBlocks(p->value); + LLVMBasicBlockRef *bbs = gb_alloc_array(temporary_allocator(), LLVMBasicBlockRef, bb_count); + LLVMGetBasicBlocks(p->value, bbs); + for (unsigned i = 0; i < bb_count; i++) { + LLVMBasicBlockRef bb = bbs[i]; + LLVMValueRef terminator = LLVMGetBasicBlockTerminator(bb); + if (terminator == nullptr || + !LLVMIsAReturnInst(terminator)) { + continue; + } + + // TODO(bill): getTerminatingMustTailCall() + // If T is preceded by a musttail call, that's the real terminator. + // if (CallInst *CI = BB.getTerminatingMustTailCall()) + // T = CI; + + + LLVMPositionBuilderBefore(dummy_builder, terminator); + lb_run_instrumentation_pass_insert_call(p, exit, dummy_builder); + } + + LLVMRemoveStringAttributeAtIndex(p->value, LLVMAttributeIndex_FunctionIndex, LLVM_V_NAME("instrument-function-exit")); + +#undef LLVM_V_NAME +} + + gb_internal void lb_run_function_pass_manager(LLVMPassManagerRef fpm, lbProcedure *p, lbFunctionPassManagerKind pass_manager_kind) { if (p == nullptr) { @@ -401,6 +481,7 @@ gb_internal void lb_run_function_pass_manager(LLVMPassManagerRef fpm, lbProcedur } break; } + lb_run_instrumentation_pass(p); LLVMRunFunctionPassManager(fpm, p->value); } @@ -552,3 +633,5 @@ gb_internal void lb_run_remove_unused_globals_pass(lbModule *m) { } } } + + diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index f64cbd52a..09bebd0cf 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -329,6 +329,18 @@ gb_internal lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool i } } + if (p->body && entity->Procedure.has_instrumentation) { + Entity *instrumentation_enter = m->info->instrumentation_enter_entity; + Entity *instrumentation_exit = m->info->instrumentation_exit_entity; + if (instrumentation_enter && instrumentation_exit) { + String enter = lb_get_entity_name(m, instrumentation_enter); + String exit = lb_get_entity_name(m, instrumentation_exit); + + lb_add_attribute_to_proc_with_string(m, p->value, make_string_c("instrument-function-entry"), enter); + lb_add_attribute_to_proc_with_string(m, p->value, make_string_c("instrument-function-exit"), exit); + } + } + lbValue proc_value = {p->value, p->type}; lb_add_entity(m, entity, proc_value); lb_add_member(m, p->name, proc_value); @@ -1826,24 +1838,41 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu } case BuiltinProc_quaternion: { - lbValue real = lb_build_expr(p, ce->args[0]); - lbValue imag = lb_build_expr(p, ce->args[1]); - lbValue jmag = lb_build_expr(p, ce->args[2]); - lbValue kmag = lb_build_expr(p, ce->args[3]); + lbValue xyzw[4] = {}; + for (i32 i = 0; i < 4; i++) { + ast_node(f, FieldValue, ce->args[i]); + GB_ASSERT(f->field->kind == Ast_Ident); + String name = f->field->Ident.token.string; + i32 index = -1; + + // @QuaternionLayout + if (name == "x" || name == "imag") { + index = 0; + } else if (name == "y" || name == "jmag") { + index = 1; + } else if (name == "z" || name == "kmag") { + index = 2; + } else if (name == "w" || name == "real") { + index = 3; + } + GB_ASSERT(index >= 0); + + xyzw[index] = lb_build_expr(p, f->value); + } + - // @QuaternionLayout lbAddr dst_addr = lb_add_local_generated(p, tv.type, false); lbValue dst = lb_addr_get_ptr(p, dst_addr); Type *ft = base_complex_elem_type(tv.type); - real = lb_emit_conv(p, real, ft); - imag = lb_emit_conv(p, imag, ft); - jmag = lb_emit_conv(p, jmag, ft); - kmag = lb_emit_conv(p, kmag, ft); - lb_emit_store(p, lb_emit_struct_ep(p, dst, 3), real); - lb_emit_store(p, lb_emit_struct_ep(p, dst, 0), imag); - lb_emit_store(p, lb_emit_struct_ep(p, dst, 1), jmag); - lb_emit_store(p, lb_emit_struct_ep(p, dst, 2), kmag); + xyzw[0] = lb_emit_conv(p, xyzw[0], ft); + xyzw[1] = lb_emit_conv(p, xyzw[1], ft); + xyzw[2] = lb_emit_conv(p, xyzw[2], ft); + xyzw[3] = lb_emit_conv(p, xyzw[3], ft); + lb_emit_store(p, lb_emit_struct_ep(p, dst, 0), xyzw[0]); + lb_emit_store(p, lb_emit_struct_ep(p, dst, 1), xyzw[1]); + lb_emit_store(p, lb_emit_struct_ep(p, dst, 2), xyzw[2]); + lb_emit_store(p, lb_emit_struct_ep(p, dst, 3), xyzw[3]); return lb_emit_load(p, dst); } @@ -3385,7 +3414,7 @@ gb_internal lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr) { } lbValue arg = args[arg_index]; - if (arg.value == nullptr) { + if (arg.value == nullptr && arg.type == nullptr) { switch (e->kind) { case Entity_TypeName: args[arg_index] = lb_const_nil(p->module, e->type); diff --git a/src/llvm_backend_stmt.cpp b/src/llvm_backend_stmt.cpp index 9d688be6a..002fef881 100644 --- a/src/llvm_backend_stmt.cpp +++ b/src/llvm_backend_stmt.cpp @@ -1846,9 +1846,25 @@ gb_internal void lb_build_return_stmt_internal(lbProcedure *p, lbValue res) { LLVMBuildRetVoid(p->builder); } else { LLVMValueRef ret_val = res.value; - ret_val = OdinLLVMBuildTransmute(p, ret_val, p->abi_function_type->ret.type); - if (p->abi_function_type->ret.cast_type != nullptr) { - ret_val = OdinLLVMBuildTransmute(p, ret_val, p->abi_function_type->ret.cast_type); + LLVMTypeRef ret_type = p->abi_function_type->ret.type; + if (LLVMTypeRef cast_type = p->abi_function_type->ret.cast_type) { + ret_type = cast_type; + } + + if (LLVMGetTypeKind(ret_type) == LLVMStructTypeKind) { + LLVMTypeRef src_type = LLVMTypeOf(ret_val); + + if (p->temp_callee_return_struct_memory == nullptr) { + i64 max_align = gb_max(lb_alignof(ret_type), lb_alignof(src_type)); + p->temp_callee_return_struct_memory = llvm_alloca(p, ret_type, max_align); + } + // reuse the temp return value memory where possible + LLVMValueRef ptr = p->temp_callee_return_struct_memory; + LLVMValueRef nptr = LLVMBuildPointerCast(p->builder, ptr, LLVMPointerType(src_type, 0), ""); + LLVMBuildStore(p->builder, ret_val, nptr); + ret_val = LLVMBuildLoad2(p->builder, ret_type, ptr, ""); + } else { + ret_val = OdinLLVMBuildTransmute(p, ret_val, ret_type); } lb_emit_defer_stmts(p, lbDeferExit_Return, nullptr); diff --git a/src/llvm_backend_type.cpp b/src/llvm_backend_type.cpp index 02dad2a3a..e291e40a5 100644 --- a/src/llvm_backend_type.cpp +++ b/src/llvm_backend_type.cpp @@ -9,7 +9,12 @@ gb_internal isize lb_type_info_index(CheckerInfo *info, Type *type, bool err_on_ } } if (err_on_not_found) { - GB_PANIC("NOT FOUND lb_type_info_index %s @ index %td", type_to_string(type), index); + gb_printf_err("NOT FOUND lb_type_info_index:\n\t%s\n\t@ index %td\n\tmax count: %u\nFound:\n", type_to_string(type), index, set->count); + for (auto const &entry : *set) { + isize type_info_index = entry.key; + gb_printf_err("\t%s\n", type_to_string(info->type_info_types[type_info_index])); + } + GB_PANIC("NOT FOUND"); } return -1; } diff --git a/src/llvm_backend_utility.cpp b/src/llvm_backend_utility.cpp index d8dbfd736..be3ae9c8a 100644 --- a/src/llvm_backend_utility.cpp +++ b/src/llvm_backend_utility.cpp @@ -57,6 +57,10 @@ gb_internal lbValue lb_correct_endianness(lbProcedure *p, lbValue value) { return value; } +gb_internal LLVMValueRef lb_mem_zero_ptr_internal(lbProcedure *p, LLVMValueRef ptr, usize len, unsigned alignment, bool is_volatile) { + return lb_mem_zero_ptr_internal(p, ptr, LLVMConstInt(lb_type(p->module, t_uint), len, false), alignment, is_volatile); +} + gb_internal LLVMValueRef lb_mem_zero_ptr_internal(lbProcedure *p, LLVMValueRef ptr, LLVMValueRef len, unsigned alignment, bool is_volatile) { bool is_inlinable = false; diff --git a/src/main.cpp b/src/main.cpp index 79c2b3561..19271d667 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -199,19 +199,19 @@ gb_internal void print_usage_line(i32 indent, char const *fmt, ...) { } gb_internal void usage(String argv0) { - print_usage_line(0, "%.*s is a tool for managing Odin source code", LIT(argv0)); + print_usage_line(0, "%.*s is a tool for managing Odin source code.", LIT(argv0)); print_usage_line(0, "Usage:"); print_usage_line(1, "%.*s command [arguments]", LIT(argv0)); print_usage_line(0, "Commands:"); - print_usage_line(1, "build compile directory of .odin files, as an executable."); - print_usage_line(1, " one must contain the program's entry point, all must be in the same package."); - print_usage_line(1, "run same as 'build', but also then runs the newly compiled executable."); - print_usage_line(1, "check parse, and type check a directory of .odin files"); - print_usage_line(1, "strip-semicolon parse, type check, and remove unneeded semicolons from the entire program"); - print_usage_line(1, "test build and runs procedures with the attribute @(test) in the initial package"); - print_usage_line(1, "doc generate documentation on a directory of .odin files"); - print_usage_line(1, "version print version"); - print_usage_line(1, "report print information useful to reporting a bug"); + print_usage_line(1, "build Compiles directory of .odin files, as an executable."); + print_usage_line(1, " One must contain the program's entry point, all must be in the same package."); + print_usage_line(1, "run Same as 'build', but also then runs the newly compiled executable."); + print_usage_line(1, "check Parses, and type checks a directory of .odin files."); + print_usage_line(1, "strip-semicolon Parses, type checks, and removes unneeded semicolons from the entire program."); + print_usage_line(1, "test Builds and runs procedures with the attribute @(test) in the initial package."); + print_usage_line(1, "doc Generates documentation on a directory of .odin files."); + print_usage_line(1, "version Prints version."); + print_usage_line(1, "report Prints information useful to reporting a bug."); print_usage_line(0, ""); print_usage_line(0, "For further details on a command, invoke command help:"); print_usage_line(1, "e.g. `odin build -help` or `odin help build`"); @@ -277,6 +277,7 @@ enum BuildFlagKind { BuildFlag_ForeignErrorProcedures, BuildFlag_NoRTTI, BuildFlag_DynamicMapCalls, + BuildFlag_ObfuscateSourceCodeLocations, BuildFlag_Compact, BuildFlag_GlobalDefinitions, @@ -467,6 +468,8 @@ gb_internal bool parse_build_flags(Array<String> args) { add_flag(&build_flags, BuildFlag_DynamicMapCalls, str_lit("dynamic-map-calls"), BuildFlagParam_None, Command__does_check); + add_flag(&build_flags, BuildFlag_ObfuscateSourceCodeLocations, str_lit("obfuscate-source-code-locations"), BuildFlagParam_None, Command__does_build); + add_flag(&build_flags, BuildFlag_Short, str_lit("short"), BuildFlagParam_None, Command_doc); add_flag(&build_flags, BuildFlag_AllPackages, str_lit("all-packages"), BuildFlagParam_None, Command_doc); add_flag(&build_flags, BuildFlag_DocFormat, str_lit("doc-format"), BuildFlagParam_None, Command_doc); @@ -1113,6 +1116,11 @@ gb_internal bool parse_build_flags(Array<String> args) { case BuildFlag_DynamicMapCalls: build_context.dynamic_map_calls = true; break; + + case BuildFlag_ObfuscateSourceCodeLocations: + build_context.obfuscate_source_code_locations = true; + break; + case BuildFlag_DefaultToNilAllocator: build_context.ODIN_DEFAULT_TO_NIL_ALLOCATOR = true; break; @@ -1152,10 +1160,12 @@ gb_internal bool parse_build_flags(Array<String> args) { case BuildFlag_TerseErrors: build_context.hide_error_line = true; + build_context.terse_errors = true; break; case BuildFlag_VerboseErrors: gb_printf_err("-verbose-errors is not the default, -terse-errors can now disable it\n"); build_context.hide_error_line = false; + build_context.terse_errors = false; break; case BuildFlag_ErrorPosStyle: @@ -1260,16 +1270,43 @@ gb_internal bool parse_build_flags(Array<String> args) { } case BuildFlag_Subsystem: { + // TODO(Jeroen): Parse optional "[,major[.minor]]" + GB_ASSERT(value.kind == ExactValue_String); String subsystem = value.value_string; - if (str_eq_ignore_case(subsystem, str_lit("console"))) { - build_context.use_subsystem_windows = false; - } else if (str_eq_ignore_case(subsystem, str_lit("window"))) { - build_context.use_subsystem_windows = true; - } else if (str_eq_ignore_case(subsystem, str_lit("windows"))) { - build_context.use_subsystem_windows = true; - } else { - gb_printf_err("Invalid -subsystem string, got %.*s, expected either 'console' or 'windows'\n", LIT(subsystem)); + bool subsystem_found = false; + for (int i = 0; i < Windows_Subsystem_COUNT; i++) { + if (str_eq_ignore_case(subsystem, windows_subsystem_names[i])) { + build_context.ODIN_WINDOWS_SUBSYSTEM = windows_subsystem_names[i]; + subsystem_found = true; + break; + } + } + + // WINDOW is a hidden alias for WINDOWS. Check it. + String subsystem_windows_alias = str_lit("WINDOW"); + if (!subsystem_found && str_eq_ignore_case(subsystem, subsystem_windows_alias)) { + build_context.ODIN_WINDOWS_SUBSYSTEM = windows_subsystem_names[Windows_Subsystem_WINDOWS]; + subsystem_found = true; + break; + } + + if (!subsystem_found) { + gb_printf_err("Invalid -subsystem string, got %.*s. Expected one of:\n", LIT(subsystem)); + gb_printf_err("\t"); + for (int i = 0; i < Windows_Subsystem_COUNT; i++) { + if (i > 0) { + gb_printf_err(", "); + } + gb_printf_err("%.*s", LIT(windows_subsystem_names[i])); + if (i == Windows_Subsystem_CONSOLE) { + gb_printf_err(" (default)"); + } + if (i == Windows_Subsystem_WINDOWS) { + gb_printf_err(" (or WINDOW)"); + } + } + gb_printf_err("\n"); bad_flags = true; } break; @@ -1580,45 +1617,45 @@ gb_internal void remove_temp_files(lbGenerator *gen) { gb_internal void print_show_help(String const arg0, String const &command) { - print_usage_line(0, "%.*s is a tool for managing Odin source code", LIT(arg0)); + print_usage_line(0, "%.*s is a tool for managing Odin source code.", LIT(arg0)); print_usage_line(0, "Usage:"); print_usage_line(1, "%.*s %.*s [arguments]", LIT(arg0), LIT(command)); print_usage_line(0, ""); if (command == "build") { - print_usage_line(1, "build Compile directory of .odin files as an executable."); + print_usage_line(1, "build Compiles directory of .odin files as an executable."); print_usage_line(2, "One must contain the program's entry point, all must be in the same package."); print_usage_line(2, "Use `-file` to build a single file instead."); print_usage_line(2, "Examples:"); - print_usage_line(3, "odin build . # Build package in current directory"); - print_usage_line(3, "odin build <dir> # Build package in <dir>"); - print_usage_line(3, "odin build filename.odin -file # Build single-file package, must contain entry point."); + print_usage_line(3, "odin build . Builds package in current directory."); + print_usage_line(3, "odin build <dir> Builds package in <dir>."); + print_usage_line(3, "odin build filename.odin -file Builds single-file package, must contain entry point."); } else if (command == "run") { print_usage_line(1, "run Same as 'build', but also then runs the newly compiled executable."); print_usage_line(2, "Append an empty flag and then the args, '-- <args>', to specify args for the output."); print_usage_line(2, "Examples:"); - print_usage_line(3, "odin run . # Build and run package in current directory"); - print_usage_line(3, "odin run <dir> # Build and run package in <dir>"); - print_usage_line(3, "odin run filename.odin -file # Build and run single-file package, must contain entry point."); + print_usage_line(3, "odin run . Builds and runs package in current directory."); + print_usage_line(3, "odin run <dir> Builds and runs package in <dir>."); + print_usage_line(3, "odin run filename.odin -file Builds and runs single-file package, must contain entry point."); } else if (command == "check") { - print_usage_line(1, "check Parse and type check directory of .odin files"); + print_usage_line(1, "check Parses and type checks directory of .odin files."); print_usage_line(2, "Examples:"); - print_usage_line(3, "odin check . # Type check package in current directory"); - print_usage_line(3, "odin check <dir> # Type check package in <dir>"); - print_usage_line(3, "odin check filename.odin -file # Type check single-file package, must contain entry point."); + print_usage_line(3, "odin check . Type checks package in current directory."); + print_usage_line(3, "odin check <dir> Type checks package in <dir>."); + print_usage_line(3, "odin check filename.odin -file Type checks single-file package, must contain entry point."); } else if (command == "test") { - print_usage_line(1, "test Build and runs procedures with the attribute @(test) in the initial package"); + print_usage_line(1, "test Builds and runs procedures with the attribute @(test) in the initial package."); } else if (command == "doc") { - print_usage_line(1, "doc generate documentation from a directory of .odin files"); + print_usage_line(1, "doc Generates documentation from a directory of .odin files."); print_usage_line(2, "Examples:"); - print_usage_line(3, "odin doc . # Generate documentation on package in current directory"); - print_usage_line(3, "odin doc <dir> # Generate documentation on package in <dir>"); - print_usage_line(3, "odin doc filename.odin -file # Generate documentation on single-file package."); + print_usage_line(3, "odin doc . Generates documentation on package in current directory."); + print_usage_line(3, "odin doc <dir> Generates documentation on package in <dir>."); + print_usage_line(3, "odin doc filename.odin -file Generates documentation on single-file package."); } else if (command == "version") { - print_usage_line(1, "version print version"); + print_usage_line(1, "version Prints version."); } else if (command == "strip-semicolon") { print_usage_line(1, "strip-semicolon"); - print_usage_line(2, "Parse and type check .odin file(s) and then remove unneeded semicolons from the entire project"); + print_usage_line(2, "Parses and type checks .odin file(s) and then removes unneeded semicolons from the entire project."); } bool doc = command == "doc"; @@ -1642,311 +1679,322 @@ gb_internal void print_show_help(String const arg0, String const &command) { if (doc) { print_usage_line(1, "-short"); - print_usage_line(2, "Show shortened documentation for the packages"); + print_usage_line(2, "Shows shortened documentation for the packages."); print_usage_line(0, ""); print_usage_line(1, "-all-packages"); - print_usage_line(2, "Generates documentation for all packages used in the current project"); + print_usage_line(2, "Generates documentation for all packages used in the current project."); print_usage_line(0, ""); print_usage_line(1, "-doc-format"); - print_usage_line(2, "Generates documentation as the .odin-doc format (useful for external tooling)"); + print_usage_line(2, "Generates documentation as the .odin-doc format (useful for external tooling)."); print_usage_line(0, ""); } if (run_or_build) { print_usage_line(1, "-out:<filepath>"); - print_usage_line(2, "Set the file name of the outputted executable"); + print_usage_line(2, "Sets the file name of the outputted executable."); print_usage_line(2, "Example: -out:foo.exe"); print_usage_line(0, ""); print_usage_line(1, "-o:<string>"); - print_usage_line(2, "Set the optimization mode for compilation"); + print_usage_line(2, "Sets the optimization mode for compilation."); + print_usage_line(2, "Available options:"); + print_usage_line(3, "-o:none"); + print_usage_line(3, "-o:minimal"); + print_usage_line(3, "-o:size"); + print_usage_line(3, "-o:speed"); if (LB_USE_NEW_PASS_SYSTEM) { - print_usage_line(2, "Accepted values: none, minimal, size, speed, aggressive"); - } else { - print_usage_line(2, "Accepted values: none, minimal, size, speed"); + print_usage_line(3, "-o:aggressive"); } - print_usage_line(2, "Example: -o:speed"); - print_usage_line(2, "The default is -o:minimal"); + print_usage_line(2, "The default is -o:minimal."); print_usage_line(0, ""); } if (check) { print_usage_line(1, "-show-timings"); - print_usage_line(2, "Shows basic overview of the timings of different stages within the compiler in milliseconds"); + print_usage_line(2, "Shows basic overview of the timings of different stages within the compiler in milliseconds."); print_usage_line(0, ""); print_usage_line(1, "-show-more-timings"); - print_usage_line(2, "Shows an advanced overview of the timings of different stages within the compiler in milliseconds"); + print_usage_line(2, "Shows an advanced overview of the timings of different stages within the compiler in milliseconds."); print_usage_line(0, ""); print_usage_line(1, "-show-system-calls"); - print_usage_line(2, "Prints the whole command and arguments for calls to external tools like linker and assembler"); + print_usage_line(2, "Prints the whole command and arguments for calls to external tools like linker and assembler."); print_usage_line(0, ""); print_usage_line(1, "-export-timings:<format>"); - print_usage_line(2, "Export timings to one of a few formats. Requires `-show-timings` or `-show-more-timings`"); + print_usage_line(2, "Exports timings to one of a few formats. Requires `-show-timings` or `-show-more-timings`."); print_usage_line(2, "Available options:"); - print_usage_line(3, "-export-timings:json Export compile time stats to JSON"); - print_usage_line(3, "-export-timings:csv Export compile time stats to CSV"); + print_usage_line(3, "-export-timings:json Exports compile time stats to JSON."); + print_usage_line(3, "-export-timings:csv Exports compile time stats to CSV."); print_usage_line(0, ""); print_usage_line(1, "-export-timings-file:<filename>"); - print_usage_line(2, "Specify the filename for `-export-timings`"); + print_usage_line(2, "Specifies the filename for `-export-timings`."); print_usage_line(2, "Example: -export-timings-file:timings.json"); print_usage_line(0, ""); print_usage_line(1, "-thread-count:<integer>"); - print_usage_line(2, "Override the number of threads the compiler will use to compile with"); + print_usage_line(2, "Overrides the number of threads the compiler will use to compile with."); print_usage_line(2, "Example: -thread-count:2"); print_usage_line(0, ""); } if (check_only) { print_usage_line(1, "-show-unused"); - print_usage_line(2, "Shows unused package declarations within the current project"); + print_usage_line(2, "Shows unused package declarations within the current project."); print_usage_line(0, ""); print_usage_line(1, "-show-unused-with-location"); - print_usage_line(2, "Shows unused package declarations within the current project with the declarations source location"); + print_usage_line(2, "Shows unused package declarations within the current project with the declarations source location."); print_usage_line(0, ""); } if (run_or_build) { print_usage_line(1, "-keep-temp-files"); - print_usage_line(2, "Keeps the temporary files generated during compilation"); + print_usage_line(2, "Keeps the temporary files generated during compilation."); print_usage_line(0, ""); } else if (strip_semicolon) { print_usage_line(1, "-keep-temp-files"); - print_usage_line(2, "Keeps the temporary files generated during stripping the unneeded semicolons from files"); + print_usage_line(2, "Keeps the temporary files generated during stripping the unneeded semicolons from files."); print_usage_line(0, ""); } if (check) { print_usage_line(1, "-collection:<name>=<filepath>"); - print_usage_line(2, "Defines a library collection used for imports"); + print_usage_line(2, "Defines a library collection used for imports."); print_usage_line(2, "Example: -collection:shared=dir/to/shared"); print_usage_line(2, "Usage in Code:"); print_usage_line(3, "import \"shared:foo\""); print_usage_line(0, ""); print_usage_line(1, "-define:<name>=<value>"); - print_usage_line(2, "Defines a scalar boolean, integer or string as global constant"); + print_usage_line(2, "Defines a scalar boolean, integer or string as global constant."); print_usage_line(2, "Example: -define:SPAM=123"); - print_usage_line(2, "To use: #config(SPAM, default_value)"); + print_usage_line(2, "Usage in code:"); + print_usage_line(3, "#config(SPAM, default_value)"); print_usage_line(0, ""); } if (build) { print_usage_line(1, "-build-mode:<mode>"); - print_usage_line(2, "Sets the build mode"); + print_usage_line(2, "Sets the build mode."); print_usage_line(2, "Available options:"); - print_usage_line(3, "-build-mode:exe Build as an executable"); - print_usage_line(3, "-build-mode:dll Build as a dynamically linked library"); - print_usage_line(3, "-build-mode:shared Build as a dynamically linked library"); - print_usage_line(3, "-build-mode:obj Build as an object file"); - print_usage_line(3, "-build-mode:object Build as an object file"); - print_usage_line(3, "-build-mode:assembly Build as an assembly file"); - print_usage_line(3, "-build-mode:assembler Build as an assembly file"); - print_usage_line(3, "-build-mode:asm Build as an assembly file"); - print_usage_line(3, "-build-mode:llvm-ir Build as an LLVM IR file"); - print_usage_line(3, "-build-mode:llvm Build as an LLVM IR file"); + print_usage_line(3, "-build-mode:exe Builds as an executable."); + print_usage_line(3, "-build-mode:dll Builds as a dynamically linked library."); + print_usage_line(3, "-build-mode:shared Builds as a dynamically linked library."); + print_usage_line(3, "-build-mode:obj Builds as an object file."); + print_usage_line(3, "-build-mode:object Builds as an object file."); + print_usage_line(3, "-build-mode:assembly Builds as an assembly file."); + print_usage_line(3, "-build-mode:assembler Builds as an assembly file."); + print_usage_line(3, "-build-mode:asm Builds as an assembly file."); + print_usage_line(3, "-build-mode:llvm-ir Builds as an LLVM IR file."); + print_usage_line(3, "-build-mode:llvm Builds as an LLVM IR file."); print_usage_line(0, ""); } if (check) { print_usage_line(1, "-target:<string>"); - print_usage_line(2, "Sets the target for the executable to be built in"); + print_usage_line(2, "Sets the target for the executable to be built in."); print_usage_line(0, ""); } if (run_or_build) { print_usage_line(1, "-debug"); - print_usage_line(2, "Enabled debug information, and defines the global constant ODIN_DEBUG to be 'true'"); + print_usage_line(2, "Enables debug information, and defines the global constant ODIN_DEBUG to be 'true'."); print_usage_line(0, ""); print_usage_line(1, "-disable-assert"); - print_usage_line(2, "Disable the code generation of the built-in run-time 'assert' procedure, and defines the global constant ODIN_DISABLE_ASSERT to be 'true'"); + print_usage_line(2, "Disables the code generation of the built-in run-time 'assert' procedure, and defines the global constant ODIN_DISABLE_ASSERT to be 'true'."); print_usage_line(0, ""); print_usage_line(1, "-no-bounds-check"); - print_usage_line(2, "Disables bounds checking program wide"); + print_usage_line(2, "Disables bounds checking program wide."); print_usage_line(0, ""); print_usage_line(1, "-no-crt"); - print_usage_line(2, "Disables automatic linking with the C Run Time"); + print_usage_line(2, "Disables automatic linking with the C Run Time."); print_usage_line(0, ""); print_usage_line(1, "-no-thread-local"); - print_usage_line(2, "Ignore @thread_local attribute, effectively treating the program as if it is single-threaded"); + print_usage_line(2, "Ignores @thread_local attribute, effectively treating the program as if it is single-threaded."); print_usage_line(0, ""); print_usage_line(1, "-lld"); - print_usage_line(2, "Use the LLD linker rather than the default"); + print_usage_line(2, "Uses the LLD linker rather than the default."); print_usage_line(0, ""); print_usage_line(1, "-use-separate-modules"); print_usage_line(1, "[EXPERIMENTAL]"); - print_usage_line(2, "The backend generates multiple build units which are then linked together"); - print_usage_line(2, "Normally, a single build unit is generated for a standard project"); + print_usage_line(2, "The backend generates multiple build units which are then linked together."); + print_usage_line(2, "Normally, a single build unit is generated for a standard project."); print_usage_line(0, ""); } if (check) { print_usage_line(1, "-no-threaded-checker"); - print_usage_line(2, "Disabled multithreading in the semantic checker stage"); + print_usage_line(2, "Disables multithreading in the semantic checker stage."); print_usage_line(0, ""); } if (check) { print_usage_line(1, "-vet"); - print_usage_line(2, "Do extra checks on the code"); + print_usage_line(2, "Does extra checks on the code."); print_usage_line(2, "Extra checks include:"); - print_usage_line(2, "-vet-unused"); - print_usage_line(2, "-vet-shadowing"); - print_usage_line(2, "-vet-using-stmt"); + print_usage_line(3, "-vet-unused"); + print_usage_line(3, "-vet-shadowing"); + print_usage_line(3, "-vet-using-stmt"); print_usage_line(0, ""); print_usage_line(1, "-vet-unused"); - print_usage_line(2, "Checks for unused declarations"); + print_usage_line(2, "Checks for unused declarations."); print_usage_line(0, ""); print_usage_line(1, "-vet-shadowing"); - print_usage_line(2, "Checks for variable shadowing within procedures"); + print_usage_line(2, "Checks for variable shadowing within procedures."); print_usage_line(0, ""); print_usage_line(1, "-vet-using-stmt"); - print_usage_line(2, "Checks for the use of 'using' as a statement"); - print_usage_line(2, "'using' is considered bad practice outside of immediate refactoring"); + print_usage_line(2, "Checks for the use of 'using' as a statement."); + print_usage_line(2, "'using' is considered bad practice outside of immediate refactoring."); print_usage_line(0, ""); print_usage_line(1, "-vet-using-param"); - print_usage_line(2, "Checks for the use of 'using' on procedure parameters"); - print_usage_line(2, "'using' is considered bad practice outside of immediate refactoring"); + print_usage_line(2, "Checks for the use of 'using' on procedure parameters."); + print_usage_line(2, "'using' is considered bad practice outside of immediate refactoring."); print_usage_line(0, ""); print_usage_line(1, "-vet-style"); - print_usage_line(2, "Errs on missing trailing commas followed by a newline"); - print_usage_line(2, "Errs on deprecated syntax"); - print_usage_line(2, "Does not err on unneeded tokens (unlike -strict-style)"); + print_usage_line(2, "Errs on missing trailing commas followed by a newline."); + print_usage_line(2, "Errs on deprecated syntax."); + print_usage_line(2, "Does not err on unneeded tokens (unlike -strict-style)."); print_usage_line(0, ""); print_usage_line(1, "-vet-semicolon"); - print_usage_line(2, "Errs on unneeded semicolons"); + print_usage_line(2, "Errs on unneeded semicolons."); print_usage_line(0, ""); } if (check) { print_usage_line(1, "-ignore-unknown-attributes"); - print_usage_line(2, "Ignores unknown attributes"); - print_usage_line(2, "This can be used with metaprogramming tools"); + print_usage_line(2, "Ignores unknown attributes."); + print_usage_line(2, "This can be used with metaprogramming tools."); print_usage_line(0, ""); if (command != "test") { print_usage_line(1, "-no-entry-point"); - print_usage_line(2, "Removes default requirement of an entry point (e.g. main procedure)"); + print_usage_line(2, "Removes default requirement of an entry point (e.g. main procedure)."); print_usage_line(0, ""); } } if (test_only) { print_usage_line(1, "-test-name:<string>"); - print_usage_line(2, "Run specific test only by name"); + print_usage_line(2, "Runs specific test only by name."); print_usage_line(0, ""); } if (run_or_build) { print_usage_line(1, "-minimum-os-version:<string>"); - print_usage_line(2, "Sets the minimum OS version targeted by the application"); - print_usage_line(2, "e.g. -minimum-os-version:12.0.0"); - print_usage_line(2, "(Only used when target is Darwin)"); + print_usage_line(2, "Sets the minimum OS version targeted by the application."); + print_usage_line(2, "Example: -minimum-os-version:12.0.0"); + print_usage_line(2, "(Only used when target is Darwin.)"); print_usage_line(0, ""); print_usage_line(1, "-extra-linker-flags:<string>"); - print_usage_line(2, "Adds extra linker specific flags in a string"); + print_usage_line(2, "Adds extra linker specific flags in a string."); print_usage_line(0, ""); print_usage_line(1, "-extra-assembler-flags:<string>"); - print_usage_line(2, "Adds extra assembler specific flags in a string"); + print_usage_line(2, "Adds extra assembler specific flags in a string."); print_usage_line(0, ""); print_usage_line(1, "-microarch:<string>"); - print_usage_line(2, "Specifies the specific micro-architecture for the build in a string"); + print_usage_line(2, "Specifies the specific micro-architecture for the build in a string."); print_usage_line(2, "Examples:"); print_usage_line(3, "-microarch:sandybridge"); print_usage_line(3, "-microarch:native"); + print_usage_line(3, "-microarch:? for a list"); print_usage_line(0, ""); print_usage_line(1, "-reloc-mode:<string>"); - print_usage_line(2, "Specifies the reloc mode"); - print_usage_line(2, "Options:"); - print_usage_line(3, "default"); - print_usage_line(3, "static"); - print_usage_line(3, "pic"); - print_usage_line(3, "dynamic-no-pic"); + print_usage_line(2, "Specifies the reloc mode."); + print_usage_line(2, "Available options:"); + print_usage_line(3, "-reloc-mode:default"); + print_usage_line(3, "-reloc-mode:static"); + print_usage_line(3, "-reloc-mode:pic"); + print_usage_line(3, "-reloc-mode:dynamic-no-pic"); print_usage_line(0, ""); print_usage_line(1, "-disable-red-zone"); - print_usage_line(2, "Disable red zone on a supported freestanding target"); + print_usage_line(2, "Disables red zone on a supported freestanding target."); print_usage_line(0, ""); print_usage_line(1, "-dynamic-map-calls"); - print_usage_line(2, "Use dynamic map calls to minimize code generation at the cost of runtime execution"); + print_usage_line(2, "Uses dynamic map calls to minimize code generation at the cost of runtime execution."); print_usage_line(0, ""); } if (check) { print_usage_line(1, "-disallow-do"); - print_usage_line(2, "Disallows the 'do' keyword in the project"); + print_usage_line(2, "Disallows the 'do' keyword in the project."); print_usage_line(0, ""); print_usage_line(1, "-default-to-nil-allocator"); - print_usage_line(2, "Sets the default allocator to be the nil_allocator, an allocator which does nothing"); + print_usage_line(2, "Sets the default allocator to be the nil_allocator, an allocator which does nothing."); print_usage_line(0, ""); print_usage_line(1, "-strict-style"); - print_usage_line(2, "Errs on unneeded tokens, such as unneeded semicolons"); - print_usage_line(2, "Errs on missing trailing commas followed by a newline"); - print_usage_line(2, "Errs on deprecated syntax"); + print_usage_line(2, "Errs on unneeded tokens, such as unneeded semicolons."); + print_usage_line(2, "Errs on missing trailing commas followed by a newline."); + print_usage_line(2, "Errs on deprecated syntax."); print_usage_line(0, ""); print_usage_line(1, "-ignore-warnings"); - print_usage_line(2, "Ignores warning messages"); + print_usage_line(2, "Ignores warning messages."); print_usage_line(0, ""); print_usage_line(1, "-warnings-as-errors"); - print_usage_line(2, "Treats warning messages as error messages"); + print_usage_line(2, "Treats warning messages as error messages."); print_usage_line(0, ""); print_usage_line(1, "-terse-errors"); - print_usage_line(2, "Prints a terse error message without showing the code on that line and the location in that line"); + print_usage_line(2, "Prints a terse error message without showing the code on that line and the location in that line."); print_usage_line(0, ""); print_usage_line(1, "-error-pos-style:<string>"); - print_usage_line(2, "Options are 'unix', 'odin' and 'default' (odin)"); - print_usage_line(2, "'odin' file/path(45:3)"); - print_usage_line(2, "'unix' file/path:45:3:"); + print_usage_line(2, "Available options:"); + print_usage_line(3, "-error-pos-style:unix file/path:45:3:"); + print_usage_line(3, "-error-pos-style:odin file/path(45:3)"); + print_usage_line(3, "-error-pos-style:default (Defaults to 'odin'.)"); print_usage_line(0, ""); - print_usage_line(1, "-max-error-count:<integer>"); - print_usage_line(2, "Set the maximum number of errors that can be displayed before the compiler terminates"); - print_usage_line(2, "Must be an integer >0"); - print_usage_line(2, "If not set, the default max error count is %d", DEFAULT_MAX_ERROR_COLLECTOR_COUNT); + print_usage_line(2, "Sets the maximum number of errors that can be displayed before the compiler terminates."); + print_usage_line(2, "Must be an integer >0."); + print_usage_line(2, "If not set, the default max error count is %d.", DEFAULT_MAX_ERROR_COLLECTOR_COUNT); print_usage_line(0, ""); print_usage_line(1, "-foreign-error-procedures"); - print_usage_line(2, "States that the error procedues used in the runtime are defined in a separate translation unit"); + print_usage_line(2, "States that the error procedures used in the runtime are defined in a separate translation unit."); print_usage_line(0, ""); } if (run_or_build) { + print_usage_line(1, "-obfuscate-source-code-locations"); + print_usage_line(2, "Obfuscate the file and procedure strings, and line and column numbers, stored with a 'runtime.Source_Code_Location' value."); + print_usage_line(0, ""); + print_usage_line(1, "-sanitize:<string>"); - print_usage_line(1, "Enables sanitization analysis"); - print_usage_line(1, "Options are 'address', 'memory', and 'thread'"); - print_usage_line(1, "NOTE: This flag can be used multiple times"); + print_usage_line(2, "Enables sanitization analysis."); + print_usage_line(2, "Available options:"); + print_usage_line(3, "-sanitize:address"); + print_usage_line(3, "-sanitize:memory"); + print_usage_line(3, "-sanitize:thread"); + print_usage_line(2, "NOTE: This flag can be used multiple times."); print_usage_line(0, ""); } @@ -1955,27 +2003,27 @@ gb_internal void print_show_help(String const arg0, String const &command) { #if defined(GB_SYSTEM_WINDOWS) print_usage_line(1, "-ignore-vs-search"); print_usage_line(2, "[Windows only]"); - print_usage_line(2, "Ignores the Visual Studio search for library paths"); + print_usage_line(2, "Ignores the Visual Studio search for library paths."); print_usage_line(0, ""); print_usage_line(1, "-resource:<filepath>"); print_usage_line(2, "[Windows only]"); - print_usage_line(2, "Defines the resource file for the executable"); + print_usage_line(2, "Defines the resource file for the executable."); print_usage_line(2, "Example: -resource:path/to/file.rc"); print_usage_line(0, ""); print_usage_line(1, "-pdb-name:<filepath>"); print_usage_line(2, "[Windows only]"); - print_usage_line(2, "Defines the generated PDB name when -debug is enabled"); + print_usage_line(2, "Defines the generated PDB name when -debug is enabled."); print_usage_line(2, "Example: -pdb-name:different.pdb"); print_usage_line(0, ""); print_usage_line(1, "-subsystem:<option>"); print_usage_line(2, "[Windows only]"); - print_usage_line(2, "Defines the subsystem for the application"); + print_usage_line(2, "Defines the subsystem for the application."); print_usage_line(2, "Available options:"); - print_usage_line(3, "console"); - print_usage_line(3, "windows"); + print_usage_line(3, "-subsystem:console"); + print_usage_line(3, "-subsystem:windows"); print_usage_line(0, ""); #endif @@ -2518,12 +2566,55 @@ int main(int arg_count, char const **arg_ptr) { // return 1; // } + // Check chosen microarchitecture. If not found or ?, print list. + bool print_microarch_list = true; + if (build_context.microarch.len == 0) { + // Autodetect, no need to print list. + print_microarch_list = false; + } else { + String march_list = target_microarch_list[build_context.metrics.arch]; + String_Iterator it = {march_list, 0}; + for (;;) { + String str = string_split_iterator(&it, ','); + if (str == "") break; + if (str == build_context.microarch) { + // Found matching microarch + print_microarch_list = false; + break; + } + } + } + + String default_march = get_default_microarchitecture(); + if (print_microarch_list) { + if (build_context.microarch != "?") { + gb_printf("Unknown microarchitecture '%.*s'.\n", LIT(build_context.microarch)); + } + gb_printf("Possible -microarch values for target %.*s are:\n", LIT(target_arch_names[build_context.metrics.arch])); + gb_printf("\n"); + + String march_list = target_microarch_list[build_context.metrics.arch]; + String_Iterator it = {march_list, 0}; + + for (;;) { + String str = string_split_iterator(&it, ','); + if (str == "") break; + if (str == default_march) { + gb_printf("\t%.*s (default)\n", LIT(str)); + } else { + gb_printf("\t%.*s\n", LIT(str)); + } + } + return 0; + } + // Set and check build paths... if (!init_build_paths(init_filename)) { return 1; } if (build_context.show_debug_messages) { + debugf("Selected microarch: %.*s\n", LIT(default_march)); for_array(i, build_context.build_paths) { String build_path = path_to_string(heap_allocator(), build_context.build_paths[i]); debugf("build_paths[%ld]: %.*s\n", i, LIT(build_path)); diff --git a/src/parser.cpp b/src/parser.cpp index c0498b425..2671054df 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -5919,7 +5919,7 @@ gb_internal bool parse_file(Parser *p, AstFile *f) { f->vet_flags = parse_vet_tag(tok, lc); f->vet_flags_set = true; } else if (string_starts_with(lc, str_lit("+ignore"))) { - return false; + return false; } else if (string_starts_with(lc, str_lit("+private"))) { f->flags |= AstFile_IsPrivatePkg; String command = string_trim_starts_with(lc, str_lit("+private ")); @@ -5941,6 +5941,8 @@ gb_internal bool parse_file(Parser *p, AstFile *f) { } else { f->flags |= AstFile_IsLazy; } + } else if (lc == "+no-instrumentation") { + f->flags |= AstFile_NoInstrumentation; } else { warning(tok, "Ignoring unknown tag '%.*s'", LIT(lc)); } diff --git a/src/parser.hpp b/src/parser.hpp index bce818652..cc1836ef3 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -76,6 +76,8 @@ enum AstFileFlag : u32 { AstFile_IsTest = 1<<3, AstFile_IsLazy = 1<<4, + + AstFile_NoInstrumentation = 1<<5, }; enum AstDelayQueueKind { diff --git a/src/string.cpp b/src/string.cpp index 6eac4f53b..9fb933b1b 100644 --- a/src/string.cpp +++ b/src/string.cpp @@ -10,6 +10,10 @@ struct String { return text[i]; } }; +struct String_Iterator { + String const &str; + isize pos; +}; // NOTE(bill): used for printf style arguments #define LIT(x) ((int)(x).len), (x).text #if defined(GB_COMPILER_MSVC) && _MSC_VER < 1700 @@ -201,11 +205,31 @@ gb_internal gb_inline String string_trim_starts_with(String const &s, String con } +gb_internal String string_split_iterator(String_Iterator *it, const char sep) { + isize start = it->pos; + isize end = it->str.len; + + if (start == end) { + return str_lit(""); + } + + isize i = start; + for (; i < it->str.len; i++) { + if (it->str[i] == sep) { + String res = substring(it->str, start, i); + it->pos += res.len + 1; + return res; + } + } + it->pos = end; + return substring(it->str, start, end); +} + gb_internal gb_inline isize string_extension_position(String const &str) { isize dot_pos = -1; isize i = str.len; while (i --> 0) { - if (str[i] == GB_PATH_SEPARATOR) + if (str[i] == '\\' || str[i] == '/') break; if (str[i] == '.') { dot_pos = i; diff --git a/src/threading.cpp b/src/threading.cpp index 3ddc05b0a..c283da425 100644 --- a/src/threading.cpp +++ b/src/threading.cpp @@ -210,7 +210,7 @@ gb_internal void semaphore_wait(Semaphore *s) { original_count = s->count().load(std::memory_order_relaxed); } - if (!s->count().compare_exchange_strong(original_count, original_count-1, std::memory_order_acquire, std::memory_order_acquire)) { + if (s->count().compare_exchange_strong(original_count, original_count-1, std::memory_order_acquire, std::memory_order_acquire)) { return; } } @@ -660,7 +660,7 @@ gb_internal void futex_broadcast(Futex *addr) { gb_internal void futex_wait(Futex *addr, Footex val) { for (;;) { int ret = _umtx_op(addr, UMTX_OP_WAIT_UINT, val, 0, NULL); - if (ret == 0) { + if (ret == -1) { if (errno == ETIMEDOUT || errno == EINTR) { continue; } diff --git a/src/tilde.cpp b/src/tilde.cpp index b27c42a12..06428f317 100644 --- a/src/tilde.cpp +++ b/src/tilde.cpp @@ -215,7 +215,7 @@ gb_internal void cg_set_debug_pos_from_node(cgProcedure *p, Ast *node) { TokenPos pos = ast_token(node).pos; TB_SourceFile **file = map_get(&p->module->file_id_map, cast(uintptr)pos.file_id); if (file) { - tb_inst_set_location(p->func, *file, pos.line, pos.column); + tb_inst_location(p->func, *file, pos.line, pos.column); } } } @@ -373,7 +373,7 @@ gb_internal bool cg_global_variables_create(cgModule *m, Array<cgGlobalVariable> TB_Global *global = tb_global_create(m->mod, name.len, cast(char const *)name.text, debug_type, linkage); cgValue g = cg_value(global, alloc_type_pointer(e->type)); - TB_ModuleSection *section = tb_module_get_data(m->mod); + TB_ModuleSectionHandle section = tb_module_get_data(m->mod); if (e->Variable.thread_local_model != "") { section = tb_module_get_tls(m->mod); @@ -726,6 +726,10 @@ gb_internal bool cg_generate_code(Checker *c, LinkerData *linker_data) { linker_data_init(linker_data, info, c->parser->init_fullpath); + #if defined(GB_SYSTEM_OSX) + linker_enable_system_library_linking(linker_data); + #endif + cg_global_arena_init(); cgModule *m = cg_module_create(c); diff --git a/src/tilde.hpp b/src/tilde.hpp index 7088912fd..d141b2330 100644 --- a/src/tilde.hpp +++ b/src/tilde.hpp @@ -8,8 +8,8 @@ #include "tilde/tb.h" #include "tilde/tb_arena.h" -#define TB_TYPE_F16 TB_DataType{ { TB_INT, 0, 16 } } -#define TB_TYPE_I128 TB_DataType{ { TB_INT, 0, 128 } } +#define TB_TYPE_F16 TB_DataType{ { TB_INT, 16 } } +#define TB_TYPE_I128 TB_DataType{ { TB_INT, 128 } } #define TB_TYPE_INT TB_TYPE_INTN(cast(u16)(8*build_context.int_size)) #define TB_TYPE_INTPTR TB_TYPE_INTN(cast(u16)(8*build_context.ptr_size)) diff --git a/src/tilde/tb.h b/src/tilde/tb.h index 5bb98fe70..4fe21c902 100644 --- a/src/tilde/tb.h +++ b/src/tilde/tb.h @@ -4,9 +4,15 @@ // SSA - single static assignment // GVN - global value numbering // CSE - common subexpression elimination +// CFG - control flow graph // DSE - dead store elimination // GCM - global code motion // SROA - scalar replacement of aggregates +// SCCP - sparse conditional constant propagation +// RPO - reverse postorder +// BB - basic block +// ZTC - zero trip count +// SCC - strongly connected components #ifndef TB_CORE_H #define TB_CORE_H @@ -21,7 +27,7 @@ // https://semver.org/ #define TB_VERSION_MAJOR 0 -#define TB_VERSION_MINOR 2 +#define TB_VERSION_MINOR 3 #define TB_VERSION_PATCH 0 #ifndef TB_API @@ -125,12 +131,6 @@ typedef struct TB_FeatureSet { TB_FeatureSet_X64 x64; } TB_FeatureSet; -typedef enum TB_BranchHint { - TB_BRANCH_HINT_NONE, - TB_BRANCH_HINT_LIKELY, - TB_BRANCH_HINT_UNLIKELY -} TB_BranchHint; - typedef enum TB_Linkage { TB_LINKAGE_PUBLIC, TB_LINKAGE_PRIVATE @@ -151,12 +151,6 @@ typedef enum TB_MemoryOrder { TB_MEM_ORDER_SEQ_CST, } TB_MemoryOrder; -typedef enum TB_ISelMode { - // FastISel - TB_ISEL_FAST, - TB_ISEL_COMPLEX -} TB_ISelMode; - typedef enum TB_DataTypeEnum { // Integers, note void is an i0 and bool is an i1 // i(0-64) @@ -166,12 +160,14 @@ typedef enum TB_DataTypeEnum { TB_FLOAT, // Pointers TB_PTR, - // Tuples, these cannot be used in memory ops, just accessed via projections - TB_TUPLE, // represents control flow for REGION, BRANCH TB_CONTROL, // represents memory (and I/O) TB_MEMORY, + // continuation (usually just return addresses :p) + TB_CONT, + // Tuples, these cannot be used in memory ops, just accessed via projections + TB_TUPLE, } TB_DataTypeEnum; typedef enum TB_FloatFormat { @@ -181,15 +177,13 @@ typedef enum TB_FloatFormat { typedef union TB_DataType { struct { - uint8_t type; - // Only integers and floats can be wide. - uint8_t width; + uint16_t type : 4; // for integers it's the bitwidth - uint16_t data; + uint16_t data : 12; }; - uint32_t raw; + uint16_t raw; } TB_DataType; -static_assert(sizeof(TB_DataType) == 4, "im expecting this to be a uint32_t"); +static_assert(sizeof(TB_DataType) == 2, "im expecting this to be a uint16_t"); // classify data types #define TB_IS_VOID_TYPE(x) ((x).type == TB_INT && (x).data == 0) @@ -240,6 +234,11 @@ typedef enum TB_NodeTypeEnum { TB_MACHINE_OP, // (Control, Memory) & Buffer -> (Control, Memory) // reads the TSC on x64 TB_CYCLE_COUNTER, // (Control) -> Int64 + // prefetches data for reading. The number next to the + // + // 0 is temporal + // 1-3 are just cache levels + TB_PREFETCH, // (Memory, Ptr) & Int -> Memory //////////////////////////////// // CONTROL @@ -269,7 +268,11 @@ typedef enum TB_NodeTypeEnum { // trap will not be continuable but will stop execution. TB_TRAP, // (Control) -> (Control) // unreachable means it won't trap or be continuable. - TB_UNREACHABLE, // (Control) -> (Control) + TB_UNREACHABLE, // (Control) -> () + // this is generated when a path becomes disconnected + // from the main IR, it'll be reduced by the monotonic + // rewrites. + TB_DEAD, // () -> (Control) //////////////////////////////// // CONTROL + MEMORY @@ -278,38 +281,49 @@ typedef enum TB_NodeTypeEnum { // target pointer (or syscall number) and the rest are just data args. TB_CALL, // (Control, Memory, Data, Data...) -> (Control, Memory, Data) TB_SYSCALL, // (Control, Memory, Data, Data...) -> (Control, Memory, Data) + // performs call while recycling the stack frame somewhat + TB_TAILCALL, // (Control, Memory, RPC, Data, Data...) -> () // safepoint polls are the same except they only trigger if the poll site // says to (platform specific but almost always just the page being made // unmapped/guard), 3rd argument is the poll site. - TB_SAFEPOINT_POLL, // (Control, Memory, Ptr, Data...) -> (Control) + TB_SAFEPOINT_POLL, // (Control, Memory, Ptr?, Data...) -> (Control) + // this safepoint which doesn't emit any poll site, it's just + // an address, this is used by AOT compiles to encode line info. + TB_SAFEPOINT_NOP, // (Control, Memory, Ptr?, Data...) -> (Control) //////////////////////////////// // MEMORY //////////////////////////////// + // MERGEMEM will join multiple non-aliasing memory effects, because + // they don't alias there's no ordering guarentee. + TB_MERGEMEM, // (Memory...) -> Memory // LOAD and STORE are standard memory accesses, they can be folded away. - TB_LOAD, // (Memory, Ptr) -> Data - TB_STORE, // (Memory, Ptr, Data) -> Memory + TB_LOAD, // (Control?, Memory, Ptr) -> Data + TB_STORE, // (Control, Memory, Ptr, Data) -> Memory // bulk memory ops. - TB_MEMCPY, // (Memory, Ptr, Ptr, Size) -> Memory - TB_MEMSET, // (Memory, Ptr, Int8, Size) -> Memory + TB_MEMCPY, // (Control, Memory, Ptr, Ptr, Size) -> Memory + TB_MEMSET, // (Control, Memory, Ptr, Int8, Size) -> Memory // these memory accesses represent "volatile" which means // they may produce side effects and thus cannot be eliminated. - TB_READ, // (Memory, Ptr) -> (Memory, Data) - TB_WRITE, // (Memory, Ptr, Data) -> (Memory, Data) + TB_READ, // (Control, Memory, Ptr) -> (Memory, Data) + TB_WRITE, // (Control, Memory, Ptr, Data) -> (Memory, Data) // atomics have multiple observers (if not they wouldn't need to // be atomic) and thus produce side effects everywhere just like // volatiles except they have synchronization guarentees. the atomic // data ops will return the value before the operation is performed. // Atomic CAS return the old value and a boolean for success (true if // the value was changed) - TB_ATOMIC_LOAD, // (Memory, Ptr) -> (Memory, Data) - TB_ATOMIC_XCHG, // (Memory, Ptr, Data) -> (Memory, Data) - TB_ATOMIC_ADD, // (Memory, Ptr, Data) -> (Memory, Data) - TB_ATOMIC_SUB, // (Memory, Ptr, Data) -> (Memory, Data) - TB_ATOMIC_AND, // (Memory, Ptr, Data) -> (Memory, Data) - TB_ATOMIC_XOR, // (Memory, Ptr, Data) -> (Memory, Data) - TB_ATOMIC_OR, // (Memory, Ptr, Data) -> (Memory, Data) - TB_ATOMIC_CAS, // (Memory, Data, Data) -> (Memory, Data, Bool) + TB_ATOMIC_LOAD, // (Control, Memory, Ptr) -> (Memory, Data) + TB_ATOMIC_XCHG, // (Control, Memory, Ptr, Data) -> (Memory, Data) + TB_ATOMIC_ADD, // (Control, Memory, Ptr, Data) -> (Memory, Data) + TB_ATOMIC_SUB, // (Control, Memory, Ptr, Data) -> (Memory, Data) + TB_ATOMIC_AND, // (Control, Memory, Ptr, Data) -> (Memory, Data) + TB_ATOMIC_XOR, // (Control, Memory, Ptr, Data) -> (Memory, Data) + TB_ATOMIC_OR, // (Control, Memory, Ptr, Data) -> (Memory, Data) + TB_ATOMIC_CAS, // (Control, Memory, Data, Data) -> (Memory, Data, Bool) + + // like a multi-way branch but without the control flow aspect, but for data. + TB_LOOKUP, //////////////////////////////// // POINTERS @@ -375,6 +389,8 @@ typedef enum TB_NodeTypeEnum { TB_FSUB, TB_FMUL, TB_FDIV, + TB_FMAX, + TB_FMIN, // Comparisons TB_CMP_EQ, @@ -418,10 +434,17 @@ typedef uint32_t TB_CharUnits; typedef struct { // used by the debug info export int id; + size_t len; uint8_t path[]; } TB_SourceFile; +typedef struct TB_Location { + TB_SourceFile* file; + int line, column; + uint32_t pos; +} TB_Location; + // SO refers to shared objects which mean either shared libraries (.so or .dll) // or executables (.exe or ELF executables) typedef enum { @@ -441,31 +464,38 @@ typedef struct TB_DebugType TB_DebugType; typedef struct TB_ModuleSection TB_ModuleSection; typedef struct TB_FunctionPrototype TB_FunctionPrototype; -typedef struct TB_Attrib TB_Attrib; +enum { TB_MODULE_SECTION_NONE = -1 }; +typedef int32_t TB_ModuleSectionHandle; +typedef struct TB_Attrib TB_Attrib; // target-specific, just a unique ID for the registers typedef int TB_PhysicalReg; +// Thread local module state +typedef struct TB_ThreadInfo TB_ThreadInfo; + +typedef enum { + TB_SYMBOL_NONE, + TB_SYMBOL_EXTERNAL, + TB_SYMBOL_GLOBAL, + TB_SYMBOL_FUNCTION, + TB_SYMBOL_MAX, +} TB_SymbolTag; + // Refers generically to objects within a module // // TB_Function, TB_Global, and TB_External are all subtypes of TB_Symbol // and thus are safely allowed to cast into a symbol for operations. typedef struct TB_Symbol { - enum TB_SymbolTag { - TB_SYMBOL_NONE, - - // symbol is dead now - TB_SYMBOL_TOMBSTONE, - - TB_SYMBOL_EXTERNAL, - TB_SYMBOL_GLOBAL, - TB_SYMBOL_FUNCTION, - - TB_SYMBOL_MAX, - } tag; - - // refers to the prev or next symbol with the same tag - struct TB_Symbol* next; + #ifdef __cplusplus + TB_SymbolTag tag; + #else + _Atomic TB_SymbolTag tag; + #endif + + // which thread info it's tied to (we may need to remove it, this + // is used for that) + TB_ThreadInfo* info; char* name; // It's kinda a weird circular reference but yea @@ -484,15 +514,28 @@ typedef struct TB_Symbol { } TB_Symbol; typedef struct TB_Node TB_Node; +typedef struct User User; +struct User { + User* next; + TB_Node* n; + int slot; +}; + struct TB_Node { TB_NodeType type; - uint16_t input_count; // number of node inputs. + uint16_t input_count; TB_DataType dt; // makes it easier to track in graph walks - size_t gvn; + uint32_t gvn; - TB_Attrib* attribs; + // only value while inside of a TB_Passes, + // these are unordered and usually just + // help perform certain transformations or + // analysis (not necessarily semantics) + User* users; + + // ordered def-use edges, jolly ol' semantics TB_Node** inputs; char extra[]; @@ -506,8 +549,6 @@ struct TB_Node { // this represents switch (many targets), if (one target) and goto (only default) logic. typedef struct { // TB_BRANCH size_t succ_count; - TB_Node** succ; - int64_t keys[]; } TB_NodeBranch; @@ -527,16 +568,17 @@ typedef struct { // any integer binary operator TB_ArithmeticBehavior ab; } TB_NodeBinopInt; -typedef struct { // TB_MULPAIR - TB_Node *lo, *hi; -} TB_NodeArithPair; - typedef struct { TB_CharUnits align; } TB_NodeMemAccess; typedef struct { + int level; +} TB_NodePrefetch; + +typedef struct { TB_CharUnits size, align; + int alias_index; // 0 if local is used beyond direct memops, 1...n as a unique alias name } TB_NodeLocal; typedef struct { @@ -577,32 +619,41 @@ typedef struct { } TB_NodeAtomic; typedef struct { + // line info on safepoints + TB_SourceFile* file; + int line, column; +} TB_NodeSafepoint; + +typedef struct { TB_FunctionPrototype* proto; + int proj_count; TB_Node* projs[]; } TB_NodeCall; typedef struct { - uint32_t id; -} TB_NodeSafepoint; + TB_FunctionPrototype* proto; +} TB_NodeTailcall; typedef struct { - TB_Node* end; const char* tag; - // position in a postorder walk - int postorder_id; - // immediate dominator (can be approximate) - int dom_depth; - TB_Node* dom; + // magic factor for hot-code, higher means run more often + float freq; // used for IR building only, stale after that. - // - // this represents the first and last memory values within a region, - // if a region ever has multiple predecessors we apply a join on these - // memory. TB_Node *mem_in, *mem_out; } TB_NodeRegion; +typedef struct { + int64_t key; + uint64_t val; +} TB_LookupEntry; + +typedef struct { + size_t entry_count; + TB_LookupEntry entries[]; +} TB_NodeLookup; + typedef struct TB_MultiOutput { size_t count; union { @@ -634,6 +685,12 @@ typedef struct { int32_t values[]; } TB_Safepoint; +typedef enum { + TB_MODULE_SECTION_WRITE = 1, + TB_MODULE_SECTION_EXEC = 2, + TB_MODULE_SECTION_TLS = 4, +} TB_ModuleSectionFlags; + // ******************************* // Public macros // ******************************* @@ -641,35 +698,37 @@ typedef struct { #define TB_TYPE_TUPLE TB_DataType{ { TB_TUPLE } } #define TB_TYPE_CONTROL TB_DataType{ { TB_CONTROL } } -#define TB_TYPE_VOID TB_DataType{ { TB_INT, 0, 0 } } -#define TB_TYPE_I8 TB_DataType{ { TB_INT, 0, 8 } } -#define TB_TYPE_I16 TB_DataType{ { TB_INT, 0, 16 } } -#define TB_TYPE_I32 TB_DataType{ { TB_INT, 0, 32 } } -#define TB_TYPE_I64 TB_DataType{ { TB_INT, 0, 64 } } -#define TB_TYPE_F32 TB_DataType{ { TB_FLOAT, 0, TB_FLT_32 } } -#define TB_TYPE_F64 TB_DataType{ { TB_FLOAT, 0, TB_FLT_64 } } -#define TB_TYPE_BOOL TB_DataType{ { TB_INT, 0, 1 } } -#define TB_TYPE_PTR TB_DataType{ { TB_PTR, 0, 0 } } -#define TB_TYPE_MEMORY TB_DataType{ { TB_MEMORY,0, 0 } } -#define TB_TYPE_INTN(N) TB_DataType{ { TB_INT, 0, (N) } } -#define TB_TYPE_PTRN(N) TB_DataType{ { TB_PTR, 0, (N) } } +#define TB_TYPE_VOID TB_DataType{ { TB_INT, 0 } } +#define TB_TYPE_I8 TB_DataType{ { TB_INT, 8 } } +#define TB_TYPE_I16 TB_DataType{ { TB_INT, 16 } } +#define TB_TYPE_I32 TB_DataType{ { TB_INT, 32 } } +#define TB_TYPE_I64 TB_DataType{ { TB_INT, 64 } } +#define TB_TYPE_F32 TB_DataType{ { TB_FLOAT, TB_FLT_32 } } +#define TB_TYPE_F64 TB_DataType{ { TB_FLOAT, TB_FLT_64 } } +#define TB_TYPE_BOOL TB_DataType{ { TB_INT, 1 } } +#define TB_TYPE_PTR TB_DataType{ { TB_PTR, 0 } } +#define TB_TYPE_MEMORY TB_DataType{ { TB_MEMORY,0 } } +#define TB_TYPE_CONT TB_DataType{ { TB_CONT, 0 } } +#define TB_TYPE_INTN(N) TB_DataType{ { TB_INT, (N) } } +#define TB_TYPE_PTRN(N) TB_DataType{ { TB_PTR, (N) } } #else #define TB_TYPE_TUPLE (TB_DataType){ { TB_TUPLE } } #define TB_TYPE_CONTROL (TB_DataType){ { TB_CONTROL } } -#define TB_TYPE_VOID (TB_DataType){ { TB_INT, 0, 0 } } -#define TB_TYPE_I8 (TB_DataType){ { TB_INT, 0, 8 } } -#define TB_TYPE_I16 (TB_DataType){ { TB_INT, 0, 16 } } -#define TB_TYPE_I32 (TB_DataType){ { TB_INT, 0, 32 } } -#define TB_TYPE_I64 (TB_DataType){ { TB_INT, 0, 64 } } -#define TB_TYPE_F32 (TB_DataType){ { TB_FLOAT, 0, TB_FLT_32 } } -#define TB_TYPE_F64 (TB_DataType){ { TB_FLOAT, 0, TB_FLT_64 } } -#define TB_TYPE_BOOL (TB_DataType){ { TB_INT, 0, 1 } } -#define TB_TYPE_PTR (TB_DataType){ { TB_PTR, 0, 0 } } -#define TB_TYPE_MEMORY (TB_DataType){ { TB_MEMORY,0, 0 } } -#define TB_TYPE_INTN(N) (TB_DataType){ { TB_INT, 0, (N) } } -#define TB_TYPE_PTRN(N) (TB_DataType){ { TB_PTR, 0, (N) } } +#define TB_TYPE_VOID (TB_DataType){ { TB_INT, 0 } } +#define TB_TYPE_I8 (TB_DataType){ { TB_INT, 8 } } +#define TB_TYPE_I16 (TB_DataType){ { TB_INT, 16 } } +#define TB_TYPE_I32 (TB_DataType){ { TB_INT, 32 } } +#define TB_TYPE_I64 (TB_DataType){ { TB_INT, 64 } } +#define TB_TYPE_F32 (TB_DataType){ { TB_FLOAT, TB_FLT_32 } } +#define TB_TYPE_F64 (TB_DataType){ { TB_FLOAT, TB_FLT_64 } } +#define TB_TYPE_BOOL (TB_DataType){ { TB_INT, 1 } } +#define TB_TYPE_PTR (TB_DataType){ { TB_PTR, 0 } } +#define TB_TYPE_CONT (TB_DataType){ { TB_CONT, 0 } } +#define TB_TYPE_MEMORY (TB_DataType){ { TB_MEMORY,0 } } +#define TB_TYPE_INTN(N) (TB_DataType){ { TB_INT, (N) } } +#define TB_TYPE_PTRN(N) (TB_DataType){ { TB_PTR, (N) } } #endif @@ -681,7 +740,8 @@ typedef struct TB_Arena TB_Arena; // 0 for default TB_API void tb_arena_create(TB_Arena* restrict arena, size_t chunk_size); TB_API void tb_arena_destroy(TB_Arena* restrict arena); -TB_API bool tb_arena_is_empty(TB_Arena* arena); +TB_API bool tb_arena_is_empty(TB_Arena* restrict arena); +TB_API void tb_arena_clear(TB_Arena* restrict arena); //////////////////////////////// // Module management @@ -692,8 +752,6 @@ TB_API TB_Module* tb_module_create(TB_Arch arch, TB_System sys, const TB_Feature // Creates a module but defaults on the architecture and system based on the host machine TB_API TB_Module* tb_module_create_for_host(const TB_FeatureSet* features, bool is_jit); -TB_API size_t tb_module_get_function_count(TB_Module* m); - // Frees all resources for the TB_Module and it's functions, globals and // compiled code. TB_API void tb_module_destroy(TB_Module* m); @@ -703,9 +761,16 @@ TB_API void tb_module_destroy(TB_Module* m); // dont and the tls_index is used, it'll crash TB_API void tb_module_set_tls_index(TB_Module* m, ptrdiff_t len, const char* name); -// You don't need to manually call this unless you want to resolve locations before -// exporting. -TB_API void tb_module_layout_sections(TB_Module* m); +TB_API TB_ModuleSectionHandle tb_module_create_section(TB_Module* m, ptrdiff_t len, const char* name, TB_ModuleSectionFlags flags, TB_ComdatType comdat); + +typedef struct { + TB_ThreadInfo* info; + size_t i; +} TB_SymbolIter; + +// Lovely iterator for all the symbols... it's probably not "fast" +TB_SymbolIter tb_symbol_iter(TB_Module* mod); +TB_Symbol* tb_symbol_iter_next(TB_SymbolIter* iter); //////////////////////////////// // Compiled code introspection @@ -728,6 +793,9 @@ TB_API void tb_output_print_asm(TB_FunctionOutput* out, FILE* fp); TB_API uint8_t* tb_output_get_code(TB_FunctionOutput* out, size_t* out_length); +// returns NULL if there's no line info +TB_API TB_Location* tb_output_get_locations(TB_FunctionOutput* out, size_t* out_count); + // returns NULL if no assembly was generated TB_API TB_Assembly* tb_output_get_asm(TB_FunctionOutput* out); @@ -738,17 +806,55 @@ TB_API TB_Safepoint* tb_safepoint_get(TB_Function* f, uint32_t relative_ip); // JIT compilation //////////////////////////////// typedef struct TB_JIT TB_JIT; +typedef struct TB_CPUContext TB_CPUContext; // passing 0 to jit_heap_capacity will default to 4MiB TB_API TB_JIT* tb_jit_begin(TB_Module* m, size_t jit_heap_capacity); TB_API void* tb_jit_place_function(TB_JIT* jit, TB_Function* f); TB_API void* tb_jit_place_global(TB_JIT* jit, TB_Global* g); +TB_API void tb_jit_dump_heap(TB_JIT* jit); TB_API void tb_jit_end(TB_JIT* jit); +typedef struct { + TB_Symbol* base; + uint32_t offset; +} TB_ResolvedAddr; + +typedef struct { + TB_Function* f; + TB_Location* loc; + uint32_t start, end; +} TB_ResolvedLine; + +TB_API TB_ResolvedAddr tb_jit_addr2sym(TB_JIT* jit, void* ptr); +TB_API TB_ResolvedLine tb_jit_addr2line(TB_JIT* jit, void* ptr); TB_API void* tb_jit_get_code_ptr(TB_Function* f); -// Generates a 2MiB stack -TB_API void* tb_jit_stack_create(size_t* out_size); +typedef enum { + // just keeps running + TB_DBG_NONE, + // stops after one instruction + TB_DBG_INST, + // stops once the line changes + TB_DBG_LINE, +} TB_DbgStep; + +// Debugger stuff +// creates a new context we can run JIT code in, you don't +// technically need this but it's a nice helper for writing +// JITs especially when it comes to breakpoints (and eventually +// safepoints) +TB_API TB_CPUContext* tb_jit_thread_create(void* entry, void* arg); +// runs until TB_DbgStep condition is met +TB_API bool tb_jit_thread_resume(TB_JIT* jit, TB_CPUContext* cpu, TB_DbgStep step); +TB_API void* tb_jit_thread_pc(TB_CPUContext* cpu); +TB_API void tb_jit_breakpoint(TB_JIT* jit, void* addr); +TB_API void tb_jit_thread_dump_stack(TB_JIT* jit, TB_CPUContext* cpu); + +//////////////////////////////// +// Disassembler +//////////////////////////////// +TB_API ptrdiff_t tb_print_disassembly_inst(TB_Arch arch, size_t length, const void* ptr); //////////////////////////////// // Exporter @@ -820,18 +926,7 @@ TB_API void tb_linker_append_library(TB_Linker* l, TB_Slice ar_name, TB_Slice co //////////////////////////////// // Symbols //////////////////////////////// -#define TB_FOR_FUNCTIONS(it, module) for (TB_Function* it = tb_first_function(module); it != NULL; it = tb_next_function(it)) -TB_API TB_Function* tb_first_function(TB_Module* m); -TB_API TB_Function* tb_next_function(TB_Function* f); - -#define TB_FOR_EXTERNALS(it, module) for (TB_External* it = tb_first_external(module); it != NULL; it = tb_next_external(it)) -TB_API TB_External* tb_first_external(TB_Module* m); -TB_API TB_External* tb_next_external(TB_External* e); - -// this is used JIT scenarios to tell the compiler what externals map to -TB_API TB_ExternalType tb_extern_get_type(TB_External* e); TB_API TB_Global* tb_extern_transmute(TB_External* e, TB_DebugType* dbg_type, TB_Linkage linkage); - TB_API TB_External* tb_extern_create(TB_Module* m, ptrdiff_t len, const char* name, TB_ExternalType type); TB_API TB_SourceFile* tb_get_source_file(TB_Module* m, const char* path); @@ -876,7 +971,7 @@ TB_API TB_FunctionPrototype* tb_prototype_create(TB_Module* m, TB_CallingConv cc // into the correct ABI and exposing sane looking nodes to the parameters. // // returns the parameters -TB_API TB_Node** tb_function_set_prototype_from_dbg(TB_Function* f, TB_DebugType* dbg, TB_Arena* arena, size_t* out_param_count); +TB_API TB_Node** tb_function_set_prototype_from_dbg(TB_Function* f, TB_ModuleSectionHandle section, TB_DebugType* dbg, TB_Arena* arena, size_t* out_param_count); TB_API TB_FunctionPrototype* tb_prototype_from_dbg(TB_Module* m, TB_DebugType* dbg); // used for ABI parameter passing @@ -899,7 +994,7 @@ TB_API TB_PassingRule tb_get_passing_rule_from_dbg(TB_Module* mod, TB_DebugType* TB_API TB_Global* tb_global_create(TB_Module* m, ptrdiff_t len, const char* name, TB_DebugType* dbg_type, TB_Linkage linkage); // allocate space for the global -TB_API void tb_global_set_storage(TB_Module* m, TB_ModuleSection* section, TB_Global* global, size_t size, size_t align, size_t max_objects); +TB_API void tb_global_set_storage(TB_Module* m, TB_ModuleSectionHandle section, TB_Global* global, size_t size, size_t align, size_t max_objects); // returns a buffer which the user can fill to then have represented in the initializer TB_API void* tb_global_add_region(TB_Module* m, TB_Global* global, size_t offset, size_t size); @@ -908,10 +1003,10 @@ TB_API void* tb_global_add_region(TB_Module* m, TB_Global* global, size_t offset // depends on the pointer size TB_API void tb_global_add_symbol_reloc(TB_Module* m, TB_Global* global, size_t offset, const TB_Symbol* symbol); -TB_API TB_ModuleSection* tb_module_get_text(TB_Module* m); -TB_API TB_ModuleSection* tb_module_get_rdata(TB_Module* m); -TB_API TB_ModuleSection* tb_module_get_data(TB_Module* m); -TB_API TB_ModuleSection* tb_module_get_tls(TB_Module* m); +TB_API TB_ModuleSectionHandle tb_module_get_text(TB_Module* m); +TB_API TB_ModuleSectionHandle tb_module_get_rdata(TB_Module* m); +TB_API TB_ModuleSectionHandle tb_module_get_data(TB_Module* m); +TB_API TB_ModuleSectionHandle tb_module_get_tls(TB_Module* m); //////////////////////////////// // Function Attributes @@ -919,7 +1014,6 @@ TB_API TB_ModuleSection* tb_module_get_tls(TB_Module* m); // These are parts of a function that describe metadata for instructions TB_API void tb_function_attrib_variable(TB_Function* f, TB_Node* n, TB_Node* parent, ptrdiff_t len, const char* name, TB_DebugType* type); TB_API void tb_function_attrib_scope(TB_Function* f, TB_Node* n, TB_Node* parent); -TB_API void tb_function_attrib_location(TB_Function* f, TB_Node* n, TB_SourceFile* file, int line, int column); //////////////////////////////// // Debug info Generation @@ -951,16 +1045,8 @@ TB_API TB_DebugType** tb_debug_func_params(TB_DebugType* type); TB_API TB_DebugType** tb_debug_func_returns(TB_DebugType* type); //////////////////////////////// -// IR access -//////////////////////////////// -// it is an index to the input -#define TB_FOR_INPUT_IN_NODE(it, parent) for (TB_Node **it = parent->inputs, **__end = it + (parent)->input_count; it != __end; it++) - -//////////////////////////////// // Symbols //////////////////////////////// -TB_API bool tb_symbol_is_comdat(const TB_Symbol* s); - // returns NULL if the tag doesn't match TB_API TB_Function* tb_symbol_as_function(TB_Symbol* s); TB_API TB_External* tb_symbol_as_external(TB_Symbol* s); @@ -974,16 +1060,13 @@ TB_API void tb_get_data_type_size(TB_Module* mod, TB_DataType dt, size_t* size, // the user_data is expected to be a valid FILE* TB_API void tb_default_print_callback(void* user_data, const char* fmt, ...); -TB_API void tb_inst_set_location(TB_Function* f, TB_SourceFile* file, int line, int column); -TB_API void tb_inst_reset_location(TB_Function* f); +TB_API void tb_inst_location(TB_Function* f, TB_SourceFile* file, int line, int column); // this is where the STOP will be TB_API void tb_inst_set_exit_location(TB_Function* f, TB_SourceFile* file, int line, int column); -TB_API bool tb_has_effects(TB_Node* n); - // if section is NULL, default to .text -TB_API TB_Function* tb_function_create(TB_Module* m, ptrdiff_t len, const char* name, TB_Linkage linkage, TB_ComdatType comdat); +TB_API TB_Function* tb_function_create(TB_Module* m, ptrdiff_t len, const char* name, TB_Linkage linkage); TB_API TB_Arena* tb_function_get_arena(TB_Function* f); @@ -994,11 +1077,9 @@ TB_API void tb_symbol_bind_ptr(TB_Symbol* s, void* ptr); TB_API const char* tb_symbol_get_name(TB_Symbol* s); // if arena is NULL, defaults to module arena which is freed on tb_free_thread_resources -TB_API void tb_function_set_prototype(TB_Function* f, TB_FunctionPrototype* p, TB_Arena* arena); +TB_API void tb_function_set_prototype(TB_Function* f, TB_ModuleSectionHandle section, TB_FunctionPrototype* p, TB_Arena* arena); TB_API TB_FunctionPrototype* tb_function_get_prototype(TB_Function* f); -TB_API void tb_function_print(TB_Function* f, TB_PrintCallback callback, void* user_data); - TB_API void tb_inst_set_control(TB_Function* f, TB_Node* control); TB_API TB_Node* tb_inst_get_control(TB_Function* f); @@ -1010,7 +1091,7 @@ TB_API void tb_inst_set_region_name(TB_Function* f, TB_Node* n, ptrdiff_t len, c TB_API void tb_inst_unreachable(TB_Function* f); TB_API void tb_inst_debugbreak(TB_Function* f); TB_API void tb_inst_trap(TB_Function* f); -TB_API TB_Node* tb_inst_poison(TB_Function* f); +TB_API TB_Node* tb_inst_poison(TB_Function* f, TB_DataType dt); TB_API TB_Node* tb_inst_param(TB_Function* f, int param_id); @@ -1135,6 +1216,7 @@ TB_API TB_Node* tb_inst_cmp_fge(TB_Function* f, TB_Node* a, TB_Node* b); // General intrinsics TB_API TB_Node* tb_inst_va_start(TB_Function* f, TB_Node* a); TB_API TB_Node* tb_inst_cycle_counter(TB_Function* f); +TB_API TB_Node* tb_inst_prefetch(TB_Function* f, TB_Node* addr, int level); // x86 Intrinsics TB_API TB_Node* tb_inst_x86_ldmxcsr(TB_Function* f, TB_Node* a); @@ -1145,8 +1227,8 @@ TB_API TB_Node* tb_inst_x86_rsqrt(TB_Function* f, TB_Node* a); // Control flow TB_API TB_Node* tb_inst_syscall(TB_Function* f, TB_DataType dt, TB_Node* syscall_num, size_t param_count, TB_Node** params); TB_API TB_MultiOutput tb_inst_call(TB_Function* f, TB_FunctionPrototype* proto, TB_Node* target, size_t param_count, TB_Node** params); +TB_API void tb_inst_tailcall(TB_Function* f, TB_FunctionPrototype* proto, TB_Node* target, size_t param_count, TB_Node** params); -// Managed TB_API TB_Node* tb_inst_safepoint(TB_Function* f, TB_Node* poke_site, size_t param_count, TB_Node** params); TB_API TB_Node* tb_inst_incomplete_phi(TB_Function* f, TB_DataType dt, TB_Node* region, size_t preds); @@ -1197,12 +1279,16 @@ TB_API void tb_pass_exit(TB_Passes* opt); TB_API void tb_pass_peephole(TB_Passes* opt, TB_PeepholeFlags flags); TB_API void tb_pass_sroa(TB_Passes* opt); TB_API bool tb_pass_mem2reg(TB_Passes* opt); +TB_API bool tb_pass_loop(TB_Passes* opt); -TB_API void tb_pass_schedule(TB_Passes* opt); +// this just runs the optimizer in the default configuration +TB_API void tb_pass_optimize(TB_Passes* opt); // analysis // print: prints IR in a flattened text form. TB_API bool tb_pass_print(TB_Passes* opt); +// print-dot: prints IR as DOT +TB_API void tb_pass_print_dot(TB_Passes* opt, TB_PrintCallback callback, void* user_data); // codegen TB_API TB_FunctionOutput* tb_pass_codegen(TB_Passes* opt, bool emit_asm); diff --git a/src/tilde/tb.lib b/src/tilde/tb.lib Binary files differindex 4b3cdb9b0..510569b0e 100644 --- a/src/tilde/tb.lib +++ b/src/tilde/tb.lib diff --git a/src/tilde/tb_coff.h b/src/tilde/tb_coff.h index ae8f63863..ddedd6ffe 100644 --- a/src/tilde/tb_coff.h +++ b/src/tilde/tb_coff.h @@ -313,7 +313,11 @@ size_t tb_coff_parse_symbol(TB_COFF_Parser* restrict parser, size_t i, TB_Object out_sym->name = (TB_Slice){ strlen((const char*) data), data }; } else { // normal inplace string - size_t len = strlen((const char*) sym->short_name); + size_t len = 1; + const char* name = (const char*) sym->short_name; + while (len < 8 && name[len] != 0) { + len++; + } out_sym->name = (TB_Slice){ len, sym->short_name }; } diff --git a/src/tilde/tb_x64.h b/src/tilde/tb_x64.h index 5f93f6bdb..58b3d656c 100644 --- a/src/tilde/tb_x64.h +++ b/src/tilde/tb_x64.h @@ -27,10 +27,21 @@ typedef enum { TB_X86_INSTR_DIRECTION = (1u << 6u), // uses the second data type because the instruction is weird like MOVSX or MOVZX - TB_X86_INSTR_TWO_DATA_TYPES = (1u << 7u) + TB_X86_INSTR_TWO_DATA_TYPES = (1u << 7u), + + // REP prefix is present + TB_X86_INSTR_REP = (1u << 8u), + + // REPNE prefix is present + TB_X86_INSTR_REPNE = (1u << 9u), } TB_X86_InstFlags; typedef enum { + TB_X86_RAX, TB_X86_RCX, TB_X86_RDX, TB_X86_RBX, TB_X86_RSP, TB_X86_RBP, TB_X86_RSI, TB_X86_RDI, + TB_X86_R8, TB_X86_R9, TB_X86_R10, TB_X86_R11, TB_X86_R12, TB_X86_R13, TB_X86_R14, TB_X86_R15, +} TB_X86_GPR; + +typedef enum { TB_X86_SEGMENT_DEFAULT = 0, TB_X86_SEGMENT_ES, TB_X86_SEGMENT_CS, @@ -60,20 +71,21 @@ typedef enum { } TB_X86_DataType; typedef struct { - int16_t type; + int32_t opcode; - // registers (there's 4 max taking up 4bit slots each) - uint16_t regs; - uint8_t flags; + // registers (there's 4 max taking up 8bit slots each) + int8_t regs[4]; + uint16_t flags; // bitpacking amirite - TB_X86_DataType data_type : 4; - TB_X86_DataType data_type2 : 4; + TB_X86_DataType data_type : 8; + TB_X86_DataType data_type2 : 8; TB_X86_Segment segment : 4; uint8_t length : 4; // memory operand // X86_INSTR_USE_MEMOP + uint8_t base, index, scale; int32_t disp; // immediate operand @@ -85,6 +97,9 @@ typedef struct { }; } TB_X86_Inst; -TB_X86_Inst tb_x86_disasm(size_t length, const uint8_t data[length]); +bool tb_x86_disasm(TB_X86_Inst* restrict inst, size_t length, const uint8_t* data); +const char* tb_x86_reg_name(int8_t reg, TB_X86_DataType dt); +const char* tb_x86_type_name(TB_X86_DataType dt); +const char* tb_x86_mnemonic(TB_X86_Inst* inst); #endif /* TB_X64_H */ diff --git a/src/tilde_const.cpp b/src/tilde_const.cpp index 691409fe9..456b2cdc7 100644 --- a/src/tilde_const.cpp +++ b/src/tilde_const.cpp @@ -705,7 +705,7 @@ gb_internal TB_Global *cg_global_const_comp_literal(cgModule *m, Type *original_ i64 align = type_align_of(original_type); // READ ONLY? - TB_ModuleSection *section = nullptr; + TB_ModuleSectionHandle section = 0; if (is_type_string(original_type) || is_type_cstring(original_type)) { section = tb_module_get_rdata(m->mod); } else { diff --git a/src/tilde_expr.cpp b/src/tilde_expr.cpp index 867b761d4..236d0cf7d 100644 --- a/src/tilde_expr.cpp +++ b/src/tilde_expr.cpp @@ -3262,7 +3262,7 @@ gb_internal cgValue cg_build_expr_internal(cgProcedure *p, Ast *expr) { if (is_type_untyped(type)) { return cg_value(cast(TB_Node *)nullptr, t_untyped_uninit); } - return cg_value(tb_inst_poison(p->func), type); + return cg_value(tb_inst_poison(p->func, cg_data_type(type)), type); case_end; case_ast_node(de, DerefExpr, expr); diff --git a/src/tilde_proc.cpp b/src/tilde_proc.cpp index f5f37d73e..8e9b80144 100644 --- a/src/tilde_proc.cpp +++ b/src/tilde_proc.cpp @@ -95,7 +95,7 @@ gb_internal cgProcedure *cg_procedure_create(cgModule *m, Entity *entity, bool i } if (p->symbol == nullptr) { - p->func = tb_function_create(m->mod, link_name.len, cast(char const *)link_name.text, linkage, TB_COMDAT_NONE); + p->func = tb_function_create(m->mod, link_name.len, cast(char const *)link_name.text, linkage); p->debug_type = cg_debug_type_for_proc(m, p->type); p->proto = tb_prototype_from_dbg(m->mod, p->debug_type); @@ -148,7 +148,7 @@ gb_internal cgProcedure *cg_procedure_create_dummy(cgModule *m, String const &li TB_Linkage linkage = TB_LINKAGE_PRIVATE; - p->func = tb_function_create(m->mod, link_name.len, cast(char const *)link_name.text, linkage, TB_COMDAT_NONE); + p->func = tb_function_create(m->mod, link_name.len, cast(char const *)link_name.text, linkage); p->debug_type = cg_debug_type_for_proc(m, p->type); p->proto = tb_prototype_from_dbg(m->mod, p->debug_type); @@ -224,7 +224,8 @@ gb_internal void cg_procedure_begin(cgProcedure *p) { return; } - tb_function_set_prototype(p->func, p->proto, cg_arena()); + TB_ModuleSectionHandle section = tb_module_get_text(p->module->mod); + tb_function_set_prototype(p->func, section, p->proto, cg_arena()); if (p->body == nullptr) { return; @@ -400,7 +401,7 @@ gb_internal WORKER_TASK_PROC(cg_procedure_compile_worker_proc) { fflush(stdout); } if (false) { // GraphViz printing - tb_function_print(p->func, tb_default_print_callback, stdout); + tb_pass_print_dot(opt, tb_default_print_callback, stdout); } // compile @@ -860,7 +861,6 @@ gb_internal cgValue cg_build_call_expr_internal(cgProcedure *p, Ast *expr) { } GB_ASSERT(e->kind == Entity_Variable); - if (pt->variadic && pt->variadic_index == i) { cgValue variadic_args = cg_const_nil(p, e->type); auto variadic = slice(ce->split_args->positional, pt->variadic_index, ce->split_args->positional.count); @@ -963,8 +963,8 @@ gb_internal cgValue cg_build_call_expr_internal(cgProcedure *p, Ast *expr) { if (pt->variadic && param_index == pt->variadic_index) { if (!is_c_vararg && args[arg_index].node == nullptr) { args[arg_index++] = cg_const_nil(p, e->type); + continue; } - continue; } cgValue arg = args[arg_index]; diff --git a/src/tilde_stmt.cpp b/src/tilde_stmt.cpp index 1a7dcce4d..597a4262c 100644 --- a/src/tilde_stmt.cpp +++ b/src/tilde_stmt.cpp @@ -24,7 +24,7 @@ gb_internal TB_Node *cg_control_region(cgProcedure *p, char const *name) { } gb_internal cgValue cg_emit_load(cgProcedure *p, cgValue const &ptr, bool is_volatile) { - GB_ASSERT(is_type_pointer(ptr.type)); + GB_ASSERT_MSG(is_type_pointer(ptr.type), "%s", type_to_string(ptr.type)); Type *type = type_deref(ptr.type); TB_DataType dt = cg_data_type(type); @@ -326,7 +326,7 @@ gb_internal void cg_addr_store(cgProcedure *p, cgAddr addr, cgValue value) { GB_ASSERT(value.type != nullptr); if (is_type_untyped_uninit(value.type)) { Type *t = cg_addr_type(addr); - value = cg_value(tb_inst_poison(p->func), t); + value = cg_value(tb_inst_poison(p->func, cg_data_type(t)), t); // TODO(bill): IS THIS EVEN A GOOD IDEA? } else if (is_type_untyped_nil(value.type)) { Type *t = cg_addr_type(addr); @@ -1982,16 +1982,25 @@ gb_internal bool cg_switch_stmt_can_be_trivial_jump_table(AstSwitchStmt *ss) { if (ss->tag == nullptr) { return false; } + enum { DISALLOW_64_SWITCH = true }; + bool is_typeid = false; TypeAndValue tv = type_and_value_of_expr(ss->tag); if (is_type_integer(core_type(tv.type))) { - if (type_size_of(tv.type) > 8) { + i64 sz = type_size_of(tv.type); + if (sz > 8) { + return false; + } + if (DISALLOW_64_SWITCH && sz == 8) { return false; } // okay } else if (is_type_typeid(tv.type)) { // okay is_typeid = true; + if (DISALLOW_64_SWITCH && build_context.ptr_size == 8) { + return false; + } } else { return false; } @@ -2481,7 +2490,7 @@ gb_internal void cg_build_mutable_value_decl(cgProcedure *p, Ast *node) { TB_DebugType *debug_type = cg_debug_type(m, e->type); TB_Global *global = tb_global_create(m->mod, mangled_name.len, cast(char const *)mangled_name.text, debug_type, TB_LINKAGE_PRIVATE); - TB_ModuleSection *section = tb_module_get_data(m->mod); + TB_ModuleSectionHandle section = tb_module_get_data(m->mod); if (e->Variable.thread_local_model != "") { section = tb_module_get_tls(m->mod); String model = e->Variable.thread_local_model; diff --git a/src/tilde_type_info.cpp b/src/tilde_type_info.cpp index ad219071f..58e8d3087 100644 --- a/src/tilde_type_info.cpp +++ b/src/tilde_type_info.cpp @@ -119,10 +119,6 @@ gb_internal u64 cg_typeid_as_u64(cgModule *m, Type *type) { data |= (reserved &~ (1ull<<1)) << 63ull; // reserved } - if (type == t_string) { - gb_printf_err("%llu\n", data); - } - return data; } @@ -479,6 +475,13 @@ gb_internal void cg_setup_type_info_data(cgModule *m) { // } tag_type = t_type_info_named; + i64 name_offset = type_offset_of(tag_type, 0); + String name = t->Named.type_name->token.string; + cg_global_const_string(m, name, t_string, global, offset+name_offset); + + i64 base_offset = type_offset_of(tag_type, 1); + cg_global_const_type_info_ptr(m, t->Named.base, global, offset+base_offset); + if (t->Named.type_name->pkg) { i64 pkg_offset = type_offset_of(tag_type, 2); String pkg_name = t->Named.type_name->pkg->name; @@ -499,8 +502,6 @@ gb_internal void cg_setup_type_info_data(cgModule *m) { TokenPos pos = t->Named.type_name->token.pos; cg_global_source_code_location_const(m, proc_name, pos, global, offset+loc_offset); - i64 base_offset = type_offset_of(tag_type, 1); - cg_global_const_type_info_ptr(m, t->Named.base, global, offset+base_offset); break; } |