aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorPatrick Cleavelin <patrick@uptrainsoftware.com>2024-01-27 14:12:19 -0600
committerGitHub <noreply@github.com>2024-01-27 14:12:19 -0600
commit7b9ea9eca02bf5dd295439a46ed6103a0c4a44ff (patch)
tree680a3214623087a395f66c11ddf5b6201d099bd7 /src
parent59aa05170d54edff75aed220bb1653fc369573d7 (diff)
parentda6edb3764b735a839acdd375328574833d782c1 (diff)
Merge branch 'odin-lang:master' into master
Diffstat (limited to 'src')
-rw-r--r--src/bug_report.cpp78
-rw-r--r--src/build_settings.cpp81
-rw-r--r--src/check_builtin.cpp212
-rw-r--r--src/check_decl.cpp91
-rw-r--r--src/check_expr.cpp82
-rw-r--r--src/check_stmt.cpp6
-rw-r--r--src/check_type.cpp3
-rw-r--r--src/checker.cpp62
-rw-r--r--src/checker.hpp34
-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.cpp20
-rw-r--r--src/llvm_backend.cpp45
-rw-r--r--src/llvm_backend.hpp2
-rw-r--r--src/llvm_backend_general.cpp13
-rw-r--r--src/llvm_backend_opt.cpp83
-rw-r--r--src/llvm_backend_proc.cpp55
-rw-r--r--src/main.cpp45
-rw-r--r--src/parser.cpp4
-rw-r--r--src/parser.hpp2
-rw-r--r--src/threading.cpp2
-rw-r--r--src/types.cpp4
23 files changed, 765 insertions, 187 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 d91a31ff2..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(""),
@@ -120,6 +133,19 @@ 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
@@ -287,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;
@@ -361,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;
@@ -581,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;
@@ -616,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;
@@ -1274,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:
@@ -1292,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;
@@ -1321,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.
@@ -1492,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) {
@@ -1501,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);
@@ -1512,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 b3aaab754..09ca0bc23 100644
--- a/src/check_builtin.cpp
+++ b/src/check_builtin.cpp
@@ -1666,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;
}
@@ -2299,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]);
+ }
+ 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;
+ }
+ }
+ } 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;
+ }
+ }
+ }
+
+
+ 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 (is_type_numeric(z.type) && z.value.kind == ExactValue_Float) {
- z.type = t_untyped_float;
+ }
+ 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);
}
- if (is_type_numeric(w.type) && w.value.kind == ExactValue_Float) {
- w.type = t_untyped_float;
+ 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(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);
+ 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);
@@ -2361,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;
@@ -3092,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;
}
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 71accfb81..f8c5540f4 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);
@@ -2983,6 +2988,9 @@ gb_internal bool check_is_castable_to(CheckerContext *c, Operand *operand, Type
}
// proc <-> proc
if (is_type_proc(src) && is_type_proc(dst)) {
+ if (is_type_polymorphic(src) || is_type_polymorphic(dst)) {
+ return false;
+ }
return true;
}
@@ -3062,7 +3070,6 @@ gb_internal void check_cast(CheckerContext *c, Operand *x, Type *type) {
bool is_const_expr = x->mode == Addressing_Constant;
bool can_convert = check_cast_internal(c, x, type);
-
if (!can_convert) {
TEMPORARY_ALLOCATOR_GUARD();
gbString expr_str = expr_to_string(x->expr, temporary_allocator());
@@ -3071,7 +3078,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 +3101,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 +4052,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 +4113,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 +4139,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 +4716,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 +4917,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) {
@@ -6338,8 +6343,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) {
@@ -6429,8 +6433,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();
@@ -7195,6 +7198,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) {
@@ -7641,6 +7652,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);
@@ -7884,7 +7897,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 (
@@ -9042,8 +9055,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));
@@ -9054,9 +9066,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));
+ }
}
}
@@ -9680,7 +9694,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;
@@ -9694,7 +9710,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;
@@ -9882,7 +9900,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;
@@ -10238,15 +10258,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 f11418dc0..a95026711 100644
--- a/src/check_type.cpp
+++ b/src/check_type.cpp
@@ -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 79328d648..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] = {
@@ -2581,6 +2582,9 @@ gb_internal void generate_minimum_dependency_set(Checker *c, Entity *start) {
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);
@@ -3413,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;
}
@@ -4022,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;
@@ -4074,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]));
@@ -6215,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/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 7fec11ad3..b8909ac6a 100644
--- a/src/linker.cpp
+++ b/src/linker.cpp
@@ -233,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));
@@ -265,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
@@ -283,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
@@ -484,6 +483,17 @@ gb_internal i32 linker_stage(LinkerData *gen) {
defer (gb_string_free(platform_lib_str));
if (build_context.metrics.os == TargetOs_darwin) {
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 ");
diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp
index ca71a0f45..003424e0a 100644
--- a/src/llvm_backend.cpp
+++ b/src/llvm_backend.cpp
@@ -1497,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));
@@ -2505,7 +2503,7 @@ 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;
}
@@ -2531,7 +2529,46 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
*/
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));
diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp
index 4e193bcea..fe2c2deba 100644
--- a/src/llvm_backend.hpp
+++ b/src/llvm_backend.hpp
@@ -563,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_general.cpp b/src/llvm_backend_general.cpp
index 54327cc54..f0f5327c6 100644
--- a/src/llvm_backend_general.cpp
+++ b/src/llvm_backend_general.cpp
@@ -2348,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);
@@ -2361,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 1244bd377..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);
}
diff --git a/src/main.cpp b/src/main.cpp
index 4d4e01ada..19271d667 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -1160,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:
@@ -1268,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;
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/threading.cpp b/src/threading.cpp
index 74aa3eb7e..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;
}
}
diff --git a/src/types.cpp b/src/types.cpp
index 574e628c5..2f39d5caa 100644
--- a/src/types.cpp
+++ b/src/types.cpp
@@ -825,11 +825,13 @@ gb_internal void type_path_pop(TypePath *tp) {
#define FAILURE_SIZE 0
#define FAILURE_ALIGNMENT 0
+gb_internal bool type_ptr_set_exists(PtrSet<Type *> *s, Type *t);
+
gb_internal bool type_ptr_set_update(PtrSet<Type *> *s, Type *t) {
if (t == nullptr) {
return true;
}
- if (ptr_set_exists(s, t)) {
+ if (type_ptr_set_exists(s, t)) {
return true;
}
ptr_set_add(s, t);