mirror of
https://github.com/Ed94/Odin.git
synced 2026-07-21 15:05:45 -07:00
Merge remote-tracking branch 'offical/master'
# Conflicts: # core/os/os_darwin.odin # core/os/os_freebsd.odin # core/os/os_js.odin # core/os/os_linux.odin # core/os/os_openbsd.odin # core/os/os_windows.odin
This commit is contained in:
+47
-18
@@ -326,7 +326,17 @@ enum SanitizerFlags : u32 {
|
||||
SanitizerFlag_Thread = 1u<<2,
|
||||
};
|
||||
|
||||
struct BuildCacheData {
|
||||
u64 crc;
|
||||
String cache_dir;
|
||||
|
||||
// manifests
|
||||
String files_path;
|
||||
String args_path;
|
||||
String env_path;
|
||||
|
||||
bool copy_already_done;
|
||||
};
|
||||
|
||||
// This stores the information for the specify architecture of this build
|
||||
struct BuildContext {
|
||||
@@ -426,6 +436,12 @@ struct BuildContext {
|
||||
bool linker_map_file;
|
||||
|
||||
bool use_separate_modules;
|
||||
bool module_per_file;
|
||||
bool cached;
|
||||
BuildCacheData build_cache_data;
|
||||
|
||||
bool internal_no_inline;
|
||||
|
||||
bool no_threaded_checker;
|
||||
|
||||
bool show_debug_messages;
|
||||
@@ -440,6 +456,8 @@ struct BuildContext {
|
||||
|
||||
bool min_link_libs;
|
||||
|
||||
bool print_linker_flags;
|
||||
|
||||
RelocMode reloc_mode;
|
||||
bool disable_red_zone;
|
||||
|
||||
@@ -860,15 +878,6 @@ gb_internal bool is_arch_x86(void) {
|
||||
return false;
|
||||
}
|
||||
|
||||
gb_internal bool allow_check_foreign_filepath(void) {
|
||||
switch (build_context.metrics.arch) {
|
||||
case TargetArch_wasm32:
|
||||
case TargetArch_wasm64p32:
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO(bill): OS dependent versions for the BuildContext
|
||||
// join_path
|
||||
// is_dir
|
||||
@@ -1639,14 +1648,27 @@ gb_internal void init_build_context(TargetMetrics *cross_target, Subtarget subta
|
||||
}
|
||||
}
|
||||
|
||||
if (bc->ODIN_DEBUG && !bc->custom_optimization_level) {
|
||||
if (!bc->custom_optimization_level) {
|
||||
// NOTE(bill): when building with `-debug` but not specifying an optimization level
|
||||
// default to `-o:none` to improve the debug symbol generation by default
|
||||
bc->optimization_level = -1; // -o:none
|
||||
if (bc->ODIN_DEBUG) {
|
||||
bc->optimization_level = -1; // -o:none
|
||||
} else {
|
||||
bc->optimization_level = 0; // -o:minimal
|
||||
}
|
||||
}
|
||||
|
||||
bc->optimization_level = gb_clamp(bc->optimization_level, -1, 3);
|
||||
|
||||
#if defined(GB_SYSTEM_WINDOWS)
|
||||
if (bc->optimization_level <= 0) {
|
||||
if (!is_arch_wasm()) {
|
||||
bc->use_separate_modules = true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
// TODO: Static map calls are bugged on `amd64sysv` abi.
|
||||
if (bc->metrics.os != TargetOs_windows && bc->metrics.arch == TargetArch_amd64) {
|
||||
// ENFORCE DYNAMIC MAP CALLS
|
||||
@@ -1798,10 +1820,12 @@ gb_internal bool init_build_paths(String init_filename) {
|
||||
#if defined(GB_SYSTEM_WINDOWS)
|
||||
if (bc->metrics.os == TargetOs_windows) {
|
||||
if (bc->resource_filepath.len > 0) {
|
||||
bc->build_paths[BuildPath_RC] = path_from_string(ha, bc->resource_filepath);
|
||||
bc->build_paths[BuildPath_RES] = path_from_string(ha, bc->resource_filepath);
|
||||
bc->build_paths[BuildPath_RC].ext = copy_string(ha, STR_LIT("rc"));
|
||||
bc->build_paths[BuildPath_RES].ext = copy_string(ha, STR_LIT("res"));
|
||||
bc->build_paths[BuildPath_RES] = path_from_string(ha, bc->resource_filepath);
|
||||
if (!string_ends_with(bc->resource_filepath, str_lit(".res"))) {
|
||||
bc->build_paths[BuildPath_RES].ext = copy_string(ha, STR_LIT("res"));
|
||||
bc->build_paths[BuildPath_RC] = path_from_string(ha, bc->resource_filepath);
|
||||
bc->build_paths[BuildPath_RC].ext = copy_string(ha, STR_LIT("rc"));
|
||||
}
|
||||
}
|
||||
|
||||
if (bc->pdb_filepath.len > 0) {
|
||||
@@ -1987,15 +2011,20 @@ gb_internal bool init_build_paths(String init_filename) {
|
||||
}
|
||||
}
|
||||
|
||||
String output_file = path_to_string(ha, bc->build_paths[BuildPath_Output]);
|
||||
defer (gb_free(ha, output_file.text));
|
||||
|
||||
// Check if output path is a directory.
|
||||
if (path_is_directory(bc->build_paths[BuildPath_Output])) {
|
||||
String output_file = path_to_string(ha, bc->build_paths[BuildPath_Output]);
|
||||
defer (gb_free(ha, output_file.text));
|
||||
gb_printf_err("Output path %.*s is a directory.\n", LIT(output_file));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!write_directory(bc->build_paths[BuildPath_Output].basename)) {
|
||||
gbFile output_file_test;
|
||||
gbFileError output_test_err = gb_file_open_mode(&output_file_test, gbFileMode_Append | gbFileMode_Rw, (const char*)output_file.text);
|
||||
defer (gb_file_close(&output_file_test));
|
||||
|
||||
if (output_test_err != 0) {
|
||||
String output_file = path_to_string(ha, bc->build_paths[BuildPath_Output]);
|
||||
defer (gb_free(ha, output_file.text));
|
||||
gb_printf_err("No write permissions for output path: %.*s\n", LIT(output_file));
|
||||
|
||||
+461
@@ -0,0 +1,461 @@
|
||||
gb_internal GB_COMPARE_PROC(string_cmp) {
|
||||
String const &x = *(String *)a;
|
||||
String const &y = *(String *)b;
|
||||
return string_compare(x, y);
|
||||
}
|
||||
|
||||
gb_internal bool recursively_delete_directory(wchar_t *wpath_c) {
|
||||
#if defined(GB_SYSTEM_WINDOWS)
|
||||
auto const is_dots_w = [](wchar_t const *str) -> bool {
|
||||
if (!str) {
|
||||
return false;
|
||||
}
|
||||
return wcscmp(str, L".") == 0 || wcscmp(str, L"..") == 0;
|
||||
};
|
||||
|
||||
TEMPORARY_ALLOCATOR_GUARD();
|
||||
|
||||
wchar_t dir_path[MAX_PATH] = {};
|
||||
wchar_t filename[MAX_PATH] = {};
|
||||
wcscpy_s(dir_path, wpath_c);
|
||||
wcscat_s(dir_path, L"\\*");
|
||||
|
||||
wcscpy_s(filename, wpath_c);
|
||||
wcscat_s(filename, L"\\");
|
||||
|
||||
|
||||
WIN32_FIND_DATAW find_file_data = {};
|
||||
HANDLE hfind = FindFirstFileW(dir_path, &find_file_data);
|
||||
if (hfind == INVALID_HANDLE_VALUE) {
|
||||
return false;
|
||||
}
|
||||
defer (FindClose(hfind));
|
||||
|
||||
wcscpy_s(dir_path, filename);
|
||||
|
||||
for (;;) {
|
||||
if (FindNextFileW(hfind, &find_file_data)) {
|
||||
if (is_dots_w(find_file_data.cFileName)) {
|
||||
continue;
|
||||
}
|
||||
wcscat_s(filename, find_file_data.cFileName);
|
||||
|
||||
if (find_file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
||||
if (!recursively_delete_directory(filename)) {
|
||||
return false;
|
||||
}
|
||||
RemoveDirectoryW(filename);
|
||||
wcscpy_s(filename, dir_path);
|
||||
} else {
|
||||
if (find_file_data.dwFileAttributes & FILE_ATTRIBUTE_READONLY) {
|
||||
_wchmod(filename, _S_IWRITE);
|
||||
}
|
||||
if (!DeleteFileW(filename)) {
|
||||
return false;
|
||||
}
|
||||
wcscpy_s(filename, dir_path);
|
||||
}
|
||||
} else {
|
||||
if (GetLastError() == ERROR_NO_MORE_FILES) {
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return RemoveDirectoryW(wpath_c);
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
gb_internal bool recursively_delete_directory(String const &path) {
|
||||
#if defined(GB_SYSTEM_WINDOWS)
|
||||
String16 wpath = string_to_string16(permanent_allocator(), path);
|
||||
wchar_t *wpath_c = alloc_wstring(permanent_allocator(), wpath);
|
||||
return recursively_delete_directory(wpath_c);
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
gb_internal bool try_clear_cache(void) {
|
||||
return recursively_delete_directory(str_lit(".odin-cache"));
|
||||
}
|
||||
|
||||
|
||||
gb_internal u64 crc64_with_seed(void const *data, isize len, u64 seed) {
|
||||
isize remaining;
|
||||
u64 result = ~seed;
|
||||
u8 const *c = cast(u8 const *)data;
|
||||
for (remaining = len; remaining--; c++) {
|
||||
result = (result >> 8) ^ (GB__CRC64_TABLE[(result ^ *c) & 0xff]);
|
||||
}
|
||||
return ~result;
|
||||
}
|
||||
|
||||
gb_internal bool check_if_exists_file_otherwise_create(String const &str) {
|
||||
char const *str_c = alloc_cstring(permanent_allocator(), str);
|
||||
if (!gb_file_exists(str_c)) {
|
||||
gbFile f = {};
|
||||
gb_file_create(&f, str_c);
|
||||
gb_file_close(&f);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
gb_internal bool check_if_exists_directory_otherwise_create(String const &str) {
|
||||
#if defined(GB_SYSTEM_WINDOWS)
|
||||
String16 wstr = string_to_string16(permanent_allocator(), str);
|
||||
wchar_t *wstr_c = alloc_wstring(permanent_allocator(), wstr);
|
||||
return CreateDirectoryW(wstr_c, nullptr);
|
||||
#else
|
||||
char const *str_c = alloc_cstring(permanent_allocator(), str);
|
||||
if (!gb_file_exists(str_c)) {
|
||||
int status = mkdir(str_c, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
|
||||
return status == 0;
|
||||
}
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
gb_internal bool try_copy_executable_cache_internal(bool to_cache) {
|
||||
String exe_name = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_Output]);
|
||||
defer (gb_free(heap_allocator(), exe_name.text));
|
||||
|
||||
gbString cache_name = gb_string_make(heap_allocator(), "");
|
||||
defer (gb_string_free(cache_name));
|
||||
|
||||
String cache_dir = build_context.build_cache_data.cache_dir;
|
||||
|
||||
cache_name = gb_string_append_length(cache_name, cache_dir.text, cache_dir.len);
|
||||
cache_name = gb_string_appendc(cache_name, "/");
|
||||
|
||||
cache_name = gb_string_appendc(cache_name, "cached-exe");
|
||||
if (selected_target_metrics) {
|
||||
cache_name = gb_string_appendc(cache_name, "-");
|
||||
cache_name = gb_string_append_length(cache_name, selected_target_metrics->name.text, selected_target_metrics->name.len);
|
||||
}
|
||||
if (selected_subtarget) {
|
||||
String st = subtarget_strings[selected_subtarget];
|
||||
cache_name = gb_string_appendc(cache_name, "-");
|
||||
cache_name = gb_string_append_length(cache_name, st.text, st.len);
|
||||
}
|
||||
cache_name = gb_string_appendc(cache_name, ".bin");
|
||||
|
||||
if (to_cache) {
|
||||
return gb_file_copy(
|
||||
alloc_cstring(temporary_allocator(), exe_name),
|
||||
cache_name,
|
||||
false
|
||||
);
|
||||
} else {
|
||||
return gb_file_copy(
|
||||
cache_name,
|
||||
alloc_cstring(temporary_allocator(), exe_name),
|
||||
false
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
gb_internal bool try_copy_executable_to_cache(void) {
|
||||
debugf("Cache: try_copy_executable_to_cache\n");
|
||||
|
||||
if (try_copy_executable_cache_internal(true)) {
|
||||
build_context.build_cache_data.copy_already_done = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
gb_internal bool try_copy_executable_from_cache(void) {
|
||||
debugf("Cache: try_copy_executable_from_cache\n");
|
||||
|
||||
if (try_copy_executable_cache_internal(false)) {
|
||||
build_context.build_cache_data.copy_already_done = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
#if !defined(GB_SYSTEM_WINDOWS)
|
||||
extern char **environ;
|
||||
#endif
|
||||
|
||||
// returns false if different, true if it is the same
|
||||
gb_internal bool try_cached_build(Checker *c, Array<String> const &args) {
|
||||
TEMPORARY_ALLOCATOR_GUARD();
|
||||
|
||||
Parser *p = c->parser;
|
||||
|
||||
auto files = array_make<String>(heap_allocator());
|
||||
for (AstPackage *pkg : p->packages) {
|
||||
for (AstFile *f : pkg->files) {
|
||||
array_add(&files, f->fullpath);
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(GB_SYSTEM_WINDOWS)
|
||||
if (build_context.has_resource) {
|
||||
String res_path = {};
|
||||
if (build_context.build_paths[BuildPath_RC].basename == "") {
|
||||
res_path = path_to_string(permanent_allocator(), build_context.build_paths[BuildPath_RES]);
|
||||
} else {
|
||||
res_path = path_to_string(permanent_allocator(), build_context.build_paths[BuildPath_RC]);
|
||||
}
|
||||
array_add(&files, res_path);
|
||||
}
|
||||
#endif
|
||||
|
||||
for (auto const &entry : c->info.load_file_cache) {
|
||||
auto *cache = entry.value;
|
||||
if (!cache || !cache->exists) {
|
||||
continue;
|
||||
}
|
||||
array_add(&files, cache->path);
|
||||
}
|
||||
|
||||
array_sort(files, string_cmp);
|
||||
|
||||
u64 crc = 0;
|
||||
for (String const &path : files) {
|
||||
crc = crc64_with_seed(path.text, path.len, crc);
|
||||
}
|
||||
|
||||
String base_cache_dir = build_context.build_paths[BuildPath_Output].basename;
|
||||
base_cache_dir = concatenate_strings(permanent_allocator(), base_cache_dir, str_lit("/.odin-cache"));
|
||||
(void)check_if_exists_directory_otherwise_create(base_cache_dir);
|
||||
|
||||
gbString crc_str = gb_string_make_reserve(permanent_allocator(), 16);
|
||||
crc_str = gb_string_append_fmt(crc_str, "%016llx", crc);
|
||||
String cache_dir = concatenate3_strings(permanent_allocator(), base_cache_dir, str_lit("/"), make_string_c(crc_str));
|
||||
String files_path = concatenate3_strings(permanent_allocator(), cache_dir, str_lit("/"), str_lit("files.manifest"));
|
||||
String args_path = concatenate3_strings(permanent_allocator(), cache_dir, str_lit("/"), str_lit("args.manifest"));
|
||||
String env_path = concatenate3_strings(permanent_allocator(), cache_dir, str_lit("/"), str_lit("env.manifest"));
|
||||
|
||||
build_context.build_cache_data.cache_dir = cache_dir;
|
||||
build_context.build_cache_data.files_path = files_path;
|
||||
build_context.build_cache_data.args_path = args_path;
|
||||
build_context.build_cache_data.env_path = env_path;
|
||||
|
||||
auto envs = array_make<String>(heap_allocator());
|
||||
defer (array_free(&envs));
|
||||
{
|
||||
#if defined(GB_SYSTEM_WINDOWS)
|
||||
wchar_t *strings = GetEnvironmentStringsW();
|
||||
defer (FreeEnvironmentStringsW(strings));
|
||||
|
||||
wchar_t *curr_string = strings;
|
||||
while (curr_string && *curr_string) {
|
||||
String16 wstr = make_string16_c(curr_string);
|
||||
curr_string += wstr.len+1;
|
||||
String str = string16_to_string(temporary_allocator(), wstr);
|
||||
if (string_starts_with(str, str_lit("CURR_DATE_TIME="))) {
|
||||
continue;
|
||||
}
|
||||
array_add(&envs, str);
|
||||
}
|
||||
#else
|
||||
char **curr_env = environ;
|
||||
while (curr_env && *curr_env) {
|
||||
String str = make_string_c(*curr_env++);
|
||||
if (string_starts_with(str, str_lit("PROMPT="))) {
|
||||
continue;
|
||||
}
|
||||
if (string_starts_with(str, str_lit("RPROMPT="))) {
|
||||
continue;
|
||||
}
|
||||
array_add(&envs, str);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
array_sort(envs, string_cmp);
|
||||
|
||||
if (check_if_exists_directory_otherwise_create(cache_dir)) {
|
||||
goto write_cache;
|
||||
}
|
||||
|
||||
if (check_if_exists_file_otherwise_create(files_path)) {
|
||||
goto write_cache;
|
||||
}
|
||||
if (check_if_exists_file_otherwise_create(args_path)) {
|
||||
goto write_cache;
|
||||
}
|
||||
if (check_if_exists_file_otherwise_create(env_path)) {
|
||||
goto write_cache;
|
||||
}
|
||||
|
||||
{
|
||||
// exists already
|
||||
LoadedFile loaded_file = {};
|
||||
|
||||
LoadedFileError file_err = load_file_32(
|
||||
alloc_cstring(temporary_allocator(), files_path),
|
||||
&loaded_file,
|
||||
false
|
||||
);
|
||||
if (file_err > LoadedFile_Empty) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String data = {cast(u8 *)loaded_file.data, loaded_file.size};
|
||||
String_Iterator it = {data, 0};
|
||||
|
||||
isize file_count = 0;
|
||||
|
||||
for (; it.pos < data.len; file_count++) {
|
||||
String line = string_split_iterator(&it, '\n');
|
||||
if (line.len == 0) {
|
||||
break;
|
||||
}
|
||||
isize sep = string_index_byte(line, ' ');
|
||||
if (sep < 0) {
|
||||
goto write_cache;
|
||||
}
|
||||
|
||||
String timestamp_str = substring(line, 0, sep);
|
||||
String path_str = substring(line, sep+1, line.len);
|
||||
|
||||
timestamp_str = string_trim_whitespace(timestamp_str);
|
||||
path_str = string_trim_whitespace(path_str);
|
||||
|
||||
if (file_count >= files.count) {
|
||||
goto write_cache;
|
||||
}
|
||||
if (files[file_count] != path_str) {
|
||||
goto write_cache;
|
||||
}
|
||||
|
||||
u64 timestamp = exact_value_to_u64(exact_value_integer_from_string(timestamp_str));
|
||||
gbFileTime last_write_time = gb_file_last_write_time(alloc_cstring(temporary_allocator(), path_str));
|
||||
if (last_write_time != timestamp) {
|
||||
goto write_cache;
|
||||
}
|
||||
}
|
||||
|
||||
if (file_count != files.count) {
|
||||
goto write_cache;
|
||||
}
|
||||
}
|
||||
{
|
||||
LoadedFile loaded_file = {};
|
||||
|
||||
LoadedFileError file_err = load_file_32(
|
||||
alloc_cstring(temporary_allocator(), args_path),
|
||||
&loaded_file,
|
||||
false
|
||||
);
|
||||
if (file_err > LoadedFile_Empty) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String data = {cast(u8 *)loaded_file.data, loaded_file.size};
|
||||
String_Iterator it = {data, 0};
|
||||
|
||||
isize args_count = 0;
|
||||
|
||||
for (; it.pos < data.len; args_count++) {
|
||||
String line = string_split_iterator(&it, '\n');
|
||||
line = string_trim_whitespace(line);
|
||||
if (line.len == 0) {
|
||||
break;
|
||||
}
|
||||
if (args_count >= args.count) {
|
||||
goto write_cache;
|
||||
}
|
||||
|
||||
if (line != args[args_count]) {
|
||||
goto write_cache;
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
LoadedFile loaded_file = {};
|
||||
|
||||
LoadedFileError file_err = load_file_32(
|
||||
alloc_cstring(temporary_allocator(), env_path),
|
||||
&loaded_file,
|
||||
false
|
||||
);
|
||||
if (file_err > LoadedFile_Empty) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String data = {cast(u8 *)loaded_file.data, loaded_file.size};
|
||||
String_Iterator it = {data, 0};
|
||||
|
||||
isize env_count = 0;
|
||||
|
||||
for (; it.pos < data.len; env_count++) {
|
||||
String line = string_split_iterator(&it, '\n');
|
||||
line = string_trim_whitespace(line);
|
||||
if (line.len == 0) {
|
||||
break;
|
||||
}
|
||||
if (env_count >= envs.count) {
|
||||
goto write_cache;
|
||||
}
|
||||
|
||||
if (line != envs[env_count]) {
|
||||
goto write_cache;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return try_copy_executable_from_cache();
|
||||
|
||||
write_cache:;
|
||||
{
|
||||
char const *path_c = alloc_cstring(temporary_allocator(), files_path);
|
||||
gb_file_remove(path_c);
|
||||
|
||||
debugf("Cache: updating %s\n", path_c);
|
||||
|
||||
gbFile f = {};
|
||||
defer (gb_file_close(&f));
|
||||
gb_file_open_mode(&f, gbFileMode_Write, path_c);
|
||||
|
||||
for (String const &path : files) {
|
||||
gbFileTime ft = gb_file_last_write_time(alloc_cstring(temporary_allocator(), path));
|
||||
gb_fprintf(&f, "%llu %.*s\n", cast(unsigned long long)ft, LIT(path));
|
||||
}
|
||||
}
|
||||
{
|
||||
char const *path_c = alloc_cstring(temporary_allocator(), args_path);
|
||||
gb_file_remove(path_c);
|
||||
|
||||
debugf("Cache: updating %s\n", path_c);
|
||||
|
||||
gbFile f = {};
|
||||
defer (gb_file_close(&f));
|
||||
gb_file_open_mode(&f, gbFileMode_Write, path_c);
|
||||
|
||||
for (String const &arg : args) {
|
||||
String targ = string_trim_whitespace(arg);
|
||||
gb_fprintf(&f, "%.*s\n", LIT(targ));
|
||||
}
|
||||
}
|
||||
{
|
||||
char const *path_c = alloc_cstring(temporary_allocator(), env_path);
|
||||
gb_file_remove(path_c);
|
||||
|
||||
debugf("Cache: updating %s\n", path_c);
|
||||
|
||||
gbFile f = {};
|
||||
defer (gb_file_close(&f));
|
||||
gb_file_open_mode(&f, gbFileMode_Write, path_c);
|
||||
|
||||
for (String const &env : envs) {
|
||||
gb_fprintf(&f, "%.*s\n", LIT(env));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
+61
-3
@@ -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_, LoadFileTier tier) {
|
||||
gb_internal bool cache_load_file_directive(CheckerContext *c, Ast *call, String const &original_string, bool err_on_not_found, LoadFileCache **cache_, LoadFileTier tier, bool use_mutex=true) {
|
||||
ast_node(ce, CallExpr, call);
|
||||
ast_node(bd, BasicDirective, ce->proc);
|
||||
String builtin_name = bd->name.string;
|
||||
@@ -1101,7 +1101,8 @@ gb_internal bool cache_load_file_directive(CheckerContext *c, Ast *call, String
|
||||
}
|
||||
}
|
||||
|
||||
MUTEX_GUARD(&c->info->load_file_mutex);
|
||||
if (use_mutex) mutex_lock(&c->info->load_file_mutex);
|
||||
defer (if (use_mutex) mutex_unlock(&c->info->load_file_mutex));
|
||||
|
||||
gbFileError file_error = gbFileError_None;
|
||||
String data = {};
|
||||
@@ -1296,6 +1297,9 @@ gb_internal LoadDirectiveResult check_load_directive(CheckerContext *c, Operand
|
||||
gb_internal int file_cache_sort_cmp(void const *x, void const *y) {
|
||||
LoadFileCache const *a = *(LoadFileCache const **)(x);
|
||||
LoadFileCache const *b = *(LoadFileCache const **)(y);
|
||||
if (a == b) {
|
||||
return 0;
|
||||
}
|
||||
return string_compare(a->path, b->path);
|
||||
}
|
||||
|
||||
@@ -1411,9 +1415,12 @@ gb_internal LoadDirectiveResult check_load_directory_directive(CheckerContext *c
|
||||
|
||||
file_caches = array_make<LoadFileCache *>(heap_allocator(), 0, files_to_reserve);
|
||||
|
||||
mutex_lock(&c->info->load_file_mutex);
|
||||
defer (mutex_unlock(&c->info->load_file_mutex));
|
||||
|
||||
for (FileInfo fi : list) {
|
||||
LoadFileCache *cache = nullptr;
|
||||
if (cache_load_file_directive(c, call, fi.fullpath, err_on_not_found, &cache, LoadFileTier_Contents)) {
|
||||
if (cache_load_file_directive(c, call, fi.fullpath, err_on_not_found, &cache, LoadFileTier_Contents, /*use_mutex*/false)) {
|
||||
array_add(&file_caches, cache);
|
||||
} else {
|
||||
result = LoadDirective_Error;
|
||||
@@ -2304,6 +2311,14 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
|
||||
error(o.expr, "Invalid argument to 'type_of'");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (is_type_untyped(o.type)) {
|
||||
gbString t = type_to_string(o.type);
|
||||
error(o.expr, "'type_of' of %s cannot be determined", t);
|
||||
gb_string_free(t);
|
||||
return false;
|
||||
}
|
||||
|
||||
// NOTE(bill): Prevent type cycles for procedure declarations
|
||||
if (c->curr_proc_sig == o.type) {
|
||||
gbString s = expr_to_string(o.expr);
|
||||
@@ -4301,6 +4316,49 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
|
||||
}
|
||||
break;
|
||||
|
||||
case BuiltinProc_add_sat:
|
||||
case BuiltinProc_sub_sat:
|
||||
{
|
||||
Operand x = {};
|
||||
Operand y = {};
|
||||
check_expr(c, &x, ce->args[0]);
|
||||
check_expr(c, &y, ce->args[1]);
|
||||
if (x.mode == Addressing_Invalid) {
|
||||
return false;
|
||||
}
|
||||
if (y.mode == Addressing_Invalid) {
|
||||
return false;
|
||||
}
|
||||
convert_to_typed(c, &y, x.type); if (y.mode == Addressing_Invalid) return false;
|
||||
convert_to_typed(c, &x, y.type);
|
||||
if (is_type_untyped(x.type)) {
|
||||
gbString xts = type_to_string(x.type);
|
||||
error(x.expr, "Expected a typed integer for '%.*s', got %s", LIT(builtin_name), xts);
|
||||
gb_string_free(xts);
|
||||
return false;
|
||||
}
|
||||
if (!is_type_integer(x.type)) {
|
||||
gbString xts = type_to_string(x.type);
|
||||
error(x.expr, "Expected an integer for '%.*s', got %s", LIT(builtin_name), xts);
|
||||
gb_string_free(xts);
|
||||
return false;
|
||||
}
|
||||
Type *ct = core_type(x.type);
|
||||
if (is_type_different_to_arch_endianness(ct)) {
|
||||
GB_ASSERT(ct->kind == Type_Basic);
|
||||
if (ct->Basic.flags & (BasicFlag_EndianLittle|BasicFlag_EndianBig)) {
|
||||
gbString xts = type_to_string(x.type);
|
||||
error(x.expr, "Expected an integer which does not specify the explicit endianness for '%.*s', got %s", LIT(builtin_name), xts);
|
||||
gb_string_free(xts);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
operand->mode = Addressing_Value;
|
||||
operand->type = default_type(x.type);
|
||||
}
|
||||
break;
|
||||
|
||||
case BuiltinProc_sqrt:
|
||||
{
|
||||
Operand x = {};
|
||||
|
||||
+170
-81
@@ -155,6 +155,154 @@ gb_internal void check_init_variables(CheckerContext *ctx, Entity **lhs, isize l
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
gb_internal void override_entity_in_scope(Entity *original_entity, Entity *new_entity) {
|
||||
// NOTE(bill): The original_entity's scope may not be same scope that it was inserted into
|
||||
// e.g. file entity inserted into its package scope
|
||||
String original_name = original_entity->token.string;
|
||||
Scope *found_scope = nullptr;
|
||||
Entity *found_entity = nullptr;
|
||||
scope_lookup_parent(original_entity->scope, original_name, &found_scope, &found_entity);
|
||||
if (found_scope == nullptr) {
|
||||
return;
|
||||
}
|
||||
rw_mutex_lock(&found_scope->mutex);
|
||||
defer (rw_mutex_unlock(&found_scope->mutex));
|
||||
|
||||
// IMPORTANT NOTE(bill, 2021-04-10): Overriding behaviour was flawed in that the
|
||||
// original entity was still used check checked, but the checking was only
|
||||
// relying on "constant" data such as the Entity.type and Entity.Constant.value
|
||||
//
|
||||
// Therefore two things can be done: the type can be assigned to state that it
|
||||
// has been "evaluated" and the variant data can be copied across
|
||||
|
||||
string_map_set(&found_scope->elements, original_name, new_entity);
|
||||
|
||||
original_entity->flags |= EntityFlag_Overridden;
|
||||
original_entity->type = new_entity->type;
|
||||
original_entity->aliased_of = new_entity;
|
||||
|
||||
original_entity->identifier.store(new_entity->identifier);
|
||||
|
||||
if (original_entity->identifier.load() != nullptr &&
|
||||
original_entity->identifier.load()->kind == Ast_Ident) {
|
||||
original_entity->identifier.load()->Ident.entity = new_entity;
|
||||
}
|
||||
|
||||
// IMPORTANT NOTE(bill, 2021-04-10): copy only the variants
|
||||
// This is most likely NEVER required, but it does not at all hurt to keep
|
||||
isize offset = cast(u8 *)&original_entity->Dummy.start - cast(u8 *)original_entity;
|
||||
isize size = gb_size_of(*original_entity) - offset;
|
||||
gb_memmove(cast(u8 *)original_entity, cast(u8 *)new_entity, size);
|
||||
}
|
||||
|
||||
gb_internal bool check_override_as_type_due_to_aliasing(CheckerContext *ctx, Entity *e, Entity *entity, Ast *init, Type *named_type) {
|
||||
if (entity != nullptr && entity->kind == Entity_TypeName) {
|
||||
// @TypeAliasingProblem
|
||||
// NOTE(bill, 2022-02-03): This is used to solve the problem caused by type aliases
|
||||
// being "confused" as constants
|
||||
//
|
||||
// A :: B
|
||||
// C :: proc "c" (^A)
|
||||
// B :: struct {x: C}
|
||||
//
|
||||
// A gets evaluated first, and then checks B.
|
||||
// B then checks C.
|
||||
// C then tries to check A which is unresolved but thought to be a constant.
|
||||
// Therefore within C's check, A errs as "not a type".
|
||||
//
|
||||
// This is because a const declaration may or may not be a type and this cannot
|
||||
// be determined from a syntactical standpoint.
|
||||
// This check allows the compiler to override the entity to be checked as a type.
|
||||
//
|
||||
// There is no problem if B is prefixed with the `#type` helper enforcing at
|
||||
// both a syntax and semantic level that B must be a type.
|
||||
//
|
||||
// A :: #type B
|
||||
//
|
||||
// This approach is not fool proof and can fail in case such as:
|
||||
//
|
||||
// X :: type_of(x)
|
||||
// X :: Foo(int).Type
|
||||
//
|
||||
// Since even these kind of declarations may cause weird checking cycles.
|
||||
// For the time being, these are going to be treated as an unfortunate error
|
||||
// until there is a proper delaying system to try declaration again if they
|
||||
// have failed.
|
||||
|
||||
e->kind = Entity_TypeName;
|
||||
check_type_decl(ctx, e, init, named_type);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d);
|
||||
|
||||
gb_internal bool check_try_override_const_decl(CheckerContext *ctx, Entity *e, Entity *entity, Ast *init, Type *named_type) {
|
||||
if (entity == nullptr) {
|
||||
retry_proc_lit:;
|
||||
init = unparen_expr(init);
|
||||
if (init == nullptr) {
|
||||
return false;
|
||||
}
|
||||
if (init->kind == Ast_TernaryWhenExpr) {
|
||||
ast_node(we, TernaryWhenExpr, init);
|
||||
if (we->cond == nullptr) {
|
||||
return false;
|
||||
}
|
||||
if (we->cond->tav.value.kind != ExactValue_Bool) {
|
||||
return false;
|
||||
}
|
||||
init = we->cond->tav.value.value_bool ? we->x : we->y;
|
||||
goto retry_proc_lit;
|
||||
} if (init->kind == Ast_ProcLit) {
|
||||
// NOTE(bill, 2024-07-04): Override as a procedure entity because this could be within a `when` statement
|
||||
e->kind = Entity_Procedure;
|
||||
e->type = nullptr;
|
||||
DeclInfo *d = decl_info_of_entity(e);
|
||||
d->proc_lit = init;
|
||||
check_proc_decl(ctx, e, d);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
switch (entity->kind) {
|
||||
case Entity_TypeName:
|
||||
if (check_override_as_type_due_to_aliasing(ctx, e, entity, init, named_type)) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case Entity_Builtin:
|
||||
if (e->type != nullptr) {
|
||||
return false;
|
||||
}
|
||||
e->kind = Entity_Builtin;
|
||||
e->Builtin.id = entity->Builtin.id;
|
||||
e->type = t_invalid;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (e->type != nullptr && entity->type != nullptr) {
|
||||
Operand x = {};
|
||||
x.type = entity->type;
|
||||
x.mode = Addressing_Variable;
|
||||
if (!check_is_assignable_to(ctx, &x, e->type)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE(bill): Override aliased entity
|
||||
switch (entity->kind) {
|
||||
case Entity_ProcGroup:
|
||||
case Entity_Procedure:
|
||||
override_entity_in_scope(e, entity);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
gb_internal void check_init_constant(CheckerContext *ctx, Entity *e, Operand *operand) {
|
||||
if (operand->mode == Addressing_Invalid ||
|
||||
operand->type == t_invalid ||
|
||||
@@ -165,6 +313,13 @@ gb_internal void check_init_constant(CheckerContext *ctx, Entity *e, Operand *op
|
||||
return;
|
||||
}
|
||||
|
||||
if (operand->mode != Addressing_Constant) {
|
||||
Entity *entity = entity_of_node(operand->expr);
|
||||
if (check_try_override_const_decl(ctx, e, entity, operand->expr, nullptr)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (operand->mode != Addressing_Constant) {
|
||||
gbString str = expr_to_string(operand->expr);
|
||||
error(operand->expr, "'%s' is not a compile-time known constant", str);
|
||||
@@ -373,49 +528,6 @@ gb_internal void check_type_decl(CheckerContext *ctx, Entity *e, Ast *init_expr,
|
||||
}
|
||||
|
||||
|
||||
gb_internal void override_entity_in_scope(Entity *original_entity, Entity *new_entity) {
|
||||
// NOTE(bill): The original_entity's scope may not be same scope that it was inserted into
|
||||
// e.g. file entity inserted into its package scope
|
||||
String original_name = original_entity->token.string;
|
||||
Scope *found_scope = nullptr;
|
||||
Entity *found_entity = nullptr;
|
||||
scope_lookup_parent(original_entity->scope, original_name, &found_scope, &found_entity);
|
||||
if (found_scope == nullptr) {
|
||||
return;
|
||||
}
|
||||
rw_mutex_lock(&found_scope->mutex);
|
||||
defer (rw_mutex_unlock(&found_scope->mutex));
|
||||
|
||||
// IMPORTANT NOTE(bill, 2021-04-10): Overriding behaviour was flawed in that the
|
||||
// original entity was still used check checked, but the checking was only
|
||||
// relying on "constant" data such as the Entity.type and Entity.Constant.value
|
||||
//
|
||||
// Therefore two things can be done: the type can be assigned to state that it
|
||||
// has been "evaluated" and the variant data can be copied across
|
||||
|
||||
string_map_set(&found_scope->elements, original_name, new_entity);
|
||||
|
||||
original_entity->flags |= EntityFlag_Overridden;
|
||||
original_entity->type = new_entity->type;
|
||||
original_entity->aliased_of = new_entity;
|
||||
|
||||
Ast *empty_ident = nullptr;
|
||||
original_entity->identifier.compare_exchange_strong(empty_ident, new_entity->identifier);
|
||||
|
||||
if (original_entity->identifier.load() != nullptr &&
|
||||
original_entity->identifier.load()->kind == Ast_Ident) {
|
||||
original_entity->identifier.load()->Ident.entity = new_entity;
|
||||
}
|
||||
|
||||
// IMPORTANT NOTE(bill, 2021-04-10): copy only the variants
|
||||
// This is most likely NEVER required, but it does not at all hurt to keep
|
||||
isize offset = cast(u8 *)&original_entity->Dummy.start - cast(u8 *)original_entity;
|
||||
isize size = gb_size_of(*original_entity) - offset;
|
||||
gb_memmove(cast(u8 *)original_entity, cast(u8 *)new_entity, size);
|
||||
}
|
||||
|
||||
|
||||
|
||||
gb_internal void check_const_decl(CheckerContext *ctx, Entity *e, Ast *type_expr, Ast *init, Type *named_type) {
|
||||
GB_ASSERT(e->type == nullptr);
|
||||
GB_ASSERT(e->kind == Entity_Constant);
|
||||
@@ -441,41 +553,7 @@ gb_internal void check_const_decl(CheckerContext *ctx, Entity *e, Ast *type_expr
|
||||
|
||||
if (init != nullptr) {
|
||||
Entity *entity = check_entity_from_ident_or_selector(ctx, init, false);
|
||||
if (entity != nullptr && entity->kind == Entity_TypeName) {
|
||||
// @TypeAliasingProblem
|
||||
// NOTE(bill, 2022-02-03): This is used to solve the problem caused by type aliases
|
||||
// being "confused" as constants
|
||||
//
|
||||
// A :: B
|
||||
// C :: proc "c" (^A)
|
||||
// B :: struct {x: C}
|
||||
//
|
||||
// A gets evaluated first, and then checks B.
|
||||
// B then checks C.
|
||||
// C then tries to check A which is unresolved but thought to be a constant.
|
||||
// Therefore within C's check, A errs as "not a type".
|
||||
//
|
||||
// This is because a const declaration may or may not be a type and this cannot
|
||||
// be determined from a syntactical standpoint.
|
||||
// This check allows the compiler to override the entity to be checked as a type.
|
||||
//
|
||||
// There is no problem if B is prefixed with the `#type` helper enforcing at
|
||||
// both a syntax and semantic level that B must be a type.
|
||||
//
|
||||
// A :: #type B
|
||||
//
|
||||
// This approach is not fool proof and can fail in case such as:
|
||||
//
|
||||
// X :: type_of(x)
|
||||
// X :: Foo(int).Type
|
||||
//
|
||||
// Since even these kind of declarations may cause weird checking cycles.
|
||||
// For the time being, these are going to be treated as an unfortunate error
|
||||
// until there is a proper delaying system to try declaration again if they
|
||||
// have failed.
|
||||
|
||||
e->kind = Entity_TypeName;
|
||||
check_type_decl(ctx, e, init, named_type);
|
||||
if (check_override_as_type_due_to_aliasing(ctx, e, entity, init, named_type)) {
|
||||
return;
|
||||
}
|
||||
entity = nullptr;
|
||||
@@ -951,7 +1029,6 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
|
||||
|
||||
switch (e->Procedure.optimization_mode) {
|
||||
case ProcedureOptimizationMode_None:
|
||||
case ProcedureOptimizationMode_Minimal:
|
||||
if (pl->inlining == ProcInlining_inline) {
|
||||
error(e->token, "#force_inline cannot be used in conjunction with the attribute 'optimization_mode' with neither \"none\" nor \"minimal\"");
|
||||
}
|
||||
@@ -1178,9 +1255,12 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
|
||||
if (foreign_library->LibraryName.paths.count >= 1) {
|
||||
module_name = foreign_library->LibraryName.paths[0];
|
||||
}
|
||||
name = concatenate3_strings(permanent_allocator(), module_name, WASM_MODULE_NAME_SEPARATOR, name);
|
||||
|
||||
if (!string_ends_with(module_name, str_lit(".o"))) {
|
||||
name = concatenate3_strings(permanent_allocator(), module_name, WASM_MODULE_NAME_SEPARATOR, name);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
e->Procedure.is_foreign = true;
|
||||
e->Procedure.link_name = name;
|
||||
|
||||
@@ -1788,5 +1868,14 @@ gb_internal bool check_proc_body(CheckerContext *ctx_, Token token, DeclInfo *de
|
||||
|
||||
add_deps_from_child_to_parent(decl);
|
||||
|
||||
for (VariadicReuseData const &vr : decl->variadic_reuses) {
|
||||
GB_ASSERT(vr.slice_type->kind == Type_Slice);
|
||||
Type *elem = vr.slice_type->Slice.elem;
|
||||
i64 size = type_size_of(elem);
|
||||
i64 align = type_align_of(elem);
|
||||
decl->variadic_reuse_max_bytes = gb_max(decl->variadic_reuse_max_bytes, size*vr.max_count);
|
||||
decl->variadic_reuse_max_align = gb_max(decl->variadic_reuse_max_align, align);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
+107
-17
@@ -127,6 +127,8 @@ gb_internal bool complete_soa_type(Checker *checker, Type *t, bool wait_to_finis
|
||||
|
||||
gb_internal bool check_is_castable_to(CheckerContext *c, Operand *operand, Type *y);
|
||||
|
||||
gb_internal bool is_exact_value_zero(ExactValue const &v);
|
||||
|
||||
enum LoadDirectiveResult {
|
||||
LoadDirective_Success = 0,
|
||||
LoadDirective_Error = 1,
|
||||
@@ -500,7 +502,9 @@ gb_internal bool find_or_generate_polymorphic_procedure(CheckerContext *old_c, E
|
||||
nctx.no_polymorphic_errors = false;
|
||||
|
||||
// NOTE(bill): Reset scope from the failed procedure type
|
||||
scope_reset(scope);
|
||||
scope->head_child.store(nullptr, std::memory_order_relaxed);
|
||||
string_map_clear(&scope->elements);
|
||||
ptr_set_clear(&scope->imported);
|
||||
|
||||
// LEAK NOTE(bill): Cloning this AST may be leaky but this is not really an issue due to arena-based allocation
|
||||
Ast *cloned_proc_type_node = clone_ast(pt->node);
|
||||
@@ -548,13 +552,15 @@ gb_internal bool find_or_generate_polymorphic_procedure(CheckerContext *old_c, E
|
||||
final_proc_type->Proc.is_poly_specialized = true;
|
||||
final_proc_type->Proc.is_polymorphic = true;
|
||||
|
||||
final_proc_type->Proc.variadic = src->Proc.variadic;
|
||||
final_proc_type->Proc.require_results = src->Proc.require_results;
|
||||
final_proc_type->Proc.c_vararg = src->Proc.c_vararg;
|
||||
final_proc_type->Proc.has_named_results = src->Proc.has_named_results;
|
||||
final_proc_type->Proc.diverging = src->Proc.diverging;
|
||||
final_proc_type->Proc.return_by_pointer = src->Proc.return_by_pointer;
|
||||
final_proc_type->Proc.optional_ok = src->Proc.optional_ok;
|
||||
final_proc_type->Proc.variadic = src->Proc.variadic;
|
||||
final_proc_type->Proc.require_results = src->Proc.require_results;
|
||||
final_proc_type->Proc.c_vararg = src->Proc.c_vararg;
|
||||
final_proc_type->Proc.has_named_results = src->Proc.has_named_results;
|
||||
final_proc_type->Proc.diverging = src->Proc.diverging;
|
||||
final_proc_type->Proc.return_by_pointer = src->Proc.return_by_pointer;
|
||||
final_proc_type->Proc.optional_ok = src->Proc.optional_ok;
|
||||
final_proc_type->Proc.enable_target_feature = src->Proc.enable_target_feature;
|
||||
final_proc_type->Proc.require_target_feature = src->Proc.require_target_feature;
|
||||
|
||||
|
||||
for (isize i = 0; i < operands.count; i++) {
|
||||
@@ -587,6 +593,16 @@ gb_internal bool find_or_generate_polymorphic_procedure(CheckerContext *old_c, E
|
||||
entity->file = base_entity->file;
|
||||
entity->pkg = base_entity->pkg;
|
||||
entity->flags = 0;
|
||||
|
||||
entity->Procedure.optimization_mode = base_entity->Procedure.optimization_mode;
|
||||
|
||||
if (base_entity->flags & EntityFlag_Cold) {
|
||||
entity->flags |= EntityFlag_Cold;
|
||||
}
|
||||
if (base_entity->flags & EntityFlag_Disabled) {
|
||||
entity->flags |= EntityFlag_Disabled;
|
||||
}
|
||||
|
||||
d->entity = entity;
|
||||
|
||||
AstFile *file = nullptr;
|
||||
@@ -4443,6 +4459,27 @@ gb_internal void convert_to_typed(CheckerContext *c, Operand *operand, Type *tar
|
||||
|
||||
|
||||
case Type_Union:
|
||||
// IMPORTANT NOTE HACK(bill): This is just to allow for comparisons against `0` with the `os.Error` type
|
||||
// as a kind of transition period
|
||||
if (!build_context.strict_style &&
|
||||
operand->mode == Addressing_Constant &&
|
||||
target_type->kind == Type_Named &&
|
||||
(c->pkg == nullptr || c->pkg->name != "os") &&
|
||||
target_type->Named.name == "Error") {
|
||||
Entity *e = target_type->Named.type_name;
|
||||
if (e->pkg && e->pkg->name == "os") {
|
||||
if (is_exact_value_zero(operand->value) &&
|
||||
(operand->value.kind == ExactValue_Integer ||
|
||||
operand->value.kind == ExactValue_Float)) {
|
||||
operand->mode = Addressing_Value;
|
||||
target_type = t_untyped_nil;
|
||||
operand->value = empty_exact_value;
|
||||
update_untyped_expr_value(c, operand->expr, operand->value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// "fallthrough"
|
||||
if (!is_operand_nil(*operand) && !is_operand_uninit(*operand)) {
|
||||
TEMPORARY_ALLOCATOR_GUARD();
|
||||
|
||||
@@ -4968,7 +5005,27 @@ gb_internal bool is_entity_declared_for_selector(Entity *entity, Scope *import_s
|
||||
|
||||
// NOTE(bill, 2022-02-03): see `check_const_decl` for why it exists reasoning
|
||||
gb_internal Entity *check_entity_from_ident_or_selector(CheckerContext *c, Ast *node, bool ident_only) {
|
||||
if (node->kind == Ast_Ident) {
|
||||
if (node == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
/*if (node->kind == Ast_TernaryWhenExpr) {
|
||||
ast_node(we, TernaryWhenExpr, node);
|
||||
if (we->cond == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
if (we->cond->tav.mode != Addressing_Constant) {
|
||||
return nullptr;
|
||||
}
|
||||
if (we->cond->tav.value.kind != ExactValue_Bool) {
|
||||
return nullptr;
|
||||
}
|
||||
if (we->cond->tav.value.value_bool) {
|
||||
return check_entity_from_ident_or_selector(c, we->x, ident_only);
|
||||
} else {
|
||||
Entity *e = check_entity_from_ident_or_selector(c, we->y, ident_only);
|
||||
return e;
|
||||
}
|
||||
} else */if (node->kind == Ast_Ident) {
|
||||
String name = node->Ident.token.string;
|
||||
return scope_lookup(c->scope, name);
|
||||
} else if (!ident_only) if (node->kind == Ast_SelectorExpr) {
|
||||
@@ -6001,6 +6058,22 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A
|
||||
|
||||
Entity *vt = pt->params->Tuple.variables[pt->variadic_index];
|
||||
o.type = vt->type;
|
||||
|
||||
// NOTE(bill, 2024-07-14): minimize the stack usage for variadic parameters with the backing array
|
||||
if (c->decl) {
|
||||
bool found = false;
|
||||
for (auto &vr : c->decl->variadic_reuses) {
|
||||
if (are_types_identical(vt->type, vr.slice_type)) {
|
||||
vr.max_count = gb_max(vr.max_count, variadic_operands.count);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
array_add(&c->decl->variadic_reuses, VariadicReuseData{vt->type, variadic_operands.count});
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
dummy_argument_count += 1;
|
||||
o.type = t_untyped_nil;
|
||||
@@ -7165,6 +7238,8 @@ gb_internal CallArgumentError check_polymorphic_record_type(CheckerContext *c, O
|
||||
Array<Operand> operands = {};
|
||||
defer (array_free(&operands));
|
||||
|
||||
CallArgumentError err = CallArgumentError_None;
|
||||
|
||||
bool named_fields = false;
|
||||
{
|
||||
// NOTE(bill, 2019-10-26): Allow a cycle in the parameters but not in the fields themselves
|
||||
@@ -7182,6 +7257,11 @@ gb_internal CallArgumentError check_polymorphic_record_type(CheckerContext *c, O
|
||||
Ast *arg = ce->args[i];
|
||||
ast_node(fv, FieldValue, arg);
|
||||
|
||||
if (fv->value == nullptr) {
|
||||
error(fv->eq, "Expected a value");
|
||||
err = CallArgumentError_InvalidFieldValue;
|
||||
continue;
|
||||
}
|
||||
if (fv->field->kind == Ast_Ident) {
|
||||
String name = fv->field->Ident.token.string;
|
||||
isize index = lookup_polymorphic_record_parameter(original_type, name);
|
||||
@@ -7220,7 +7300,10 @@ gb_internal CallArgumentError check_polymorphic_record_type(CheckerContext *c, O
|
||||
|
||||
}
|
||||
|
||||
CallArgumentError err = CallArgumentError_None;
|
||||
if (err != 0) {
|
||||
operand->mode = Addressing_Invalid;
|
||||
return err;
|
||||
}
|
||||
|
||||
TypeTuple *tuple = get_record_polymorphic_params(original_type);
|
||||
isize param_count = tuple->variables.count;
|
||||
@@ -7848,12 +7931,15 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c
|
||||
|
||||
// NOTE: Due to restrictions in LLVM you can not inline calls with a superset of features.
|
||||
if (is_call_inlined) {
|
||||
GB_ASSERT(c->curr_proc_decl);
|
||||
GB_ASSERT(c->curr_proc_decl->entity);
|
||||
GB_ASSERT(c->curr_proc_decl->entity->type->kind == Type_Proc);
|
||||
String scope_features = c->curr_proc_decl->entity->type->Proc.enable_target_feature;
|
||||
if (!check_target_feature_is_superset_of(scope_features, pt->Proc.enable_target_feature, &invalid)) {
|
||||
error(call, "Inlined procedure enables target feature '%.*s', this requires the calling procedure to at least enable the same feature", LIT(invalid));
|
||||
if (c->curr_proc_decl == nullptr) {
|
||||
error(call, "Calling a '#force_inline' procedure that enables target features is not allowed at file scope");
|
||||
} else {
|
||||
GB_ASSERT(c->curr_proc_decl->entity);
|
||||
GB_ASSERT(c->curr_proc_decl->entity->type->kind == Type_Proc);
|
||||
String scope_features = c->curr_proc_decl->entity->type->Proc.enable_target_feature;
|
||||
if (!check_target_feature_is_superset_of(scope_features, pt->Proc.enable_target_feature, &invalid)) {
|
||||
error(call, "Inlined procedure enables target feature '%.*s', this requires the calling procedure to at least enable the same feature", LIT(invalid));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9886,10 +9972,14 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
|
||||
}
|
||||
Type *et = base_type(t->BitSet.elem);
|
||||
isize field_count = 0;
|
||||
if (et->kind == Type_Enum) {
|
||||
if (et != nullptr && et->kind == Type_Enum) {
|
||||
field_count = et->Enum.fields.count;
|
||||
}
|
||||
|
||||
if (is_type_array(bit_set_to_int(t))) {
|
||||
is_constant = false;
|
||||
}
|
||||
|
||||
if (cl->elems[0]->kind == Ast_FieldValue) {
|
||||
error(cl->elems[0], "'field = value' in a bit_set a literal is not allowed");
|
||||
is_constant = false;
|
||||
|
||||
+19
-2
@@ -1060,6 +1060,9 @@ gb_internal void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags
|
||||
if (ss->tag != nullptr) {
|
||||
check_expr(ctx, &x, ss->tag);
|
||||
check_assignment(ctx, &x, nullptr, str_lit("switch expression"));
|
||||
if (x.type == nullptr) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
x.mode = Addressing_Constant;
|
||||
x.type = t_bool;
|
||||
@@ -1834,7 +1837,7 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags)
|
||||
|
||||
if (rs->vals.count == 1) {
|
||||
Type *t = type_deref(operand.type);
|
||||
if (is_type_map(t) || is_type_bit_set(t)) {
|
||||
if (t != NULL && (is_type_map(t) || is_type_bit_set(t))) {
|
||||
gbString v = expr_to_string(rs->vals[0]);
|
||||
defer (gb_string_free(v));
|
||||
error_line("\tSuggestion: place parentheses around the expression\n");
|
||||
@@ -2031,6 +2034,12 @@ gb_internal void check_value_decl_stmt(CheckerContext *ctx, Ast *node, u32 mod_f
|
||||
gb_string_free(str);
|
||||
init_type = t_invalid;
|
||||
}
|
||||
if (init_type == t_invalid && entity_count == 1 && (mod_flags & (Stmt_BreakAllowed|Stmt_FallthroughAllowed))) {
|
||||
Entity *e = entities[0];
|
||||
if (e != nullptr && e->token.string == "default") {
|
||||
warning(e->token, "Did you mean 'case:'?");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2508,7 +2517,7 @@ gb_internal void check_return_stmt(CheckerContext *ctx, Ast *node) {
|
||||
Entity *e = entity_of_node(x);
|
||||
if (is_entity_local_variable(e)) {
|
||||
unsafe_return_error(o, "the address of a local variable");
|
||||
} else if(x->kind == Ast_CompoundLit) {
|
||||
} else if (x->kind == Ast_CompoundLit) {
|
||||
unsafe_return_error(o, "the address of a compound literal");
|
||||
} else if (x->kind == Ast_IndexExpr) {
|
||||
Entity *f = entity_of_node(x->IndexExpr.expr);
|
||||
@@ -2523,6 +2532,14 @@ gb_internal void check_return_stmt(CheckerContext *ctx, Ast *node) {
|
||||
unsafe_return_error(o, "the address of an indexed variable", f->type);
|
||||
}
|
||||
}
|
||||
} else if (expr->kind == Ast_SliceExpr) {
|
||||
Ast *x = unparen_expr(expr->SliceExpr.expr);
|
||||
Entity *e = entity_of_node(x);
|
||||
if (is_entity_local_variable(e) && is_type_array(e->type)) {
|
||||
unsafe_return_error(o, "a slice of a local variable");
|
||||
} else if (x->kind == Ast_CompoundLit) {
|
||||
unsafe_return_error(o, "a slice of a compound literal");
|
||||
}
|
||||
} else if (o.mode == Addressing_Constant && is_type_slice(o.type)) {
|
||||
ERROR_BLOCK();
|
||||
unsafe_return_error(o, "a compound literal of a slice");
|
||||
|
||||
+95
-38
@@ -740,7 +740,7 @@ gb_internal void check_union_type(CheckerContext *ctx, Type *union_type, Ast *no
|
||||
gb_string_free(str);
|
||||
} else {
|
||||
for_array(j, variants) {
|
||||
if (are_types_identical(t, variants[j])) {
|
||||
if (union_variant_index_types_equal(t, variants[j])) {
|
||||
ok = false;
|
||||
ERROR_BLOCK();
|
||||
gbString str = type_to_string(t);
|
||||
@@ -939,22 +939,6 @@ gb_internal void check_enum_type(CheckerContext *ctx, Type *enum_type, Type *nam
|
||||
enum_type->Enum.max_value_index = max_value_index;
|
||||
}
|
||||
|
||||
gb_internal bool is_valid_bit_field_backing_type(Type *type) {
|
||||
if (type == nullptr) {
|
||||
return false;
|
||||
}
|
||||
type = base_type(type);
|
||||
if (is_type_untyped(type)) {
|
||||
return false;
|
||||
}
|
||||
if (is_type_integer(type)) {
|
||||
return true;
|
||||
}
|
||||
if (type->kind == Type_Array) {
|
||||
return is_type_integer(type->Array.elem);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
gb_internal void check_bit_field_type(CheckerContext *ctx, Type *bit_field_type, Type *named_type, Ast *node) {
|
||||
ast_node(bf, BitFieldType, node);
|
||||
@@ -1120,6 +1104,8 @@ gb_internal void check_bit_field_type(CheckerContext *ctx, Type *bit_field_type,
|
||||
// NOTE(bill): it doesn't matter, and when it does,
|
||||
// that api is absolutely stupid
|
||||
return Endian_Unknown;
|
||||
} else if (type_size_of(type) < 2) {
|
||||
return Endian_Unknown;
|
||||
} else if (is_type_endian_specific(type)) {
|
||||
if (is_type_endian_little(type)) {
|
||||
return Endian_Little;
|
||||
@@ -1266,11 +1252,14 @@ gb_internal void check_bit_set_type(CheckerContext *c, Type *type, Type *named_t
|
||||
Type *t = default_type(lhs.type);
|
||||
if (bs->underlying != nullptr) {
|
||||
Type *u = check_type(c, bs->underlying);
|
||||
// if (!is_valid_bit_field_backing_type(u)) {
|
||||
if (!is_type_integer(u)) {
|
||||
gbString ts = type_to_string(u);
|
||||
error(bs->underlying, "Expected an underlying integer for the bit set, got %s", ts);
|
||||
gb_string_free(ts);
|
||||
return;
|
||||
if (!is_valid_bit_field_backing_type(u)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
type->BitSet.underlying = u;
|
||||
}
|
||||
@@ -1570,11 +1559,30 @@ gb_internal Type *determine_type_from_polymorphic(CheckerContext *ctx, Type *pol
|
||||
return poly_type;
|
||||
}
|
||||
if (show_error) {
|
||||
ERROR_BLOCK();
|
||||
gbString pts = type_to_string(poly_type);
|
||||
gbString ots = type_to_string(operand.type, true);
|
||||
defer (gb_string_free(pts));
|
||||
defer (gb_string_free(ots));
|
||||
error(operand.expr, "Cannot determine polymorphic type from parameter: '%s' to '%s'", ots, pts);
|
||||
|
||||
Type *pt = poly_type;
|
||||
while (pt && pt->kind == Type_Generic && pt->Generic.specialized) {
|
||||
pt = pt->Generic.specialized;
|
||||
}
|
||||
if (is_type_slice(pt) &&
|
||||
(is_type_dynamic_array(operand.type) || is_type_array(operand.type))) {
|
||||
Ast *expr = unparen_expr(operand.expr);
|
||||
if (expr->kind == Ast_CompoundLit) {
|
||||
gbString es = type_to_string(base_any_array_type(operand.type));
|
||||
error_line("\tSuggestion: Try using a slice compound literal instead '[]%s{...}'\n", es);
|
||||
gb_string_free(es);
|
||||
} else {
|
||||
gbString os = expr_to_string(operand.expr);
|
||||
error_line("\tSuggestion: Try slicing the value with '%s[:]'\n", os);
|
||||
gb_string_free(os);
|
||||
}
|
||||
}
|
||||
}
|
||||
return t_invalid;
|
||||
}
|
||||
@@ -1763,6 +1771,7 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para
|
||||
if (type_expr == nullptr) {
|
||||
param_value = handle_parameter_value(ctx, nullptr, &type, default_value, true);
|
||||
} else {
|
||||
Ast *original_type_expr = type_expr;
|
||||
if (type_expr->kind == Ast_Ellipsis) {
|
||||
type_expr = type_expr->Ellipsis.expr;
|
||||
is_variadic = true;
|
||||
@@ -1771,6 +1780,9 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para
|
||||
error(param, "Invalid AST: Invalid variadic parameter with multiple names");
|
||||
success = false;
|
||||
}
|
||||
|
||||
GB_ASSERT(original_type_expr->kind == Ast_Ellipsis);
|
||||
type_expr = ast_array_type(type_expr->file(), original_type_expr->Ellipsis.token, nullptr, type_expr);
|
||||
}
|
||||
if (type_expr->kind == Ast_TypeidType) {
|
||||
ast_node(tt, TypeidType, type_expr);
|
||||
@@ -1794,6 +1806,7 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para
|
||||
if (operands != nullptr) {
|
||||
ctx->allow_polymorphic_types = true;
|
||||
}
|
||||
|
||||
type = check_type(ctx, type_expr);
|
||||
|
||||
ctx->allow_polymorphic_types = prev;
|
||||
@@ -1826,12 +1839,12 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para
|
||||
}
|
||||
type = t_invalid;
|
||||
}
|
||||
if (is_type_empty_union(type)) {
|
||||
gbString str = type_to_string(type);
|
||||
error(param, "Invalid use of an empty union '%s'", str);
|
||||
gb_string_free(str);
|
||||
type = t_invalid;
|
||||
}
|
||||
// if (is_type_empty_union(type)) {
|
||||
// gbString str = type_to_string(type);
|
||||
// error(param, "Invalid use of an empty union '%s'", str);
|
||||
// gb_string_free(str);
|
||||
// type = t_invalid;
|
||||
// }
|
||||
|
||||
if (is_type_polymorphic(type)) {
|
||||
switch (param_value.kind) {
|
||||
@@ -1946,6 +1959,10 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para
|
||||
error(name, "'#by_ptr' can only be applied to variable fields");
|
||||
p->flags &= ~FieldFlag_by_ptr;
|
||||
}
|
||||
if (p->flags&FieldFlag_no_capture) {
|
||||
error(name, "'#no_capture' can only be applied to variable fields");
|
||||
p->flags &= ~FieldFlag_no_capture;
|
||||
}
|
||||
|
||||
param = alloc_entity_type_name(scope, name->Ident.token, type, EntityState_Resolved);
|
||||
param->TypeName.is_type_alias = true;
|
||||
@@ -2047,6 +2064,28 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para
|
||||
p->flags &= ~FieldFlag_by_ptr; // Remove the flag
|
||||
}
|
||||
}
|
||||
if (p->flags&FieldFlag_no_capture) {
|
||||
if (is_variadic && variadic_index == variables.count) {
|
||||
if (p->flags & FieldFlag_c_vararg) {
|
||||
error(name, "'#no_capture' cannot be applied to a #c_vararg parameter");
|
||||
p->flags &= ~FieldFlag_no_capture;
|
||||
} else {
|
||||
error(name, "'#no_capture' is already implied on all variadic parameter");
|
||||
}
|
||||
} else if (is_type_polymorphic(type)) {
|
||||
// ignore
|
||||
} else {
|
||||
if (is_type_internally_pointer_like(type)) {
|
||||
error(name, "'#no_capture' is currently reserved for future use");
|
||||
} else {
|
||||
ERROR_BLOCK();
|
||||
error(name, "'#no_capture' can only be applied to pointer-like types");
|
||||
error_line("\t'#no_capture' does not currently do anything useful\n");
|
||||
p->flags &= ~FieldFlag_no_capture;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (is_poly_name) {
|
||||
if (p->flags&FieldFlag_no_alias) {
|
||||
@@ -2065,6 +2104,11 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para
|
||||
error(name, "'#by_ptr' can only be applied to variable fields");
|
||||
p->flags &= ~FieldFlag_by_ptr;
|
||||
}
|
||||
if (p->flags&FieldFlag_no_capture) {
|
||||
error(name, "'#no_capture' can only be applied to variable fields");
|
||||
p->flags &= ~FieldFlag_no_capture;
|
||||
}
|
||||
|
||||
|
||||
if (!is_type_polymorphic(type) && check_constant_parameter_value(type, params[i])) {
|
||||
// failed
|
||||
@@ -2079,6 +2123,16 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para
|
||||
param->Variable.type_expr = type_expr;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_variadic && variadic_index == variables.count) {
|
||||
param->flags |= EntityFlag_Ellipsis;
|
||||
if (is_c_vararg) {
|
||||
param->flags |= EntityFlag_CVarArg;
|
||||
} else {
|
||||
param->flags |= EntityFlag_NoCapture;
|
||||
}
|
||||
}
|
||||
|
||||
if (p->flags&FieldFlag_no_alias) {
|
||||
param->flags |= EntityFlag_NoAlias;
|
||||
}
|
||||
@@ -2100,6 +2154,10 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para
|
||||
if (p->flags&FieldFlag_by_ptr) {
|
||||
param->flags |= EntityFlag_ByPtr;
|
||||
}
|
||||
if (p->flags&FieldFlag_no_capture) {
|
||||
param->flags |= EntityFlag_NoCapture;
|
||||
}
|
||||
|
||||
|
||||
param->state = EntityState_Resolved; // NOTE(bill): This should have be resolved whilst determining it
|
||||
add_entity(ctx, scope, name, param);
|
||||
@@ -2113,18 +2171,7 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para
|
||||
|
||||
if (is_variadic) {
|
||||
GB_ASSERT(variadic_index >= 0);
|
||||
}
|
||||
|
||||
if (is_variadic) {
|
||||
GB_ASSERT(params.count > 0);
|
||||
// NOTE(bill): Change last variadic parameter to be a slice
|
||||
// Custom Calling convention for variadic parameters
|
||||
Entity *end = variables[variadic_index];
|
||||
end->type = alloc_type_slice(end->type);
|
||||
end->flags |= EntityFlag_Ellipsis;
|
||||
if (is_c_vararg) {
|
||||
end->flags |= EntityFlag_CVarArg;
|
||||
}
|
||||
}
|
||||
|
||||
isize specialization_count = 0;
|
||||
@@ -2426,9 +2473,15 @@ gb_internal i64 check_array_count(CheckerContext *ctx, Operand *o, Ast *e) {
|
||||
if (e == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
if (e->kind == Ast_UnaryExpr &&
|
||||
e->UnaryExpr.op.kind == Token_Question) {
|
||||
return -1;
|
||||
if (e->kind == Ast_UnaryExpr) {
|
||||
Token op = e->UnaryExpr.op;
|
||||
if (op.kind == Token_Question) {
|
||||
return -1;
|
||||
}
|
||||
if (e->UnaryExpr.expr == nullptr) {
|
||||
error(op, "Invalid array count '[%.*s]'", LIT(op.string));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
check_expr_or_type(ctx, o, e);
|
||||
@@ -3333,6 +3386,10 @@ gb_internal bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, T
|
||||
gbString s = expr_to_string(pt->type);
|
||||
error(e, "^ is used for pointer types, did you mean '&%s'?", s);
|
||||
gb_string_free(s);
|
||||
} else if (is_type_pointer(o.type)) {
|
||||
gbString s = expr_to_string(pt->type);
|
||||
error(e, "^ is used for pointer types, did you mean a dereference: '%s^'?", s);
|
||||
gb_string_free(s);
|
||||
} else {
|
||||
// NOTE(bill): call check_type_expr again to get a consistent error message
|
||||
elem = check_type_expr(&c, pt->type, nullptr);
|
||||
|
||||
+47
-27
@@ -50,15 +50,6 @@ gb_internal bool check_rtti_type_disallowed(Ast *expr, Type *type, char const *f
|
||||
return check_rtti_type_disallowed(ast_token(expr), type, format);
|
||||
}
|
||||
|
||||
gb_internal void scope_reset(Scope *scope) {
|
||||
if (scope == nullptr) return;
|
||||
|
||||
rw_mutex_lock(&scope->mutex);
|
||||
scope->head_child.store(nullptr, std::memory_order_relaxed);
|
||||
string_map_clear(&scope->elements);
|
||||
ptr_set_clear(&scope->imported);
|
||||
rw_mutex_unlock(&scope->mutex);
|
||||
}
|
||||
|
||||
gb_internal void scope_reserve(Scope *scope, isize count) {
|
||||
string_map_reserve(&scope->elements, 2*count);
|
||||
@@ -168,9 +159,6 @@ gb_internal void import_graph_node_swap(ImportGraphNode **data, isize i, isize j
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
gb_internal void init_decl_info(DeclInfo *d, Scope *scope, DeclInfo *parent) {
|
||||
gb_zero_item(d);
|
||||
if (parent) {
|
||||
@@ -184,6 +172,9 @@ gb_internal void init_decl_info(DeclInfo *d, Scope *scope, DeclInfo *parent) {
|
||||
ptr_set_init(&d->deps, 0);
|
||||
ptr_set_init(&d->type_info_deps, 0);
|
||||
d->labels.allocator = heap_allocator();
|
||||
d->variadic_reuses.allocator = heap_allocator();
|
||||
d->variadic_reuse_max_bytes = 0;
|
||||
d->variadic_reuse_max_align = 1;
|
||||
}
|
||||
|
||||
gb_internal DeclInfo *make_decl_info(Scope *scope, DeclInfo *parent) {
|
||||
@@ -381,6 +372,7 @@ gb_internal Entity *scope_lookup_current(Scope *s, String const &name) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
gb_internal void scope_lookup_parent(Scope *scope, String const &name, Scope **scope_, Entity **entity_) {
|
||||
if (scope != nullptr) {
|
||||
bool gone_thru_proc = false;
|
||||
@@ -508,9 +500,15 @@ end:;
|
||||
return result;
|
||||
}
|
||||
|
||||
gb_global bool in_single_threaded_checker_stage = false;
|
||||
|
||||
gb_internal Entity *scope_insert(Scope *s, Entity *entity) {
|
||||
String name = entity->token.string;
|
||||
return scope_insert_with_name(s, name, entity);
|
||||
if (in_single_threaded_checker_stage) {
|
||||
return scope_insert_with_name_no_mutex(s, name, entity);
|
||||
} else {
|
||||
return scope_insert_with_name(s, name, entity);
|
||||
}
|
||||
}
|
||||
|
||||
gb_internal Entity *scope_insert_no_mutex(Scope *s, Entity *entity) {
|
||||
@@ -519,7 +517,7 @@ gb_internal Entity *scope_insert_no_mutex(Scope *s, Entity *entity) {
|
||||
}
|
||||
|
||||
|
||||
GB_COMPARE_PROC(entity_variable_pos_cmp) {
|
||||
gb_internal GB_COMPARE_PROC(entity_variable_pos_cmp) {
|
||||
Entity *x = *cast(Entity **)a;
|
||||
Entity *y = *cast(Entity **)b;
|
||||
|
||||
@@ -655,7 +653,7 @@ gb_internal bool check_vet_shadowing(Checker *c, Entity *e, VettedEntity *ve) {
|
||||
}
|
||||
}
|
||||
|
||||
zero_item(ve);
|
||||
gb_zero_item(ve);
|
||||
ve->kind = VettedEntity_Shadowed;
|
||||
ve->entity = e;
|
||||
ve->other = shadowed;
|
||||
@@ -674,7 +672,7 @@ gb_internal bool check_vet_unused(Checker *c, Entity *e, VettedEntity *ve) {
|
||||
}
|
||||
case Entity_ImportName:
|
||||
case Entity_LibraryName:
|
||||
zero_item(ve);
|
||||
gb_zero_item(ve);
|
||||
ve->kind = VettedEntity_Unused;
|
||||
ve->entity = e;
|
||||
return true;
|
||||
@@ -847,6 +845,10 @@ gb_internal void add_declaration_dependency(CheckerContext *c, Entity *e) {
|
||||
if (e == nullptr) {
|
||||
return;
|
||||
}
|
||||
if (e->flags & EntityFlag_Disabled) {
|
||||
// ignore the dependencies if it has been `@(disabled=true)`
|
||||
return;
|
||||
}
|
||||
if (c->decl != nullptr) {
|
||||
add_dependency(c->info, c->decl, e);
|
||||
}
|
||||
@@ -1110,7 +1112,11 @@ gb_internal void init_universal(void) {
|
||||
int minimum_os_version = 0;
|
||||
if (build_context.minimum_os_version_string != "") {
|
||||
int major, minor, revision = 0;
|
||||
#if defined(GB_SYSTEM_WINDOWS)
|
||||
sscanf_s(cast(const char *)(build_context.minimum_os_version_string.text), "%d.%d.%d", &major, &minor, &revision);
|
||||
#else
|
||||
sscanf(cast(const char *)(build_context.minimum_os_version_string.text), "%d.%d.%d", &major, &minor, &revision);
|
||||
#endif
|
||||
minimum_os_version = (major*10000)+(minor*100)+revision;
|
||||
}
|
||||
add_global_constant("ODIN_MINIMUM_OS_VERSION", t_untyped_integer, exact_value_i64(minimum_os_version));
|
||||
@@ -1382,7 +1388,7 @@ gb_internal void reset_checker_context(CheckerContext *ctx, AstFile *file, Untyp
|
||||
auto type_path = ctx->type_path;
|
||||
array_clear(type_path);
|
||||
|
||||
zero_size(&ctx->pkg, gb_size_of(CheckerContext) - gb_offset_of(CheckerContext, pkg));
|
||||
gb_zero_size(&ctx->pkg, gb_size_of(CheckerContext) - gb_offset_of(CheckerContext, pkg));
|
||||
|
||||
ctx->file = nullptr;
|
||||
ctx->scope = builtin_pkg->scope;
|
||||
@@ -1466,6 +1472,7 @@ gb_internal Entity *implicit_entity_of_node(Ast *clause) {
|
||||
}
|
||||
|
||||
gb_internal Entity *entity_of_node(Ast *expr) {
|
||||
retry:;
|
||||
expr = unparen_expr(expr);
|
||||
switch (expr->kind) {
|
||||
case_ast_node(ident, Ident, expr);
|
||||
@@ -1486,6 +1493,17 @@ gb_internal Entity *entity_of_node(Ast *expr) {
|
||||
case_ast_node(ce, CallExpr, expr);
|
||||
return ce->entity_procedure_of;
|
||||
case_end;
|
||||
|
||||
case_ast_node(we, TernaryWhenExpr, expr);
|
||||
if (we->cond == nullptr) {
|
||||
break;
|
||||
}
|
||||
if (we->cond->tav.value.kind != ExactValue_Bool) {
|
||||
break;
|
||||
}
|
||||
expr = we->cond->tav.value.value_bool ? we->x : we->y;
|
||||
goto retry;
|
||||
case_end;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
@@ -1772,8 +1790,7 @@ gb_internal void add_entity_use(CheckerContext *c, Ast *identifier, Entity *enti
|
||||
if (identifier == nullptr || identifier->kind != Ast_Ident) {
|
||||
return;
|
||||
}
|
||||
Ast *empty_ident = nullptr;
|
||||
entity->identifier.compare_exchange_strong(empty_ident, identifier);
|
||||
entity->identifier.store(identifier);
|
||||
|
||||
identifier->Ident.entity = entity;
|
||||
|
||||
@@ -3528,19 +3545,19 @@ gb_internal DECL_ATTRIBUTE_PROC(proc_decl_attribute) {
|
||||
String mode = ev.value_string;
|
||||
if (mode == "none") {
|
||||
ac->optimization_mode = ProcedureOptimizationMode_None;
|
||||
} else if (mode == "favor_size") {
|
||||
ac->optimization_mode = ProcedureOptimizationMode_FavorSize;
|
||||
} else if (mode == "minimal") {
|
||||
ac->optimization_mode = ProcedureOptimizationMode_Minimal;
|
||||
error(elem, "Invalid optimization_mode 'minimal' for '%.*s', mode has been removed due to confusion, but 'none' has the same behaviour", LIT(name));
|
||||
} else if (mode == "size") {
|
||||
ac->optimization_mode = ProcedureOptimizationMode_Size;
|
||||
error(elem, "Invalid optimization_mode 'size' for '%.*s', mode has been removed due to confusion, but 'favor_size' has the same behaviour", LIT(name));
|
||||
} else if (mode == "speed") {
|
||||
ac->optimization_mode = ProcedureOptimizationMode_Speed;
|
||||
error(elem, "Invalid optimization_mode 'speed' for '%.*s', mode has been removed due to confusion, but 'favor_size' has the same behaviour", LIT(name));
|
||||
} else {
|
||||
ERROR_BLOCK();
|
||||
error(elem, "Invalid optimization_mode for '%.*s'. Valid modes:", LIT(name));
|
||||
error_line("\tnone\n");
|
||||
error_line("\tminimal\n");
|
||||
error_line("\tsize\n");
|
||||
error_line("\tspeed\n");
|
||||
error_line("\tfavor_size\n");
|
||||
}
|
||||
} else {
|
||||
error(elem, "Expected a string for '%.*s'", LIT(name));
|
||||
@@ -4568,6 +4585,8 @@ gb_internal void check_single_global_entity(Checker *c, Entity *e, DeclInfo *d)
|
||||
}
|
||||
|
||||
gb_internal void check_all_global_entities(Checker *c) {
|
||||
in_single_threaded_checker_stage = true;
|
||||
|
||||
// NOTE(bill): This must be single threaded
|
||||
// Don't bother trying
|
||||
for_array(i, c->info.entities) {
|
||||
@@ -4587,6 +4606,8 @@ gb_internal void check_all_global_entities(Checker *c) {
|
||||
(void)type_align_of(e->type);
|
||||
}
|
||||
}
|
||||
|
||||
in_single_threaded_checker_stage = false;
|
||||
}
|
||||
|
||||
|
||||
@@ -5000,9 +5021,8 @@ gb_internal void check_foreign_import_fullpaths(Checker *c) {
|
||||
|
||||
String file_str = op.value.value_string;
|
||||
file_str = string_trim_whitespace(file_str);
|
||||
|
||||
String fullpath = file_str;
|
||||
if (allow_check_foreign_filepath()) {
|
||||
if (!is_arch_wasm() || string_ends_with(file_str, str_lit(".o"))) {
|
||||
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) {
|
||||
|
||||
@@ -181,6 +181,11 @@ char const *ProcCheckedState_strings[ProcCheckedState_COUNT] {
|
||||
"Checked",
|
||||
};
|
||||
|
||||
struct VariadicReuseData {
|
||||
Type *slice_type; // ..elem_type
|
||||
i64 max_count;
|
||||
};
|
||||
|
||||
// DeclInfo is used to store information of certain declarations to allow for "any order" usage
|
||||
struct DeclInfo {
|
||||
DeclInfo * parent; // NOTE(bill): only used for procedure literals at the moment
|
||||
@@ -219,6 +224,10 @@ struct DeclInfo {
|
||||
|
||||
Array<BlockLabel> labels;
|
||||
|
||||
Array<VariadicReuseData> variadic_reuses;
|
||||
i64 variadic_reuse_max_bytes;
|
||||
i64 variadic_reuse_max_align;
|
||||
|
||||
// NOTE(bill): this is to prevent a race condition since these procedure literals can be created anywhere at any time
|
||||
struct lbModule *code_gen_module;
|
||||
};
|
||||
|
||||
@@ -70,6 +70,9 @@ enum BuiltinProcId {
|
||||
BuiltinProc_overflow_sub,
|
||||
BuiltinProc_overflow_mul,
|
||||
|
||||
BuiltinProc_add_sat,
|
||||
BuiltinProc_sub_sat,
|
||||
|
||||
BuiltinProc_sqrt,
|
||||
BuiltinProc_fused_mul_add,
|
||||
|
||||
@@ -393,6 +396,9 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
|
||||
{STR_LIT("overflow_sub"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
|
||||
{STR_LIT("overflow_mul"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
|
||||
|
||||
{STR_LIT("add_sat"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
|
||||
{STR_LIT("sub_sat"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
|
||||
|
||||
{STR_LIT("sqrt"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
|
||||
{STR_LIT("fused_mul_add"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
|
||||
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
#undef NOMINMAX
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#define GB_WINDOWS_H_INCLUDED
|
||||
#define GB_IMPLEMENTATION
|
||||
#include "gb/gb.h"
|
||||
|
||||
+76
-21
@@ -2,13 +2,6 @@
|
||||
#include <malloc.h>
|
||||
#endif
|
||||
|
||||
gb_internal gb_inline void zero_size(void *ptr, isize len) {
|
||||
memset(ptr, 0, len);
|
||||
}
|
||||
|
||||
#define zero_item(ptr) zero_size((ptr), gb_size_of(ptr))
|
||||
|
||||
|
||||
template <typename U, typename V>
|
||||
gb_internal gb_inline U bit_cast(V &v) { return reinterpret_cast<U &>(v); }
|
||||
|
||||
@@ -39,6 +32,8 @@ gb_internal void virtual_memory_init(void) {
|
||||
}
|
||||
|
||||
|
||||
gb_internal Thread *get_current_thread(void);
|
||||
|
||||
|
||||
struct MemoryBlock {
|
||||
MemoryBlock *prev;
|
||||
@@ -50,8 +45,9 @@ struct MemoryBlock {
|
||||
struct Arena {
|
||||
MemoryBlock * curr_block;
|
||||
isize minimum_block_size;
|
||||
BlockingMutex mutex;
|
||||
// BlockingMutex mutex;
|
||||
isize temp_count;
|
||||
Thread * parent_thread;
|
||||
};
|
||||
|
||||
enum { DEFAULT_MINIMUM_BLOCK_SIZE = 8ll*1024ll*1024ll };
|
||||
@@ -73,10 +69,20 @@ gb_internal isize arena_align_forward_offset(Arena *arena, isize alignment) {
|
||||
return alignment_offset;
|
||||
}
|
||||
|
||||
gb_internal void thread_init_arenas(Thread *t) {
|
||||
t->permanent_arena = gb_alloc_item(heap_allocator(), Arena);
|
||||
t->temporary_arena = gb_alloc_item(heap_allocator(), Arena);
|
||||
|
||||
t->permanent_arena->parent_thread = t;
|
||||
t->temporary_arena->parent_thread = t;
|
||||
|
||||
t->permanent_arena->minimum_block_size = DEFAULT_MINIMUM_BLOCK_SIZE;
|
||||
t->temporary_arena->minimum_block_size = DEFAULT_MINIMUM_BLOCK_SIZE;
|
||||
}
|
||||
|
||||
gb_internal void *arena_alloc(Arena *arena, isize min_size, isize alignment) {
|
||||
GB_ASSERT(gb_is_power_of_two(alignment));
|
||||
|
||||
mutex_lock(&arena->mutex);
|
||||
GB_ASSERT(arena->parent_thread == get_current_thread());
|
||||
|
||||
isize size = 0;
|
||||
if (arena->curr_block != nullptr) {
|
||||
@@ -102,9 +108,7 @@ gb_internal void *arena_alloc(Arena *arena, isize min_size, isize alignment) {
|
||||
|
||||
curr_block->used += size;
|
||||
GB_ASSERT(curr_block->used <= curr_block->size);
|
||||
|
||||
mutex_unlock(&arena->mutex);
|
||||
|
||||
|
||||
// NOTE(bill): memory will be zeroed by default due to virtual memory
|
||||
return ptr;
|
||||
}
|
||||
@@ -259,7 +263,7 @@ struct ArenaTemp {
|
||||
|
||||
ArenaTemp arena_temp_begin(Arena *arena) {
|
||||
GB_ASSERT(arena);
|
||||
MUTEX_GUARD(&arena->mutex);
|
||||
GB_ASSERT(arena->parent_thread == get_current_thread());
|
||||
|
||||
ArenaTemp temp = {};
|
||||
temp.arena = arena;
|
||||
@@ -274,7 +278,7 @@ ArenaTemp arena_temp_begin(Arena *arena) {
|
||||
void arena_temp_end(ArenaTemp const &temp) {
|
||||
GB_ASSERT(temp.arena);
|
||||
Arena *arena = temp.arena;
|
||||
MUTEX_GUARD(&arena->mutex);
|
||||
GB_ASSERT(arena->parent_thread == get_current_thread());
|
||||
|
||||
if (temp.block) {
|
||||
bool memory_block_found = false;
|
||||
@@ -310,7 +314,7 @@ void arena_temp_end(ArenaTemp const &temp) {
|
||||
void arena_temp_ignore(ArenaTemp const &temp) {
|
||||
GB_ASSERT(temp.arena);
|
||||
Arena *arena = temp.arena;
|
||||
MUTEX_GUARD(&arena->mutex);
|
||||
GB_ASSERT(arena->parent_thread == get_current_thread());
|
||||
|
||||
GB_ASSERT_MSG(arena->temp_count > 0, "double-use of arena_temp_end");
|
||||
arena->temp_count -= 1;
|
||||
@@ -370,14 +374,65 @@ gb_internal GB_ALLOCATOR_PROC(arena_allocator_proc) {
|
||||
}
|
||||
|
||||
|
||||
gb_global gb_thread_local Arena permanent_arena = {nullptr, DEFAULT_MINIMUM_BLOCK_SIZE};
|
||||
enum ThreadArenaKind : uintptr {
|
||||
ThreadArena_Permanent,
|
||||
ThreadArena_Temporary,
|
||||
};
|
||||
|
||||
gb_global Arena default_permanent_arena = {nullptr, DEFAULT_MINIMUM_BLOCK_SIZE};
|
||||
gb_global Arena default_temporary_arena = {nullptr, DEFAULT_MINIMUM_BLOCK_SIZE};
|
||||
|
||||
|
||||
gb_internal Arena *get_arena(ThreadArenaKind kind) {
|
||||
Thread *t = get_current_thread();
|
||||
switch (kind) {
|
||||
case ThreadArena_Permanent: return t ? t->permanent_arena : &default_permanent_arena;
|
||||
case ThreadArena_Temporary: return t ? t->temporary_arena : &default_temporary_arena;
|
||||
}
|
||||
GB_PANIC("INVALID ARENA KIND");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
|
||||
gb_internal GB_ALLOCATOR_PROC(thread_arena_allocator_proc) {
|
||||
void *ptr = nullptr;
|
||||
ThreadArenaKind kind = cast(ThreadArenaKind)cast(uintptr)allocator_data;
|
||||
Arena *arena = get_arena(kind);
|
||||
|
||||
switch (type) {
|
||||
case gbAllocation_Alloc:
|
||||
ptr = arena_alloc(arena, size, alignment);
|
||||
break;
|
||||
case gbAllocation_Free:
|
||||
break;
|
||||
case gbAllocation_Resize:
|
||||
if (size == 0) {
|
||||
ptr = nullptr;
|
||||
} else if (size <= old_size) {
|
||||
ptr = old_memory;
|
||||
} else {
|
||||
ptr = arena_alloc(arena, size, alignment);
|
||||
gb_memmove(ptr, old_memory, old_size);
|
||||
}
|
||||
break;
|
||||
case gbAllocation_FreeAll:
|
||||
GB_PANIC("use arena_free_all directly");
|
||||
arena_free_all(arena);
|
||||
break;
|
||||
}
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
|
||||
|
||||
gb_internal gbAllocator permanent_allocator() {
|
||||
return arena_allocator(&permanent_arena);
|
||||
return {thread_arena_allocator_proc, cast(void *)cast(uintptr)ThreadArena_Permanent};
|
||||
}
|
||||
|
||||
gb_global gb_thread_local Arena temporary_arena = {nullptr, DEFAULT_MINIMUM_BLOCK_SIZE};
|
||||
gb_internal gbAllocator temporary_allocator() {
|
||||
return arena_allocator(&temporary_arena);
|
||||
return {thread_arena_allocator_proc, cast(void *)cast(uintptr)ThreadArena_Permanent};
|
||||
}
|
||||
|
||||
|
||||
@@ -385,7 +440,7 @@ gb_internal gbAllocator temporary_allocator() {
|
||||
|
||||
|
||||
// #define TEMPORARY_ALLOCATOR_GUARD()
|
||||
#define TEMPORARY_ALLOCATOR_GUARD() TEMP_ARENA_GUARD(&temporary_arena)
|
||||
#define TEMPORARY_ALLOCATOR_GUARD() TEMP_ARENA_GUARD(get_arena(ThreadArena_Temporary))
|
||||
#define PERMANENT_ALLOCATOR_GUARD()
|
||||
|
||||
|
||||
|
||||
+5
-4
@@ -45,7 +45,7 @@ enum EntityFlag : u64 {
|
||||
EntityFlag_Value = 1ull<<11,
|
||||
EntityFlag_BitFieldField = 1ull<<12,
|
||||
|
||||
|
||||
EntityFlag_NoCapture = 1ull<<13, // #no_capture
|
||||
|
||||
EntityFlag_PolyConst = 1ull<<15,
|
||||
EntityFlag_NotExported = 1ull<<16,
|
||||
@@ -133,9 +133,7 @@ enum EntityConstantFlags : u32 {
|
||||
enum ProcedureOptimizationMode : u8 {
|
||||
ProcedureOptimizationMode_Default,
|
||||
ProcedureOptimizationMode_None,
|
||||
ProcedureOptimizationMode_Minimal,
|
||||
ProcedureOptimizationMode_Size,
|
||||
ProcedureOptimizationMode_Speed,
|
||||
ProcedureOptimizationMode_FavorSize,
|
||||
};
|
||||
|
||||
|
||||
@@ -338,6 +336,9 @@ gb_internal Entity *alloc_entity(EntityKind kind, Scope *scope, Token token, Typ
|
||||
entity->token = token;
|
||||
entity->type = type;
|
||||
entity->id = 1 + global_entity_id.fetch_add(1);
|
||||
if (token.pos.file_id) {
|
||||
entity->file = thread_safe_get_ast_file_from_id(token.pos.file_id);
|
||||
}
|
||||
return entity;
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -2534,7 +2534,7 @@ gb_inline void const *gb_pointer_add_const(void const *ptr, isize bytes) {
|
||||
gb_inline void const *gb_pointer_sub_const(void const *ptr, isize bytes) { return cast(void const *)(cast(u8 const *)ptr - bytes); }
|
||||
gb_inline isize gb_pointer_diff (void const *begin, void const *end) { return cast(isize)(cast(u8 const *)end - cast(u8 const *)begin); }
|
||||
|
||||
gb_inline void gb_zero_size(void *ptr, isize size) { gb_memset(ptr, 0, size); }
|
||||
gb_inline void gb_zero_size(void *ptr, isize size) { memset(ptr, 0, size); }
|
||||
|
||||
|
||||
#if defined(_MSC_VER) && !defined(__clang__)
|
||||
|
||||
+63
-20
@@ -85,6 +85,20 @@ gb_internal i32 linker_stage(LinkerData *gen) {
|
||||
if (extra_linker_flags.len != 0) {
|
||||
lib_str = gb_string_append_fmt(lib_str, " %.*s", LIT(extra_linker_flags));
|
||||
}
|
||||
|
||||
for_array(i, e->LibraryName.paths) {
|
||||
String lib = e->LibraryName.paths[i];
|
||||
|
||||
if (lib.len == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!string_ends_with(lib, str_lit(".o"))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
inputs = gb_string_append_fmt(inputs, " \"%.*s\"", LIT(lib));
|
||||
}
|
||||
}
|
||||
|
||||
if (build_context.metrics.os == TargetOs_orca) {
|
||||
@@ -208,7 +222,21 @@ gb_internal i32 linker_stage(LinkerData *gen) {
|
||||
if (has_asm_extension(lib)) {
|
||||
if (!string_set_update(&asm_files, lib)) {
|
||||
String asm_file = lib;
|
||||
String obj_file = concatenate_strings(permanent_allocator(), asm_file, str_lit(".obj"));
|
||||
String obj_file = {};
|
||||
String temp_dir = temporary_directory(temporary_allocator());
|
||||
if (temp_dir.len != 0) {
|
||||
String filename = filename_without_directory(asm_file);
|
||||
|
||||
gbString str = gb_string_make(heap_allocator(), "");
|
||||
str = gb_string_append_length(str, temp_dir.text, temp_dir.len);
|
||||
str = gb_string_appendc(str, "/");
|
||||
str = gb_string_append_length(str, filename.text, filename.len);
|
||||
str = gb_string_append_fmt(str, "-%p.obj", asm_file.text);
|
||||
obj_file = make_string_c(str);
|
||||
} else {
|
||||
obj_file = concatenate_strings(permanent_allocator(), asm_file, str_lit(".obj"));
|
||||
}
|
||||
|
||||
String obj_format = str_lit("win64");
|
||||
#if defined(GB_ARCH_32_BIT)
|
||||
obj_format = str_lit("win32");
|
||||
@@ -277,30 +305,30 @@ gb_internal i32 linker_stage(LinkerData *gen) {
|
||||
defer (gb_free(heap_allocator(), windows_sdk_bin_path.text));
|
||||
|
||||
if (!build_context.use_lld) { // msvc
|
||||
String res_path = {};
|
||||
String res_path = quote_path(heap_allocator(), build_context.build_paths[BuildPath_RES]);
|
||||
String rc_path = quote_path(heap_allocator(), build_context.build_paths[BuildPath_RC]);
|
||||
defer (gb_free(heap_allocator(), res_path.text));
|
||||
defer (gb_free(heap_allocator(), rc_path.text));
|
||||
|
||||
// TODO(Jeroen): Add ability to reuse .res file instead of recompiling, if `-resource:file.res` is given.
|
||||
if (build_context.has_resource) {
|
||||
String temp_res_path = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_RES]);
|
||||
res_path = concatenate3_strings(heap_allocator(), str_lit("\""), temp_res_path, str_lit("\""));
|
||||
gb_free(heap_allocator(), temp_res_path.text);
|
||||
if (build_context.build_paths[BuildPath_RC].basename == "") {
|
||||
debugf("Using precompiled resource %.*s\n", LIT(res_path));
|
||||
} else {
|
||||
debugf("Compiling resource %.*s\n", LIT(res_path));
|
||||
|
||||
String temp_rc_path = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_RC]);
|
||||
String rc_path = concatenate3_strings(heap_allocator(), str_lit("\""), temp_rc_path, str_lit("\""));
|
||||
gb_free(heap_allocator(), temp_rc_path.text);
|
||||
defer (gb_free(heap_allocator(), rc_path.text));
|
||||
result = system_exec_command_line_app("msvc-link",
|
||||
"\"%.*src.exe\" /nologo /fo %.*s %.*s",
|
||||
LIT(windows_sdk_bin_path),
|
||||
LIT(res_path),
|
||||
LIT(rc_path)
|
||||
);
|
||||
|
||||
result = system_exec_command_line_app("msvc-link",
|
||||
"\"%.*src.exe\" /nologo /fo %.*s %.*s",
|
||||
LIT(windows_sdk_bin_path),
|
||||
LIT(res_path),
|
||||
LIT(rc_path)
|
||||
);
|
||||
|
||||
if (result) {
|
||||
return result;
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
res_path = {};
|
||||
}
|
||||
|
||||
String linker_name = str_lit("link.exe");
|
||||
@@ -399,7 +427,22 @@ gb_internal i32 linker_stage(LinkerData *gen) {
|
||||
continue; // already handled
|
||||
}
|
||||
String asm_file = lib;
|
||||
String obj_file = concatenate_strings(permanent_allocator(), asm_file, str_lit(".o"));
|
||||
String obj_file = {};
|
||||
|
||||
String temp_dir = temporary_directory(temporary_allocator());
|
||||
if (temp_dir.len != 0) {
|
||||
String filename = filename_without_directory(asm_file);
|
||||
|
||||
gbString str = gb_string_make(heap_allocator(), "");
|
||||
str = gb_string_append_length(str, temp_dir.text, temp_dir.len);
|
||||
str = gb_string_appendc(str, "/");
|
||||
str = gb_string_append_length(str, filename.text, filename.len);
|
||||
str = gb_string_append_fmt(str, "-%p.o", asm_file.text);
|
||||
obj_file = make_string_c(str);
|
||||
} else {
|
||||
obj_file = concatenate_strings(permanent_allocator(), asm_file, str_lit(".o"));
|
||||
}
|
||||
|
||||
String obj_format;
|
||||
#if defined(GB_ARCH_64_BIT)
|
||||
if (is_osx) {
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
#ifndef LLVM_C_ANALYSIS_H
|
||||
#define LLVM_C_ANALYSIS_H
|
||||
|
||||
#include "llvm-c/ExternC.h"
|
||||
#include "llvm-c/Types.h"
|
||||
#include "ExternC.h"
|
||||
#include "Types.h"
|
||||
|
||||
LLVM_C_EXTERN_C_BEGIN
|
||||
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
#ifndef LLVM_C_BITREADER_H
|
||||
#define LLVM_C_BITREADER_H
|
||||
|
||||
#include "llvm-c/ExternC.h"
|
||||
#include "llvm-c/Types.h"
|
||||
#include "ExternC.h"
|
||||
#include "Types.h"
|
||||
|
||||
LLVM_C_EXTERN_C_BEGIN
|
||||
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
#ifndef LLVM_C_BITWRITER_H
|
||||
#define LLVM_C_BITWRITER_H
|
||||
|
||||
#include "llvm-c/ExternC.h"
|
||||
#include "llvm-c/Types.h"
|
||||
#include "ExternC.h"
|
||||
#include "Types.h"
|
||||
|
||||
LLVM_C_EXTERN_C_BEGIN
|
||||
|
||||
|
||||
+2
-2
@@ -14,8 +14,8 @@
|
||||
#ifndef LLVM_C_COMDAT_H
|
||||
#define LLVM_C_COMDAT_H
|
||||
|
||||
#include "llvm-c/ExternC.h"
|
||||
#include "llvm-c/Types.h"
|
||||
#include "ExternC.h"
|
||||
#include "Types.h"
|
||||
|
||||
LLVM_C_EXTERN_C_BEGIN
|
||||
|
||||
|
||||
@@ -27,18 +27,20 @@
|
||||
LLVM_ASM_PARSER(AArch64)
|
||||
LLVM_ASM_PARSER(AMDGPU)
|
||||
LLVM_ASM_PARSER(ARM)
|
||||
LLVM_ASM_PARSER(AVR)
|
||||
LLVM_ASM_PARSER(BPF)
|
||||
LLVM_ASM_PARSER(Hexagon)
|
||||
LLVM_ASM_PARSER(Lanai)
|
||||
LLVM_ASM_PARSER(LoongArch)
|
||||
LLVM_ASM_PARSER(Mips)
|
||||
LLVM_ASM_PARSER(MSP430)
|
||||
LLVM_ASM_PARSER(PowerPC)
|
||||
LLVM_ASM_PARSER(RISCV)
|
||||
LLVM_ASM_PARSER(Sparc)
|
||||
LLVM_ASM_PARSER(SystemZ)
|
||||
LLVM_ASM_PARSER(VE)
|
||||
LLVM_ASM_PARSER(WebAssembly)
|
||||
LLVM_ASM_PARSER(X86)
|
||||
LLVM_ASM_PARSER(AVR)
|
||||
|
||||
|
||||
#undef LLVM_ASM_PARSER
|
||||
|
||||
@@ -27,9 +27,11 @@
|
||||
LLVM_ASM_PRINTER(AArch64)
|
||||
LLVM_ASM_PRINTER(AMDGPU)
|
||||
LLVM_ASM_PRINTER(ARM)
|
||||
LLVM_ASM_PRINTER(AVR)
|
||||
LLVM_ASM_PRINTER(BPF)
|
||||
LLVM_ASM_PRINTER(Hexagon)
|
||||
LLVM_ASM_PRINTER(Lanai)
|
||||
LLVM_ASM_PRINTER(LoongArch)
|
||||
LLVM_ASM_PRINTER(Mips)
|
||||
LLVM_ASM_PRINTER(MSP430)
|
||||
LLVM_ASM_PRINTER(NVPTX)
|
||||
@@ -37,10 +39,10 @@ LLVM_ASM_PRINTER(PowerPC)
|
||||
LLVM_ASM_PRINTER(RISCV)
|
||||
LLVM_ASM_PRINTER(Sparc)
|
||||
LLVM_ASM_PRINTER(SystemZ)
|
||||
LLVM_ASM_PRINTER(VE)
|
||||
LLVM_ASM_PRINTER(WebAssembly)
|
||||
LLVM_ASM_PRINTER(X86)
|
||||
LLVM_ASM_PRINTER(XCore)
|
||||
LLVM_ASM_PRINTER(AVR)
|
||||
|
||||
|
||||
#undef LLVM_ASM_PRINTER
|
||||
|
||||
@@ -27,19 +27,21 @@
|
||||
LLVM_DISASSEMBLER(AArch64)
|
||||
LLVM_DISASSEMBLER(AMDGPU)
|
||||
LLVM_DISASSEMBLER(ARM)
|
||||
LLVM_DISASSEMBLER(AVR)
|
||||
LLVM_DISASSEMBLER(BPF)
|
||||
LLVM_DISASSEMBLER(Hexagon)
|
||||
LLVM_DISASSEMBLER(Lanai)
|
||||
LLVM_DISASSEMBLER(LoongArch)
|
||||
LLVM_DISASSEMBLER(Mips)
|
||||
LLVM_DISASSEMBLER(MSP430)
|
||||
LLVM_DISASSEMBLER(PowerPC)
|
||||
LLVM_DISASSEMBLER(RISCV)
|
||||
LLVM_DISASSEMBLER(Sparc)
|
||||
LLVM_DISASSEMBLER(SystemZ)
|
||||
LLVM_DISASSEMBLER(VE)
|
||||
LLVM_DISASSEMBLER(WebAssembly)
|
||||
LLVM_DISASSEMBLER(X86)
|
||||
LLVM_DISASSEMBLER(XCore)
|
||||
LLVM_DISASSEMBLER(AVR)
|
||||
|
||||
|
||||
#undef LLVM_DISASSEMBLER
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
/*===----- llvm/Config/TargetExegesis.def - LLVM Target Exegesis-*- C++ -*-===*\
|
||||
|* *|
|
||||
|* Part of the LLVM Project, under the Apache License v2.0 with LLVM *|
|
||||
|* Exceptions. *|
|
||||
|* See https://llvm.org/LICENSE.txt for license information. *|
|
||||
|* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception *|
|
||||
|* *|
|
||||
|*===----------------------------------------------------------------------===*|
|
||||
|* *|
|
||||
|* This file enumerates all of the target's of llvm-exegesis *|
|
||||
|* supported by this build of LLVM. Clients of this file should define *|
|
||||
|* the LLVM_EXEGISIS macro to be a function-like macro with a *|
|
||||
|* single parameter (the name of the target whose assembly can be *|
|
||||
|* generated); including this file will then enumerate all of the *|
|
||||
|* targets with target llvm-exegsis support. *|
|
||||
|* *|
|
||||
|* The set of targets supported by LLVM is generated at configuration *|
|
||||
|* time, at which point this header is generated. Do not modify this *|
|
||||
|* header directly. *|
|
||||
|* *|
|
||||
\*===----------------------------------------------------------------------===*/
|
||||
|
||||
#ifndef LLVM_EXEGESIS
|
||||
# error Please define the macro LLVM_EXEGESIS(TargetName)
|
||||
#endif
|
||||
|
||||
LLVM_EXEGESIS(AArch64)
|
||||
LLVM_EXEGESIS(Mips)
|
||||
LLVM_EXEGESIS(PowerPC)
|
||||
LLVM_EXEGESIS(X86)
|
||||
|
||||
|
||||
#undef LLVM_EXEGESIS
|
||||
@@ -0,0 +1,32 @@
|
||||
/*===------ llvm/Config/TargetMCAs.def - LLVM Target MCAs -------*- C++ -*-===*\
|
||||
|* *|
|
||||
|* Part of the LLVM Project, under the Apache License v2.0 with LLVM *|
|
||||
|* Exceptions. *|
|
||||
|* See https://llvm.org/LICENSE.txt for license information. *|
|
||||
|* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception *|
|
||||
|* *|
|
||||
|*===----------------------------------------------------------------------===*|
|
||||
|* *|
|
||||
|* This file enumerates all of the target MCAs *|
|
||||
|* supported by this build of LLVM. Clients of this file should define *|
|
||||
|* the LLVM_TARGETMCA macro to be a function-like macro with a *|
|
||||
|* single parameter (the name of the target whose assembly can be *|
|
||||
|* generated); including this file will then enumerate all of the *|
|
||||
|* targets with target MCAs. *|
|
||||
|* *|
|
||||
|* The set of targets supported by LLVM is generated at configuration *|
|
||||
|* time, at which point this header is generated. Do not modify this *|
|
||||
|* header directly. *|
|
||||
|* *|
|
||||
\*===----------------------------------------------------------------------===*/
|
||||
|
||||
#ifndef LLVM_TARGETMCA
|
||||
# error Please define the macro LLVM_TARGETMCA(TargetName)
|
||||
#endif
|
||||
|
||||
LLVM_TARGETMCA(AMDGPU)
|
||||
LLVM_TARGETMCA(RISCV)
|
||||
LLVM_TARGETMCA(X86)
|
||||
|
||||
|
||||
#undef LLVM_TARGETMCA
|
||||
@@ -26,9 +26,11 @@
|
||||
LLVM_TARGET(AArch64)
|
||||
LLVM_TARGET(AMDGPU)
|
||||
LLVM_TARGET(ARM)
|
||||
LLVM_TARGET(AVR)
|
||||
LLVM_TARGET(BPF)
|
||||
LLVM_TARGET(Hexagon)
|
||||
LLVM_TARGET(Lanai)
|
||||
LLVM_TARGET(LoongArch)
|
||||
LLVM_TARGET(Mips)
|
||||
LLVM_TARGET(MSP430)
|
||||
LLVM_TARGET(NVPTX)
|
||||
@@ -36,10 +38,10 @@ LLVM_TARGET(PowerPC)
|
||||
LLVM_TARGET(RISCV)
|
||||
LLVM_TARGET(Sparc)
|
||||
LLVM_TARGET(SystemZ)
|
||||
LLVM_TARGET(VE)
|
||||
LLVM_TARGET(WebAssembly)
|
||||
LLVM_TARGET(X86)
|
||||
LLVM_TARGET(XCore)
|
||||
LLVM_TARGET(AVR)
|
||||
|
||||
|
||||
#undef LLVM_TARGET
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/*===------- llvm-c/Config//abi-breaking.h - llvm configuration -------*- C -*-===*/
|
||||
/*===------- llvm/Config/abi-breaking.h - llvm configuration -------*- C -*-===*/
|
||||
/* */
|
||||
/* Part of the LLVM Project, under the Apache License v2.0 with LLVM */
|
||||
/* Exceptions. */
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
/* Allow selectively disabling link-time mismatch checking so that header-only
|
||||
ADT content from LLVM can be used without linking libSupport. */
|
||||
#if !LLVM_DISABLE_ABI_BREAKING_CHECKS_ENFORCING
|
||||
#if !defined(LLVM_DISABLE_ABI_BREAKING_CHECKS_ENFORCING) || !LLVM_DISABLE_ABI_BREAKING_CHECKS_ENFORCING
|
||||
|
||||
// ABI_BREAKING_CHECKS protection: provides link-time failure when clients build
|
||||
// mismatch with LLVM
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/*===------- llvm-c/Config//llvm-config.h - llvm configuration -------*- C -*-===*/
|
||||
/*===------- llvm/Config/llvm-config.h - llvm configuration -------*- C -*-===*/
|
||||
/* */
|
||||
/* Part of the LLVM Project, under the Apache License v2.0 with LLVM */
|
||||
/* Exceptions. */
|
||||
@@ -17,10 +17,8 @@
|
||||
/* Define if LLVM_ENABLE_DUMP is enabled */
|
||||
/* #undef LLVM_ENABLE_DUMP */
|
||||
|
||||
/* Define if we link Polly to the tools */
|
||||
/* #undef LINK_POLLY_INTO_TOOLS */
|
||||
|
||||
/* Target triple LLVM will generate code for by default */
|
||||
/* Doesn't use `cmakedefine` because it is allowed to be empty. */
|
||||
#define LLVM_DEFAULT_TARGET_TRIPLE "x86_64-pc-windows-msvc"
|
||||
|
||||
/* Define if threads enabled */
|
||||
@@ -53,6 +51,84 @@
|
||||
/* LLVM name for the native target MC init function, if available */
|
||||
#define LLVM_NATIVE_TARGETMC LLVMInitializeX86TargetMC
|
||||
|
||||
/* LLVM name for the native target MCA init function, if available */
|
||||
/* #undef LLVM_NATIVE_TARGETMCA */
|
||||
|
||||
/* Define if the AArch64 target is built in */
|
||||
#define LLVM_HAS_AARCH64_TARGET 1
|
||||
|
||||
/* Define if the AMDGPU target is built in */
|
||||
#define LLVM_HAS_AMDGPU_TARGET 1
|
||||
|
||||
/* Define if the ARC target is built in */
|
||||
#define LLVM_HAS_ARC_TARGET 0
|
||||
|
||||
/* Define if the ARM target is built in */
|
||||
#define LLVM_HAS_ARM_TARGET 1
|
||||
|
||||
/* Define if the AVR target is built in */
|
||||
#define LLVM_HAS_AVR_TARGET 1
|
||||
|
||||
/* Define if the BPF target is built in */
|
||||
#define LLVM_HAS_BPF_TARGET 1
|
||||
|
||||
/* Define if the CSKY target is built in */
|
||||
#define LLVM_HAS_CSKY_TARGET 0
|
||||
|
||||
/* Define if the DirectX target is built in */
|
||||
#define LLVM_HAS_DIRECTX_TARGET 0
|
||||
|
||||
/* Define if the Hexagon target is built in */
|
||||
#define LLVM_HAS_HEXAGON_TARGET 1
|
||||
|
||||
/* Define if the Lanai target is built in */
|
||||
#define LLVM_HAS_LANAI_TARGET 1
|
||||
|
||||
/* Define if the LoongArch target is built in */
|
||||
#define LLVM_HAS_LOONGARCH_TARGET 1
|
||||
|
||||
/* Define if the M68k target is built in */
|
||||
#define LLVM_HAS_M68K_TARGET 0
|
||||
|
||||
/* Define if the Mips target is built in */
|
||||
#define LLVM_HAS_MIPS_TARGET 1
|
||||
|
||||
/* Define if the MSP430 target is built in */
|
||||
#define LLVM_HAS_MSP430_TARGET 1
|
||||
|
||||
/* Define if the NVPTX target is built in */
|
||||
#define LLVM_HAS_NVPTX_TARGET 1
|
||||
|
||||
/* Define if the PowerPC target is built in */
|
||||
#define LLVM_HAS_POWERPC_TARGET 1
|
||||
|
||||
/* Define if the RISCV target is built in */
|
||||
#define LLVM_HAS_RISCV_TARGET 1
|
||||
|
||||
/* Define if the Sparc target is built in */
|
||||
#define LLVM_HAS_SPARC_TARGET 1
|
||||
|
||||
/* Define if the SPIRV target is built in */
|
||||
#define LLVM_HAS_SPIRV_TARGET 0
|
||||
|
||||
/* Define if the SystemZ target is built in */
|
||||
#define LLVM_HAS_SYSTEMZ_TARGET 1
|
||||
|
||||
/* Define if the VE target is built in */
|
||||
#define LLVM_HAS_VE_TARGET 1
|
||||
|
||||
/* Define if the WebAssembly target is built in */
|
||||
#define LLVM_HAS_WEBASSEMBLY_TARGET 1
|
||||
|
||||
/* Define if the X86 target is built in */
|
||||
#define LLVM_HAS_X86_TARGET 1
|
||||
|
||||
/* Define if the XCore target is built in */
|
||||
#define LLVM_HAS_XCORE_TARGET 1
|
||||
|
||||
/* Define if the Xtensa target is built in */
|
||||
#define LLVM_HAS_XTENSA_TARGET 0
|
||||
|
||||
/* Define if this is Unixish platform */
|
||||
/* #undef LLVM_ON_UNIX */
|
||||
|
||||
@@ -66,20 +142,60 @@
|
||||
#define LLVM_USE_PERF 0
|
||||
|
||||
/* Major version of the LLVM API */
|
||||
#define LLVM_VERSION_MAJOR 17
|
||||
#define LLVM_VERSION_MAJOR 18
|
||||
|
||||
/* Minor version of the LLVM API */
|
||||
#define LLVM_VERSION_MINOR 0
|
||||
#define LLVM_VERSION_MINOR 1
|
||||
|
||||
/* Patch version of the LLVM API */
|
||||
#define LLVM_VERSION_PATCH 1
|
||||
#define LLVM_VERSION_PATCH 8
|
||||
|
||||
/* LLVM version string */
|
||||
#define LLVM_VERSION_STRING "17.0.1"
|
||||
#define LLVM_VERSION_STRING "18.1.8"
|
||||
|
||||
/* Whether LLVM records statistics for use with GetStatistics(),
|
||||
* PrintStatistics() or PrintStatisticsJSON()
|
||||
*/
|
||||
#define LLVM_FORCE_ENABLE_STATS 0
|
||||
|
||||
/* Define if we have z3 and want to build it */
|
||||
/* #undef LLVM_WITH_Z3 */
|
||||
|
||||
/* Define if we have curl and want to use it */
|
||||
/* #undef LLVM_ENABLE_CURL */
|
||||
|
||||
/* Define if we have cpp-httplib and want to use it */
|
||||
/* #undef LLVM_ENABLE_HTTPLIB */
|
||||
|
||||
/* Define if zlib compression is available */
|
||||
#define LLVM_ENABLE_ZLIB 0
|
||||
|
||||
/* Define if zstd compression is available */
|
||||
#define LLVM_ENABLE_ZSTD 0
|
||||
|
||||
/* Define if LLVM is using tflite */
|
||||
/* #undef LLVM_HAVE_TFLITE */
|
||||
|
||||
/* Define to 1 if you have the <sysexits.h> header file. */
|
||||
/* #undef HAVE_SYSEXITS_H */
|
||||
|
||||
/* Define if building libLLVM shared library */
|
||||
/* #undef LLVM_BUILD_LLVM_DYLIB */
|
||||
|
||||
/* Define if building LLVM with BUILD_SHARED_LIBS */
|
||||
/* #undef LLVM_BUILD_SHARED_LIBS */
|
||||
|
||||
/* Define if building LLVM with LLVM_FORCE_USE_OLD_TOOLCHAIN_LIBS */
|
||||
/* #undef LLVM_FORCE_USE_OLD_TOOLCHAIN */
|
||||
|
||||
/* Define if llvm_unreachable should be optimized with undefined behavior
|
||||
* in non assert builds */
|
||||
#define LLVM_UNREACHABLE_OPTIMIZE 1
|
||||
|
||||
/* Define to 1 if you have the DIA SDK installed, and to 0 if you don't. */
|
||||
#define LLVM_ENABLE_DIA_SDK 1
|
||||
|
||||
/* Define if plugins enabled */
|
||||
/* #undef LLVM_ENABLE_PLUGINS */
|
||||
|
||||
#endif
|
||||
|
||||
+247
-26
@@ -15,11 +15,11 @@
|
||||
#ifndef LLVM_C_CORE_H
|
||||
#define LLVM_C_CORE_H
|
||||
|
||||
#include "llvm-c/Deprecated.h"
|
||||
#include "llvm-c/ErrorHandling.h"
|
||||
#include "llvm-c/ExternC.h"
|
||||
#include "Deprecated.h"
|
||||
#include "ErrorHandling.h"
|
||||
#include "ExternC.h"
|
||||
|
||||
#include "llvm-c/Types.h"
|
||||
#include "Types.h"
|
||||
|
||||
LLVM_C_EXTERN_C_BEGIN
|
||||
|
||||
@@ -216,7 +216,6 @@ typedef enum {
|
||||
LLVMColdCallConv = 9,
|
||||
LLVMGHCCallConv = 10,
|
||||
LLVMHiPECallConv = 11,
|
||||
LLVMWebKitJSCallConv = 12,
|
||||
LLVMAnyRegCallConv = 13,
|
||||
LLVMPreserveMostCallConv = 14,
|
||||
LLVMPreserveAllCallConv = 15,
|
||||
@@ -468,8 +467,45 @@ enum {
|
||||
LLVMAttributeFunctionIndex = -1,
|
||||
};
|
||||
|
||||
/**
|
||||
* Tail call kind for LLVMSetTailCallKind and LLVMGetTailCallKind.
|
||||
*
|
||||
* Note that 'musttail' implies 'tail'.
|
||||
*
|
||||
* @see CallInst::TailCallKind
|
||||
*/
|
||||
typedef enum {
|
||||
LLVMTailCallKindNone = 0,
|
||||
LLVMTailCallKindTail = 1,
|
||||
LLVMTailCallKindMustTail = 2,
|
||||
LLVMTailCallKindNoTail = 3,
|
||||
} LLVMTailCallKind;
|
||||
|
||||
typedef unsigned LLVMAttributeIndex;
|
||||
|
||||
enum {
|
||||
LLVMFastMathAllowReassoc = (1 << 0),
|
||||
LLVMFastMathNoNaNs = (1 << 1),
|
||||
LLVMFastMathNoInfs = (1 << 2),
|
||||
LLVMFastMathNoSignedZeros = (1 << 3),
|
||||
LLVMFastMathAllowReciprocal = (1 << 4),
|
||||
LLVMFastMathAllowContract = (1 << 5),
|
||||
LLVMFastMathApproxFunc = (1 << 6),
|
||||
LLVMFastMathNone = 0,
|
||||
LLVMFastMathAll = LLVMFastMathAllowReassoc | LLVMFastMathNoNaNs |
|
||||
LLVMFastMathNoInfs | LLVMFastMathNoSignedZeros |
|
||||
LLVMFastMathAllowReciprocal | LLVMFastMathAllowContract |
|
||||
LLVMFastMathApproxFunc,
|
||||
};
|
||||
|
||||
/**
|
||||
* Flags to indicate what fast-math-style optimizations are allowed
|
||||
* on operations.
|
||||
*
|
||||
* See https://llvm.org/docs/LangRef.html#fast-math-flags
|
||||
*/
|
||||
typedef unsigned LLVMFastMathFlags;
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
@@ -890,12 +926,58 @@ void LLVMAppendModuleInlineAsm(LLVMModuleRef M, const char *Asm, size_t Len);
|
||||
*
|
||||
* @see InlineAsm::get()
|
||||
*/
|
||||
LLVMValueRef LLVMGetInlineAsm(LLVMTypeRef Ty, char *AsmString,
|
||||
size_t AsmStringSize, char *Constraints,
|
||||
LLVMValueRef LLVMGetInlineAsm(LLVMTypeRef Ty, const char *AsmString,
|
||||
size_t AsmStringSize, const char *Constraints,
|
||||
size_t ConstraintsSize, LLVMBool HasSideEffects,
|
||||
LLVMBool IsAlignStack,
|
||||
LLVMInlineAsmDialect Dialect, LLVMBool CanThrow);
|
||||
|
||||
/**
|
||||
* Get the template string used for an inline assembly snippet
|
||||
*
|
||||
*/
|
||||
const char *LLVMGetInlineAsmAsmString(LLVMValueRef InlineAsmVal, size_t *Len);
|
||||
|
||||
/**
|
||||
* Get the raw constraint string for an inline assembly snippet
|
||||
*
|
||||
*/
|
||||
const char *LLVMGetInlineAsmConstraintString(LLVMValueRef InlineAsmVal,
|
||||
size_t *Len);
|
||||
|
||||
/**
|
||||
* Get the dialect used by the inline asm snippet
|
||||
*
|
||||
*/
|
||||
LLVMInlineAsmDialect LLVMGetInlineAsmDialect(LLVMValueRef InlineAsmVal);
|
||||
|
||||
/**
|
||||
* Get the function type of the inline assembly snippet. The same type that
|
||||
* was passed into LLVMGetInlineAsm originally
|
||||
*
|
||||
* @see LLVMGetInlineAsm
|
||||
*
|
||||
*/
|
||||
LLVMTypeRef LLVMGetInlineAsmFunctionType(LLVMValueRef InlineAsmVal);
|
||||
|
||||
/**
|
||||
* Get if the inline asm snippet has side effects
|
||||
*
|
||||
*/
|
||||
LLVMBool LLVMGetInlineAsmHasSideEffects(LLVMValueRef InlineAsmVal);
|
||||
|
||||
/**
|
||||
* Get if the inline asm snippet needs an aligned stack
|
||||
*
|
||||
*/
|
||||
LLVMBool LLVMGetInlineAsmNeedsAlignedStack(LLVMValueRef InlineAsmVal);
|
||||
|
||||
/**
|
||||
* Get if the inline asm snippet may unwind the stack
|
||||
*
|
||||
*/
|
||||
LLVMBool LLVMGetInlineAsmCanUnwind(LLVMValueRef InlineAsmVal);
|
||||
|
||||
/**
|
||||
* Obtain the context to which this module is associated.
|
||||
*
|
||||
@@ -2216,45 +2298,26 @@ LLVMValueRef LLVMConstNUWSub(LLVMValueRef LHSConstant, LLVMValueRef RHSConstant)
|
||||
LLVMValueRef LLVMConstMul(LLVMValueRef LHSConstant, LLVMValueRef RHSConstant);
|
||||
LLVMValueRef LLVMConstNSWMul(LLVMValueRef LHSConstant, LLVMValueRef RHSConstant);
|
||||
LLVMValueRef LLVMConstNUWMul(LLVMValueRef LHSConstant, LLVMValueRef RHSConstant);
|
||||
LLVMValueRef LLVMConstAnd(LLVMValueRef LHSConstant, LLVMValueRef RHSConstant);
|
||||
LLVMValueRef LLVMConstOr(LLVMValueRef LHSConstant, LLVMValueRef RHSConstant);
|
||||
LLVMValueRef LLVMConstXor(LLVMValueRef LHSConstant, LLVMValueRef RHSConstant);
|
||||
LLVMValueRef LLVMConstICmp(LLVMIntPredicate Predicate,
|
||||
LLVMValueRef LHSConstant, LLVMValueRef RHSConstant);
|
||||
LLVMValueRef LLVMConstFCmp(LLVMRealPredicate Predicate,
|
||||
LLVMValueRef LHSConstant, LLVMValueRef RHSConstant);
|
||||
LLVMValueRef LLVMConstShl(LLVMValueRef LHSConstant, LLVMValueRef RHSConstant);
|
||||
LLVMValueRef LLVMConstLShr(LLVMValueRef LHSConstant, LLVMValueRef RHSConstant);
|
||||
LLVMValueRef LLVMConstAShr(LLVMValueRef LHSConstant, LLVMValueRef RHSConstant);
|
||||
LLVMValueRef LLVMConstGEP2(LLVMTypeRef Ty, LLVMValueRef ConstantVal,
|
||||
LLVMValueRef *ConstantIndices, unsigned NumIndices);
|
||||
LLVMValueRef LLVMConstInBoundsGEP2(LLVMTypeRef Ty, LLVMValueRef ConstantVal,
|
||||
LLVMValueRef *ConstantIndices,
|
||||
unsigned NumIndices);
|
||||
LLVMValueRef LLVMConstTrunc(LLVMValueRef ConstantVal, LLVMTypeRef ToType);
|
||||
LLVMValueRef LLVMConstSExt(LLVMValueRef ConstantVal, LLVMTypeRef ToType);
|
||||
LLVMValueRef LLVMConstZExt(LLVMValueRef ConstantVal, LLVMTypeRef ToType);
|
||||
LLVMValueRef LLVMConstFPTrunc(LLVMValueRef ConstantVal, LLVMTypeRef ToType);
|
||||
LLVMValueRef LLVMConstFPExt(LLVMValueRef ConstantVal, LLVMTypeRef ToType);
|
||||
LLVMValueRef LLVMConstUIToFP(LLVMValueRef ConstantVal, LLVMTypeRef ToType);
|
||||
LLVMValueRef LLVMConstSIToFP(LLVMValueRef ConstantVal, LLVMTypeRef ToType);
|
||||
LLVMValueRef LLVMConstFPToUI(LLVMValueRef ConstantVal, LLVMTypeRef ToType);
|
||||
LLVMValueRef LLVMConstFPToSI(LLVMValueRef ConstantVal, LLVMTypeRef ToType);
|
||||
LLVMValueRef LLVMConstPtrToInt(LLVMValueRef ConstantVal, LLVMTypeRef ToType);
|
||||
LLVMValueRef LLVMConstIntToPtr(LLVMValueRef ConstantVal, LLVMTypeRef ToType);
|
||||
LLVMValueRef LLVMConstBitCast(LLVMValueRef ConstantVal, LLVMTypeRef ToType);
|
||||
LLVMValueRef LLVMConstAddrSpaceCast(LLVMValueRef ConstantVal, LLVMTypeRef ToType);
|
||||
LLVMValueRef LLVMConstZExtOrBitCast(LLVMValueRef ConstantVal,
|
||||
LLVMTypeRef ToType);
|
||||
LLVMValueRef LLVMConstSExtOrBitCast(LLVMValueRef ConstantVal,
|
||||
LLVMTypeRef ToType);
|
||||
LLVMValueRef LLVMConstTruncOrBitCast(LLVMValueRef ConstantVal,
|
||||
LLVMTypeRef ToType);
|
||||
LLVMValueRef LLVMConstPointerCast(LLVMValueRef ConstantVal,
|
||||
LLVMTypeRef ToType);
|
||||
LLVMValueRef LLVMConstIntCast(LLVMValueRef ConstantVal, LLVMTypeRef ToType,
|
||||
LLVMBool isSigned);
|
||||
LLVMValueRef LLVMConstFPCast(LLVMValueRef ConstantVal, LLVMTypeRef ToType);
|
||||
LLVMValueRef LLVMConstExtractElement(LLVMValueRef VectorConstant,
|
||||
LLVMValueRef IndexConstant);
|
||||
LLVMValueRef LLVMConstInsertElement(LLVMValueRef VectorConstant,
|
||||
@@ -2960,6 +3023,74 @@ LLVMValueRef LLVMMDNodeInContext(LLVMContextRef C, LLVMValueRef *Vals,
|
||||
/** Deprecated: Use LLVMMDNodeInContext2 instead. */
|
||||
LLVMValueRef LLVMMDNode(LLVMValueRef *Vals, unsigned Count);
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup LLVMCCoreOperandBundle Operand Bundles
|
||||
*
|
||||
* Functions in this group operate on LLVMOperandBundleRef instances that
|
||||
* correspond to llvm::OperandBundleDef instances.
|
||||
*
|
||||
* @see llvm::OperandBundleDef
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Create a new operand bundle.
|
||||
*
|
||||
* Every invocation should be paired with LLVMDisposeOperandBundle() or memory
|
||||
* will be leaked.
|
||||
*
|
||||
* @param Tag Tag name of the operand bundle
|
||||
* @param TagLen Length of Tag
|
||||
* @param Args Memory address of an array of bundle operands
|
||||
* @param NumArgs Length of Args
|
||||
*/
|
||||
LLVMOperandBundleRef LLVMCreateOperandBundle(const char *Tag, size_t TagLen,
|
||||
LLVMValueRef *Args,
|
||||
unsigned NumArgs);
|
||||
|
||||
/**
|
||||
* Destroy an operand bundle.
|
||||
*
|
||||
* This must be called for every created operand bundle or memory will be
|
||||
* leaked.
|
||||
*/
|
||||
void LLVMDisposeOperandBundle(LLVMOperandBundleRef Bundle);
|
||||
|
||||
/**
|
||||
* Obtain the tag of an operand bundle as a string.
|
||||
*
|
||||
* @param Bundle Operand bundle to obtain tag of.
|
||||
* @param Len Out parameter which holds the length of the returned string.
|
||||
* @return The tag name of Bundle.
|
||||
* @see OperandBundleDef::getTag()
|
||||
*/
|
||||
const char *LLVMGetOperandBundleTag(LLVMOperandBundleRef Bundle, size_t *Len);
|
||||
|
||||
/**
|
||||
* Obtain the number of operands for an operand bundle.
|
||||
*
|
||||
* @param Bundle Operand bundle to obtain operand count of.
|
||||
* @return The number of operands.
|
||||
* @see OperandBundleDef::input_size()
|
||||
*/
|
||||
unsigned LLVMGetNumOperandBundleArgs(LLVMOperandBundleRef Bundle);
|
||||
|
||||
/**
|
||||
* Obtain the operand for an operand bundle at the given index.
|
||||
*
|
||||
* @param Bundle Operand bundle to obtain operand of.
|
||||
* @param Index An operand index, must be less than
|
||||
* LLVMGetNumOperandBundleArgs().
|
||||
* @return The operand.
|
||||
*/
|
||||
LLVMValueRef LLVMGetOperandBundleArgAtIndex(LLVMOperandBundleRef Bundle,
|
||||
unsigned Index);
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
@@ -3411,6 +3542,24 @@ LLVMTypeRef LLVMGetCalledFunctionType(LLVMValueRef C);
|
||||
*/
|
||||
LLVMValueRef LLVMGetCalledValue(LLVMValueRef Instr);
|
||||
|
||||
/**
|
||||
* Obtain the number of operand bundles attached to this instruction.
|
||||
*
|
||||
* This only works on llvm::CallInst and llvm::InvokeInst instructions.
|
||||
*
|
||||
* @see llvm::CallBase::getNumOperandBundles()
|
||||
*/
|
||||
unsigned LLVMGetNumOperandBundles(LLVMValueRef C);
|
||||
|
||||
/**
|
||||
* Obtain the operand bundle attached to this instruction at the given index.
|
||||
* Use LLVMDisposeOperandBundle to free the operand bundle.
|
||||
*
|
||||
* This only works on llvm::CallInst and llvm::InvokeInst instructions.
|
||||
*/
|
||||
LLVMOperandBundleRef LLVMGetOperandBundleAtIndex(LLVMValueRef C,
|
||||
unsigned Index);
|
||||
|
||||
/**
|
||||
* Obtain whether a call instruction is a tail call.
|
||||
*
|
||||
@@ -3429,6 +3578,20 @@ LLVMBool LLVMIsTailCall(LLVMValueRef CallInst);
|
||||
*/
|
||||
void LLVMSetTailCall(LLVMValueRef CallInst, LLVMBool IsTailCall);
|
||||
|
||||
/**
|
||||
* Obtain a tail call kind of the call instruction.
|
||||
*
|
||||
* @see llvm::CallInst::setTailCallKind()
|
||||
*/
|
||||
LLVMTailCallKind LLVMGetTailCallKind(LLVMValueRef CallInst);
|
||||
|
||||
/**
|
||||
* Set the call kind of the call instruction.
|
||||
*
|
||||
* @see llvm::CallInst::getTailCallKind()
|
||||
*/
|
||||
void LLVMSetTailCallKind(LLVMValueRef CallInst, LLVMTailCallKind kind);
|
||||
|
||||
/**
|
||||
* Return the normal destination basic block.
|
||||
*
|
||||
@@ -3761,6 +3924,10 @@ LLVMValueRef LLVMBuildInvoke2(LLVMBuilderRef, LLVMTypeRef Ty, LLVMValueRef Fn,
|
||||
LLVMValueRef *Args, unsigned NumArgs,
|
||||
LLVMBasicBlockRef Then, LLVMBasicBlockRef Catch,
|
||||
const char *Name);
|
||||
LLVMValueRef LLVMBuildInvokeWithOperandBundles(
|
||||
LLVMBuilderRef, LLVMTypeRef Ty, LLVMValueRef Fn, LLVMValueRef *Args,
|
||||
unsigned NumArgs, LLVMBasicBlockRef Then, LLVMBasicBlockRef Catch,
|
||||
LLVMOperandBundleRef *Bundles, unsigned NumBundles, const char *Name);
|
||||
LLVMValueRef LLVMBuildUnreachable(LLVMBuilderRef);
|
||||
|
||||
/* Exception Handling */
|
||||
@@ -3920,6 +4087,55 @@ void LLVMSetNSW(LLVMValueRef ArithInst, LLVMBool HasNSW);
|
||||
LLVMBool LLVMGetExact(LLVMValueRef DivOrShrInst);
|
||||
void LLVMSetExact(LLVMValueRef DivOrShrInst, LLVMBool IsExact);
|
||||
|
||||
/**
|
||||
* Gets if the instruction has the non-negative flag set.
|
||||
* Only valid for zext instructions.
|
||||
*/
|
||||
LLVMBool LLVMGetNNeg(LLVMValueRef NonNegInst);
|
||||
/**
|
||||
* Sets the non-negative flag for the instruction.
|
||||
* Only valid for zext instructions.
|
||||
*/
|
||||
void LLVMSetNNeg(LLVMValueRef NonNegInst, LLVMBool IsNonNeg);
|
||||
|
||||
/**
|
||||
* Get the flags for which fast-math-style optimizations are allowed for this
|
||||
* value.
|
||||
*
|
||||
* Only valid on floating point instructions.
|
||||
* @see LLVMCanValueUseFastMathFlags
|
||||
*/
|
||||
LLVMFastMathFlags LLVMGetFastMathFlags(LLVMValueRef FPMathInst);
|
||||
|
||||
/**
|
||||
* Sets the flags for which fast-math-style optimizations are allowed for this
|
||||
* value.
|
||||
*
|
||||
* Only valid on floating point instructions.
|
||||
* @see LLVMCanValueUseFastMathFlags
|
||||
*/
|
||||
void LLVMSetFastMathFlags(LLVMValueRef FPMathInst, LLVMFastMathFlags FMF);
|
||||
|
||||
/**
|
||||
* Check if a given value can potentially have fast math flags.
|
||||
*
|
||||
* Will return true for floating point arithmetic instructions, and for select,
|
||||
* phi, and call instructions whose type is a floating point type, or a vector
|
||||
* or array thereof. See https://llvm.org/docs/LangRef.html#fast-math-flags
|
||||
*/
|
||||
LLVMBool LLVMCanValueUseFastMathFlags(LLVMValueRef Inst);
|
||||
|
||||
/**
|
||||
* Gets whether the instruction has the disjoint flag set.
|
||||
* Only valid for or instructions.
|
||||
*/
|
||||
LLVMBool LLVMGetIsDisjoint(LLVMValueRef Inst);
|
||||
/**
|
||||
* Sets the disjoint flag for the instruction.
|
||||
* Only valid for or instructions.
|
||||
*/
|
||||
void LLVMSetIsDisjoint(LLVMValueRef Inst, LLVMBool IsDisjoint);
|
||||
|
||||
/* Memory */
|
||||
LLVMValueRef LLVMBuildMalloc(LLVMBuilderRef, LLVMTypeRef Ty, const char *Name);
|
||||
LLVMValueRef LLVMBuildArrayMalloc(LLVMBuilderRef, LLVMTypeRef Ty,
|
||||
@@ -4045,6 +4261,11 @@ LLVMValueRef LLVMBuildPhi(LLVMBuilderRef, LLVMTypeRef Ty, const char *Name);
|
||||
LLVMValueRef LLVMBuildCall2(LLVMBuilderRef, LLVMTypeRef, LLVMValueRef Fn,
|
||||
LLVMValueRef *Args, unsigned NumArgs,
|
||||
const char *Name);
|
||||
LLVMValueRef
|
||||
LLVMBuildCallWithOperandBundles(LLVMBuilderRef, LLVMTypeRef, LLVMValueRef Fn,
|
||||
LLVMValueRef *Args, unsigned NumArgs,
|
||||
LLVMOperandBundleRef *Bundles,
|
||||
unsigned NumBundles, const char *Name);
|
||||
LLVMValueRef LLVMBuildSelect(LLVMBuilderRef, LLVMValueRef If,
|
||||
LLVMValueRef Then, LLVMValueRef Else,
|
||||
const char *Name);
|
||||
|
||||
@@ -16,8 +16,8 @@
|
||||
#ifndef LLVM_C_DEBUGINFO_H
|
||||
#define LLVM_C_DEBUGINFO_H
|
||||
|
||||
#include "llvm-c/ExternC.h"
|
||||
#include "llvm-c/Types.h"
|
||||
#include "ExternC.h"
|
||||
#include "Types.h"
|
||||
|
||||
LLVM_C_EXTERN_C_BEGIN
|
||||
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
#ifndef LLVM_C_DISASSEMBLER_H
|
||||
#define LLVM_C_DISASSEMBLER_H
|
||||
|
||||
#include "llvm-c/DisassemblerTypes.h"
|
||||
#include "llvm-c/ExternC.h"
|
||||
#include "DisassemblerTypes.h"
|
||||
#include "ExternC.h"
|
||||
|
||||
/**
|
||||
* @defgroup LLVMCDisassembler Disassembler
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
#ifndef LLVM_C_DISASSEMBLERTYPES_H
|
||||
#define LLVM_C_DISASSEMBLERTYPES_H
|
||||
|
||||
#include "llvm-c/DataTypes.h"
|
||||
#include "DataTypes.h"
|
||||
#ifdef __cplusplus
|
||||
#include <cstddef>
|
||||
#else
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@
|
||||
#ifndef LLVM_C_ERROR_H
|
||||
#define LLVM_C_ERROR_H
|
||||
|
||||
#include "llvm-c/ExternC.h"
|
||||
#include "ExternC.h"
|
||||
|
||||
LLVM_C_EXTERN_C_BEGIN
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
#ifndef LLVM_C_ERRORHANDLING_H
|
||||
#define LLVM_C_ERRORHANDLING_H
|
||||
|
||||
#include "llvm-c/ExternC.h"
|
||||
#include "ExternC.h"
|
||||
|
||||
LLVM_C_EXTERN_C_BEGIN
|
||||
|
||||
|
||||
@@ -19,10 +19,10 @@
|
||||
#ifndef LLVM_C_EXECUTIONENGINE_H
|
||||
#define LLVM_C_EXECUTIONENGINE_H
|
||||
|
||||
#include "llvm-c/ExternC.h"
|
||||
#include "llvm-c/Target.h"
|
||||
#include "llvm-c/TargetMachine.h"
|
||||
#include "llvm-c/Types.h"
|
||||
#include "ExternC.h"
|
||||
#include "Target.h"
|
||||
#include "TargetMachine.h"
|
||||
#include "Types.h"
|
||||
|
||||
LLVM_C_EXTERN_C_BEGIN
|
||||
|
||||
|
||||
@@ -14,8 +14,8 @@
|
||||
#ifndef LLVM_C_IRREADER_H
|
||||
#define LLVM_C_IRREADER_H
|
||||
|
||||
#include "llvm-c/ExternC.h"
|
||||
#include "llvm-c/Types.h"
|
||||
#include "ExternC.h"
|
||||
#include "Types.h"
|
||||
|
||||
LLVM_C_EXTERN_C_BEGIN
|
||||
|
||||
|
||||
+5
-5
@@ -1,4 +1,4 @@
|
||||
/*===----------- llvm-c/LLJIT.h - OrcV2 LLJIT C bindings --------*- C++ -*-===*\
|
||||
/*===----------- llvm-c/LLJIT.h - OrcV2 LLJIT C bindings ----------*- C -*-===*\
|
||||
|* *|
|
||||
|* Part of the LLVM Project, under the Apache License v2.0 with LLVM *|
|
||||
|* Exceptions. *|
|
||||
@@ -24,10 +24,10 @@
|
||||
#ifndef LLVM_C_LLJIT_H
|
||||
#define LLVM_C_LLJIT_H
|
||||
|
||||
#include "llvm-c/Error.h"
|
||||
#include "llvm-c/Orc.h"
|
||||
#include "llvm-c/TargetMachine.h"
|
||||
#include "llvm-c/Types.h"
|
||||
#include "Error.h"
|
||||
#include "Orc.h"
|
||||
#include "TargetMachine.h"
|
||||
#include "Types.h"
|
||||
|
||||
LLVM_C_EXTERN_C_BEGIN
|
||||
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
/*===------- llvm-c/LLJITUtils.h - Advanced LLJIT features --------*- C -*-===*\
|
||||
|* *|
|
||||
|* Part of the LLVM Project, under the Apache License v2.0 with LLVM *|
|
||||
|* Exceptions. *|
|
||||
|* See https://llvm.org/LICENSE.txt for license information. *|
|
||||
|* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception *|
|
||||
|* *|
|
||||
|*===----------------------------------------------------------------------===*|
|
||||
|* *|
|
||||
|* This header declares the C interface for extra utilities to be used with *|
|
||||
|* the LLJIT class from the llvm-c/LLJIT.h header. It requires to following *|
|
||||
|* link libraries in addition to libLLVMOrcJIT.a: *|
|
||||
|* - libLLVMOrcDebugging.a *|
|
||||
|* *|
|
||||
|* Many exotic languages can interoperate with C code but have a harder time *|
|
||||
|* with C++ due to name mangling. So in addition to C, this interface enables *|
|
||||
|* tools written in such languages. *|
|
||||
|* *|
|
||||
|* Note: This interface is experimental. It is *NOT* stable, and may be *|
|
||||
|* changed without warning. Only C API usage documentation is *|
|
||||
|* provided. See the C++ documentation for all higher level ORC API *|
|
||||
|* details. *|
|
||||
|* *|
|
||||
\*===----------------------------------------------------------------------===*/
|
||||
|
||||
#ifndef LLVM_C_LLJITUTILS_H
|
||||
#define LLVM_C_LLJITUTILS_H
|
||||
|
||||
#include "LLJIT.h"
|
||||
|
||||
LLVM_C_EXTERN_C_BEGIN
|
||||
|
||||
/**
|
||||
* @defgroup LLVMCExecutionEngineLLJITUtils LLJIT Utilities
|
||||
* @ingroup LLVMCExecutionEngineLLJIT
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Install the plugin that submits debug objects to the executor. Executors must
|
||||
* expose the llvm_orc_registerJITLoaderGDBWrapper symbol.
|
||||
*/
|
||||
LLVMErrorRef LLVMOrcLLJITEnableDebugSupport(LLVMOrcLLJITRef J);
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
LLVM_C_EXTERN_C_END
|
||||
|
||||
#endif /* LLVM_C_LLJITUTILS_H */
|
||||
+2
-2
@@ -14,8 +14,8 @@
|
||||
#ifndef LLVM_C_LINKER_H
|
||||
#define LLVM_C_LINKER_H
|
||||
|
||||
#include "llvm-c/ExternC.h"
|
||||
#include "llvm-c/Types.h"
|
||||
#include "ExternC.h"
|
||||
#include "Types.h"
|
||||
|
||||
LLVM_C_EXTERN_C_BEGIN
|
||||
|
||||
|
||||
+3
-3
@@ -19,9 +19,9 @@
|
||||
#ifndef LLVM_C_OBJECT_H
|
||||
#define LLVM_C_OBJECT_H
|
||||
|
||||
#include "llvm-c/ExternC.h"
|
||||
#include "llvm-c/Types.h"
|
||||
#include "llvm-c/Config//llvm-config.h"
|
||||
#include "ExternC.h"
|
||||
#include "Types.h"
|
||||
#include "Config/llvm-config.h"
|
||||
|
||||
LLVM_C_EXTERN_C_BEGIN
|
||||
|
||||
|
||||
+8
-8
@@ -27,9 +27,9 @@
|
||||
#ifndef LLVM_C_ORC_H
|
||||
#define LLVM_C_ORC_H
|
||||
|
||||
#include "llvm-c/Error.h"
|
||||
#include "llvm-c/TargetMachine.h"
|
||||
#include "llvm-c/Types.h"
|
||||
#include "Error.h"
|
||||
#include "TargetMachine.h"
|
||||
#include "Types.h"
|
||||
|
||||
LLVM_C_EXTERN_C_BEGIN
|
||||
|
||||
@@ -346,7 +346,7 @@ typedef struct LLVMOrcOpaqueLookupState *LLVMOrcLookupStateRef;
|
||||
* into.
|
||||
*
|
||||
* The JDLookupFlags argument can be inspected to determine whether the original
|
||||
* lookup included non-exported symobls.
|
||||
* lookup included non-exported symbols.
|
||||
*
|
||||
* Finally, the LookupSet argument contains the set of symbols that could not
|
||||
* be found in JD already (the set of generation candidates).
|
||||
@@ -508,7 +508,7 @@ void LLVMOrcSymbolStringPoolClearDeadEntries(LLVMOrcSymbolStringPoolRef SSP);
|
||||
* Intern a string in the ExecutionSession's SymbolStringPool and return a
|
||||
* reference to it. This increments the ref-count of the pool entry, and the
|
||||
* returned value should be released once the client is done with it by
|
||||
* calling LLVMOrReleaseSymbolStringPoolEntry.
|
||||
* calling LLVMOrcReleaseSymbolStringPoolEntry.
|
||||
*
|
||||
* Since strings are uniqued within the SymbolStringPool
|
||||
* LLVMOrcSymbolStringPoolEntryRefs can be compared by value to test string
|
||||
@@ -796,7 +796,7 @@ void LLVMOrcDisposeSymbols(LLVMOrcSymbolStringPoolEntryRef *Symbols);
|
||||
* method returns an error then clients should log it and call
|
||||
* LLVMOrcMaterializationResponsibilityFailMaterialization. If no dependencies
|
||||
* have been registered for the symbols covered by this
|
||||
* MaterializationResponsibiility then this method is guaranteed to return
|
||||
* MaterializationResponsibility then this method is guaranteed to return
|
||||
* LLVMErrorSuccess.
|
||||
*/
|
||||
LLVMErrorRef LLVMOrcMaterializationResponsibilityNotifyResolved(
|
||||
@@ -813,7 +813,7 @@ LLVMErrorRef LLVMOrcMaterializationResponsibilityNotifyResolved(
|
||||
* method returns an error then clients should log it and call
|
||||
* LLVMOrcMaterializationResponsibilityFailMaterialization.
|
||||
* If no dependencies have been registered for the symbols covered by this
|
||||
* MaterializationResponsibiility then this method is guaranteed to return
|
||||
* MaterializationResponsibility then this method is guaranteed to return
|
||||
* LLVMErrorSuccess.
|
||||
*/
|
||||
LLVMErrorRef LLVMOrcMaterializationResponsibilityNotifyEmitted(
|
||||
@@ -839,7 +839,7 @@ LLVMErrorRef LLVMOrcMaterializationResponsibilityDefineMaterializing(
|
||||
/**
|
||||
* Notify all not-yet-emitted covered by this MaterializationResponsibility
|
||||
* instance that an error has occurred.
|
||||
* This will remove all symbols covered by this MaterializationResponsibilty
|
||||
* This will remove all symbols covered by this MaterializationResponsibility
|
||||
* from the target JITDylib, and send an error to any queries waiting on
|
||||
* these symbols.
|
||||
*/
|
||||
|
||||
+5
-5
@@ -24,11 +24,11 @@
|
||||
#ifndef LLVM_C_ORCEE_H
|
||||
#define LLVM_C_ORCEE_H
|
||||
|
||||
#include "llvm-c/Error.h"
|
||||
#include "llvm-c/ExecutionEngine.h"
|
||||
#include "llvm-c/Orc.h"
|
||||
#include "llvm-c/TargetMachine.h"
|
||||
#include "llvm-c/Types.h"
|
||||
#include "Error.h"
|
||||
#include "ExecutionEngine.h"
|
||||
#include "Orc.h"
|
||||
#include "TargetMachine.h"
|
||||
#include "Types.h"
|
||||
|
||||
LLVM_C_EXTERN_C_BEGIN
|
||||
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
#ifndef LLVM_C_REMARKS_H
|
||||
#define LLVM_C_REMARKS_H
|
||||
|
||||
#include "llvm-c/ExternC.h"
|
||||
#include "llvm-c/Types.h"
|
||||
#include "ExternC.h"
|
||||
#include "Types.h"
|
||||
#ifdef __cplusplus
|
||||
#include <cstddef>
|
||||
#else
|
||||
|
||||
@@ -14,9 +14,9 @@
|
||||
#ifndef LLVM_C_SUPPORT_H
|
||||
#define LLVM_C_SUPPORT_H
|
||||
|
||||
#include "llvm-c/DataTypes.h"
|
||||
#include "llvm-c/ExternC.h"
|
||||
#include "llvm-c/Types.h"
|
||||
#include "DataTypes.h"
|
||||
#include "ExternC.h"
|
||||
#include "Types.h"
|
||||
|
||||
LLVM_C_EXTERN_C_BEGIN
|
||||
|
||||
|
||||
+15
-15
@@ -19,9 +19,9 @@
|
||||
#ifndef LLVM_C_TARGET_H
|
||||
#define LLVM_C_TARGET_H
|
||||
|
||||
#include "llvm-c/ExternC.h"
|
||||
#include "llvm-c/Types.h"
|
||||
#include "llvm-c/Config//llvm-config.h"
|
||||
#include "ExternC.h"
|
||||
#include "Types.h"
|
||||
#include "Config/llvm-config.h"
|
||||
|
||||
LLVM_C_EXTERN_C_BEGIN
|
||||
|
||||
@@ -40,34 +40,34 @@ typedef struct LLVMOpaqueTargetLibraryInfotData *LLVMTargetLibraryInfoRef;
|
||||
/* Declare all of the target-initialization functions that are available. */
|
||||
#define LLVM_TARGET(TargetName) \
|
||||
void LLVMInitialize##TargetName##TargetInfo(void);
|
||||
#include "llvm-c/Config//Targets.def"
|
||||
#include "Config/Targets.def"
|
||||
#undef LLVM_TARGET /* Explicit undef to make SWIG happier */
|
||||
|
||||
#define LLVM_TARGET(TargetName) void LLVMInitialize##TargetName##Target(void);
|
||||
#include "llvm-c/Config//Targets.def"
|
||||
#include "Config/Targets.def"
|
||||
#undef LLVM_TARGET /* Explicit undef to make SWIG happier */
|
||||
|
||||
#define LLVM_TARGET(TargetName) \
|
||||
void LLVMInitialize##TargetName##TargetMC(void);
|
||||
#include "llvm-c/Config//Targets.def"
|
||||
#include "Config/Targets.def"
|
||||
#undef LLVM_TARGET /* Explicit undef to make SWIG happier */
|
||||
|
||||
/* Declare all of the available assembly printer initialization functions. */
|
||||
#define LLVM_ASM_PRINTER(TargetName) \
|
||||
void LLVMInitialize##TargetName##AsmPrinter(void);
|
||||
#include "llvm-c/Config//AsmPrinters.def"
|
||||
#include "Config/AsmPrinters.def"
|
||||
#undef LLVM_ASM_PRINTER /* Explicit undef to make SWIG happier */
|
||||
|
||||
/* Declare all of the available assembly parser initialization functions. */
|
||||
#define LLVM_ASM_PARSER(TargetName) \
|
||||
void LLVMInitialize##TargetName##AsmParser(void);
|
||||
#include "llvm-c/Config//AsmParsers.def"
|
||||
#include "Config/AsmParsers.def"
|
||||
#undef LLVM_ASM_PARSER /* Explicit undef to make SWIG happier */
|
||||
|
||||
/* Declare all of the available disassembler initialization functions. */
|
||||
#define LLVM_DISASSEMBLER(TargetName) \
|
||||
void LLVMInitialize##TargetName##Disassembler(void);
|
||||
#include "llvm-c/Config//Disassemblers.def"
|
||||
#include "Config/Disassemblers.def"
|
||||
#undef LLVM_DISASSEMBLER /* Explicit undef to make SWIG happier */
|
||||
|
||||
/** LLVMInitializeAllTargetInfos - The main program should call this function if
|
||||
@@ -75,7 +75,7 @@ typedef struct LLVMOpaqueTargetLibraryInfotData *LLVMTargetLibraryInfoRef;
|
||||
support. */
|
||||
static inline void LLVMInitializeAllTargetInfos(void) {
|
||||
#define LLVM_TARGET(TargetName) LLVMInitialize##TargetName##TargetInfo();
|
||||
#include "llvm-c/Config//Targets.def"
|
||||
#include "Config/Targets.def"
|
||||
#undef LLVM_TARGET /* Explicit undef to make SWIG happier */
|
||||
}
|
||||
|
||||
@@ -84,7 +84,7 @@ static inline void LLVMInitializeAllTargetInfos(void) {
|
||||
support. */
|
||||
static inline void LLVMInitializeAllTargets(void) {
|
||||
#define LLVM_TARGET(TargetName) LLVMInitialize##TargetName##Target();
|
||||
#include "llvm-c/Config//Targets.def"
|
||||
#include "Config/Targets.def"
|
||||
#undef LLVM_TARGET /* Explicit undef to make SWIG happier */
|
||||
}
|
||||
|
||||
@@ -93,7 +93,7 @@ static inline void LLVMInitializeAllTargets(void) {
|
||||
support. */
|
||||
static inline void LLVMInitializeAllTargetMCs(void) {
|
||||
#define LLVM_TARGET(TargetName) LLVMInitialize##TargetName##TargetMC();
|
||||
#include "llvm-c/Config//Targets.def"
|
||||
#include "Config/Targets.def"
|
||||
#undef LLVM_TARGET /* Explicit undef to make SWIG happier */
|
||||
}
|
||||
|
||||
@@ -102,7 +102,7 @@ static inline void LLVMInitializeAllTargetMCs(void) {
|
||||
available via the TargetRegistry. */
|
||||
static inline void LLVMInitializeAllAsmPrinters(void) {
|
||||
#define LLVM_ASM_PRINTER(TargetName) LLVMInitialize##TargetName##AsmPrinter();
|
||||
#include "llvm-c/Config//AsmPrinters.def"
|
||||
#include "Config/AsmPrinters.def"
|
||||
#undef LLVM_ASM_PRINTER /* Explicit undef to make SWIG happier */
|
||||
}
|
||||
|
||||
@@ -111,7 +111,7 @@ static inline void LLVMInitializeAllAsmPrinters(void) {
|
||||
available via the TargetRegistry. */
|
||||
static inline void LLVMInitializeAllAsmParsers(void) {
|
||||
#define LLVM_ASM_PARSER(TargetName) LLVMInitialize##TargetName##AsmParser();
|
||||
#include "llvm-c/Config//AsmParsers.def"
|
||||
#include "Config/AsmParsers.def"
|
||||
#undef LLVM_ASM_PARSER /* Explicit undef to make SWIG happier */
|
||||
}
|
||||
|
||||
@@ -121,7 +121,7 @@ static inline void LLVMInitializeAllAsmParsers(void) {
|
||||
static inline void LLVMInitializeAllDisassemblers(void) {
|
||||
#define LLVM_DISASSEMBLER(TargetName) \
|
||||
LLVMInitialize##TargetName##Disassembler();
|
||||
#include "llvm-c/Config//Disassemblers.def"
|
||||
#include "Config/Disassemblers.def"
|
||||
#undef LLVM_DISASSEMBLER /* Explicit undef to make SWIG happier */
|
||||
}
|
||||
|
||||
|
||||
@@ -19,9 +19,9 @@
|
||||
#ifndef LLVM_C_TARGETMACHINE_H
|
||||
#define LLVM_C_TARGETMACHINE_H
|
||||
|
||||
#include "llvm-c/ExternC.h"
|
||||
#include "llvm-c/Target.h"
|
||||
#include "llvm-c/Types.h"
|
||||
#include "ExternC.h"
|
||||
#include "Target.h"
|
||||
#include "Types.h"
|
||||
|
||||
LLVM_C_EXTERN_C_BEGIN
|
||||
|
||||
@@ -31,6 +31,7 @@ LLVM_C_EXTERN_C_BEGIN
|
||||
* @{
|
||||
*/
|
||||
|
||||
typedef struct LLVMOpaqueTargetMachineOptions *LLVMTargetMachineOptionsRef;
|
||||
typedef struct LLVMOpaqueTargetMachine *LLVMTargetMachineRef;
|
||||
typedef struct LLVMTarget *LLVMTargetRef;
|
||||
|
||||
@@ -66,6 +67,12 @@ typedef enum {
|
||||
LLVMObjectFile
|
||||
} LLVMCodeGenFileType;
|
||||
|
||||
typedef enum {
|
||||
LLVMGlobalISelAbortEnable,
|
||||
LLVMGlobalISelAbortDisable,
|
||||
LLVMGlobalISelAbortDisableWithDiag,
|
||||
} LLVMGlobalISelAbortMode;
|
||||
|
||||
/** Returns the first llvm::Target in the registered targets list. */
|
||||
LLVMTargetRef LLVMGetFirstTarget(void);
|
||||
/** Returns the next llvm::Target given a previous one (or null if there's none) */
|
||||
@@ -98,6 +105,55 @@ LLVMBool LLVMTargetHasTargetMachine(LLVMTargetRef T);
|
||||
LLVMBool LLVMTargetHasAsmBackend(LLVMTargetRef T);
|
||||
|
||||
/*===-- Target Machine ----------------------------------------------------===*/
|
||||
/**
|
||||
* Create a new set of options for an llvm::TargetMachine.
|
||||
*
|
||||
* The returned option structure must be released with
|
||||
* LLVMDisposeTargetMachineOptions() after the call to
|
||||
* LLVMCreateTargetMachineWithOptions().
|
||||
*/
|
||||
LLVMTargetMachineOptionsRef LLVMCreateTargetMachineOptions(void);
|
||||
|
||||
/**
|
||||
* Dispose of an LLVMTargetMachineOptionsRef instance.
|
||||
*/
|
||||
void LLVMDisposeTargetMachineOptions(LLVMTargetMachineOptionsRef Options);
|
||||
|
||||
void LLVMTargetMachineOptionsSetCPU(LLVMTargetMachineOptionsRef Options,
|
||||
const char *CPU);
|
||||
|
||||
/**
|
||||
* Set the list of features for the target machine.
|
||||
*
|
||||
* \param Features a comma-separated list of features.
|
||||
*/
|
||||
void LLVMTargetMachineOptionsSetFeatures(LLVMTargetMachineOptionsRef Options,
|
||||
const char *Features);
|
||||
|
||||
void LLVMTargetMachineOptionsSetABI(LLVMTargetMachineOptionsRef Options,
|
||||
const char *ABI);
|
||||
|
||||
void LLVMTargetMachineOptionsSetCodeGenOptLevel(
|
||||
LLVMTargetMachineOptionsRef Options, LLVMCodeGenOptLevel Level);
|
||||
|
||||
void LLVMTargetMachineOptionsSetRelocMode(LLVMTargetMachineOptionsRef Options,
|
||||
LLVMRelocMode Reloc);
|
||||
|
||||
void LLVMTargetMachineOptionsSetCodeModel(LLVMTargetMachineOptionsRef Options,
|
||||
LLVMCodeModel CodeModel);
|
||||
|
||||
/**
|
||||
* Create a new llvm::TargetMachine.
|
||||
*
|
||||
* \param T the target to create a machine for.
|
||||
* \param Triple a triple describing the target machine.
|
||||
* \param Options additional configuration (see
|
||||
* LLVMCreateTargetMachineOptions()).
|
||||
*/
|
||||
LLVMTargetMachineRef
|
||||
LLVMCreateTargetMachineWithOptions(LLVMTargetRef T, const char *Triple,
|
||||
LLVMTargetMachineOptionsRef Options);
|
||||
|
||||
/** Creates a new llvm::TargetMachine. See llvm::Target::createTargetMachine */
|
||||
LLVMTargetMachineRef LLVMCreateTargetMachine(LLVMTargetRef T,
|
||||
const char *Triple, const char *CPU, const char *Features,
|
||||
@@ -132,6 +188,21 @@ LLVMTargetDataRef LLVMCreateTargetDataLayout(LLVMTargetMachineRef T);
|
||||
void LLVMSetTargetMachineAsmVerbosity(LLVMTargetMachineRef T,
|
||||
LLVMBool VerboseAsm);
|
||||
|
||||
/** Enable fast-path instruction selection. */
|
||||
void LLVMSetTargetMachineFastISel(LLVMTargetMachineRef T, LLVMBool Enable);
|
||||
|
||||
/** Enable global instruction selection. */
|
||||
void LLVMSetTargetMachineGlobalISel(LLVMTargetMachineRef T, LLVMBool Enable);
|
||||
|
||||
/** Set abort behaviour when global instruction selection fails to lower/select
|
||||
* an instruction. */
|
||||
void LLVMSetTargetMachineGlobalISelAbort(LLVMTargetMachineRef T,
|
||||
LLVMGlobalISelAbortMode Mode);
|
||||
|
||||
/** Enable the MachineOutliner pass. */
|
||||
void LLVMSetTargetMachineMachineOutliner(LLVMTargetMachineRef T,
|
||||
LLVMBool Enable);
|
||||
|
||||
/** Emits an asm or object file for the given module to the filename. This
|
||||
wraps several c++ only classes (among them a file stream). Returns any
|
||||
error in ErrorMessage. Use LLVMDisposeMessage to dispose the message. */
|
||||
|
||||
@@ -14,9 +14,9 @@
|
||||
#ifndef LLVM_C_TRANSFORMS_PASSBUILDER_H
|
||||
#define LLVM_C_TRANSFORMS_PASSBUILDER_H
|
||||
|
||||
#include "llvm-c/Error.h"
|
||||
#include "llvm-c/TargetMachine.h"
|
||||
#include "llvm-c/Types.h"
|
||||
#include "../Error.h"
|
||||
#include "../TargetMachine.h"
|
||||
#include "../Types.h"
|
||||
|
||||
/**
|
||||
* @defgroup LLVMCCoreNewPM New Pass Manager
|
||||
|
||||
+7
-2
@@ -14,8 +14,8 @@
|
||||
#ifndef LLVM_C_TYPES_H
|
||||
#define LLVM_C_TYPES_H
|
||||
|
||||
#include "llvm-c/DataTypes.h"
|
||||
#include "llvm-c/ExternC.h"
|
||||
#include "DataTypes.h"
|
||||
#include "ExternC.h"
|
||||
|
||||
LLVM_C_EXTERN_C_BEGIN
|
||||
|
||||
@@ -132,6 +132,11 @@ typedef struct LLVMOpaquePassManager *LLVMPassManagerRef;
|
||||
* @see llvm::Use */
|
||||
typedef struct LLVMOpaqueUse *LLVMUseRef;
|
||||
|
||||
/**
|
||||
* @see llvm::OperandBundleDef
|
||||
*/
|
||||
typedef struct LLVMOpaqueOperandBundle *LLVMOperandBundleRef;
|
||||
|
||||
/**
|
||||
* Used to represent an attributes.
|
||||
*
|
||||
|
||||
+1
-1
@@ -16,7 +16,7 @@
|
||||
#ifndef LLVM_C_LTO_H
|
||||
#define LLVM_C_LTO_H
|
||||
|
||||
#include "llvm-c/ExternC.h"
|
||||
#include "ExternC.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
#include <cstddef>
|
||||
|
||||
+10
-4
@@ -15,6 +15,7 @@ struct lbArgType {
|
||||
LLVMAttributeRef align_attribute; // Optional
|
||||
i64 byval_alignment;
|
||||
bool is_byval;
|
||||
bool no_capture;
|
||||
};
|
||||
|
||||
|
||||
@@ -159,6 +160,11 @@ gb_internal void lb_add_function_type_attributes(LLVMValueRef fn, lbFunctionType
|
||||
LLVMAddAttributeAtIndex(fn, arg_index+1, arg->align_attribute);
|
||||
}
|
||||
|
||||
if (arg->no_capture) {
|
||||
LLVMAddAttributeAtIndex(fn, arg_index+1, nocapture_attr);
|
||||
}
|
||||
|
||||
|
||||
if (ft->multiple_return_original_type) {
|
||||
if (ft->original_arg_count <= i) {
|
||||
LLVMAddAttributeAtIndex(fn, arg_index+1, noalias_attr);
|
||||
@@ -645,10 +651,10 @@ namespace lbAbiAmd64SysV {
|
||||
if (is_mem_cls(cls, attribute_kind)) {
|
||||
LLVMAttributeRef attribute = nullptr;
|
||||
if (attribute_kind == Amd64TypeAttribute_ByVal) {
|
||||
// if (!is_calling_convention_odin(calling_convention)) {
|
||||
return lb_arg_type_indirect_byval(c, type);
|
||||
// }
|
||||
// attribute = nullptr;
|
||||
if (is_calling_convention_odin(calling_convention)) {
|
||||
return lb_arg_type_indirect(type, attribute);
|
||||
}
|
||||
return lb_arg_type_indirect_byval(c, type);
|
||||
} else if (attribute_kind == Amd64TypeAttribute_StructRect) {
|
||||
attribute = lb_create_enum_attribute_with_type(c, "sret", type);
|
||||
}
|
||||
|
||||
+109
-44
@@ -1,13 +1,11 @@
|
||||
#define MULTITHREAD_OBJECT_GENERATION 1
|
||||
|
||||
#ifndef USE_SEPARATE_MODULES
|
||||
#define USE_SEPARATE_MODULES build_context.use_separate_modules
|
||||
#endif
|
||||
|
||||
#ifndef MULTITHREAD_OBJECT_GENERATION
|
||||
#define MULTITHREAD_OBJECT_GENERATION 0
|
||||
#endif
|
||||
|
||||
#ifndef USE_SEPARATE_MODULES
|
||||
#define USE_SEPARATE_MODULES build_context.use_separate_modules
|
||||
#endif
|
||||
|
||||
#ifndef LLVM_IGNORE_VERIFICATION
|
||||
#define LLVM_IGNORE_VERIFICATION 0
|
||||
@@ -137,19 +135,28 @@ gb_internal void lb_set_entity_from_other_modules_linkage_correctly(lbModule *ot
|
||||
if (other_module == nullptr) {
|
||||
return;
|
||||
}
|
||||
char const *cname = alloc_cstring(temporary_allocator(), name);
|
||||
char const *cname = alloc_cstring(permanent_allocator(), name);
|
||||
mpsc_enqueue(&other_module->gen->entities_to_correct_linkage, lbEntityCorrection{other_module, e, cname});
|
||||
}
|
||||
|
||||
LLVMValueRef other_global = nullptr;
|
||||
if (e->kind == Entity_Variable) {
|
||||
other_global = LLVMGetNamedGlobal(other_module->mod, cname);
|
||||
} else if (e->kind == Entity_Procedure) {
|
||||
other_global = LLVMGetNamedFunction(other_module->mod, cname);
|
||||
}
|
||||
if (other_global) {
|
||||
LLVMSetLinkage(other_global, LLVMExternalLinkage);
|
||||
gb_internal void lb_correct_entity_linkage(lbGenerator *gen) {
|
||||
for (lbEntityCorrection ec = {}; mpsc_dequeue(&gen->entities_to_correct_linkage, &ec); /**/) {
|
||||
LLVMValueRef other_global = nullptr;
|
||||
if (ec.e->kind == Entity_Variable) {
|
||||
other_global = LLVMGetNamedGlobal(ec.other_module->mod, ec.cname);
|
||||
if (other_global) {
|
||||
LLVMSetLinkage(other_global, LLVMWeakAnyLinkage);
|
||||
}
|
||||
} else if (ec.e->kind == Entity_Procedure) {
|
||||
other_global = LLVMGetNamedFunction(ec.other_module->mod, ec.cname);
|
||||
if (other_global) {
|
||||
LLVMSetLinkage(other_global, LLVMWeakAnyLinkage);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
gb_internal void lb_emit_init_context(lbProcedure *p, lbAddr addr) {
|
||||
TEMPORARY_ALLOCATOR_GUARD();
|
||||
|
||||
@@ -969,14 +976,16 @@ gb_internal lbValue lb_const_hash(lbModule *m, lbValue key, Type *key_type) {
|
||||
gb_internal lbValue lb_gen_map_key_hash(lbProcedure *p, lbValue const &map_ptr, lbValue key, lbValue *key_ptr_) {
|
||||
TEMPORARY_ALLOCATOR_GUARD();
|
||||
|
||||
lbValue key_ptr = lb_address_from_load_or_generate_local(p, key);
|
||||
Type* key_type = base_type(type_deref(map_ptr.type))->Map.key;
|
||||
|
||||
lbValue real_key = lb_emit_conv(p, key, key_type);
|
||||
|
||||
lbValue key_ptr = lb_address_from_load_or_generate_local(p, real_key);
|
||||
key_ptr = lb_emit_conv(p, key_ptr, t_rawptr);
|
||||
|
||||
if (key_ptr_) *key_ptr_ = key_ptr;
|
||||
|
||||
Type* key_type = base_type(type_deref(map_ptr.type))->Map.key;
|
||||
|
||||
lbValue hashed_key = lb_const_hash(p->module, key, key_type);
|
||||
lbValue hashed_key = lb_const_hash(p->module, real_key, key_type);
|
||||
if (hashed_key.value == nullptr) {
|
||||
lbValue hasher = lb_hasher_proc_for_type(p->module, key_type);
|
||||
|
||||
@@ -1321,6 +1330,24 @@ gb_internal WORKER_TASK_PROC(lb_generate_procedures_and_types_per_module) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
gb_internal GB_COMPARE_PROC(llvm_global_entity_cmp) {
|
||||
Entity *x = *cast(Entity **)a;
|
||||
Entity *y = *cast(Entity **)b;
|
||||
if (x == y) {
|
||||
return 0;
|
||||
}
|
||||
if (x->kind != y->kind) {
|
||||
return cast(i32)(x->kind - y->kind);
|
||||
}
|
||||
|
||||
i32 cmp = 0;
|
||||
cmp = token_pos_cmp(x->token.pos, y->token.pos);
|
||||
if (!cmp) {
|
||||
return cmp;
|
||||
}
|
||||
return cmp;
|
||||
}
|
||||
|
||||
gb_internal void lb_create_global_procedures_and_types(lbGenerator *gen, CheckerInfo *info, bool do_threading) {
|
||||
auto *min_dep_set = &info->minimum_dependency_set;
|
||||
|
||||
@@ -1369,6 +1396,7 @@ gb_internal void lb_create_global_procedures_and_types(lbGenerator *gen, Checker
|
||||
if (USE_SEPARATE_MODULES) {
|
||||
m = lb_module_of_entity(gen, e);
|
||||
}
|
||||
GB_ASSERT(m != nullptr);
|
||||
|
||||
if (e->kind == Entity_Procedure) {
|
||||
array_add(&m->global_procedures_to_create, e);
|
||||
@@ -1377,6 +1405,12 @@ gb_internal void lb_create_global_procedures_and_types(lbGenerator *gen, Checker
|
||||
}
|
||||
}
|
||||
|
||||
for (auto const &entry : gen->modules) {
|
||||
lbModule *m = entry.value;
|
||||
array_sort(m->global_types_to_create, llvm_global_entity_cmp);
|
||||
array_sort(m->global_procedures_to_create, llvm_global_entity_cmp);
|
||||
}
|
||||
|
||||
if (do_threading) {
|
||||
for (auto const &entry : gen->modules) {
|
||||
lbModule *m = entry.value;
|
||||
@@ -1408,7 +1442,9 @@ gb_internal bool lb_is_module_empty(lbModule *m) {
|
||||
}
|
||||
|
||||
for (auto g = LLVMGetFirstGlobal(m->mod); g != nullptr; g = LLVMGetNextGlobal(g)) {
|
||||
if (LLVMGetLinkage(g) == LLVMExternalLinkage) {
|
||||
LLVMLinkage linkage = LLVMGetLinkage(g);
|
||||
if (linkage == LLVMExternalLinkage ||
|
||||
linkage == LLVMWeakAnyLinkage) {
|
||||
continue;
|
||||
}
|
||||
if (!LLVMIsExternallyInitialized(g)) {
|
||||
@@ -1462,10 +1498,6 @@ gb_internal WORKER_TASK_PROC(lb_llvm_function_pass_per_module) {
|
||||
lb_populate_function_pass_manager(m, m->function_pass_managers[lbFunctionPassManager_default], false, build_context.optimization_level);
|
||||
lb_populate_function_pass_manager(m, m->function_pass_managers[lbFunctionPassManager_default_without_memcpy], true, build_context.optimization_level);
|
||||
lb_populate_function_pass_manager_specific(m, m->function_pass_managers[lbFunctionPassManager_none], -1);
|
||||
lb_populate_function_pass_manager_specific(m, m->function_pass_managers[lbFunctionPassManager_minimal], 0);
|
||||
lb_populate_function_pass_manager_specific(m, m->function_pass_managers[lbFunctionPassManager_size], 1);
|
||||
lb_populate_function_pass_manager_specific(m, m->function_pass_managers[lbFunctionPassManager_speed], 2);
|
||||
lb_populate_function_pass_manager_specific(m, m->function_pass_managers[lbFunctionPassManager_aggressive], 3);
|
||||
|
||||
for (i32 i = 0; i < lbFunctionPassManager_COUNT; i++) {
|
||||
LLVMFinalizeFunctionPassManager(m->function_pass_managers[i]);
|
||||
@@ -1489,15 +1521,12 @@ gb_internal WORKER_TASK_PROC(lb_llvm_function_pass_per_module) {
|
||||
if (p->entity && p->entity->kind == Entity_Procedure) {
|
||||
switch (p->entity->Procedure.optimization_mode) {
|
||||
case ProcedureOptimizationMode_None:
|
||||
case ProcedureOptimizationMode_Minimal:
|
||||
pass_manager_kind = lbFunctionPassManager_minimal;
|
||||
pass_manager_kind = lbFunctionPassManager_none;
|
||||
GB_ASSERT(lb_proc_has_attribute(p->module, p->value, "optnone"));
|
||||
GB_ASSERT(lb_proc_has_attribute(p->module, p->value, "noinline"));
|
||||
break;
|
||||
case ProcedureOptimizationMode_Size:
|
||||
pass_manager_kind = lbFunctionPassManager_size;
|
||||
lb_add_attribute_to_proc(p->module, p->value, "optsize");
|
||||
break;
|
||||
case ProcedureOptimizationMode_Speed:
|
||||
pass_manager_kind = lbFunctionPassManager_speed;
|
||||
case ProcedureOptimizationMode_FavorSize:
|
||||
GB_ASSERT(lb_proc_has_attribute(p->module, p->value, "optsize"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1553,6 +1582,7 @@ gb_internal WORKER_TASK_PROC(lb_llvm_module_pass_worker_proc) {
|
||||
|
||||
switch (build_context.optimization_level) {
|
||||
case -1:
|
||||
array_add(&passes, "function(annotation-remarks)");
|
||||
break;
|
||||
case 0:
|
||||
array_add(&passes, "always-inline");
|
||||
@@ -2532,20 +2562,37 @@ gb_internal String lb_filepath_ll_for_module(lbModule *m) {
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
gb_internal String lb_filepath_obj_for_module(lbModule *m) {
|
||||
String path = concatenate3_strings(permanent_allocator(),
|
||||
build_context.build_paths[BuildPath_Output].basename,
|
||||
STR_LIT("/"),
|
||||
build_context.build_paths[BuildPath_Output].name
|
||||
);
|
||||
String basename = build_context.build_paths[BuildPath_Output].basename;
|
||||
String name = build_context.build_paths[BuildPath_Output].name;
|
||||
|
||||
bool use_temporary_directory = false;
|
||||
if (USE_SEPARATE_MODULES && build_context.build_mode == BuildMode_Executable) {
|
||||
// NOTE(bill): use a temporary directory
|
||||
String dir = temporary_directory(permanent_allocator());
|
||||
if (dir.len != 0) {
|
||||
basename = dir;
|
||||
use_temporary_directory = true;
|
||||
}
|
||||
}
|
||||
|
||||
gbString path = gb_string_make_length(heap_allocator(), basename.text, basename.len);
|
||||
path = gb_string_appendc(path, "/");
|
||||
path = gb_string_append_length(path, name.text, name.len);
|
||||
|
||||
if (m->file) {
|
||||
char buf[32] = {};
|
||||
isize n = gb_snprintf(buf, gb_size_of(buf), "-%u", m->file->id);
|
||||
String suffix = make_string((u8 *)buf, n-1);
|
||||
path = concatenate_strings(permanent_allocator(), path, suffix);
|
||||
path = gb_string_append_length(path, suffix.text, suffix.len);
|
||||
} else if (m->pkg) {
|
||||
path = concatenate3_strings(permanent_allocator(), path, STR_LIT("-"), m->pkg->name);
|
||||
path = gb_string_appendc(path, "-");
|
||||
path = gb_string_append_length(path, m->pkg->name.text, m->pkg->name.len);
|
||||
}
|
||||
|
||||
if (use_temporary_directory) {
|
||||
path = gb_string_append_fmt(path, "-%p", m);
|
||||
}
|
||||
|
||||
String ext = {};
|
||||
@@ -2583,7 +2630,10 @@ gb_internal String lb_filepath_obj_for_module(lbModule *m) {
|
||||
}
|
||||
}
|
||||
|
||||
return concatenate_strings(permanent_allocator(), path, ext);
|
||||
path = gb_string_append_length(path, ext.text, ext.len);
|
||||
|
||||
return make_string(cast(u8 *)path, gb_string_length(path));
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -2642,7 +2692,6 @@ gb_internal bool lb_llvm_object_generation(lbGenerator *gen, bool do_threading)
|
||||
|
||||
String filepath_ll = lb_filepath_ll_for_module(m);
|
||||
String filepath_obj = lb_filepath_obj_for_module(m);
|
||||
// gb_printf_err("%.*s\n", LIT(filepath_obj));
|
||||
array_add(&gen->output_object_paths, filepath_obj);
|
||||
array_add(&gen->output_temp_paths, filepath_ll);
|
||||
|
||||
@@ -3224,7 +3273,7 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
|
||||
LLVMSetLinkage(g.value, LLVMDLLExportLinkage);
|
||||
LLVMSetDLLStorageClass(g.value, LLVMDLLExportStorageClass);
|
||||
} else if (!is_foreign) {
|
||||
LLVMSetLinkage(g.value, USE_SEPARATE_MODULES ? LLVMExternalLinkage : LLVMInternalLinkage);
|
||||
LLVMSetLinkage(g.value, USE_SEPARATE_MODULES ? LLVMWeakAnyLinkage : LLVMInternalLinkage);
|
||||
}
|
||||
lb_set_linkage_from_entity_flags(m, g.value, e->flags);
|
||||
|
||||
@@ -3241,11 +3290,12 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
|
||||
if (!is_type_any(e->type) && !is_type_union(e->type)) {
|
||||
if (tav.mode != Addressing_Invalid) {
|
||||
if (tav.value.kind != ExactValue_Invalid) {
|
||||
bool is_rodata = e->kind == Entity_Variable && e->Variable.is_rodata;
|
||||
ExactValue v = tav.value;
|
||||
lbValue init = lb_const_value(m, tav.type, v);
|
||||
lbValue init = lb_const_value(m, tav.type, v, false, is_rodata);
|
||||
LLVMSetInitializer(g.value, init.value);
|
||||
var.is_initialized = true;
|
||||
if (e->kind == Entity_Variable && e->Variable.is_rodata) {
|
||||
if (is_rodata) {
|
||||
LLVMSetGlobalConstant(g.value, true);
|
||||
}
|
||||
}
|
||||
@@ -3394,7 +3444,22 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
|
||||
TIME_SECTION("LLVM Add Foreign Library Paths");
|
||||
lb_add_foreign_library_paths(gen);
|
||||
|
||||
TIME_SECTION("LLVM Object Generation");
|
||||
TIME_SECTION("LLVM Correct Entity Linkage");
|
||||
lb_correct_entity_linkage(gen);
|
||||
|
||||
////////////////////////////////////////////
|
||||
for (auto const &entry: gen->modules) {
|
||||
lbModule *m = entry.value;
|
||||
if (!lb_is_module_empty(m)) {
|
||||
gen->used_module_count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
gbString label_object_generation = gb_string_make(heap_allocator(), "LLVM Object Generation");
|
||||
if (gen->used_module_count > 1) {
|
||||
label_object_generation = gb_string_append_fmt(label_object_generation, " (%td used modules)", gen->used_module_count);
|
||||
}
|
||||
TIME_SECTION_WITH_LEN(label_object_generation, gb_string_length(label_object_generation));
|
||||
|
||||
if (build_context.ignore_llvm_build) {
|
||||
gb_printf_err("LLVM object generation has been ignored!\n");
|
||||
|
||||
+21
-8
@@ -134,11 +134,6 @@ enum lbFunctionPassManagerKind {
|
||||
lbFunctionPassManager_default,
|
||||
lbFunctionPassManager_default_without_memcpy,
|
||||
lbFunctionPassManager_none,
|
||||
lbFunctionPassManager_minimal,
|
||||
lbFunctionPassManager_size,
|
||||
lbFunctionPassManager_speed,
|
||||
lbFunctionPassManager_aggressive,
|
||||
|
||||
lbFunctionPassManager_COUNT
|
||||
};
|
||||
|
||||
@@ -152,6 +147,7 @@ struct lbModule {
|
||||
CheckerInfo *info;
|
||||
AstPackage *pkg; // possibly associated
|
||||
AstFile *file; // possibly associated
|
||||
char const *module_name;
|
||||
|
||||
PtrMap<Type *, LLVMTypeRef> types; // mutex: types_mutex
|
||||
PtrMap<void *, lbStructFieldRemapping> struct_field_remapping; // Key: LLVMTypeRef or Type *, mutex: types_mutex
|
||||
@@ -205,6 +201,12 @@ struct lbModule {
|
||||
LLVMPassManagerRef function_pass_managers[lbFunctionPassManager_COUNT];
|
||||
};
|
||||
|
||||
struct lbEntityCorrection {
|
||||
lbModule * other_module;
|
||||
Entity * e;
|
||||
char const *cname;
|
||||
};
|
||||
|
||||
struct lbGenerator : LinkerData {
|
||||
CheckerInfo *info;
|
||||
|
||||
@@ -218,9 +220,13 @@ struct lbGenerator : LinkerData {
|
||||
std::atomic<u32> global_array_index;
|
||||
std::atomic<u32> global_generated_index;
|
||||
|
||||
isize used_module_count;
|
||||
|
||||
lbProcedure *startup_runtime;
|
||||
lbProcedure *cleanup_runtime;
|
||||
lbProcedure *objc_names;
|
||||
|
||||
MPSCQueue<lbEntityCorrection> entities_to_correct_linkage;
|
||||
};
|
||||
|
||||
|
||||
@@ -299,6 +305,11 @@ enum lbProcedureFlag : u32 {
|
||||
lbProcedureFlag_DebugAllocaCopy = 1<<1,
|
||||
};
|
||||
|
||||
struct lbVariadicReuseSlices {
|
||||
Type *slice_type;
|
||||
lbAddr slice_addr;
|
||||
};
|
||||
|
||||
struct lbProcedure {
|
||||
u32 flags;
|
||||
u16 state_flags;
|
||||
@@ -339,8 +350,10 @@ struct lbProcedure {
|
||||
bool in_multi_assignment;
|
||||
Array<LLVMValueRef> raw_input_parameters;
|
||||
|
||||
LLVMValueRef temp_callee_return_struct_memory;
|
||||
Array<lbVariadicReuseSlices> variadic_reuses;
|
||||
lbAddr variadic_reuse_base_array_ptr;
|
||||
|
||||
LLVMValueRef temp_callee_return_struct_memory;
|
||||
Ast *curr_stmt;
|
||||
|
||||
Array<Scope *> scope_stack;
|
||||
@@ -367,7 +380,7 @@ struct lbProcedure {
|
||||
|
||||
gb_internal bool lb_init_generator(lbGenerator *gen, Checker *c);
|
||||
|
||||
gb_internal String lb_mangle_name(lbModule *m, Entity *e);
|
||||
gb_internal String lb_mangle_name(Entity *e);
|
||||
gb_internal String lb_get_entity_name(lbModule *m, Entity *e, String name = {});
|
||||
|
||||
gb_internal LLVMAttributeRef lb_create_enum_attribute(LLVMContextRef ctx, char const *name, u64 value=0);
|
||||
@@ -385,7 +398,7 @@ gb_internal lbBlock *lb_create_block(lbProcedure *p, char const *name, bool appe
|
||||
|
||||
gb_internal lbValue lb_const_nil(lbModule *m, Type *type);
|
||||
gb_internal lbValue lb_const_undef(lbModule *m, Type *type);
|
||||
gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_local=true);
|
||||
gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_local=true, bool is_rodata=false);
|
||||
gb_internal lbValue lb_const_bool(lbModule *m, Type *type, bool value);
|
||||
gb_internal lbValue lb_const_int(lbModule *m, Type *type, u64 value);
|
||||
|
||||
|
||||
+52
-32
@@ -94,9 +94,6 @@ gb_internal LLVMValueRef llvm_const_cast(LLVMValueRef val, LLVMTypeRef dst) {
|
||||
LLVMTypeKind kind = LLVMGetTypeKind(dst);
|
||||
switch (kind) {
|
||||
case LLVMPointerTypeKind:
|
||||
if (LB_USE_NEW_PASS_SYSTEM) {
|
||||
return val;
|
||||
}
|
||||
return LLVMConstPointerCast(val, dst);
|
||||
case LLVMStructTypeKind:
|
||||
// GB_PANIC("%s -> %s", LLVMPrintValueToString(val), LLVMPrintTypeToString(dst));
|
||||
@@ -341,6 +338,15 @@ gb_internal lbValue lb_emit_source_code_location_as_global_ptr(lbProcedure *p, S
|
||||
return addr.addr;
|
||||
}
|
||||
|
||||
gb_internal lbValue lb_const_source_code_location_as_global_ptr(lbModule *m, String const &procedure, TokenPos const &pos) {
|
||||
lbValue loc = lb_const_source_code_location_const(m, procedure, pos);
|
||||
lbAddr addr = lb_add_global_generated(m, loc.type, loc, nullptr);
|
||||
lb_make_global_private_const(addr);
|
||||
return addr.addr;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
gb_internal lbValue lb_emit_source_code_location_as_global_ptr(lbProcedure *p, Ast *node) {
|
||||
lbValue loc = lb_emit_source_code_location_const(p, node);
|
||||
@@ -359,7 +365,11 @@ gb_internal lbValue lb_emit_source_code_location_as_global(lbProcedure *p, Ast *
|
||||
|
||||
|
||||
|
||||
gb_internal LLVMValueRef lb_build_constant_array_values(lbModule *m, Type *type, Type *elem_type, isize count, LLVMValueRef *values, bool allow_local) {
|
||||
gb_internal LLVMValueRef lb_build_constant_array_values(lbModule *m, Type *type, Type *elem_type, isize count, LLVMValueRef *values, bool allow_local, bool is_rodata) {
|
||||
if (allow_local) {
|
||||
is_rodata = false;
|
||||
}
|
||||
|
||||
bool is_local = allow_local && m->curr_procedure != nullptr;
|
||||
bool is_const = true;
|
||||
if (is_local) {
|
||||
@@ -428,6 +438,8 @@ gb_internal LLVMValueRef lb_big_int_to_llvm(lbModule *m, Type *original_type, Bi
|
||||
}
|
||||
}
|
||||
|
||||
GB_ASSERT(!is_type_array(original_type));
|
||||
|
||||
LLVMValueRef value = LLVMConstIntOfArbitraryPrecision(lb_type(m, original_type), cast(unsigned)((sz+7)/8), cast(u64 *)rop);
|
||||
if (big_int_is_neg(a)) {
|
||||
value = LLVMConstNeg(value);
|
||||
@@ -462,7 +474,11 @@ gb_internal bool lb_is_nested_possibly_constant(Type *ft, Selection const &sel,
|
||||
return lb_is_elem_const(elem, ft);
|
||||
}
|
||||
|
||||
gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_local) {
|
||||
gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_local, bool is_rodata) {
|
||||
if (allow_local) {
|
||||
is_rodata = false;
|
||||
}
|
||||
|
||||
LLVMContextRef ctx = m->ctx;
|
||||
|
||||
type = default_type(type);
|
||||
@@ -520,7 +536,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
|
||||
count = gb_max(cast(isize)cl->max_count, count);
|
||||
Type *elem = base_type(type)->Slice.elem;
|
||||
Type *t = alloc_type_array(elem, count);
|
||||
lbValue backing_array = lb_const_value(m, t, value, allow_local);
|
||||
lbValue backing_array = lb_const_value(m, t, value, allow_local, is_rodata);
|
||||
|
||||
LLVMValueRef array_data = nullptr;
|
||||
|
||||
@@ -557,6 +573,10 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
|
||||
array_data = LLVMAddGlobal(m->mod, lb_type(m, t), str);
|
||||
LLVMSetInitializer(array_data, backing_array.value);
|
||||
|
||||
if (is_rodata) {
|
||||
LLVMSetGlobalConstant(array_data, true);
|
||||
}
|
||||
|
||||
lbValue g = {};
|
||||
g.value = array_data;
|
||||
g.type = t;
|
||||
@@ -604,7 +624,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
|
||||
}
|
||||
// NOTE(bill, 2021-10-07): Allow for array programming value constants
|
||||
Type *core_elem = core_array_type(type);
|
||||
return lb_const_value(m, core_elem, value, allow_local);
|
||||
return lb_const_value(m, core_elem, value, allow_local, is_rodata);
|
||||
} else if (is_type_u8_array(type) && value.kind == ExactValue_String) {
|
||||
GB_ASSERT(type->Array.count == value.value_string.len);
|
||||
LLVMValueRef data = LLVMConstStringInContext(ctx,
|
||||
@@ -622,7 +642,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
|
||||
Type *elem = type->Array.elem;
|
||||
|
||||
|
||||
lbValue single_elem = lb_const_value(m, elem, value, allow_local);
|
||||
lbValue single_elem = lb_const_value(m, elem, value, allow_local, is_rodata);
|
||||
|
||||
LLVMValueRef *elems = gb_alloc_array(permanent_allocator(), LLVMValueRef, cast(isize)count);
|
||||
for (i64 i = 0; i < count; i++) {
|
||||
@@ -640,7 +660,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
|
||||
|
||||
Type *elem = type->Matrix.elem;
|
||||
|
||||
lbValue single_elem = lb_const_value(m, elem, value, allow_local);
|
||||
lbValue single_elem = lb_const_value(m, elem, value, allow_local, is_rodata);
|
||||
single_elem.value = llvm_const_cast(single_elem.value, lb_type(m, elem));
|
||||
|
||||
i64 total_elem_count = matrix_type_total_internal_elems(type);
|
||||
@@ -662,7 +682,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
|
||||
i64 count = type->SimdVector.count;
|
||||
Type *elem = type->SimdVector.elem;
|
||||
|
||||
lbValue single_elem = lb_const_value(m, elem, value, allow_local);
|
||||
lbValue single_elem = lb_const_value(m, elem, value, allow_local, is_rodata);
|
||||
single_elem.value = llvm_const_cast(single_elem.value, lb_type(m, elem));
|
||||
|
||||
LLVMValueRef *elems = gb_alloc_array(permanent_allocator(), LLVMValueRef, count);
|
||||
@@ -791,7 +811,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
|
||||
|
||||
case ExactValue_Compound:
|
||||
if (is_type_slice(type)) {
|
||||
return lb_const_value(m, type, value, allow_local);
|
||||
return lb_const_value(m, type, value, allow_local, is_rodata);
|
||||
} else if (is_type_array(type)) {
|
||||
ast_node(cl, CompoundLit, value.value_compound);
|
||||
Type *elem_type = type->Array.elem;
|
||||
@@ -825,7 +845,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
|
||||
}
|
||||
if (lo == i) {
|
||||
TypeAndValue tav = fv->value->tav;
|
||||
LLVMValueRef val = lb_const_value(m, elem_type, tav.value, allow_local).value;
|
||||
LLVMValueRef val = lb_const_value(m, elem_type, tav.value, allow_local, is_rodata).value;
|
||||
for (i64 k = lo; k < hi; k++) {
|
||||
values[value_index++] = val;
|
||||
}
|
||||
@@ -840,7 +860,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
|
||||
i64 index = exact_value_to_i64(index_tav.value);
|
||||
if (index == i) {
|
||||
TypeAndValue tav = fv->value->tav;
|
||||
LLVMValueRef val = lb_const_value(m, elem_type, tav.value, allow_local).value;
|
||||
LLVMValueRef val = lb_const_value(m, elem_type, tav.value, allow_local, is_rodata).value;
|
||||
values[value_index++] = val;
|
||||
found = true;
|
||||
break;
|
||||
@@ -853,7 +873,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
|
||||
}
|
||||
}
|
||||
|
||||
res.value = lb_build_constant_array_values(m, type, elem_type, cast(isize)type->Array.count, values, allow_local);
|
||||
res.value = lb_build_constant_array_values(m, type, elem_type, cast(isize)type->Array.count, values, allow_local, is_rodata);
|
||||
return res;
|
||||
} else {
|
||||
GB_ASSERT_MSG(elem_count == type->Array.count, "%td != %td", elem_count, type->Array.count);
|
||||
@@ -863,13 +883,13 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
|
||||
for (isize i = 0; i < elem_count; i++) {
|
||||
TypeAndValue tav = cl->elems[i]->tav;
|
||||
GB_ASSERT(tav.mode != Addressing_Invalid);
|
||||
values[i] = lb_const_value(m, elem_type, tav.value, allow_local).value;
|
||||
values[i] = lb_const_value(m, elem_type, tav.value, allow_local, is_rodata).value;
|
||||
}
|
||||
for (isize i = elem_count; i < type->Array.count; i++) {
|
||||
values[i] = LLVMConstNull(lb_type(m, elem_type));
|
||||
}
|
||||
|
||||
res.value = lb_build_constant_array_values(m, type, elem_type, cast(isize)type->Array.count, values, allow_local);
|
||||
res.value = lb_build_constant_array_values(m, type, elem_type, cast(isize)type->Array.count, values, allow_local, is_rodata);
|
||||
return res;
|
||||
}
|
||||
} else if (is_type_enumerated_array(type)) {
|
||||
@@ -909,7 +929,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
|
||||
}
|
||||
if (lo == i) {
|
||||
TypeAndValue tav = fv->value->tav;
|
||||
LLVMValueRef val = lb_const_value(m, elem_type, tav.value, allow_local).value;
|
||||
LLVMValueRef val = lb_const_value(m, elem_type, tav.value, allow_local, is_rodata).value;
|
||||
for (i64 k = lo; k < hi; k++) {
|
||||
values[value_index++] = val;
|
||||
}
|
||||
@@ -924,7 +944,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
|
||||
i64 index = exact_value_to_i64(index_tav.value);
|
||||
if (index == i) {
|
||||
TypeAndValue tav = fv->value->tav;
|
||||
LLVMValueRef val = lb_const_value(m, elem_type, tav.value, allow_local).value;
|
||||
LLVMValueRef val = lb_const_value(m, elem_type, tav.value, allow_local, is_rodata).value;
|
||||
values[value_index++] = val;
|
||||
found = true;
|
||||
break;
|
||||
@@ -937,7 +957,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
|
||||
}
|
||||
}
|
||||
|
||||
res.value = lb_build_constant_array_values(m, type, elem_type, cast(isize)type->EnumeratedArray.count, values, allow_local);
|
||||
res.value = lb_build_constant_array_values(m, type, elem_type, cast(isize)type->EnumeratedArray.count, values, allow_local, is_rodata);
|
||||
return res;
|
||||
} else {
|
||||
GB_ASSERT_MSG(elem_count == type->EnumeratedArray.count, "%td != %td", elem_count, type->EnumeratedArray.count);
|
||||
@@ -947,13 +967,13 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
|
||||
for (isize i = 0; i < elem_count; i++) {
|
||||
TypeAndValue tav = cl->elems[i]->tav;
|
||||
GB_ASSERT(tav.mode != Addressing_Invalid);
|
||||
values[i] = lb_const_value(m, elem_type, tav.value, allow_local).value;
|
||||
values[i] = lb_const_value(m, elem_type, tav.value, allow_local, is_rodata).value;
|
||||
}
|
||||
for (isize i = elem_count; i < type->EnumeratedArray.count; i++) {
|
||||
values[i] = LLVMConstNull(lb_type(m, elem_type));
|
||||
}
|
||||
|
||||
res.value = lb_build_constant_array_values(m, type, elem_type, cast(isize)type->EnumeratedArray.count, values, allow_local);
|
||||
res.value = lb_build_constant_array_values(m, type, elem_type, cast(isize)type->EnumeratedArray.count, values, allow_local, is_rodata);
|
||||
return res;
|
||||
}
|
||||
} else if (is_type_simd_vector(type)) {
|
||||
@@ -992,7 +1012,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
|
||||
}
|
||||
if (lo == i) {
|
||||
TypeAndValue tav = fv->value->tav;
|
||||
LLVMValueRef val = lb_const_value(m, elem_type, tav.value, allow_local).value;
|
||||
LLVMValueRef val = lb_const_value(m, elem_type, tav.value, allow_local, is_rodata).value;
|
||||
for (i64 k = lo; k < hi; k++) {
|
||||
values[value_index++] = val;
|
||||
}
|
||||
@@ -1007,7 +1027,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
|
||||
i64 index = exact_value_to_i64(index_tav.value);
|
||||
if (index == i) {
|
||||
TypeAndValue tav = fv->value->tav;
|
||||
LLVMValueRef val = lb_const_value(m, elem_type, tav.value, allow_local).value;
|
||||
LLVMValueRef val = lb_const_value(m, elem_type, tav.value, allow_local, is_rodata).value;
|
||||
values[value_index++] = val;
|
||||
found = true;
|
||||
break;
|
||||
@@ -1026,7 +1046,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
|
||||
for (isize i = 0; i < elem_count; i++) {
|
||||
TypeAndValue tav = cl->elems[i]->tav;
|
||||
GB_ASSERT(tav.mode != Addressing_Invalid);
|
||||
values[i] = lb_const_value(m, elem_type, tav.value, allow_local).value;
|
||||
values[i] = lb_const_value(m, elem_type, tav.value, allow_local, is_rodata).value;
|
||||
}
|
||||
LLVMTypeRef et = lb_type(m, elem_type);
|
||||
|
||||
@@ -1075,7 +1095,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
|
||||
i32 index = field_remapping[f->Variable.field_index];
|
||||
if (elem_type_can_be_constant(f->type)) {
|
||||
if (sel.index.count == 1) {
|
||||
values[index] = lb_const_value(m, f->type, tav.value, allow_local).value;
|
||||
values[index] = lb_const_value(m, f->type, tav.value, allow_local, is_rodata).value;
|
||||
visited[index] = true;
|
||||
} else {
|
||||
if (!visited[index]) {
|
||||
@@ -1117,7 +1137,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
|
||||
}
|
||||
}
|
||||
if (is_constant) {
|
||||
LLVMValueRef elem_value = lb_const_value(m, tav.type, tav.value, allow_local).value;
|
||||
LLVMValueRef elem_value = lb_const_value(m, tav.type, tav.value, allow_local, is_rodata).value;
|
||||
if (LLVMIsConstant(elem_value)) {
|
||||
values[index] = llvm_const_insert_value(m, values[index], elem_value, idx_list, idx_list_len);
|
||||
} else {
|
||||
@@ -1139,7 +1159,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
|
||||
|
||||
i32 index = field_remapping[f->Variable.field_index];
|
||||
if (elem_type_can_be_constant(f->type)) {
|
||||
values[index] = lb_const_value(m, f->type, val, allow_local).value;
|
||||
values[index] = lb_const_value(m, f->type, val, allow_local, is_rodata).value;
|
||||
visited[index] = true;
|
||||
}
|
||||
}
|
||||
@@ -1265,7 +1285,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
|
||||
|
||||
|
||||
TypeAndValue tav = fv->value->tav;
|
||||
LLVMValueRef val = lb_const_value(m, elem_type, tav.value, allow_local).value;
|
||||
LLVMValueRef val = lb_const_value(m, elem_type, tav.value, allow_local, is_rodata).value;
|
||||
for (i64 k = lo; k < hi; k++) {
|
||||
i64 offset = matrix_row_major_index_to_offset(type, k);
|
||||
GB_ASSERT(values[offset] == nullptr);
|
||||
@@ -1277,7 +1297,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
|
||||
i64 index = exact_value_to_i64(index_tav.value);
|
||||
GB_ASSERT(index < max_count);
|
||||
TypeAndValue tav = fv->value->tav;
|
||||
LLVMValueRef val = lb_const_value(m, elem_type, tav.value, allow_local).value;
|
||||
LLVMValueRef val = lb_const_value(m, elem_type, tav.value, allow_local, is_rodata).value;
|
||||
i64 offset = matrix_row_major_index_to_offset(type, index);
|
||||
GB_ASSERT(values[offset] == nullptr);
|
||||
values[offset] = val;
|
||||
@@ -1290,7 +1310,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
|
||||
}
|
||||
}
|
||||
|
||||
res.value = lb_build_constant_array_values(m, type, elem_type, cast(isize)total_count, values, allow_local);
|
||||
res.value = lb_build_constant_array_values(m, type, elem_type, cast(isize)total_count, values, allow_local, is_rodata);
|
||||
return res;
|
||||
} else {
|
||||
GB_ASSERT_MSG(elem_count == max_count, "%td != %td", elem_count, max_count);
|
||||
@@ -1301,7 +1321,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
|
||||
GB_ASSERT(tav.mode != Addressing_Invalid);
|
||||
i64 offset = 0;
|
||||
offset = matrix_row_major_index_to_offset(type, i);
|
||||
values[offset] = lb_const_value(m, elem_type, tav.value, allow_local).value;
|
||||
values[offset] = lb_const_value(m, elem_type, tav.value, allow_local, is_rodata).value;
|
||||
}
|
||||
for (isize i = 0; i < total_count; i++) {
|
||||
if (values[i] == nullptr) {
|
||||
@@ -1309,7 +1329,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
|
||||
}
|
||||
}
|
||||
|
||||
res.value = lb_build_constant_array_values(m, type, elem_type, cast(isize)total_count, values, allow_local);
|
||||
res.value = lb_build_constant_array_values(m, type, elem_type, cast(isize)total_count, values, allow_local, is_rodata);
|
||||
return res;
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -1187,6 +1187,7 @@ gb_internal void add_debug_info_for_global_constant_from_entity(lbGenerator *gen
|
||||
if (USE_SEPARATE_MODULES) {
|
||||
m = lb_module_of_entity(gen, e);
|
||||
}
|
||||
GB_ASSERT(m != nullptr);
|
||||
|
||||
if (is_type_integer(e->type)) {
|
||||
ExactValue const &value = e->Constant.value;
|
||||
|
||||
+97
-24
@@ -296,12 +296,6 @@ gb_internal bool lb_try_direct_vector_arith(lbProcedure *p, TokenKind op, lbValu
|
||||
GB_ASSERT(vector_type0 == vector_type1);
|
||||
LLVMTypeRef vector_type = vector_type0;
|
||||
|
||||
LLVMValueRef lhs_vp = LLVMBuildPointerCast(p->builder, lhs_ptr.value, LLVMPointerType(vector_type, 0), "");
|
||||
LLVMValueRef rhs_vp = LLVMBuildPointerCast(p->builder, rhs_ptr.value, LLVMPointerType(vector_type, 0), "");
|
||||
LLVMValueRef x = LLVMBuildLoad2(p->builder, vector_type, lhs_vp, "");
|
||||
LLVMValueRef y = LLVMBuildLoad2(p->builder, vector_type, rhs_vp, "");
|
||||
LLVMValueRef z = nullptr;
|
||||
|
||||
Type *integral_type = base_type(elem_type);
|
||||
if (is_type_simd_vector(integral_type)) {
|
||||
integral_type = core_array_type(integral_type);
|
||||
@@ -311,8 +305,18 @@ gb_internal bool lb_try_direct_vector_arith(lbProcedure *p, TokenKind op, lbValu
|
||||
case Token_Add: op = Token_Or; break;
|
||||
case Token_Sub: op = Token_AndNot; break;
|
||||
}
|
||||
Type *u = bit_set_to_int(type);
|
||||
if (is_type_array(u)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
LLVMValueRef lhs_vp = LLVMBuildPointerCast(p->builder, lhs_ptr.value, LLVMPointerType(vector_type, 0), "");
|
||||
LLVMValueRef rhs_vp = LLVMBuildPointerCast(p->builder, rhs_ptr.value, LLVMPointerType(vector_type, 0), "");
|
||||
LLVMValueRef x = LLVMBuildLoad2(p->builder, vector_type, lhs_vp, "");
|
||||
LLVMValueRef y = LLVMBuildLoad2(p->builder, vector_type, rhs_vp, "");
|
||||
LLVMValueRef z = nullptr;
|
||||
|
||||
if (is_type_float(integral_type)) {
|
||||
switch (op) {
|
||||
case Token_Add:
|
||||
@@ -1286,6 +1290,14 @@ handle_op:;
|
||||
case Token_Add: op = Token_Or; break;
|
||||
case Token_Sub: op = Token_AndNot; break;
|
||||
}
|
||||
Type *u = bit_set_to_int(type);
|
||||
if (is_type_array(u)) {
|
||||
lhs.type = u;
|
||||
rhs.type = u;
|
||||
res = lb_emit_arith(p, op, lhs, rhs, u);
|
||||
res.type = type;
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
Type *integral_type = type;
|
||||
@@ -1441,6 +1453,7 @@ gb_internal lbValue lb_build_binary_in(lbProcedure *p, lbValue left, lbValue rig
|
||||
GB_ASSERT(are_types_identical(left.type, key_type));
|
||||
|
||||
Type *it = bit_set_to_int(rt);
|
||||
|
||||
left = lb_emit_conv(p, left, it);
|
||||
if (is_type_different_to_arch_endianness(it)) {
|
||||
left = lb_emit_byte_swap(p, left, integer_endian_type_to_platform_type(it));
|
||||
@@ -2054,6 +2067,26 @@ gb_internal lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
|
||||
}
|
||||
}
|
||||
|
||||
// bit_set <-> backing type
|
||||
if (is_type_bit_set(src)) {
|
||||
Type *backing = bit_set_to_int(src);
|
||||
if (are_types_identical(backing, dst)) {
|
||||
lbValue res = {};
|
||||
res.type = t;
|
||||
res.value = value.value;
|
||||
return res;
|
||||
}
|
||||
}
|
||||
if (is_type_bit_set(dst)) {
|
||||
Type *backing = bit_set_to_int(dst);
|
||||
if (are_types_identical(src, backing)) {
|
||||
lbValue res = {};
|
||||
res.type = t;
|
||||
res.value = value.value;
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Pointer <-> uintptr
|
||||
if (is_type_pointer(src) && is_type_uintptr(dst)) {
|
||||
@@ -2491,9 +2524,16 @@ gb_internal lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left
|
||||
if (are_types_identical(a, b)) {
|
||||
// NOTE(bill): No need for a conversion
|
||||
} else if (lb_is_const(left) || lb_is_const_nil(left)) {
|
||||
if (lb_is_const_nil(left)) {
|
||||
return lb_emit_comp_against_nil(p, op_kind, right);
|
||||
}
|
||||
left = lb_emit_conv(p, left, right.type);
|
||||
} else if (lb_is_const(right) || lb_is_const_nil(right)) {
|
||||
if (lb_is_const_nil(right)) {
|
||||
return lb_emit_comp_against_nil(p, op_kind, left);
|
||||
}
|
||||
right = lb_emit_conv(p, right, left.type);
|
||||
|
||||
} else {
|
||||
Type *lt = left.type;
|
||||
Type *rt = right.type;
|
||||
@@ -2951,13 +2991,32 @@ gb_internal lbValue lb_emit_comp_against_nil(lbProcedure *p, TokenKind op_kind,
|
||||
case Type_Pointer:
|
||||
case Type_MultiPointer:
|
||||
case Type_Proc:
|
||||
case Type_BitSet:
|
||||
if (op_kind == Token_CmpEq) {
|
||||
res.value = LLVMBuildIsNull(p->builder, x.value, "");
|
||||
} else if (op_kind == Token_NotEq) {
|
||||
res.value = LLVMBuildIsNotNull(p->builder, x.value, "");
|
||||
}
|
||||
return res;
|
||||
case Type_BitSet:
|
||||
{
|
||||
Type *u = bit_set_to_int(bt);
|
||||
if (is_type_array(u)) {
|
||||
auto args = array_make<lbValue>(permanent_allocator(), 2);
|
||||
lbValue lhs = lb_address_from_load_or_generate_local(p, x);
|
||||
args[0] = lb_emit_conv(p, lhs, t_rawptr);
|
||||
args[1] = lb_const_int(p->module, t_int, type_size_of(t));
|
||||
lbValue val = lb_emit_runtime_call(p, "memory_compare_zero", args);
|
||||
lbValue res = lb_emit_comp(p, op_kind, val, lb_const_int(p->module, t_int, 0));
|
||||
return res;
|
||||
} else {
|
||||
if (op_kind == Token_CmpEq) {
|
||||
res.value = LLVMBuildIsNull(p->builder, x.value, "");
|
||||
} else if (op_kind == Token_NotEq) {
|
||||
res.value = LLVMBuildIsNotNull(p->builder, x.value, "");
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
case Type_Slice:
|
||||
{
|
||||
@@ -4878,29 +4937,43 @@ gb_internal lbAddr lb_build_addr_compound_lit(lbProcedure *p, Ast *expr) {
|
||||
case Type_BitSet: {
|
||||
i64 sz = type_size_of(type);
|
||||
if (cl->elems.count > 0 && sz > 0) {
|
||||
lb_addr_store(p, v, lb_const_value(p->module, type, exact_value_compound(expr)));
|
||||
|
||||
lbValue lower = lb_const_value(p->module, t_int, exact_value_i64(bt->BitSet.lower));
|
||||
for (Ast *elem : cl->elems) {
|
||||
GB_ASSERT(elem->kind != Ast_FieldValue);
|
||||
|
||||
if (lb_is_elem_const(elem, et)) {
|
||||
continue;
|
||||
Type *backing = bit_set_to_int(type);
|
||||
if (is_type_array(backing)) {
|
||||
GB_PANIC("TODO: bit_set [N]T");
|
||||
Type *base_it = core_array_type(backing);
|
||||
i64 bits_per_elem = 8*type_size_of(base_it);
|
||||
gb_unused(bits_per_elem);
|
||||
lbValue one = lb_const_value(p->module, t_i64, exact_value_i64(1));
|
||||
for (Ast *elem : cl->elems) {
|
||||
GB_ASSERT(elem->kind != Ast_FieldValue);
|
||||
lbValue expr = lb_build_expr(p, elem);
|
||||
GB_ASSERT(expr.type->kind != Type_Tuple);
|
||||
|
||||
lbValue e = lb_emit_conv(p, expr, t_i64);
|
||||
e = lb_emit_arith(p, Token_Sub, e, lower, t_i64);
|
||||
// lbValue idx = lb_emit_arith(p, Token_Div, e, bits_per_elem, t_i64);
|
||||
// lbValue val = lb_emit_arith(p, Token_Div, e, bits_per_elem, t_i64);
|
||||
}
|
||||
|
||||
lbValue expr = lb_build_expr(p, elem);
|
||||
GB_ASSERT(expr.type->kind != Type_Tuple);
|
||||
|
||||
} else {
|
||||
Type *it = bit_set_to_int(bt);
|
||||
lbValue one = lb_const_value(p->module, it, exact_value_i64(1));
|
||||
lbValue e = lb_emit_conv(p, expr, it);
|
||||
e = lb_emit_arith(p, Token_Sub, e, lower, it);
|
||||
e = lb_emit_arith(p, Token_Shl, one, e, it);
|
||||
for (Ast *elem : cl->elems) {
|
||||
GB_ASSERT(elem->kind != Ast_FieldValue);
|
||||
|
||||
lbValue old_value = lb_emit_transmute(p, lb_addr_load(p, v), it);
|
||||
lbValue new_value = lb_emit_arith(p, Token_Or, old_value, e, it);
|
||||
new_value = lb_emit_transmute(p, new_value, type);
|
||||
lb_addr_store(p, v, new_value);
|
||||
lbValue expr = lb_build_expr(p, elem);
|
||||
GB_ASSERT(expr.type->kind != Type_Tuple);
|
||||
|
||||
lbValue e = lb_emit_conv(p, expr, it);
|
||||
e = lb_emit_arith(p, Token_Sub, e, lower, it);
|
||||
e = lb_emit_arith(p, Token_Shl, one, e, it);
|
||||
|
||||
lbValue old_value = lb_emit_transmute(p, lb_addr_load(p, v), it);
|
||||
lbValue new_value = lb_emit_arith(p, Token_Or, old_value, e, it);
|
||||
new_value = lb_emit_transmute(p, new_value, type);
|
||||
lb_addr_store(p, v, new_value);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -29,8 +29,9 @@ gb_internal void lb_init_module(lbModule *m, Checker *c) {
|
||||
module_name = gb_string_appendc(module_name, "-builtin");
|
||||
}
|
||||
|
||||
m->module_name = module_name ? module_name : "odin_package";
|
||||
m->ctx = LLVMContextCreate();
|
||||
m->mod = LLVMModuleCreateWithNameInContext(module_name ? module_name : "odin_package", m->ctx);
|
||||
m->mod = LLVMModuleCreateWithNameInContext(m->module_name, m->ctx);
|
||||
// m->debug_builder = nullptr;
|
||||
if (build_context.ODIN_DEBUG) {
|
||||
enum {DEBUG_METADATA_VERSION = 3};
|
||||
@@ -71,7 +72,7 @@ gb_internal void lb_init_module(lbModule *m, Checker *c) {
|
||||
map_init(&m->hasher_procs);
|
||||
map_init(&m->map_get_procs);
|
||||
map_init(&m->map_set_procs);
|
||||
if (build_context.use_separate_modules) {
|
||||
if (USE_SEPARATE_MODULES) {
|
||||
array_init(&m->procedures_to_generate, a, 0, 1<<10);
|
||||
map_init(&m->procedure_values, 1<<11);
|
||||
} else {
|
||||
@@ -118,15 +119,17 @@ gb_internal bool lb_init_generator(lbGenerator *gen, Checker *c) {
|
||||
map_init(&gen->anonymous_proc_lits, 1024);
|
||||
|
||||
if (USE_SEPARATE_MODULES) {
|
||||
bool module_per_file = build_context.module_per_file && build_context.optimization_level <= 0;
|
||||
for (auto const &entry : gen->info->packages) {
|
||||
AstPackage *pkg = entry.value;
|
||||
#if 1
|
||||
auto m = gb_alloc_item(permanent_allocator(), lbModule);
|
||||
m->pkg = pkg;
|
||||
m->gen = gen;
|
||||
map_set(&gen->modules, cast(void *)pkg, m);
|
||||
lb_init_module(m, c);
|
||||
#else
|
||||
if (!module_per_file) {
|
||||
continue;
|
||||
}
|
||||
// NOTE(bill): Probably per file is not a good idea, so leave this for later
|
||||
for (AstFile *file : pkg->files) {
|
||||
auto m = gb_alloc_item(permanent_allocator(), lbModule);
|
||||
@@ -136,7 +139,6 @@ gb_internal bool lb_init_generator(lbGenerator *gen, Checker *c) {
|
||||
map_set(&gen->modules, cast(void *)file, m);
|
||||
lb_init_module(m, c);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,13 +146,14 @@ gb_internal bool lb_init_generator(lbGenerator *gen, Checker *c) {
|
||||
map_set(&gen->modules, cast(void *)1, &gen->default_module);
|
||||
lb_init_module(&gen->default_module, c);
|
||||
|
||||
|
||||
for (auto const &entry : gen->modules) {
|
||||
lbModule *m = entry.value;
|
||||
LLVMContextRef ctx = LLVMGetModuleContext(m->mod);
|
||||
map_set(&gen->modules_through_ctx, ctx, m);
|
||||
}
|
||||
|
||||
mpsc_init(&gen->entities_to_correct_linkage, heap_allocator());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -387,12 +390,14 @@ gb_internal lbModule *lb_module_of_entity(lbGenerator *gen, Entity *e) {
|
||||
if (e->file) {
|
||||
found = map_get(&gen->modules, cast(void *)e->file);
|
||||
if (found) {
|
||||
GB_ASSERT(*found != nullptr);
|
||||
return *found;
|
||||
}
|
||||
}
|
||||
if (e->pkg) {
|
||||
found = map_get(&gen->modules, cast(void *)e->pkg);
|
||||
if (found) {
|
||||
GB_ASSERT(*found != nullptr);
|
||||
return *found;
|
||||
}
|
||||
}
|
||||
@@ -1011,13 +1016,15 @@ gb_internal void lb_emit_store(lbProcedure *p, lbValue ptr, lbValue value) {
|
||||
return;
|
||||
}
|
||||
|
||||
Type *a = type_deref(ptr.type);
|
||||
Type *a = type_deref(ptr.type, true);
|
||||
if (LLVMIsNull(value.value)) {
|
||||
LLVMTypeRef src_t = llvm_addr_type(p->module, ptr);
|
||||
if (is_type_proc(a)) {
|
||||
LLVMTypeRef rawptr_type = lb_type(p->module, t_rawptr);
|
||||
LLVMTypeRef rawptr_ptr_type = LLVMPointerType(rawptr_type, 0);
|
||||
LLVMBuildStore(p->builder, LLVMConstNull(rawptr_type), LLVMBuildBitCast(p->builder, ptr.value, rawptr_ptr_type, ""));
|
||||
} else if (is_type_bit_set(a)) {
|
||||
lb_mem_zero_ptr(p, ptr.value, a, 1);
|
||||
} else if (lb_sizeof(src_t) <= lb_max_zero_init_size()) {
|
||||
LLVMBuildStore(p->builder, LLVMConstNull(src_t), ptr.value);
|
||||
} else {
|
||||
@@ -1105,13 +1112,17 @@ gb_internal lbValue lb_emit_load(lbProcedure *p, lbValue value) {
|
||||
return lb_addr_load(p, addr);
|
||||
}
|
||||
|
||||
GB_ASSERT(is_type_pointer(value.type));
|
||||
GB_ASSERT_MSG(is_type_pointer(value.type), "%s", type_to_string(value.type));
|
||||
Type *t = type_deref(value.type);
|
||||
LLVMValueRef v = LLVMBuildLoad2(p->builder, lb_type(p->module, t), value.value, "");
|
||||
|
||||
u64 is_packed = lb_get_metadata_custom_u64(p->module, value.value, ODIN_METADATA_IS_PACKED);
|
||||
if (is_packed != 0) {
|
||||
LLVMSetAlignment(v, 1);
|
||||
// If it is not an instruction it isn't a GEP, so we don't need to track alignment in the metadata,
|
||||
// which is not possible anyway (only LLVM instructions can have metadata).
|
||||
if (LLVMIsAInstruction(value.value)) {
|
||||
u64 is_packed = lb_get_metadata_custom_u64(p->module, value.value, ODIN_METADATA_IS_PACKED);
|
||||
if (is_packed != 0) {
|
||||
LLVMSetAlignment(v, 1);
|
||||
}
|
||||
}
|
||||
|
||||
return lbValue{v, t};
|
||||
@@ -1159,11 +1170,12 @@ gb_internal lbValue lb_addr_load(lbProcedure *p, lbAddr const &addr) {
|
||||
r = lb_addr_load(p, dst);
|
||||
r.value = LLVMBuildShl(p->builder, r.value, shift_amount, "");
|
||||
} else if ((addr.bitfield.bit_offset % 8) == 0) {
|
||||
do_mask = 8*dst_byte_size != addr.bitfield.bit_size;
|
||||
|
||||
lbValue copy_size = byte_size;
|
||||
lbValue src_offset = lb_emit_conv(p, src, t_u8_ptr);
|
||||
src_offset = lb_emit_ptr_offset(p, src_offset, byte_offset);
|
||||
if (addr.bitfield.bit_offset + 8*dst_byte_size <= total_bitfield_bit_size) {
|
||||
do_mask = true;
|
||||
copy_size = lb_const_int(p->module, t_uintptr, dst_byte_size);
|
||||
}
|
||||
lb_mem_copy_non_overlapping(p, dst.addr, src_offset, copy_size, false);
|
||||
@@ -1525,7 +1537,7 @@ gb_internal void lb_clone_struct_type(LLVMTypeRef dst, LLVMTypeRef src) {
|
||||
LLVMStructSetBody(dst, fields, field_count, LLVMIsPackedStruct(src));
|
||||
}
|
||||
|
||||
gb_internal String lb_mangle_name(lbModule *m, Entity *e) {
|
||||
gb_internal String lb_mangle_name(Entity *e) {
|
||||
String name = e->token.string;
|
||||
|
||||
AstPackage *pkg = e->pkg;
|
||||
@@ -1625,6 +1637,7 @@ gb_internal String lb_set_nested_type_name_ir_mangled_name(Entity *e, lbProcedur
|
||||
}
|
||||
|
||||
gb_internal String lb_get_entity_name(lbModule *m, Entity *e, String default_name) {
|
||||
GB_ASSERT(m != nullptr);
|
||||
if (e != nullptr && e->kind == Entity_TypeName && e->TypeName.ir_mangled_name.len != 0) {
|
||||
return e->TypeName.ir_mangled_name;
|
||||
}
|
||||
@@ -1656,7 +1669,7 @@ gb_internal String lb_get_entity_name(lbModule *m, Entity *e, String default_nam
|
||||
}
|
||||
|
||||
if (!no_name_mangle) {
|
||||
name = lb_mangle_name(m, e);
|
||||
name = lb_mangle_name(e);
|
||||
}
|
||||
if (name.len == 0) {
|
||||
name = e->token.string;
|
||||
@@ -2519,6 +2532,12 @@ gb_internal void lb_add_proc_attribute_at_index(lbProcedure *p, isize index, cha
|
||||
gb_internal void lb_add_attribute_to_proc(lbModule *m, LLVMValueRef proc_value, char const *name, u64 value=0) {
|
||||
LLVMAddAttributeAtIndex(proc_value, LLVMAttributeIndex_FunctionIndex, lb_create_enum_attribute(m->ctx, name, value));
|
||||
}
|
||||
|
||||
gb_internal bool lb_proc_has_attribute(lbModule *m, LLVMValueRef proc_value, char const *name) {
|
||||
LLVMAttributeRef ref = LLVMGetEnumAttributeAtIndex(proc_value, LLVMAttributeIndex_FunctionIndex, LLVMGetEnumAttributeKindForName(name, gb_strlen(name)));
|
||||
return ref != nullptr;
|
||||
}
|
||||
|
||||
gb_internal void lb_add_attribute_to_proc_with_string(lbModule *m, LLVMValueRef proc_value, String const &name, String const &value) {
|
||||
LLVMAttributeRef attr = lb_create_string_attribute(m->ctx, name, value);
|
||||
LLVMAddAttributeAtIndex(proc_value, LLVMAttributeIndex_FunctionIndex, attr);
|
||||
@@ -3034,7 +3053,7 @@ gb_internal lbValue lb_find_value_from_entity(lbModule *m, Entity *e) {
|
||||
if (e->code_gen_module != nullptr) {
|
||||
other_module = e->code_gen_module;
|
||||
} else {
|
||||
other_module = nullptr;
|
||||
other_module = &m->gen->default_module;
|
||||
}
|
||||
is_external = other_module != m;
|
||||
}
|
||||
@@ -3052,8 +3071,6 @@ gb_internal lbValue lb_find_value_from_entity(lbModule *m, Entity *e) {
|
||||
|
||||
lb_set_entity_from_other_modules_linkage_correctly(other_module, e, name);
|
||||
|
||||
// LLVMSetLinkage(other_g.value, LLVMExternalLinkage);
|
||||
|
||||
if (e->Variable.thread_local_model != "") {
|
||||
LLVMSetThreadLocal(g.value, true);
|
||||
|
||||
@@ -3077,7 +3094,9 @@ gb_internal lbValue lb_find_value_from_entity(lbModule *m, Entity *e) {
|
||||
return g;
|
||||
}
|
||||
}
|
||||
GB_PANIC("\n\tError in: %s, missing value '%.*s'\n", token_pos_to_string(e->token.pos), LIT(e->token.string));
|
||||
|
||||
GB_PANIC("\n\tError in: %s, missing value '%.*s' in module %s\n",
|
||||
token_pos_to_string(e->token.pos), LIT(e->token.string), m->module_name);
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
@@ -396,18 +396,22 @@ gb_internal LLVMValueRef lb_run_instrumentation_pass_insert_call(lbProcedure *p,
|
||||
lbValue cc = lb_find_procedure_value_from_entity(m, entity);
|
||||
|
||||
LLVMValueRef args[3] = {};
|
||||
args[0] = p->value;
|
||||
args[0] = LLVMConstPointerCast(p->value, lb_type(m, t_rawptr));
|
||||
|
||||
LLVMValueRef returnaddress_args[1] = {};
|
||||
if (is_arch_wasm()) {
|
||||
args[1] = LLVMConstPointerNull(lb_type(m, t_rawptr));
|
||||
} else {
|
||||
LLVMValueRef returnaddress_args[1] = {};
|
||||
|
||||
returnaddress_args[0] = LLVMConstInt(LLVMInt32TypeInContext(m->ctx), 0, false);
|
||||
returnaddress_args[0] = LLVMConstInt(LLVMInt32TypeInContext(m->ctx), 0, false);
|
||||
|
||||
char const *instrinsic_name = "llvm.returnaddress";
|
||||
unsigned id = LLVMLookupIntrinsicID(instrinsic_name, gb_strlen(instrinsic_name));
|
||||
GB_ASSERT_MSG(id != 0, "Unable to find %s", instrinsic_name);
|
||||
LLVMValueRef ip = LLVMGetIntrinsicDeclaration(m->mod, id, nullptr, 0);
|
||||
LLVMTypeRef call_type = LLVMIntrinsicGetType(m->ctx, id, nullptr, 0);
|
||||
args[1] = LLVMBuildCall2(dummy_builder, call_type, ip, returnaddress_args, gb_count_of(returnaddress_args), "");
|
||||
char const *instrinsic_name = "llvm.returnaddress";
|
||||
unsigned id = LLVMLookupIntrinsicID(instrinsic_name, gb_strlen(instrinsic_name));
|
||||
GB_ASSERT_MSG(id != 0, "Unable to find %s", instrinsic_name);
|
||||
LLVMValueRef ip = LLVMGetIntrinsicDeclaration(m->mod, id, nullptr, 0);
|
||||
LLVMTypeRef call_type = LLVMIntrinsicGetType(m->ctx, id, nullptr, 0);
|
||||
args[1] = LLVMBuildCall2(dummy_builder, call_type, ip, returnaddress_args, gb_count_of(returnaddress_args), "");
|
||||
}
|
||||
|
||||
Token name = {};
|
||||
if (p->entity) {
|
||||
|
||||
+99
-11
@@ -159,20 +159,19 @@ gb_internal lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool i
|
||||
case ProcInlining_no_inline:
|
||||
lb_add_attribute_to_proc(m, p->value, "noinline");
|
||||
break;
|
||||
default:
|
||||
if (build_context.internal_no_inline) {
|
||||
lb_add_attribute_to_proc(m, p->value, "noinline");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
switch (entity->Procedure.optimization_mode) {
|
||||
case ProcedureOptimizationMode_None:
|
||||
break;
|
||||
case ProcedureOptimizationMode_Minimal:
|
||||
lb_add_attribute_to_proc(m, p->value, "optnone");
|
||||
lb_add_attribute_to_proc(m, p->value, "noinline");
|
||||
break;
|
||||
case ProcedureOptimizationMode_Size:
|
||||
lb_add_attribute_to_proc(m, p->value, "optsize");
|
||||
break;
|
||||
case ProcedureOptimizationMode_Speed:
|
||||
// TODO(bill): handle this correctly
|
||||
case ProcedureOptimizationMode_FavorSize:
|
||||
lb_add_attribute_to_proc(m, p->value, "optsize");
|
||||
break;
|
||||
}
|
||||
@@ -259,6 +258,11 @@ gb_internal lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool i
|
||||
if (e->flags&EntityFlag_NoAlias) {
|
||||
lb_add_proc_attribute_at_index(p, offset+parameter_index, "noalias");
|
||||
}
|
||||
if (e->flags&EntityFlag_NoCapture) {
|
||||
if (is_type_internally_pointer_like(e->type)) {
|
||||
lb_add_proc_attribute_at_index(p, offset+parameter_index, "nocapture");
|
||||
}
|
||||
}
|
||||
parameter_index += 1;
|
||||
}
|
||||
}
|
||||
@@ -523,6 +527,7 @@ gb_internal void lb_begin_procedure_body(lbProcedure *p) {
|
||||
lb_start_block(p, p->entry_block);
|
||||
|
||||
map_init(&p->direct_parameters);
|
||||
p->variadic_reuses.allocator = heap_allocator();
|
||||
|
||||
GB_ASSERT(p->type != nullptr);
|
||||
|
||||
@@ -2280,6 +2285,39 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
|
||||
return res;
|
||||
}
|
||||
|
||||
case BuiltinProc_add_sat:
|
||||
case BuiltinProc_sub_sat:
|
||||
{
|
||||
Type *main_type = tv.type;
|
||||
Type *type = main_type;
|
||||
|
||||
lbValue x = lb_build_expr(p, ce->args[0]);
|
||||
lbValue y = lb_build_expr(p, ce->args[1]);
|
||||
x = lb_emit_conv(p, x, type);
|
||||
y = lb_emit_conv(p, y, type);
|
||||
|
||||
char const *name = nullptr;
|
||||
if (is_type_unsigned(type)) {
|
||||
switch (id) {
|
||||
case BuiltinProc_add_sat: name = "llvm.uadd.sat"; break;
|
||||
case BuiltinProc_sub_sat: name = "llvm.usub.sat"; break;
|
||||
}
|
||||
} else {
|
||||
switch (id) {
|
||||
case BuiltinProc_add_sat: name = "llvm.sadd.sat"; break;
|
||||
case BuiltinProc_sub_sat: name = "llvm.ssub.sat"; break;
|
||||
}
|
||||
}
|
||||
LLVMTypeRef types[1] = {lb_type(p->module, type)};
|
||||
|
||||
LLVMValueRef args[2] = { x.value, y.value };
|
||||
|
||||
lbValue res = {};
|
||||
res.value = lb_call_intrinsic(p, name, args, gb_count_of(args), types, gb_count_of(types));
|
||||
res.type = type;
|
||||
return res;
|
||||
}
|
||||
|
||||
case BuiltinProc_sqrt:
|
||||
{
|
||||
Type *type = tv.type;
|
||||
@@ -3456,17 +3494,67 @@ gb_internal lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr) {
|
||||
}
|
||||
isize slice_len = var_args.count;
|
||||
if (slice_len > 0) {
|
||||
lbAddr slice = lb_add_local_generated(p, slice_type, true);
|
||||
lbAddr base_array = lb_add_local_generated(p, alloc_type_array(elem_type, slice_len), true);
|
||||
lbAddr slice = {};
|
||||
|
||||
for (auto const &vr : p->variadic_reuses) {
|
||||
if (are_types_identical(vr.slice_type, slice_type)) {
|
||||
slice = vr.slice_addr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
DeclInfo *d = decl_info_of_entity(p->entity);
|
||||
if (d != nullptr && slice.addr.value == nullptr) {
|
||||
for (auto const &vr : d->variadic_reuses) {
|
||||
if (are_types_identical(vr.slice_type, slice_type)) {
|
||||
#if LLVM_VERSION_MAJOR >= 13
|
||||
// NOTE(bill): No point wasting even more memory, just reuse this stack variable too
|
||||
if (p->variadic_reuses.count > 0) {
|
||||
slice = p->variadic_reuses[0].slice_addr;
|
||||
} else {
|
||||
slice = lb_add_local_generated(p, slice_type, true);
|
||||
}
|
||||
// NOTE(bill): Change the underlying type to match the specific type
|
||||
slice.addr.type = alloc_type_pointer(slice_type);
|
||||
#else
|
||||
slice = lb_add_local_generated(p, slice_type, true);
|
||||
#endif
|
||||
array_add(&p->variadic_reuses, lbVariadicReuseSlices{slice_type, slice});
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lbValue base_array_ptr = p->variadic_reuse_base_array_ptr.addr;
|
||||
if (base_array_ptr.value == nullptr) {
|
||||
if (d != nullptr) {
|
||||
i64 max_bytes = d->variadic_reuse_max_bytes;
|
||||
i64 max_align = gb_max(d->variadic_reuse_max_align, 16);
|
||||
p->variadic_reuse_base_array_ptr = lb_add_local_generated(p, alloc_type_array(t_u8, max_bytes), true);
|
||||
lb_try_update_alignment(p->variadic_reuse_base_array_ptr.addr, cast(unsigned)max_align);
|
||||
base_array_ptr = p->variadic_reuse_base_array_ptr.addr;
|
||||
} else {
|
||||
base_array_ptr = lb_add_local_generated(p, alloc_type_array(elem_type, slice_len), true).addr;
|
||||
}
|
||||
}
|
||||
|
||||
if (slice.addr.value == nullptr) {
|
||||
slice = lb_add_local_generated(p, slice_type, true);
|
||||
}
|
||||
|
||||
GB_ASSERT(base_array_ptr.value != nullptr);
|
||||
GB_ASSERT(slice.addr.value != nullptr);
|
||||
|
||||
base_array_ptr = lb_emit_conv(p, base_array_ptr, alloc_type_pointer(alloc_type_array(elem_type, slice_len)));
|
||||
|
||||
for (isize i = 0; i < var_args.count; i++) {
|
||||
lbValue addr = lb_emit_array_epi(p, base_array.addr, cast(i32)i);
|
||||
lbValue addr = lb_emit_array_epi(p, base_array_ptr, cast(i32)i);
|
||||
lbValue var_arg = var_args[i];
|
||||
var_arg = lb_emit_conv(p, var_arg, elem_type);
|
||||
lb_emit_store(p, addr, var_arg);
|
||||
}
|
||||
|
||||
lbValue base_elem = lb_emit_array_epi(p, base_array.addr, 0);
|
||||
lbValue base_elem = lb_emit_array_epi(p, base_array_ptr, 0);
|
||||
lbValue len = lb_const_int(p->module, t_int, slice_len);
|
||||
lb_fill_slice(p, slice, base_elem, len);
|
||||
|
||||
|
||||
@@ -1579,7 +1579,8 @@ gb_internal void lb_store_type_case_implicit(lbProcedure *p, Ast *clause, lbValu
|
||||
lb_addr_store(p, x, value);
|
||||
} else {
|
||||
if (!is_default_case) {
|
||||
GB_ASSERT_MSG(are_types_identical(e->type, type_deref(value.type)), "%s %s", type_to_string(e->type), type_to_string(value.type));
|
||||
Type *clause_type = e->type;
|
||||
GB_ASSERT_MSG(are_types_identical(type_deref(clause_type), type_deref(value.type)), "%s %s", type_to_string(clause_type), type_to_string(value.type));
|
||||
}
|
||||
lb_add_entity(p->module, e, value);
|
||||
}
|
||||
@@ -1735,10 +1736,17 @@ gb_internal void lb_build_type_switch_stmt(lbProcedure *p, AstTypeSwitchStmt *ss
|
||||
|
||||
for (Ast *clause : body->stmts) {
|
||||
ast_node(cc, CaseClause, clause);
|
||||
|
||||
Entity *case_entity = implicit_entity_of_node(clause);
|
||||
lb_open_scope(p, cc->scope);
|
||||
|
||||
if (cc->list.count == 0) {
|
||||
lb_start_block(p, default_block);
|
||||
lb_store_type_case_implicit(p, clause, parent_value, true);
|
||||
if (case_entity->flags & EntityFlag_Value) {
|
||||
lb_store_type_case_implicit(p, clause, parent_value, true);
|
||||
} else {
|
||||
lb_store_type_case_implicit(p, clause, parent_ptr, true);
|
||||
}
|
||||
lb_type_case_body(p, ss->label, clause, p->curr_block, done);
|
||||
continue;
|
||||
}
|
||||
@@ -1768,7 +1776,6 @@ gb_internal void lb_build_type_switch_stmt(lbProcedure *p, AstTypeSwitchStmt *ss
|
||||
LLVMAddCase(switch_instr, on_val.value, body->block);
|
||||
}
|
||||
|
||||
Entity *case_entity = implicit_entity_of_node(clause);
|
||||
|
||||
lb_start_block(p, body);
|
||||
|
||||
@@ -1781,6 +1788,7 @@ gb_internal void lb_build_type_switch_stmt(lbProcedure *p, AstTypeSwitchStmt *ss
|
||||
} else if (switch_kind == TypeSwitch_Any) {
|
||||
data = lb_emit_load(p, lb_emit_struct_ep(p, parent_ptr, 0));
|
||||
}
|
||||
GB_ASSERT(is_type_pointer(data.type));
|
||||
|
||||
Type *ct = case_entity->type;
|
||||
Type *ct_ptr = alloc_type_pointer(ct);
|
||||
|
||||
+28
-27
@@ -421,7 +421,7 @@ gb_internal void lb_setup_type_info_data_giant_array(lbModule *m, i64 global_typ
|
||||
}
|
||||
TokenPos pos = t->Named.type_name->token.pos;
|
||||
|
||||
lbValue loc = lb_const_source_code_location_const(m, proc_name, pos);
|
||||
lbValue loc = lb_const_source_code_location_as_global_ptr(m, proc_name, pos);
|
||||
|
||||
LLVMValueRef vals[4] = {
|
||||
lb_const_string(m, t->Named.type_name->token.string).value,
|
||||
@@ -810,19 +810,18 @@ gb_internal void lb_setup_type_info_data_giant_array(lbModule *m, i64 global_typ
|
||||
case Type_Struct: {
|
||||
tag_type = t_type_info_struct;
|
||||
|
||||
LLVMValueRef vals[13] = {};
|
||||
LLVMValueRef vals[11] = {};
|
||||
|
||||
{
|
||||
lbValue is_packed = lb_const_bool(m, t_bool, t->Struct.is_packed);
|
||||
lbValue is_raw_union = lb_const_bool(m, t_bool, t->Struct.is_raw_union);
|
||||
lbValue is_no_copy = lb_const_bool(m, t_bool, t->Struct.is_no_copy);
|
||||
lbValue is_custom_align = lb_const_bool(m, t_bool, t->Struct.custom_align != 0);
|
||||
vals[5] = is_packed.value;
|
||||
vals[6] = is_raw_union.value;
|
||||
vals[7] = is_no_copy.value;
|
||||
vals[8] = is_custom_align.value;
|
||||
u8 flags = 0;
|
||||
if (t->Struct.is_packed) flags |= 1<<0;
|
||||
if (t->Struct.is_raw_union) flags |= 1<<1;
|
||||
if (t->Struct.is_no_copy) flags |= 1<<2;
|
||||
if (t->Struct.custom_align) flags |= 1<<3;
|
||||
|
||||
vals[6] = lb_const_int(m, t_u8, flags).value;
|
||||
if (is_type_comparable(t) && !is_type_simple_compare(t)) {
|
||||
vals[9] = lb_equal_proc_for_type(m, t).value;
|
||||
vals[10] = lb_equal_proc_for_type(m, t).value;
|
||||
}
|
||||
|
||||
|
||||
@@ -831,11 +830,11 @@ gb_internal void lb_setup_type_info_data_giant_array(lbModule *m, i64 global_typ
|
||||
|
||||
lbValue soa_kind = lb_const_value(m, kind_type, exact_value_i64(t->Struct.soa_kind));
|
||||
LLVMValueRef soa_type = get_type_info_ptr(m, t->Struct.soa_elem);
|
||||
lbValue soa_len = lb_const_int(m, t_int, t->Struct.soa_count);
|
||||
lbValue soa_len = lb_const_int(m, t_i32, t->Struct.soa_count);
|
||||
|
||||
vals[10] = soa_kind.value;
|
||||
vals[11] = soa_type;
|
||||
vals[12] = soa_len.value;
|
||||
vals[7] = soa_kind.value;
|
||||
vals[8] = soa_len.value;
|
||||
vals[9] = soa_type;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -882,12 +881,13 @@ gb_internal void lb_setup_type_info_data_giant_array(lbModule *m, i64 global_typ
|
||||
|
||||
}
|
||||
|
||||
lbValue cv = lb_const_int(m, t_int, count);
|
||||
vals[0] = llvm_const_slice(m, memory_types, cv);
|
||||
vals[1] = llvm_const_slice(m, memory_names, cv);
|
||||
vals[2] = llvm_const_slice(m, memory_offsets, cv);
|
||||
vals[3] = llvm_const_slice(m, memory_usings, cv);
|
||||
vals[4] = llvm_const_slice(m, memory_tags, cv);
|
||||
lbValue cv = lb_const_int(m, t_i32, count);
|
||||
vals[0] = memory_types.value;
|
||||
vals[1] = memory_names.value;
|
||||
vals[2] = memory_offsets.value;
|
||||
vals[3] = memory_usings.value;
|
||||
vals[4] = memory_tags.value;
|
||||
vals[5] = cv.value;
|
||||
}
|
||||
for (isize i = 0; i < gb_count_of(vals); i++) {
|
||||
if (vals[i] == nullptr) {
|
||||
@@ -994,7 +994,7 @@ gb_internal void lb_setup_type_info_data_giant_array(lbModule *m, i64 global_typ
|
||||
{
|
||||
tag_type = t_type_info_bit_field;
|
||||
|
||||
LLVMValueRef vals[6] = {};
|
||||
LLVMValueRef vals[7] = {};
|
||||
vals[0] = get_type_info_ptr(m, t->BitField.backing_type);
|
||||
isize count = t->BitField.fields.count;
|
||||
if (count > 0) {
|
||||
@@ -1035,11 +1035,12 @@ gb_internal void lb_setup_type_info_data_giant_array(lbModule *m, i64 global_typ
|
||||
}
|
||||
|
||||
lbValue cv = lb_const_int(m, t_int, count);
|
||||
vals[1] = llvm_const_slice(m, memory_names, cv);
|
||||
vals[2] = llvm_const_slice(m, memory_types, cv);
|
||||
vals[3] = llvm_const_slice(m, memory_bit_sizes, cv);
|
||||
vals[4] = llvm_const_slice(m, memory_bit_offsets, cv);
|
||||
vals[5] = llvm_const_slice(m, memory_tags, cv);
|
||||
vals[1] = memory_names.value;
|
||||
vals[2] = memory_types.value;
|
||||
vals[3] = memory_bit_sizes.value;
|
||||
vals[4] = memory_bit_offsets.value;
|
||||
vals[5] = memory_tags.value;
|
||||
vals[6] = cv.value;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -2029,7 +2029,11 @@ gb_internal void lb_set_wasm_procedure_import_attributes(LLVMValueRef value, Ent
|
||||
GB_ASSERT(foreign_library->LibraryName.paths.count == 1);
|
||||
|
||||
module_name = foreign_library->LibraryName.paths[0];
|
||||
|
||||
|
||||
if (string_ends_with(module_name, str_lit(".o"))) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (string_starts_with(import_name, module_name)) {
|
||||
import_name = substring(import_name, module_name.len+WASM_MODULE_NAME_SEPARATOR.len, import_name.len);
|
||||
}
|
||||
|
||||
+198
-39
@@ -61,6 +61,7 @@ gb_global Timings global_timings = {0};
|
||||
#include "llvm-c/Types.h"
|
||||
#else
|
||||
#include <llvm-c/Types.h>
|
||||
#include <signal.h>
|
||||
#endif
|
||||
|
||||
#include "parser.hpp"
|
||||
@@ -70,6 +71,8 @@ gb_global Timings global_timings = {0};
|
||||
#include "checker.cpp"
|
||||
#include "docs.cpp"
|
||||
|
||||
#include "cached.cpp"
|
||||
|
||||
#include "linker.cpp"
|
||||
|
||||
#if defined(GB_SYSTEM_WINDOWS) && defined(ODIN_TILDE_BACKEND)
|
||||
@@ -94,16 +97,38 @@ gb_global Timings global_timings = {0};
|
||||
#include "bug_report.cpp"
|
||||
|
||||
// NOTE(bill): 'name' is used in debugging and profiling modes
|
||||
gb_internal i32 system_exec_command_line_app(char const *name, char const *fmt, ...) {
|
||||
gb_internal i32 system_exec_command_line_app_internal(bool exit_on_err, char const *name, char const *fmt, va_list va) {
|
||||
isize const cmd_cap = 64<<20; // 64 MiB should be more than enough
|
||||
char *cmd_line = gb_alloc_array(gb_heap_allocator(), char, cmd_cap);
|
||||
isize cmd_len = 0;
|
||||
va_list va;
|
||||
i32 exit_code = 0;
|
||||
|
||||
va_start(va, fmt);
|
||||
cmd_len = gb_snprintf_va(cmd_line, cmd_cap-1, fmt, va);
|
||||
va_end(va);
|
||||
|
||||
if (build_context.print_linker_flags) {
|
||||
// NOTE(bill): remove the first argument (the executable) from the executable list
|
||||
// and then print it for the "linker flags"
|
||||
while (*cmd_line && gb_char_is_space(*cmd_line)) {
|
||||
cmd_line++;
|
||||
}
|
||||
if (*cmd_line == '\"') for (cmd_line++; *cmd_line; cmd_line++) {
|
||||
if (*cmd_line == '\\') {
|
||||
cmd_line++;
|
||||
if (*cmd_line == '\"') {
|
||||
cmd_line++;
|
||||
}
|
||||
} else if (*cmd_line == '\"') {
|
||||
cmd_line++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
while (*cmd_line && gb_char_is_space(*cmd_line)) {
|
||||
cmd_line++;
|
||||
}
|
||||
|
||||
fprintf(stdout, "%s\n", cmd_line);
|
||||
return exit_code;
|
||||
}
|
||||
|
||||
#if defined(GB_SYSTEM_WINDOWS)
|
||||
STARTUPINFOW start_info = {gb_size_of(STARTUPINFOW)};
|
||||
@@ -143,18 +168,36 @@ gb_internal i32 system_exec_command_line_app(char const *name, char const *fmt,
|
||||
gb_printf_err("%s\n\n", cmd_line);
|
||||
}
|
||||
exit_code = system(cmd_line);
|
||||
if (exit_on_err && WIFSIGNALED(exit_code)) {
|
||||
raise(WTERMSIG(exit_code));
|
||||
}
|
||||
if (WIFEXITED(exit_code)) {
|
||||
exit_code = WEXITSTATUS(exit_code);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (exit_code) {
|
||||
if (exit_on_err && exit_code) {
|
||||
exit(exit_code);
|
||||
}
|
||||
|
||||
return exit_code;
|
||||
}
|
||||
|
||||
gb_internal i32 system_exec_command_line_app(char const *name, char const *fmt, ...) {
|
||||
va_list va;
|
||||
va_start(va, fmt);
|
||||
i32 exit_code = system_exec_command_line_app_internal(/* exit_on_err= */ false, name, fmt, va);
|
||||
va_end(va);
|
||||
return exit_code;
|
||||
}
|
||||
|
||||
gb_internal void system_must_exec_command_line_app(char const *name, char const *fmt, ...) {
|
||||
va_list va;
|
||||
va_start(va, fmt);
|
||||
system_exec_command_line_app_internal(/* exit_on_err= */ true, name, fmt, va);
|
||||
va_end(va);
|
||||
}
|
||||
|
||||
#if defined(GB_SYSTEM_WINDOWS)
|
||||
#define popen _popen
|
||||
#define pclose _pclose
|
||||
@@ -343,10 +386,15 @@ enum BuildFlagKind {
|
||||
|
||||
BuildFlag_MinLinkLibs,
|
||||
|
||||
BuildFlag_PrintLinkerFlags,
|
||||
|
||||
// internal use only
|
||||
BuildFlag_InternalIgnoreLazy,
|
||||
BuildFlag_InternalIgnoreLLVMBuild,
|
||||
BuildFlag_InternalIgnorePanic,
|
||||
BuildFlag_InternalModulePerFile,
|
||||
BuildFlag_InternalCached,
|
||||
BuildFlag_InternalNoInline,
|
||||
|
||||
BuildFlag_Tilde,
|
||||
|
||||
@@ -544,9 +592,14 @@ gb_internal bool parse_build_flags(Array<String> args) {
|
||||
|
||||
add_flag(&build_flags, BuildFlag_MinLinkLibs, str_lit("min-link-libs"), BuildFlagParam_None, Command__does_build);
|
||||
|
||||
add_flag(&build_flags, BuildFlag_PrintLinkerFlags, str_lit("print-linker-flags"), BuildFlagParam_None, Command_build);
|
||||
|
||||
add_flag(&build_flags, BuildFlag_InternalIgnoreLazy, str_lit("internal-ignore-lazy"), BuildFlagParam_None, Command_all);
|
||||
add_flag(&build_flags, BuildFlag_InternalIgnoreLLVMBuild, str_lit("internal-ignore-llvm-build"),BuildFlagParam_None, Command_all);
|
||||
add_flag(&build_flags, BuildFlag_InternalIgnorePanic, str_lit("internal-ignore-panic"), BuildFlagParam_None, Command_all);
|
||||
add_flag(&build_flags, BuildFlag_InternalIgnorePanic, str_lit("internal-ignore-panic"), BuildFlagParam_None, Command_all);
|
||||
add_flag(&build_flags, BuildFlag_InternalModulePerFile, str_lit("internal-module-per-file"), BuildFlagParam_None, Command_all);
|
||||
add_flag(&build_flags, BuildFlag_InternalCached, str_lit("internal-cached"), BuildFlagParam_None, Command_all);
|
||||
add_flag(&build_flags, BuildFlag_InternalNoInline, str_lit("internal-no-inline"), BuildFlagParam_None, Command_all);
|
||||
|
||||
#if ALLOW_TILDE
|
||||
add_flag(&build_flags, BuildFlag_Tilde, str_lit("tilde"), BuildFlagParam_None, Command__does_build);
|
||||
@@ -554,6 +607,7 @@ gb_internal bool parse_build_flags(Array<String> args) {
|
||||
|
||||
add_flag(&build_flags, BuildFlag_Sanitize, str_lit("sanitize"), BuildFlagParam_String, Command__does_build, true);
|
||||
|
||||
|
||||
#if defined(GB_SYSTEM_WINDOWS)
|
||||
add_flag(&build_flags, BuildFlag_IgnoreVsSearch, str_lit("ignore-vs-search"), BuildFlagParam_None, Command__does_build);
|
||||
add_flag(&build_flags, BuildFlag_ResourceFile, str_lit("resource"), BuildFlagParam_String, Command__does_build);
|
||||
@@ -812,8 +866,7 @@ gb_internal bool parse_build_flags(Array<String> args) {
|
||||
gb_printf_err("\tjson\n");
|
||||
bad_flags = true;
|
||||
}
|
||||
|
||||
break;
|
||||
break;
|
||||
}
|
||||
case BuildFlag_ExportDependenciesFile: {
|
||||
GB_ASSERT(value.kind == ExactValue_String);
|
||||
@@ -1351,6 +1404,10 @@ gb_internal bool parse_build_flags(Array<String> args) {
|
||||
build_context.min_link_libs = true;
|
||||
break;
|
||||
|
||||
case BuildFlag_PrintLinkerFlags:
|
||||
build_context.print_linker_flags = true;
|
||||
break;
|
||||
|
||||
case BuildFlag_InternalIgnoreLazy:
|
||||
build_context.ignore_lazy = true;
|
||||
break;
|
||||
@@ -1360,6 +1417,18 @@ gb_internal bool parse_build_flags(Array<String> args) {
|
||||
case BuildFlag_InternalIgnorePanic:
|
||||
build_context.ignore_panic = true;
|
||||
break;
|
||||
case BuildFlag_InternalModulePerFile:
|
||||
build_context.module_per_file = true;
|
||||
build_context.use_separate_modules = true;
|
||||
break;
|
||||
case BuildFlag_InternalCached:
|
||||
build_context.cached = true;
|
||||
build_context.use_separate_modules = true;
|
||||
break;
|
||||
case BuildFlag_InternalNoInline:
|
||||
build_context.internal_no_inline = true;
|
||||
break;
|
||||
|
||||
case BuildFlag_Tilde:
|
||||
build_context.tilde_backend = true;
|
||||
break;
|
||||
@@ -1379,6 +1448,7 @@ gb_internal bool parse_build_flags(Array<String> args) {
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
#if defined(GB_SYSTEM_WINDOWS)
|
||||
case BuildFlag_IgnoreVsSearch: {
|
||||
GB_ASSERT(value.kind == ExactValue_Invalid);
|
||||
@@ -1390,8 +1460,9 @@ gb_internal bool parse_build_flags(Array<String> args) {
|
||||
String path = value.value_string;
|
||||
path = string_trim_whitespace(path);
|
||||
if (is_build_flag_path_valid(path)) {
|
||||
if(!string_ends_with(path, str_lit(".rc"))) {
|
||||
gb_printf_err("Invalid -resource path %.*s, missing .rc\n", LIT(path));
|
||||
bool is_resource = string_ends_with(path, str_lit(".rc")) || string_ends_with(path, str_lit(".res"));
|
||||
if(!is_resource) {
|
||||
gb_printf_err("Invalid -resource path %.*s, missing .rc or .res file\n", LIT(path));
|
||||
bad_flags = true;
|
||||
break;
|
||||
} else if (!gb_file_exists((const char *)path.text)) {
|
||||
@@ -1519,6 +1590,16 @@ gb_internal bool parse_build_flags(Array<String> args) {
|
||||
gb_printf_err("`-export-timings:<format>` requires `-show-timings` or `-show-more-timings` to be present\n");
|
||||
bad_flags = true;
|
||||
}
|
||||
|
||||
|
||||
if (build_context.export_dependencies_format != DependenciesExportUnspecified && build_context.print_linker_flags) {
|
||||
gb_printf_err("-export-dependencies cannot be used with -print-linker-flags\n");
|
||||
bad_flags = true;
|
||||
} else if (build_context.show_timings && build_context.print_linker_flags) {
|
||||
gb_printf_err("-show-timings/-show-more-timings cannot be used with -print-linker-flags\n");
|
||||
bad_flags = true;
|
||||
}
|
||||
|
||||
return !bad_flags;
|
||||
}
|
||||
|
||||
@@ -1854,7 +1935,13 @@ gb_internal void show_timings(Checker *c, Timings *t) {
|
||||
}
|
||||
}
|
||||
|
||||
gb_internal void export_dependencies(Parser *p) {
|
||||
gb_internal GB_COMPARE_PROC(file_path_cmp) {
|
||||
AstFile *x = *(AstFile **)a;
|
||||
AstFile *y = *(AstFile **)b;
|
||||
return string_compare(x->fullpath, y->fullpath);
|
||||
}
|
||||
|
||||
gb_internal void export_dependencies(Checker *c) {
|
||||
GB_ASSERT(build_context.export_dependencies_format != DependenciesExportUnspecified);
|
||||
|
||||
if (build_context.export_dependencies_file.len <= 0) {
|
||||
@@ -1863,6 +1950,8 @@ gb_internal void export_dependencies(Parser *p) {
|
||||
return;
|
||||
}
|
||||
|
||||
Parser *p = c->parser;
|
||||
|
||||
gbFile f = {};
|
||||
char * fileName = (char *)build_context.export_dependencies_file.text;
|
||||
gbFileError err = gb_file_open_mode(&f, gbFileMode_Write, fileName);
|
||||
@@ -1873,6 +1962,26 @@ gb_internal void export_dependencies(Parser *p) {
|
||||
}
|
||||
defer (gb_file_close(&f));
|
||||
|
||||
|
||||
auto files = array_make<AstFile *>(heap_allocator());
|
||||
for (AstPackage *pkg : p->packages) {
|
||||
for (AstFile *f : pkg->files) {
|
||||
array_add(&files, f);
|
||||
}
|
||||
}
|
||||
array_sort(files, file_path_cmp);
|
||||
|
||||
|
||||
auto load_files = array_make<LoadFileCache *>(heap_allocator());
|
||||
for (auto const &entry : c->info.load_file_cache) {
|
||||
auto *cache = entry.value;
|
||||
if (!cache || !cache->exists) {
|
||||
continue;
|
||||
}
|
||||
array_add(&load_files, cache);
|
||||
}
|
||||
array_sort(files, file_cache_sort_cmp);
|
||||
|
||||
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));
|
||||
@@ -1881,26 +1990,25 @@ gb_internal void export_dependencies(Parser *p) {
|
||||
|
||||
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;
|
||||
}
|
||||
for_array(i, files) {
|
||||
AstFile *file = files[i];
|
||||
/* 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++;
|
||||
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);
|
||||
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++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1910,14 +2018,30 @@ gb_internal void export_dependencies(Parser *p) {
|
||||
|
||||
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));
|
||||
for_array(i, files) {
|
||||
AstFile *file = files[i];
|
||||
gb_fprintf(&f, "\t\t\"%.*s\"", LIT(file->fullpath));
|
||||
if (i+1 == files.count) {
|
||||
gb_fprintf(&f, ",");
|
||||
}
|
||||
gb_fprintf(&f, "\n");
|
||||
}
|
||||
|
||||
gb_fprintf(&f, "\t],\n");
|
||||
|
||||
gb_fprintf(&f, "\t\"load_files\": [\n");
|
||||
|
||||
for_array(i, load_files) {
|
||||
LoadFileCache *cache = load_files[i];
|
||||
gb_fprintf(&f, "\t\t\"%.*s\"", LIT(cache->path));
|
||||
if (i+1 == load_files.count) {
|
||||
gb_fprintf(&f, ",");
|
||||
}
|
||||
gb_fprintf(&f, "\n");
|
||||
}
|
||||
|
||||
gb_fprintf(&f, "\t]\n");
|
||||
|
||||
gb_fprintf(&f, "}\n");
|
||||
}
|
||||
}
|
||||
@@ -2148,7 +2272,6 @@ gb_internal void print_show_help(String const arg0, String const &command) {
|
||||
print_usage_line(3, "-build-mode:shared Builds as a dynamically linked library.");
|
||||
print_usage_line(3, "-build-mode:lib Builds as a statically linked library.");
|
||||
print_usage_line(3, "-build-mode:static Builds as a statically linked library.");
|
||||
print_usage_line(3, "-build-mode:lib Builds as an static library.");
|
||||
print_usage_line(3, "-build-mode:obj Builds as an object file.");
|
||||
print_usage_line(3, "-build-mode:object Builds as an object file.");
|
||||
print_usage_line(3, "-build-mode:assembly Builds as an assembly file.");
|
||||
@@ -2195,9 +2318,9 @@ gb_internal void print_show_help(String const arg0, String const &command) {
|
||||
print_usage_line(0, "");
|
||||
|
||||
print_usage_line(1, "-use-separate-modules");
|
||||
print_usage_line(1, "[EXPERIMENTAL]");
|
||||
print_usage_line(2, "The backend generates multiple build units which are then linked together.");
|
||||
print_usage_line(2, "Normally, a single build unit is generated for a standard project.");
|
||||
print_usage_line(2, "This is the default behaviour on Windows for '-o:none' and '-o:minimal' builds.");
|
||||
print_usage_line(0, "");
|
||||
|
||||
}
|
||||
@@ -2346,6 +2469,12 @@ gb_internal void print_show_help(String const arg0, String const &command) {
|
||||
print_usage_line(0, "");
|
||||
}
|
||||
|
||||
if (build) {
|
||||
print_usage_line(1, "-print-linker-flags");
|
||||
print_usage_line(2, "Prints the all of the flags/arguments that will be passed to the linker.");
|
||||
print_usage_line(0, "");
|
||||
}
|
||||
|
||||
if (check) {
|
||||
print_usage_line(1, "-disallow-do");
|
||||
print_usage_line(2, "Disallows the 'do' keyword in the project.");
|
||||
@@ -2432,6 +2561,7 @@ gb_internal void print_show_help(String const arg0, String const &command) {
|
||||
print_usage_line(2, "[Windows only]");
|
||||
print_usage_line(2, "Defines the resource file for the executable.");
|
||||
print_usage_line(2, "Example: -resource:path/to/file.rc");
|
||||
print_usage_line(2, "or: -resource:path/to/file.res for a precompiled one.");
|
||||
print_usage_line(0, "");
|
||||
|
||||
print_usage_line(1, "-pdb-name:<filepath>");
|
||||
@@ -2937,6 +3067,8 @@ int main(int arg_count, char const **arg_ptr) {
|
||||
} else if (command == "root") {
|
||||
gb_printf("%.*s", LIT(odin_root_dir()));
|
||||
return 0;
|
||||
} else if (command == "clear-cache") {
|
||||
return try_clear_cache() ? 0 : 1;
|
||||
} else {
|
||||
String argv1 = {};
|
||||
if (args.count > 1) {
|
||||
@@ -3149,12 +3281,19 @@ int main(int arg_count, char const **arg_ptr) {
|
||||
print_all_errors();
|
||||
}
|
||||
|
||||
MAIN_TIME_SECTION("type check");
|
||||
|
||||
checker->parser = parser;
|
||||
init_checker(checker);
|
||||
defer (destroy_checker(checker));
|
||||
defer (destroy_checker(checker)); // this is here because of a `goto`
|
||||
|
||||
if (build_context.cached && parser->total_seen_load_directive_count.load() == 0) {
|
||||
MAIN_TIME_SECTION("check cached build (pre-semantic check)");
|
||||
if (try_cached_build(checker, args)) {
|
||||
goto end_of_code_gen;
|
||||
}
|
||||
}
|
||||
|
||||
MAIN_TIME_SECTION("type check");
|
||||
check_parsed_files(checker);
|
||||
check_defines(&build_context, checker);
|
||||
if (any_errors()) {
|
||||
@@ -3207,6 +3346,13 @@ int main(int arg_count, char const **arg_ptr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (build_context.cached) {
|
||||
MAIN_TIME_SECTION("check cached build");
|
||||
if (try_cached_build(checker, args)) {
|
||||
goto end_of_code_gen;
|
||||
}
|
||||
}
|
||||
|
||||
#if ALLOW_TILDE
|
||||
if (build_context.tilde_backend) {
|
||||
LinkerData linker_data = {};
|
||||
@@ -3226,7 +3372,7 @@ int main(int arg_count, char const **arg_ptr) {
|
||||
}
|
||||
|
||||
if (build_context.export_dependencies_format != DependenciesExportUnspecified) {
|
||||
export_dependencies(parser);
|
||||
export_dependencies(checker);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -3235,11 +3381,16 @@ int main(int arg_count, char const **arg_ptr) {
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
MAIN_TIME_SECTION("LLVM API Code Gen");
|
||||
lbGenerator *gen = gb_alloc_item(permanent_allocator(), lbGenerator);
|
||||
if (!lb_init_generator(gen, checker)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
gbString label_code_gen = gb_string_make(heap_allocator(), "LLVM API Code Gen");
|
||||
if (gen->modules.count > 1) {
|
||||
label_code_gen = gb_string_append_fmt(label_code_gen, " ( %4td modules )", gen->modules.count);
|
||||
}
|
||||
MAIN_TIME_SECTION_WITH_LEN(label_code_gen, gb_string_length(label_code_gen));
|
||||
if (lb_generate_code(gen)) {
|
||||
switch (build_context.build_mode) {
|
||||
case BuildMode_Executable:
|
||||
@@ -3252,7 +3403,7 @@ int main(int arg_count, char const **arg_ptr) {
|
||||
}
|
||||
|
||||
if (build_context.export_dependencies_format != DependenciesExportUnspecified) {
|
||||
export_dependencies(parser);
|
||||
export_dependencies(checker);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -3263,19 +3414,27 @@ int main(int arg_count, char const **arg_ptr) {
|
||||
remove_temp_files(gen);
|
||||
}
|
||||
|
||||
end_of_code_gen:;
|
||||
|
||||
if (build_context.show_timings) {
|
||||
show_timings(checker, &global_timings);
|
||||
}
|
||||
|
||||
if (build_context.export_dependencies_format != DependenciesExportUnspecified) {
|
||||
export_dependencies(parser);
|
||||
export_dependencies(checker);
|
||||
}
|
||||
|
||||
|
||||
if (!build_context.build_cache_data.copy_already_done &&
|
||||
build_context.cached) {
|
||||
try_copy_executable_to_cache();
|
||||
}
|
||||
|
||||
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));
|
||||
|
||||
return system_exec_command_line_app("odin run", "\"%.*s\" %.*s", LIT(exe_name), LIT(run_args_string));
|
||||
system_must_exec_command_line_app("odin run", "\"%.*s\" %.*s", LIT(exe_name), LIT(run_args_string));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
+53
-18
@@ -112,17 +112,17 @@ gb_internal isize ast_node_size(AstKind kind) {
|
||||
|
||||
}
|
||||
|
||||
gb_global std::atomic<isize> global_total_node_memory_allocated;
|
||||
// gb_global std::atomic<isize> global_total_node_memory_allocated;
|
||||
|
||||
// NOTE(bill): And this below is why is I/we need a new language! Discriminated unions are a pain in C/C++
|
||||
gb_internal Ast *alloc_ast_node(AstFile *f, AstKind kind) {
|
||||
isize size = ast_node_size(kind);
|
||||
|
||||
Ast *node = cast(Ast *)arena_alloc(&global_thread_local_ast_arena, size, 16);
|
||||
Ast *node = cast(Ast *)arena_alloc(get_arena(ThreadArena_Permanent), size, 16);
|
||||
node->kind = kind;
|
||||
node->file_id = f ? f->id : 0;
|
||||
|
||||
global_total_node_memory_allocated.fetch_add(size);
|
||||
// global_total_node_memory_allocated.fetch_add(size);
|
||||
|
||||
return node;
|
||||
}
|
||||
@@ -787,6 +787,9 @@ gb_internal Ast *ast_basic_directive(AstFile *f, Token token, Token name) {
|
||||
Ast *result = alloc_ast_node(f, Ast_BasicDirective);
|
||||
result->BasicDirective.token = token;
|
||||
result->BasicDirective.name = name;
|
||||
if (string_starts_with(name.string, str_lit("load"))) {
|
||||
f->seen_load_directive_count++;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -3135,7 +3138,7 @@ gb_internal Ast *parse_call_expr(AstFile *f, Ast *operand) {
|
||||
Ast *call = ast_call_expr(f, operand, args, open_paren, close_paren, ellipsis);
|
||||
|
||||
Ast *o = unparen_expr(operand);
|
||||
if (o->kind == Ast_SelectorExpr && o->SelectorExpr.token.kind == Token_ArrowRight) {
|
||||
if (o && o->kind == Ast_SelectorExpr && o->SelectorExpr.token.kind == Token_ArrowRight) {
|
||||
return ast_selector_call_expr(f, o->SelectorExpr.token, o, call);
|
||||
}
|
||||
|
||||
@@ -4011,6 +4014,7 @@ struct ParseFieldPrefixMapping {
|
||||
gb_global ParseFieldPrefixMapping const parse_field_prefix_mappings[] = {
|
||||
{str_lit("using"), Token_using, FieldFlag_using},
|
||||
{str_lit("no_alias"), Token_Hash, FieldFlag_no_alias},
|
||||
{str_lit("no_capture"), Token_Hash, FieldFlag_no_capture},
|
||||
{str_lit("c_vararg"), Token_Hash, FieldFlag_c_vararg},
|
||||
{str_lit("const"), Token_Hash, FieldFlag_const},
|
||||
{str_lit("any_int"), Token_Hash, FieldFlag_any_int},
|
||||
@@ -5254,6 +5258,38 @@ gb_internal Ast *parse_stmt(AstFile *f) {
|
||||
} else if (tag == "include") {
|
||||
syntax_error(token, "#include is not a valid import declaration kind. Did you mean 'import'?");
|
||||
s = ast_bad_stmt(f, token, f->curr_token);
|
||||
} else if (tag == "define") {
|
||||
s = ast_bad_stmt(f, token, f->curr_token);
|
||||
|
||||
if (name.pos.line == f->curr_token.pos.line) {
|
||||
bool call_like = false;
|
||||
Ast *macro_expr = nullptr;
|
||||
Token ident = f->curr_token;
|
||||
if (allow_token(f, Token_Ident) &&
|
||||
name.pos.line == f->curr_token.pos.line) {
|
||||
if (f->curr_token.kind == Token_OpenParen && f->curr_token.pos.column == ident.pos.column+ident.string.len) {
|
||||
call_like = true;
|
||||
(void)parse_call_expr(f, nullptr);
|
||||
}
|
||||
|
||||
if (name.pos.line == f->curr_token.pos.line && f->curr_token.kind != Token_Semicolon) {
|
||||
macro_expr = parse_expr(f, false);
|
||||
}
|
||||
}
|
||||
|
||||
ERROR_BLOCK();
|
||||
syntax_error(ident, "#define is not a valid declaration, Odin does not have a C-like preprocessor.");
|
||||
if (macro_expr == nullptr || call_like) {
|
||||
error_line("\tNote: Odin does not support macros\n");
|
||||
} else {
|
||||
gbString s = expr_to_string(macro_expr);
|
||||
error_line("\tSuggestion: Did you mean '%.*s :: %s'?\n", LIT(ident.string), s);
|
||||
gb_string_free(s);
|
||||
}
|
||||
} else {
|
||||
syntax_error(token, "#define is not a valid declaration, Odin does not have a C-like preprocessor.");
|
||||
}
|
||||
|
||||
} else {
|
||||
syntax_error(token, "Unknown tag directive used: '%.*s'", LIT(tag));
|
||||
s = ast_bad_stmt(f, token, f->curr_token);
|
||||
@@ -5377,7 +5413,7 @@ gb_internal ParseFileError init_ast_file(AstFile *f, String const &fullpath, Tok
|
||||
if (!string_ends_with(f->fullpath, str_lit(".odin"))) {
|
||||
return ParseFile_WrongExtension;
|
||||
}
|
||||
zero_item(&f->tokenizer);
|
||||
gb_zero_item(&f->tokenizer);
|
||||
f->tokenizer.curr_file_id = f->id;
|
||||
|
||||
TokenizerInitError err = init_tokenizer_from_fullpath(&f->tokenizer, f->fullpath, build_context.copy_file_contents);
|
||||
@@ -5609,7 +5645,7 @@ gb_internal AstPackage *try_add_import_path(Parser *p, String path, String const
|
||||
pkg->foreign_files.allocator = permanent_allocator();
|
||||
|
||||
// NOTE(bill): Single file initial package
|
||||
if (kind == Package_Init && string_ends_with(path, FILE_EXT)) {
|
||||
if (kind == Package_Init && !path_is_directory(path) && string_ends_with(path, FILE_EXT)) {
|
||||
FileInfo fi = {};
|
||||
fi.name = filename_from_path(path);
|
||||
fi.fullpath = path;
|
||||
@@ -5861,7 +5897,6 @@ gb_internal bool determine_path_from_string(BlockingMutex *file_mutex, Ast *node
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if (collection_name.len > 0) {
|
||||
// NOTE(bill): `base:runtime` == `core:runtime`
|
||||
if (collection_name == "core") {
|
||||
@@ -6016,7 +6051,7 @@ gb_internal void parse_setup_file_decls(Parser *p, AstFile *f, String const &bas
|
||||
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()) {
|
||||
if (!is_arch_wasm() || string_ends_with(fullpath, str_lit(".o"))) {
|
||||
String foreign_path = {};
|
||||
bool ok = determine_path_from_string(&p->file_decl_mutex, node, base_dir, file_str, &foreign_path);
|
||||
if (!ok) {
|
||||
@@ -6380,8 +6415,6 @@ gb_internal bool parse_file(Parser *p, AstFile *f) {
|
||||
} else if (lc == "+lazy") {
|
||||
if (build_context.ignore_lazy) {
|
||||
// Ignore
|
||||
} else if (f->flags & AstFile_IsTest) {
|
||||
// Ignore
|
||||
} else if (f->pkg->kind == Package_Init && build_context.command_kind == Command_doc) {
|
||||
// Ignore
|
||||
} else {
|
||||
@@ -6499,11 +6532,6 @@ gb_internal ParseFileError process_imported_file(Parser *p, ImportedFile importe
|
||||
if (build_context.command_kind == Command_test) {
|
||||
String name = file->fullpath;
|
||||
name = remove_extension_from_path(name);
|
||||
|
||||
String test_suffix = str_lit("_test");
|
||||
if (string_ends_with(name, test_suffix) && name != test_suffix) {
|
||||
file->flags |= AstFile_IsTest;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6538,6 +6566,7 @@ gb_internal ParseFileError parse_packages(Parser *p, String init_filename) {
|
||||
GB_ASSERT(init_filename.text[init_filename.len] == 0);
|
||||
|
||||
String init_fullpath = path_to_full_path(permanent_allocator(), init_filename);
|
||||
|
||||
if (!path_is_directory(init_fullpath)) {
|
||||
String const ext = str_lit(".odin");
|
||||
if (!string_ends_with(init_fullpath, ext)) {
|
||||
@@ -6551,9 +6580,8 @@ gb_internal ParseFileError parse_packages(Parser *p, String init_filename) {
|
||||
}
|
||||
if ((build_context.command_kind & Command__does_build) &&
|
||||
build_context.build_mode == BuildMode_Executable) {
|
||||
String short_path = filename_from_path(path);
|
||||
char *cpath = alloc_cstring(temporary_allocator(), short_path);
|
||||
if (gb_file_exists(cpath)) {
|
||||
String output_path = path_to_string(temporary_allocator(), build_context.build_paths[8]);
|
||||
if (path_is_directory(output_path)) {
|
||||
error({}, "Please specify the executable name with -out:<string> as a directory exists with the same name in the current working directory");
|
||||
return ParseFile_DirectoryAlreadyExists;
|
||||
}
|
||||
@@ -6621,6 +6649,13 @@ gb_internal ParseFileError parse_packages(Parser *p, String init_filename) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (AstPackage *pkg : p->packages) {
|
||||
for (AstFile *file : pkg->files) {
|
||||
p->total_seen_load_directive_count += file->seen_load_directive_count;
|
||||
}
|
||||
}
|
||||
|
||||
return ParseFile_None;
|
||||
}
|
||||
|
||||
|
||||
+12
-6
@@ -74,7 +74,6 @@ enum AstFileFlag : u32 {
|
||||
AstFile_IsPrivatePkg = 1<<0,
|
||||
AstFile_IsPrivateFile = 1<<1,
|
||||
|
||||
AstFile_IsTest = 1<<3,
|
||||
AstFile_IsLazy = 1<<4,
|
||||
|
||||
AstFile_NoInstrumentation = 1<<5,
|
||||
@@ -141,6 +140,8 @@ struct AstFile {
|
||||
// This is effectively a queue but does not require any multi-threading capabilities
|
||||
Array<Ast *> delayed_decls_queues[AstDelayQueue_COUNT];
|
||||
|
||||
std::atomic<isize> seen_load_directive_count;
|
||||
|
||||
#define PARSER_MAX_FIX_COUNT 6
|
||||
isize fix_count;
|
||||
TokenPos fix_prev_pos;
|
||||
@@ -211,6 +212,8 @@ struct Parser {
|
||||
std::atomic<isize> total_token_count;
|
||||
std::atomic<isize> total_line_count;
|
||||
|
||||
std::atomic<isize> total_seen_load_directive_count;
|
||||
|
||||
// TODO(bill): What should this mutex be per?
|
||||
// * Parser
|
||||
// * Package
|
||||
@@ -328,8 +331,10 @@ enum FieldFlag : u32 {
|
||||
FieldFlag_by_ptr = 1<<8,
|
||||
FieldFlag_no_broadcast = 1<<9, // disallow array programming
|
||||
|
||||
FieldFlag_no_capture = 1<<11,
|
||||
|
||||
// Internal use by the parser only
|
||||
FieldFlag_Tags = 1<<10,
|
||||
FieldFlag_Tags = 1<<15,
|
||||
FieldFlag_Results = 1<<16,
|
||||
|
||||
|
||||
@@ -337,7 +342,10 @@ enum FieldFlag : u32 {
|
||||
FieldFlag_Invalid = 1u<<31,
|
||||
|
||||
// Parameter List Restrictions
|
||||
FieldFlag_Signature = FieldFlag_ellipsis|FieldFlag_using|FieldFlag_no_alias|FieldFlag_c_vararg|FieldFlag_const|FieldFlag_any_int|FieldFlag_by_ptr|FieldFlag_no_broadcast,
|
||||
FieldFlag_Signature = FieldFlag_ellipsis|FieldFlag_using|FieldFlag_no_alias|FieldFlag_c_vararg|
|
||||
FieldFlag_const|FieldFlag_any_int|FieldFlag_by_ptr|FieldFlag_no_broadcast|
|
||||
FieldFlag_no_capture,
|
||||
|
||||
FieldFlag_Struct = FieldFlag_using|FieldFlag_subtype|FieldFlag_Tags,
|
||||
};
|
||||
|
||||
@@ -870,10 +878,8 @@ gb_internal gb_inline bool is_ast_when_stmt(Ast *node) {
|
||||
return node->kind == Ast_WhenStmt;
|
||||
}
|
||||
|
||||
gb_global gb_thread_local Arena global_thread_local_ast_arena = {};
|
||||
|
||||
gb_internal gb_inline gbAllocator ast_allocator(AstFile *f) {
|
||||
return arena_allocator(&global_thread_local_ast_arena);
|
||||
return permanent_allocator();
|
||||
}
|
||||
|
||||
gb_internal Ast *alloc_ast_node(AstFile *f, AstKind kind);
|
||||
|
||||
@@ -152,6 +152,13 @@ gb_internal String path_to_string(gbAllocator a, Path path) {
|
||||
return res;
|
||||
}
|
||||
|
||||
gb_internal String quote_path(gbAllocator a, Path path) {
|
||||
String temp = path_to_string(a, path);
|
||||
String quoted = concatenate3_strings(a, str_lit("\""), temp, str_lit("\""));
|
||||
gb_free(a, temp.text);
|
||||
return quoted;
|
||||
}
|
||||
|
||||
// NOTE(Jeroen): Naively turns a Path into a string, then normalizes it using `path_to_full_path`.
|
||||
gb_internal String path_to_full_path(gbAllocator a, Path path) {
|
||||
String temp = path_to_string(heap_allocator(), path);
|
||||
|
||||
+1
-1
@@ -16,7 +16,7 @@ struct MPSCQueue {
|
||||
std::atomic<isize> count;
|
||||
};
|
||||
|
||||
template <typename T> gb_internal void mpsc_init (MPSCQueue<T> *q);
|
||||
template <typename T> gb_internal void mpsc_init (MPSCQueue<T> *q, gbAllocator const &allocator);
|
||||
template <typename T> gb_internal void mpsc_destroy(MPSCQueue<T> *q);
|
||||
template <typename T> gb_internal isize mpsc_enqueue(MPSCQueue<T> *q, T const &value);
|
||||
template <typename T> gb_internal bool mpsc_dequeue(MPSCQueue<T> *q, T *value_);
|
||||
|
||||
+56
-15
@@ -88,6 +88,13 @@ gb_internal char *alloc_cstring(gbAllocator a, String s) {
|
||||
return c_str;
|
||||
}
|
||||
|
||||
gb_internal wchar_t *alloc_wstring(gbAllocator a, String16 s) {
|
||||
wchar_t *c_str = gb_alloc_array(a, wchar_t, s.len+1);
|
||||
gb_memmove(c_str, s.text, s.len*2);
|
||||
c_str[s.len] = '\0';
|
||||
return c_str;
|
||||
}
|
||||
|
||||
|
||||
gb_internal gb_inline bool str_eq_ignore_case(String const &a, String const &b) {
|
||||
if (a.len == b.len) {
|
||||
@@ -329,22 +336,22 @@ gb_internal bool string_contains_char(String const &s, u8 c) {
|
||||
}
|
||||
|
||||
gb_internal bool string_contains_string(String const &haystack, String const &needle) {
|
||||
if (needle.len == 0) return true;
|
||||
if (needle.len > haystack.len) return false;
|
||||
if (needle.len == 0) return true;
|
||||
if (needle.len > haystack.len) return false;
|
||||
|
||||
for (isize i = 0; i <= haystack.len - needle.len; i++) {
|
||||
bool found = true;
|
||||
for (isize j = 0; j < needle.len; j++) {
|
||||
if (haystack[i + j] != needle[j]) {
|
||||
found = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
for (isize i = 0; i <= haystack.len - needle.len; i++) {
|
||||
bool found = true;
|
||||
for (isize j = 0; j < needle.len; j++) {
|
||||
if (haystack[i + j] != needle[j]) {
|
||||
found = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
gb_internal String filename_from_path(String s) {
|
||||
@@ -543,6 +550,40 @@ gb_internal String string16_to_string(gbAllocator a, String16 s) {
|
||||
|
||||
|
||||
|
||||
gb_internal String temporary_directory(gbAllocator allocator) {
|
||||
#if defined(GB_SYSTEM_WINDOWS)
|
||||
DWORD n = GetTempPathW(0, nullptr);
|
||||
if (n == 0) {
|
||||
return String{0};
|
||||
}
|
||||
DWORD len = gb_max(MAX_PATH, n);
|
||||
wchar_t *b = gb_alloc_array(heap_allocator(), wchar_t, len+1);
|
||||
defer (gb_free(heap_allocator(), b));
|
||||
n = GetTempPathW(len, b);
|
||||
if (n == 3 && b[1] == ':' && b[2] == '\\') {
|
||||
|
||||
} else if (n > 0 && b[n-1] == '\\') {
|
||||
n -= 1;
|
||||
}
|
||||
b[n] = 0;
|
||||
String16 s = make_string16(b, n);
|
||||
return string16_to_string(allocator, s);
|
||||
#else
|
||||
char const *tmp_env = gb_get_env("TMPDIR", allocator);
|
||||
if (tmp_env) {
|
||||
return make_string_c(tmp_env);
|
||||
}
|
||||
|
||||
#if defined(P_tmpdir)
|
||||
String tmp_macro = make_string_c(P_tmpdir);
|
||||
if (tmp_macro.len != 0) {
|
||||
return copy_string(allocator, tmp_macro);
|
||||
}
|
||||
#endif
|
||||
|
||||
return copy_string(allocator, str_lit("/tmp"));
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
+89
-39
@@ -3,20 +3,28 @@
|
||||
struct WorkerTask;
|
||||
struct ThreadPool;
|
||||
|
||||
gb_thread_local Thread *current_thread;
|
||||
gb_global gb_thread_local Thread *current_thread;
|
||||
gb_internal Thread *get_current_thread(void) {
|
||||
return current_thread;
|
||||
}
|
||||
|
||||
gb_internal void thread_pool_init(ThreadPool *pool, isize worker_count, char const *worker_name);
|
||||
gb_internal void thread_pool_destroy(ThreadPool *pool);
|
||||
gb_internal bool thread_pool_add_task(ThreadPool *pool, WorkerTaskProc *proc, void *data);
|
||||
gb_internal void thread_pool_wait(ThreadPool *pool);
|
||||
|
||||
enum GrabState {
|
||||
Grab_Success = 0,
|
||||
Grab_Empty = 1,
|
||||
Grab_Failed = 2,
|
||||
};
|
||||
|
||||
struct ThreadPool {
|
||||
gbAllocator threads_allocator;
|
||||
Slice<Thread> threads;
|
||||
gbAllocator threads_allocator;
|
||||
Slice<Thread> threads;
|
||||
std::atomic<bool> running;
|
||||
|
||||
Futex tasks_available;
|
||||
|
||||
Futex tasks_left;
|
||||
};
|
||||
|
||||
@@ -46,7 +54,7 @@ gb_internal void thread_pool_destroy(ThreadPool *pool) {
|
||||
|
||||
for_array_off(i, 1, pool->threads) {
|
||||
Thread *t = &pool->threads[i];
|
||||
pool->tasks_available.fetch_add(1, std::memory_order_relaxed);
|
||||
pool->tasks_available.fetch_add(1, std::memory_order_acquire);
|
||||
futex_broadcast(&pool->tasks_available);
|
||||
thread_join_and_destroy(t);
|
||||
}
|
||||
@@ -54,51 +62,86 @@ gb_internal void thread_pool_destroy(ThreadPool *pool) {
|
||||
gb_free(pool->threads_allocator, pool->threads.data);
|
||||
}
|
||||
|
||||
TaskRingBuffer *task_ring_grow(TaskRingBuffer *ring, isize bottom, isize top) {
|
||||
TaskRingBuffer *new_ring = task_ring_init(ring->size * 2);
|
||||
for (isize i = top; i < bottom; i++) {
|
||||
new_ring->buffer[i % new_ring->size] = ring->buffer[i % ring->size];
|
||||
}
|
||||
return new_ring;
|
||||
}
|
||||
|
||||
void thread_pool_queue_push(Thread *thread, WorkerTask task) {
|
||||
u64 capture;
|
||||
u64 new_capture;
|
||||
do {
|
||||
capture = thread->head_and_tail.load();
|
||||
isize bot = thread->queue.bottom.load(std::memory_order_relaxed);
|
||||
isize top = thread->queue.top.load(std::memory_order_acquire);
|
||||
TaskRingBuffer *cur_ring = thread->queue.ring.load(std::memory_order_relaxed);
|
||||
|
||||
u64 mask = thread->capacity - 1;
|
||||
u64 head = (capture >> 32) & mask;
|
||||
u64 tail = ((u32)capture) & mask;
|
||||
isize size = bot - top;
|
||||
if (size > (cur_ring->size - 1)) {
|
||||
// Queue is full
|
||||
thread->queue.ring = task_ring_grow(thread->queue.ring, bot, top);
|
||||
cur_ring = thread->queue.ring.load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
u64 new_head = (head + 1) & mask;
|
||||
GB_ASSERT_MSG(new_head != tail, "Thread Queue Full!");
|
||||
|
||||
// This *must* be done in here, to avoid a potential race condition where we no longer own the slot by the time we're assigning
|
||||
thread->queue[head] = task;
|
||||
new_capture = (new_head << 32) | tail;
|
||||
} while (!thread->head_and_tail.compare_exchange_weak(capture, new_capture));
|
||||
cur_ring->buffer[bot % cur_ring->size] = task;
|
||||
std::atomic_thread_fence(std::memory_order_release);
|
||||
thread->queue.bottom.store(bot + 1, std::memory_order_relaxed);
|
||||
|
||||
thread->pool->tasks_left.fetch_add(1, std::memory_order_release);
|
||||
thread->pool->tasks_available.fetch_add(1, std::memory_order_relaxed);
|
||||
futex_broadcast(&thread->pool->tasks_available);
|
||||
}
|
||||
|
||||
bool thread_pool_queue_pop(Thread *thread, WorkerTask *task) {
|
||||
u64 capture;
|
||||
u64 new_capture;
|
||||
do {
|
||||
capture = thread->head_and_tail.load(std::memory_order_acquire);
|
||||
GrabState thread_pool_queue_take(Thread *thread, WorkerTask *task) {
|
||||
isize bot = thread->queue.bottom.load(std::memory_order_relaxed) - 1;
|
||||
TaskRingBuffer *cur_ring = thread->queue.ring.load(std::memory_order_relaxed);
|
||||
thread->queue.bottom.store(bot, std::memory_order_relaxed);
|
||||
std::atomic_thread_fence(std::memory_order_seq_cst);
|
||||
|
||||
u64 mask = thread->capacity - 1;
|
||||
u64 head = (capture >> 32) & mask;
|
||||
u64 tail = ((u32)capture) & mask;
|
||||
isize top = thread->queue.top.load(std::memory_order_relaxed);
|
||||
if (top <= bot) {
|
||||
|
||||
u64 new_tail = (tail + 1) & mask;
|
||||
if (tail == head) {
|
||||
return false;
|
||||
// Queue is not empty
|
||||
*task = cur_ring->buffer[bot % cur_ring->size];
|
||||
if (top == bot) {
|
||||
// Only one entry left in queue
|
||||
if (!thread->queue.top.compare_exchange_strong(top, top + 1, std::memory_order_seq_cst, std::memory_order_relaxed)) {
|
||||
// Race failed
|
||||
thread->queue.bottom.store(bot + 1, std::memory_order_relaxed);
|
||||
return Grab_Empty;
|
||||
}
|
||||
|
||||
thread->queue.bottom.store(bot + 1, std::memory_order_relaxed);
|
||||
return Grab_Success;
|
||||
}
|
||||
|
||||
// Making a copy of the task before we increment the tail, avoiding the same potential race condition as above
|
||||
*task = thread->queue[tail];
|
||||
// We got a task without hitting a race
|
||||
return Grab_Success;
|
||||
} else {
|
||||
// Queue is empty
|
||||
thread->queue.bottom.store(bot + 1, std::memory_order_relaxed);
|
||||
return Grab_Empty;
|
||||
}
|
||||
}
|
||||
|
||||
new_capture = (head << 32) | new_tail;
|
||||
} while (!thread->head_and_tail.compare_exchange_weak(capture, new_capture, std::memory_order_release));
|
||||
GrabState thread_pool_queue_steal(Thread *thread, WorkerTask *task) {
|
||||
isize top = thread->queue.top.load(std::memory_order_acquire);
|
||||
std::atomic_thread_fence(std::memory_order_seq_cst);
|
||||
isize bot = thread->queue.bottom.load(std::memory_order_acquire);
|
||||
|
||||
return true;
|
||||
GrabState ret = Grab_Empty;
|
||||
if (top < bot) {
|
||||
// Queue is not empty
|
||||
TaskRingBuffer *cur_ring = thread->queue.ring.load(std::memory_order_consume);
|
||||
*task = cur_ring->buffer[top % cur_ring->size];
|
||||
|
||||
if (!thread->queue.top.compare_exchange_strong(top, top + 1, std::memory_order_seq_cst, std::memory_order_relaxed)) {
|
||||
// Race failed
|
||||
ret = Grab_Failed;
|
||||
} else {
|
||||
ret = Grab_Success;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
gb_internal bool thread_pool_add_task(ThreadPool *pool, WorkerTaskProc *proc, void *data) {
|
||||
@@ -115,12 +158,11 @@ gb_internal void thread_pool_wait(ThreadPool *pool) {
|
||||
|
||||
while (pool->tasks_left.load(std::memory_order_acquire)) {
|
||||
// if we've got tasks on our queue, run them
|
||||
while (thread_pool_queue_pop(current_thread, &task)) {
|
||||
while (!thread_pool_queue_take(current_thread, &task)) {
|
||||
task.do_work(task.data);
|
||||
pool->tasks_left.fetch_sub(1, std::memory_order_release);
|
||||
}
|
||||
|
||||
|
||||
// is this mem-barriered enough?
|
||||
// This *must* be executed in this order, so the futex wakes immediately
|
||||
// if rem_tasks has changed since we checked last, otherwise the program
|
||||
@@ -145,7 +187,7 @@ gb_internal THREAD_PROC(thread_pool_thread_proc) {
|
||||
usize finished_tasks = 0;
|
||||
i32 state;
|
||||
|
||||
while (thread_pool_queue_pop(current_thread, &task)) {
|
||||
while (!thread_pool_queue_take(current_thread, &task)) {
|
||||
task.do_work(task.data);
|
||||
pool->tasks_left.fetch_sub(1, std::memory_order_release);
|
||||
|
||||
@@ -167,7 +209,12 @@ gb_internal THREAD_PROC(thread_pool_thread_proc) {
|
||||
|
||||
Thread *thread = &pool->threads.data[idx];
|
||||
WorkerTask task;
|
||||
if (thread_pool_queue_pop(thread, &task)) {
|
||||
|
||||
GrabState ret = thread_pool_queue_steal(thread, &task);
|
||||
switch (ret) {
|
||||
case Grab_Empty:
|
||||
continue;
|
||||
case Grab_Success:
|
||||
task.do_work(task.data);
|
||||
pool->tasks_left.fetch_sub(1, std::memory_order_release);
|
||||
|
||||
@@ -175,6 +222,8 @@ gb_internal THREAD_PROC(thread_pool_thread_proc) {
|
||||
futex_signal(&pool->tasks_left);
|
||||
}
|
||||
|
||||
/*fallthrough*/
|
||||
case Grab_Failed:
|
||||
goto main_loop_continue;
|
||||
}
|
||||
}
|
||||
@@ -182,6 +231,7 @@ gb_internal THREAD_PROC(thread_pool_thread_proc) {
|
||||
|
||||
// if we've done all our work, and there's nothing to steal, go to sleep
|
||||
state = pool->tasks_available.load(std::memory_order_acquire);
|
||||
if (!pool->running) { break; }
|
||||
futex_wait(&pool->tasks_available, state);
|
||||
|
||||
main_loop_continue:;
|
||||
|
||||
+58
-18
@@ -46,6 +46,18 @@ typedef struct WorkerTask {
|
||||
void *data;
|
||||
} WorkerTask;
|
||||
|
||||
typedef struct TaskRingBuffer {
|
||||
std::atomic<isize> size;
|
||||
std::atomic<WorkerTask *> buffer;
|
||||
} TaskRingBuffer;
|
||||
|
||||
typedef struct TaskQueue {
|
||||
std::atomic<isize> top;
|
||||
std::atomic<isize> bottom;
|
||||
|
||||
std::atomic<TaskRingBuffer *> ring;
|
||||
} TaskQueue;
|
||||
|
||||
struct Thread {
|
||||
#if defined(GB_SYSTEM_WINDOWS)
|
||||
void *win32_handle;
|
||||
@@ -54,13 +66,13 @@ struct Thread {
|
||||
#endif
|
||||
|
||||
isize idx;
|
||||
isize stack_size;
|
||||
|
||||
WorkerTask *queue;
|
||||
size_t capacity;
|
||||
std::atomic<uint64_t> head_and_tail;
|
||||
|
||||
isize stack_size;
|
||||
struct TaskQueue queue;
|
||||
struct ThreadPool *pool;
|
||||
|
||||
struct Arena *permanent_arena;
|
||||
struct Arena *temporary_arena;
|
||||
};
|
||||
|
||||
typedef std::atomic<i32> Futex;
|
||||
@@ -551,6 +563,20 @@ gb_internal void *internal_thread_proc(void *arg) {
|
||||
}
|
||||
#endif
|
||||
|
||||
gb_internal TaskRingBuffer *task_ring_init(isize size) {
|
||||
TaskRingBuffer *ring = gb_alloc_item(heap_allocator(), TaskRingBuffer);
|
||||
ring->size = size;
|
||||
ring->buffer = gb_alloc_array(heap_allocator(), WorkerTask, ring->size);
|
||||
return ring;
|
||||
}
|
||||
|
||||
gb_internal void thread_queue_destroy(TaskQueue *q) {
|
||||
gb_free(heap_allocator(), (*q->ring).buffer);
|
||||
gb_free(heap_allocator(), q->ring);
|
||||
}
|
||||
|
||||
gb_internal void thread_init_arenas(Thread *t);
|
||||
|
||||
gb_internal void thread_init(ThreadPool *pool, Thread *t, isize idx) {
|
||||
gb_zero_item(t);
|
||||
#if defined(GB_SYSTEM_WINDOWS)
|
||||
@@ -559,13 +585,13 @@ gb_internal void thread_init(ThreadPool *pool, Thread *t, isize idx) {
|
||||
t->posix_handle = 0;
|
||||
#endif
|
||||
|
||||
t->capacity = 1 << 14; // must be a power of 2
|
||||
t->queue = gb_alloc_array(heap_allocator(), WorkerTask, t->capacity);
|
||||
t->head_and_tail = 0;
|
||||
// Size must be a power of 2
|
||||
t->queue.ring = task_ring_init(1 << 14);
|
||||
t->pool = pool;
|
||||
t->idx = idx;
|
||||
}
|
||||
|
||||
thread_init_arenas(t);
|
||||
}
|
||||
|
||||
gb_internal void thread_init_and_start(ThreadPool *pool, Thread *t, isize idx) {
|
||||
thread_init(pool, t, idx);
|
||||
@@ -598,7 +624,7 @@ gb_internal void thread_join_and_destroy(Thread *t) {
|
||||
t->posix_handle = 0;
|
||||
#endif
|
||||
|
||||
gb_free(heap_allocator(), t->queue);
|
||||
thread_queue_destroy(&t->queue);
|
||||
}
|
||||
|
||||
gb_internal void thread_set_name(Thread *t, char const *name) {
|
||||
@@ -770,13 +796,27 @@ gb_internal void futex_wait(Futex *f, Footex val) {
|
||||
|
||||
#elif defined(GB_SYSTEM_OSX)
|
||||
|
||||
// IMPORTANT NOTE(laytan): We use `OS_SYNC_*_SHARED` and `UL_COMPARE_AND_WAIT_SHARED` flags here.
|
||||
// these flags tell the kernel that we are using these futexes across different processes which
|
||||
// causes it to opt-out of some optimisations.
|
||||
//
|
||||
// BUT this is not actually the case! We should be using the normal non-shared version and letting
|
||||
// the kernel optimize (I've measured it to be about 10% faster at the parsing/type checking stages).
|
||||
//
|
||||
// However we have reports of people on MacOS running into kernel panics, and this seems to fix it for them.
|
||||
// Which means there is probably a bug in the kernel in one of these non-shared optimisations causing the panic.
|
||||
//
|
||||
// The panic also doesn't seem to happen on normal M1 CPUs, and happen more on later CPUs or pro/max series.
|
||||
// Probably because they have more going on in terms of threads etc.
|
||||
|
||||
#if __has_include(<os/os_sync_wait_on_address.h>)
|
||||
#define DARWIN_WAIT_ON_ADDRESS_AVAILABLE
|
||||
#include <os/os_sync_wait_on_address.h>
|
||||
#endif
|
||||
|
||||
#define UL_COMPARE_AND_WAIT 0x00000001
|
||||
#define ULF_NO_ERRNO 0x01000000
|
||||
#define UL_COMPARE_AND_WAIT 0x00000001
|
||||
#define UL_COMPARE_AND_WAIT_SHARED 0x00000003
|
||||
#define ULF_NO_ERRNO 0x01000000
|
||||
|
||||
extern "C" int __ulock_wait(uint32_t operation, void *addr, uint64_t value, uint32_t timeout); /* timeout is specified in microseconds */
|
||||
extern "C" int __ulock_wake(uint32_t operation, void *addr, uint64_t wake_value);
|
||||
@@ -785,7 +825,7 @@ gb_internal void futex_signal(Futex *f) {
|
||||
#ifdef DARWIN_WAIT_ON_ADDRESS_AVAILABLE
|
||||
if (__builtin_available(macOS 14.4, *)) {
|
||||
for (;;) {
|
||||
int ret = os_sync_wake_by_address_any(f, sizeof(Futex), OS_SYNC_WAKE_BY_ADDRESS_NONE);
|
||||
int ret = os_sync_wake_by_address_any(f, sizeof(Futex), OS_SYNC_WAKE_BY_ADDRESS_SHARED);
|
||||
if (ret >= 0) {
|
||||
return;
|
||||
}
|
||||
@@ -800,7 +840,7 @@ gb_internal void futex_signal(Futex *f) {
|
||||
} else {
|
||||
#endif
|
||||
for (;;) {
|
||||
int ret = __ulock_wake(UL_COMPARE_AND_WAIT | ULF_NO_ERRNO, f, 0);
|
||||
int ret = __ulock_wake(UL_COMPARE_AND_WAIT_SHARED | ULF_NO_ERRNO, f, 0);
|
||||
if (ret >= 0) {
|
||||
return;
|
||||
}
|
||||
@@ -821,7 +861,7 @@ gb_internal void futex_broadcast(Futex *f) {
|
||||
#ifdef DARWIN_WAIT_ON_ADDRESS_AVAILABLE
|
||||
if (__builtin_available(macOS 14.4, *)) {
|
||||
for (;;) {
|
||||
int ret = os_sync_wake_by_address_all(f, sizeof(Footex), OS_SYNC_WAKE_BY_ADDRESS_NONE);
|
||||
int ret = os_sync_wake_by_address_all(f, sizeof(Footex), OS_SYNC_WAKE_BY_ADDRESS_SHARED);
|
||||
if (ret >= 0) {
|
||||
return;
|
||||
}
|
||||
@@ -837,7 +877,7 @@ gb_internal void futex_broadcast(Futex *f) {
|
||||
#endif
|
||||
for (;;) {
|
||||
enum { ULF_WAKE_ALL = 0x00000100 };
|
||||
int ret = __ulock_wake(UL_COMPARE_AND_WAIT | ULF_NO_ERRNO | ULF_WAKE_ALL, f, 0);
|
||||
int ret = __ulock_wake(UL_COMPARE_AND_WAIT_SHARED | ULF_NO_ERRNO | ULF_WAKE_ALL, f, 0);
|
||||
if (ret == 0) {
|
||||
return;
|
||||
}
|
||||
@@ -858,7 +898,7 @@ gb_internal void futex_wait(Futex *f, Footex val) {
|
||||
#ifdef DARWIN_WAIT_ON_ADDRESS_AVAILABLE
|
||||
if (__builtin_available(macOS 14.4, *)) {
|
||||
for (;;) {
|
||||
int ret = os_sync_wait_on_address(f, cast(uint64_t)(val), sizeof(Footex), OS_SYNC_WAIT_ON_ADDRESS_NONE);
|
||||
int ret = os_sync_wait_on_address(f, cast(uint64_t)(val), sizeof(Footex), OS_SYNC_WAIT_ON_ADDRESS_SHARED);
|
||||
if (ret >= 0) {
|
||||
if (*f != val) {
|
||||
return;
|
||||
@@ -876,7 +916,7 @@ gb_internal void futex_wait(Futex *f, Footex val) {
|
||||
} else {
|
||||
#endif
|
||||
for (;;) {
|
||||
int ret = __ulock_wait(UL_COMPARE_AND_WAIT | ULF_NO_ERRNO, f, val, 0);
|
||||
int ret = __ulock_wait(UL_COMPARE_AND_WAIT_SHARED | ULF_NO_ERRNO, f, val, 0);
|
||||
if (ret >= 0) {
|
||||
if (*f != val) {
|
||||
return;
|
||||
|
||||
+4
-3
@@ -146,9 +146,10 @@ gb_internal f64 time_stamp_as_us(TimeStamp const &ts, u64 freq) {
|
||||
return 1000000.0*time_stamp_as_s(ts, freq);
|
||||
}
|
||||
|
||||
#define MAIN_TIME_SECTION(str) do { debugf("[Section] %s\n", str); timings_start_section(&global_timings, str_lit(str)); } while (0)
|
||||
#define TIME_SECTION(str) do { debugf("[Section] %s\n", str); if (build_context.show_more_timings) timings_start_section(&global_timings, str_lit(str)); } while (0)
|
||||
#define TIME_SECTION_WITH_LEN(str, len) do { debugf("[Section] %s\n", str); if (build_context.show_more_timings) timings_start_section(&global_timings, make_string((u8 *)str, len)); } while (0)
|
||||
#define MAIN_TIME_SECTION(str) do { debugf("[Section] %s\n", str); timings_start_section(&global_timings, str_lit(str)); } while (0)
|
||||
#define MAIN_TIME_SECTION_WITH_LEN(str, len) do { debugf("[Section] %s\n", str); timings_start_section(&global_timings, make_string((u8 *)str, len)); } while (0)
|
||||
#define TIME_SECTION(str) do { debugf("[Section] %s\n", str); if (build_context.show_more_timings) timings_start_section(&global_timings, str_lit(str)); } while (0)
|
||||
#define TIME_SECTION_WITH_LEN(str, len) do { debugf("[Section] %s\n", str); if (build_context.show_more_timings) timings_start_section(&global_timings, make_string((u8 *)str, len)); } while (0)
|
||||
|
||||
|
||||
enum TimingUnit {
|
||||
|
||||
+45
-1
@@ -964,7 +964,7 @@ gb_internal Type *alloc_type(TypeKind kind) {
|
||||
// gbAllocator a = heap_allocator();
|
||||
gbAllocator a = permanent_allocator();
|
||||
Type *t = gb_alloc_item(a, Type);
|
||||
zero_item(t);
|
||||
gb_zero_item(t);
|
||||
t->kind = kind;
|
||||
t->cached_size = -1;
|
||||
t->cached_align = -1;
|
||||
@@ -1637,6 +1637,26 @@ gb_internal Type *base_array_type(Type *t) {
|
||||
return t;
|
||||
}
|
||||
|
||||
|
||||
gb_internal Type *base_any_array_type(Type *t) {
|
||||
Type *bt = base_type(t);
|
||||
if (is_type_array(bt)) {
|
||||
return bt->Array.elem;
|
||||
} else if (is_type_slice(bt)) {
|
||||
return bt->Slice.elem;
|
||||
} else if (is_type_dynamic_array(bt)) {
|
||||
return bt->DynamicArray.elem;
|
||||
} else if (is_type_enumerated_array(bt)) {
|
||||
return bt->EnumeratedArray.elem;
|
||||
} else if (is_type_simd_vector(bt)) {
|
||||
return bt->SimdVector.elem;
|
||||
} else if (is_type_matrix(bt)) {
|
||||
return bt->Matrix.elem;
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
|
||||
gb_internal bool is_type_generic(Type *t) {
|
||||
t = base_type(t);
|
||||
return t->kind == Type_Generic;
|
||||
@@ -2011,6 +2031,24 @@ gb_internal bool is_type_valid_bit_set_elem(Type *t) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
gb_internal bool is_valid_bit_field_backing_type(Type *type) {
|
||||
if (type == nullptr) {
|
||||
return false;
|
||||
}
|
||||
type = base_type(type);
|
||||
if (is_type_untyped(type)) {
|
||||
return false;
|
||||
}
|
||||
if (is_type_integer(type)) {
|
||||
return true;
|
||||
}
|
||||
if (type->kind == Type_Array) {
|
||||
return is_type_integer(type->Array.elem);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
gb_internal Type *bit_set_to_int(Type *t) {
|
||||
GB_ASSERT(is_type_bit_set(t));
|
||||
Type *bt = base_type(t);
|
||||
@@ -2018,6 +2056,9 @@ gb_internal Type *bit_set_to_int(Type *t) {
|
||||
if (underlying != nullptr && is_type_integer(underlying)) {
|
||||
return underlying;
|
||||
}
|
||||
if (underlying != nullptr && is_valid_bit_field_backing_type(underlying)) {
|
||||
return underlying;
|
||||
}
|
||||
|
||||
i64 sz = type_size_of(t);
|
||||
switch (sz) {
|
||||
@@ -2923,11 +2964,14 @@ gb_internal Type *c_vararg_promote_type(Type *type) {
|
||||
|
||||
if (core->kind == Type_Basic) {
|
||||
switch (core->Basic.kind) {
|
||||
case Basic_f16:
|
||||
case Basic_f32:
|
||||
case Basic_UntypedFloat:
|
||||
return t_f64;
|
||||
case Basic_f16le:
|
||||
case Basic_f32le:
|
||||
return t_f64le;
|
||||
case Basic_f16be:
|
||||
case Basic_f32be:
|
||||
return t_f64be;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user