Merge branch 'odin-lang:master' into master

This commit is contained in:
Patrick Cleavelin
2024-01-27 14:12:19 -06:00
committed by GitHub
205 changed files with 3466 additions and 2261 deletions
+66 -12
View File
@@ -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]);
+60 -21
View File
@@ -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++] = '"';
+155 -57
View File
@@ -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]);
}
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);
@@ -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;
}
+87 -4
View File
@@ -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;
+51 -31
View File
@@ -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;
+2 -4
View File
@@ -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;
+1 -2
View File
@@ -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;
}
+54 -8
View File
@@ -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); /**/) {
+24 -10
View File
@@ -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 {
+2
View File
@@ -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 {
+20 -4
View File
@@ -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);
}
+1 -1
View File
@@ -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
+15 -5
View File
@@ -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 ");
+41 -4
View File
@@ -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));
+2
View File
@@ -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
+13
View File
@@ -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);
}
+83
View File
@@ -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) {
}
}
}
+42 -13
View File
@@ -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);
}
+37 -8
View File
@@ -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;
+3 -1
View File
@@ -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));
}
+2
View File
@@ -76,6 +76,8 @@ enum AstFileFlag : u32 {
AstFile_IsTest = 1<<3,
AstFile_IsLazy = 1<<4,
AstFile_NoInstrumentation = 1<<5,
};
enum AstDelayQueueKind {
+1 -1
View File
@@ -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;
}
}
+3 -1
View File
@@ -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);