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