aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorFourteenBrush <naessensarthur2@protonmail.com>2024-01-25 10:15:25 +0100
committerFourteenBrush <naessensarthur2@protonmail.com>2024-01-25 10:15:25 +0100
commit967ccfc7ccc0f76c653ff4bb625bac60e89a7904 (patch)
treef989a0f99337c7a0c93ee84249d73ec0c33e306d /src
parent712ae1c5ac73493498aa8e5076d91a6558337117 (diff)
parent9cfd4a953e2a7d19237891993b643b2d1477f8b3 (diff)
Merge branch 'master' of https://github.com/FourteenBrush/Odin
Diffstat (limited to 'src')
-rw-r--r--src/bug_report.cpp78
-rw-r--r--src/build_settings.cpp103
-rw-r--r--src/check_builtin.cpp425
-rw-r--r--src/check_decl.cpp91
-rw-r--r--src/check_expr.cpp93
-rw-r--r--src/check_stmt.cpp6
-rw-r--r--src/check_type.cpp37
-rw-r--r--src/checker.cpp371
-rw-r--r--src/checker.hpp34
-rw-r--r--src/checker_builtin_procs.hpp14
-rw-r--r--src/entity.cpp2
-rw-r--r--src/exact_value.cpp24
-rw-r--r--src/gb/gb.h2
-rw-r--r--src/linker.cpp91
-rw-r--r--src/llvm_backend.cpp157
-rw-r--r--src/llvm_backend.hpp5
-rw-r--r--src/llvm_backend_const.cpp41
-rw-r--r--src/llvm_backend_expr.cpp8
-rw-r--r--src/llvm_backend_general.cpp29
-rw-r--r--src/llvm_backend_opt.cpp83
-rw-r--r--src/llvm_backend_proc.cpp57
-rw-r--r--src/llvm_backend_stmt.cpp22
-rw-r--r--src/llvm_backend_type.cpp7
-rw-r--r--src/llvm_backend_utility.cpp4
-rw-r--r--src/main.cpp371
-rw-r--r--src/parser.cpp4
-rw-r--r--src/parser.hpp2
-rw-r--r--src/string.cpp26
-rw-r--r--src/threading.cpp4
-rw-r--r--src/tilde.cpp8
-rw-r--r--src/tilde.hpp4
-rw-r--r--src/tilde/tb.h372
-rw-r--r--src/tilde/tb.libbin4265424 -> 2347218 bytes
-rw-r--r--src/tilde/tb_coff.h6
-rw-r--r--src/tilde/tb_x64.h31
-rw-r--r--src/tilde_const.cpp2
-rw-r--r--src/tilde_expr.cpp2
-rw-r--r--src/tilde_proc.cpp12
-rw-r--r--src/tilde_stmt.cpp17
-rw-r--r--src/tilde_type_info.cpp13
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
index 4b3cdb9b0..510569b0e 100644
--- a/src/tilde/tb.lib
+++ b/src/tilde/tb.lib
Binary files differ
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;
}