aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorCourtney Strachan <courtney.strachan@gmail.com>2025-10-06 02:41:44 +0100
committerGitHub <noreply@github.com>2025-10-06 02:41:44 +0100
commit6de2d6e8ca687c989bbb7806e5cbe8d791e425bf (patch)
tree03a2e0a84c7c1530215f8e3f59a7f643b39b3677 /src
parentdbbe96ae5c343f0e803de6ee508207a62571534f (diff)
parent0f97382fa3e46da80705c00dfe02f3deb9562e4f (diff)
Merge branch 'odin-lang:master' into master
Diffstat (limited to 'src')
-rw-r--r--src/bug_report.cpp11
-rw-r--r--src/build_settings.cpp631
-rw-r--r--src/build_settings_microarch.cpp559
-rw-r--r--src/bundle_command.cpp212
-rw-r--r--src/cached.cpp2
-rw-r--r--src/check_builtin.cpp886
-rw-r--r--src/check_decl.cpp418
-rw-r--r--src/check_expr.cpp1607
-rw-r--r--src/check_stmt.cpp256
-rw-r--r--src/check_type.cpp132
-rw-r--r--src/checker.cpp1233
-rw-r--r--src/checker.hpp71
-rw-r--r--src/checker_builtin_procs.hpp45
-rw-r--r--src/common.cpp10
-rw-r--r--src/common_memory.cpp58
-rw-r--r--src/docs_writer.cpp30
-rw-r--r--src/entity.cpp14
-rw-r--r--src/error.cpp29
-rw-r--r--src/exact_value.cpp56
-rw-r--r--src/gb/gb.h58
-rw-r--r--src/linker.cpp362
-rw-r--r--src/llvm-c/Analysis.h4
-rw-r--r--src/llvm-c/BitReader.h4
-rw-r--r--src/llvm-c/BitWriter.h4
-rw-r--r--src/llvm-c/Comdat.h4
-rw-r--r--src/llvm-c/Config/llvm-config.h6
-rw-r--r--src/llvm-c/Core.h536
-rw-r--r--src/llvm-c/DebugInfo.h149
-rw-r--r--src/llvm-c/Disassembler.h8
-rw-r--r--src/llvm-c/DisassemblerTypes.h2
-rw-r--r--src/llvm-c/Error.h10
-rw-r--r--src/llvm-c/ErrorHandling.h2
-rw-r--r--src/llvm-c/ExecutionEngine.h8
-rw-r--r--src/llvm-c/IRReader.h4
-rw-r--r--src/llvm-c/LLJIT.h8
-rw-r--r--src/llvm-c/LLJITUtils.h2
-rw-r--r--src/llvm-c/Linker.h4
-rw-r--r--src/llvm-c/Object.h6
-rw-r--r--src/llvm-c/Orc.h63
-rw-r--r--src/llvm-c/OrcEE.h10
-rw-r--r--src/llvm-c/Remarks.h4
-rw-r--r--src/llvm-c/Support.h6
-rw-r--r--src/llvm-c/Target.h32
-rw-r--r--src/llvm-c/TargetMachine.h6
-rw-r--r--src/llvm-c/Transforms/PassBuilder.h24
-rw-r--r--src/llvm-c/Types.h9
-rw-r--r--src/llvm-c/lto.h2
-rw-r--r--src/llvm_abi.cpp277
-rw-r--r--src/llvm_backend.cpp2331
-rw-r--r--src/llvm_backend.hpp86
-rw-r--r--src/llvm_backend_const.cpp653
-rw-r--r--src/llvm_backend_debug.cpp210
-rw-r--r--src/llvm_backend_expr.cpp596
-rw-r--r--src/llvm_backend_general.cpp706
-rw-r--r--src/llvm_backend_opt.cpp4
-rw-r--r--src/llvm_backend_passes.cpp1195
-rw-r--r--src/llvm_backend_proc.cpp645
-rw-r--r--src/llvm_backend_stmt.cpp372
-rw-r--r--src/llvm_backend_type.cpp61
-rw-r--r--src/llvm_backend_utility.cpp1051
-rw-r--r--src/main.cpp592
-rw-r--r--src/microsoft_craziness.h4
-rw-r--r--src/name_canonicalization.cpp112
-rw-r--r--src/name_canonicalization.hpp2
-rw-r--r--src/parser.cpp146
-rw-r--r--src/parser.hpp22
-rw-r--r--src/path.cpp84
-rw-r--r--src/ptr_map.cpp6
-rw-r--r--src/ptr_set.cpp48
-rw-r--r--src/queue.cpp3
-rw-r--r--src/string.cpp292
-rw-r--r--src/string16_map.cpp538
-rw-r--r--src/thread_pool.cpp20
-rw-r--r--src/threading.cpp56
-rw-r--r--src/timings.cpp4
-rw-r--r--src/tokenizer.cpp1
-rw-r--r--src/types.cpp648
77 files changed, 14803 insertions, 3559 deletions
diff --git a/src/bug_report.cpp b/src/bug_report.cpp
index c44c4be33..98a376895 100644
--- a/src/bug_report.cpp
+++ b/src/bug_report.cpp
@@ -527,7 +527,7 @@ gb_internal void report_os_info() {
#elif defined(GB_SYSTEM_OSX)
gbString sw_vers = gb_string_make(heap_allocator(), "");
- if (!system_exec_command_line_app_output("sw_vers --productVersion", &sw_vers)) {
+ if (!system_exec_command_line_app_output("sw_vers -productVersion", &sw_vers)) {
gb_printf("macOS Unknown\n");
return;
}
@@ -540,6 +540,7 @@ gb_internal void report_os_info() {
}
switch (major) {
+ case 26: gb_printf("macOS Tahoe"); break;
case 15: gb_printf("macOS Sequoia"); break;
case 14: gb_printf("macOS Sonoma"); break;
case 13: gb_printf("macOS Ventura"); break;
@@ -667,8 +668,14 @@ gb_internal void print_bug_report_help() {
gb_printf("-nightly");
#endif
+ String version = {};
+
#ifdef GIT_SHA
- gb_printf(":%s", GIT_SHA);
+ version.text = cast(u8 *)GIT_SHA;
+ version.len = gb_strlen(GIT_SHA);
+ if (version != "") {
+ gb_printf(":%.*s", LIT(version));
+ }
#endif
gb_printf("\n");
diff --git a/src/build_settings.cpp b/src/build_settings.cpp
index 08df34c57..53953600e 100644
--- a/src/build_settings.cpp
+++ b/src/build_settings.cpp
@@ -31,6 +31,24 @@ enum TargetOsKind : u16 {
TargetOs_COUNT,
};
+gb_global String target_os_names[TargetOs_COUNT] = {
+ str_lit(""),
+ str_lit("windows"),
+ str_lit("darwin"),
+ str_lit("linux"),
+ str_lit("essence"),
+ str_lit("freebsd"),
+ str_lit("openbsd"),
+ str_lit("netbsd"),
+ str_lit("haiku"),
+
+ str_lit("wasi"),
+ str_lit("js"),
+ str_lit("orca"),
+
+ str_lit("freestanding"),
+};
+
enum TargetArchKind : u16 {
TargetArch_Invalid,
@@ -45,6 +63,17 @@ enum TargetArchKind : u16 {
TargetArch_COUNT,
};
+gb_global String target_arch_names[TargetArch_COUNT] = {
+ str_lit(""),
+ str_lit("amd64"),
+ str_lit("i386"),
+ str_lit("arm32"),
+ str_lit("arm64"),
+ str_lit("wasm32"),
+ str_lit("wasm64p32"),
+ str_lit("riscv64"),
+};
+
enum TargetEndianKind : u8 {
TargetEndian_Little,
TargetEndian_Big,
@@ -52,6 +81,11 @@ enum TargetEndianKind : u8 {
TargetEndian_COUNT,
};
+gb_global String target_endian_names[TargetEndian_COUNT] = {
+ str_lit("little"),
+ str_lit("big"),
+};
+
enum TargetABIKind : u16 {
TargetABI_Default,
@@ -61,7 +95,14 @@ enum TargetABIKind : u16 {
TargetABI_COUNT,
};
+gb_global String target_abi_names[TargetABI_COUNT] = {
+ str_lit(""),
+ str_lit("win64"),
+ str_lit("sysv"),
+};
+
enum Windows_Subsystem : u8 {
+ Windows_Subsystem_UNKNOWN,
Windows_Subsystem_BOOT_APPLICATION,
Windows_Subsystem_CONSOLE, // Default,
Windows_Subsystem_EFI_APPLICATION,
@@ -75,53 +116,32 @@ enum Windows_Subsystem : u8 {
Windows_Subsystem_COUNT,
};
-struct MicroarchFeatureList {
- String microarch;
- String features;
-};
-
-gb_global String target_os_names[TargetOs_COUNT] = {
+gb_global String windows_subsystem_names[Windows_Subsystem_COUNT] = {
str_lit(""),
- str_lit("windows"),
- str_lit("darwin"),
- str_lit("linux"),
- str_lit("essence"),
- str_lit("freebsd"),
- str_lit("openbsd"),
- str_lit("netbsd"),
- str_lit("haiku"),
-
- str_lit("wasi"),
- str_lit("js"),
- str_lit("orca"),
-
- str_lit("freestanding"),
+ str_lit("BOOT_APPLICATION"),
+ str_lit("CONSOLE"), // Default
+ str_lit("EFI_APPLICATION"),
+ str_lit("EFI_BOOT_SERVICE_DRIVER"),
+ str_lit("EFI_ROM"),
+ str_lit("EFI_RUNTIME_DRIVER"),
+ str_lit("NATIVE"),
+ str_lit("POSIX"),
+ str_lit("WINDOWS"),
+ str_lit("WINDOWSCE"),
};
-gb_global String target_arch_names[TargetArch_COUNT] = {
- str_lit(""),
- str_lit("amd64"),
- str_lit("i386"),
- str_lit("arm32"),
- str_lit("arm64"),
- str_lit("wasm32"),
- str_lit("wasm64p32"),
- str_lit("riscv64"),
+struct MicroarchFeatureList {
+ String microarch;
+ String features;
};
+#if defined(GB_SYSTEM_WINDOWS)
+ #include <llvm-c/Config/llvm-config.h>
+#else
+ #include <llvm/Config/llvm-config.h>
+#endif
#include "build_settings_microarch.cpp"
-gb_global String target_endian_names[TargetEndian_COUNT] = {
- str_lit("little"),
- str_lit("big"),
-};
-
-gb_global String target_abi_names[TargetABI_COUNT] = {
- str_lit(""),
- str_lit("win64"),
- str_lit("sysv"),
-};
-
gb_global TargetEndianKind target_endians[TargetArch_COUNT] = {
TargetEndian_Little,
TargetEndian_Little,
@@ -132,19 +152,6 @@ gb_global TargetEndianKind target_endians[TargetArch_COUNT] = {
TargetEndian_Little,
};
-gb_global String windows_subsystem_names[Windows_Subsystem_COUNT] = {
- str_lit("BOOT_APPLICATION"),
- str_lit("CONSOLE"), // Default
- str_lit("EFI_APPLICATION"),
- str_lit("EFI_BOOT_SERVICE_DRIVER"),
- str_lit("EFI_ROM"),
- str_lit("EFI_RUNTIME_DRIVER"),
- str_lit("NATIVE"),
- str_lit("POSIX"),
- str_lit("WINDOWS"),
- str_lit("WINDOWSCE"),
-};
-
#ifndef ODIN_VERSION_RAW
#define ODIN_VERSION_RAW "dev-unknown-unknown"
#endif
@@ -164,14 +171,19 @@ struct TargetMetrics {
enum Subtarget : u32 {
Subtarget_Default,
- Subtarget_iOS,
-
+ Subtarget_iPhone,
+ Subtarget_iPhoneSimulator,
+ Subtarget_Android,
+
Subtarget_COUNT,
+ Subtarget_Invalid, // NOTE(harold): Must appear after _COUNT as this is not a real subtarget
};
gb_global String subtarget_strings[Subtarget_COUNT] = {
str_lit(""),
- str_lit("ios"),
+ str_lit("iphone"),
+ str_lit("iphonesimulator"),
+ str_lit("android"),
};
@@ -198,20 +210,25 @@ enum BuildModeKind {
BuildMode_COUNT,
};
-enum CommandKind : u32 {
+enum CommandKind : u64 {
Command_run = 1<<0,
Command_build = 1<<1,
- Command_check = 1<<3,
- Command_doc = 1<<5,
- Command_version = 1<<6,
- Command_test = 1<<7,
+ Command_check = 1<<2,
+ Command_doc = 1<<3,
+ Command_version = 1<<4,
+ Command_test = 1<<5,
- Command_strip_semicolon = 1<<8,
- Command_bug_report = 1<<9,
+ Command_strip_semicolon = 1<<6,
+ Command_bug_report = 1<<7,
+
+ Command_bundle_android = 1<<8,
+ Command_bundle_macos = 1<<9,
+ Command_bundle_ios = 1<<10,
+ Command_bundle_orca = 1<<11,
Command__does_check = Command_run|Command_build|Command_check|Command_doc|Command_test|Command_strip_semicolon,
Command__does_build = Command_run|Command_build|Command_test,
- Command_all = ~(u32)0,
+ Command_all = ~(CommandKind)0,
};
gb_global char const *odin_command_strings[32] = {
@@ -222,6 +239,11 @@ gb_global char const *odin_command_strings[32] = {
"version",
"test",
"strip-semicolon",
+ "",
+ "bundle android",
+ "bundle macos",
+ "bundle ios",
+ "bundle orca",
};
@@ -269,7 +291,7 @@ enum BuildPath : u8 {
BuildPath_VS_LIB, // vs_library_path
BuildPath_Output, // Output Path for .exe, .dll, .so, etc. Can be overridden with `-out:`.
- BuildPath_PDB, // Output Path for .pdb file, can be overridden with `-pdb-name:`.
+ BuildPath_Symbols, // Output Path for .pdb or .dSym file, can be overridden with `-pdb-name:`.
BuildPathCOUNT,
};
@@ -287,6 +309,7 @@ enum VetFlags : u64 {
VetFlag_Cast = 1u<<8,
VetFlag_Tabs = 1u<<9,
VetFlag_UnusedProcedures = 1u<<10,
+ VetFlag_ExplicitAllocators = 1u<<11,
VetFlag_Unused = VetFlag_UnusedVariables|VetFlag_UnusedImports,
@@ -320,6 +343,8 @@ u64 get_vet_flag_from_name(String const &name) {
return VetFlag_Tabs;
} else if (name == "unused-procedures") {
return VetFlag_UnusedProcedures;
+ } else if (name == "explicit-allocators") {
+ return VetFlag_ExplicitAllocators;
}
return VetFlag_NONE;
}
@@ -327,12 +352,43 @@ u64 get_vet_flag_from_name(String const &name) {
enum OptInFeatureFlags : u64 {
OptInFeatureFlag_NONE = 0,
OptInFeatureFlag_DynamicLiterals = 1u<<0,
+
+ OptInFeatureFlag_GlobalContext = 1u<<1,
+
+ OptInFeatureFlag_IntegerDivisionByZero_Trap = 1u<<2,
+ OptInFeatureFlag_IntegerDivisionByZero_Zero = 1u<<3,
+ OptInFeatureFlag_IntegerDivisionByZero_Self = 1u<<4,
+ OptInFeatureFlag_IntegerDivisionByZero_AllBits = 1u<<5,
+
+
+ OptInFeatureFlag_IntegerDivisionByZero_ALL = OptInFeatureFlag_IntegerDivisionByZero_Trap|
+ OptInFeatureFlag_IntegerDivisionByZero_Zero|
+ OptInFeatureFlag_IntegerDivisionByZero_Self|
+ OptInFeatureFlag_IntegerDivisionByZero_AllBits,
+
};
u64 get_feature_flag_from_name(String const &name) {
if (name == "dynamic-literals") {
return OptInFeatureFlag_DynamicLiterals;
}
+ if (name == "integer-division-by-zero:trap") {
+ return OptInFeatureFlag_IntegerDivisionByZero_Trap;
+ }
+ if (name == "integer-division-by-zero:zero") {
+ return OptInFeatureFlag_IntegerDivisionByZero_Zero;
+ }
+ if (name == "integer-division-by-zero:self") {
+ return OptInFeatureFlag_IntegerDivisionByZero_Self;
+ }
+ if (name == "integer-division-by-zero:all-bits") {
+ return OptInFeatureFlag_IntegerDivisionByZero_AllBits;
+ }
+
+
+ if (name == "global-context") {
+ return OptInFeatureFlag_GlobalContext;
+ }
return OptInFeatureFlag_NONE;
}
@@ -362,30 +418,46 @@ enum LinkerChoice : i32 {
Linker_Default = 0,
Linker_lld,
Linker_radlink,
+ Linker_mold,
Linker_COUNT,
};
+enum SourceCodeLocationInfo : u8 {
+ SourceCodeLocationInfo_Normal = 0,
+ SourceCodeLocationInfo_Obfuscated = 1,
+ SourceCodeLocationInfo_Filename = 2,
+ SourceCodeLocationInfo_None = 3,
+};
+
String linker_choices[Linker_COUNT] = {
str_lit("default"),
str_lit("lld"),
str_lit("radlink"),
+ str_lit("mold"),
+};
+
+enum IntegerDivisionByZeroKind : u8 {
+ IntegerDivisionByZero_Trap,
+ IntegerDivisionByZero_Zero,
+ IntegerDivisionByZero_Self,
+ IntegerDivisionByZero_AllBits,
};
// This stores the information for the specify architecture of this build
struct BuildContext {
// Constants
- String ODIN_OS; // Target operating system
- String ODIN_ARCH; // Target architecture
- String ODIN_VENDOR; // Compiler vendor
- String ODIN_VERSION; // Compiler version
- String ODIN_ROOT; // Odin ROOT
- String ODIN_BUILD_PROJECT_NAME; // Odin main/initial package's directory name
- String ODIN_WINDOWS_SUBSYSTEM; // Empty string for non-Windows targets
- bool ODIN_DEBUG; // Odin in debug mode
- bool ODIN_DISABLE_ASSERT; // Whether the default 'assert' et al is disabled in code or not
- bool ODIN_DEFAULT_TO_NIL_ALLOCATOR; // Whether the default allocator is a "nil" allocator or not (i.e. it does nothing)
- bool ODIN_DEFAULT_TO_PANIC_ALLOCATOR; // Whether the default allocator is a "panic" allocator or not (i.e. panics on any call to it)
+ String ODIN_OS; // Target operating system
+ String ODIN_ARCH; // Target architecture
+ String ODIN_VENDOR; // Compiler vendor
+ String ODIN_VERSION; // Compiler version
+ String ODIN_ROOT; // Odin ROOT
+ String ODIN_BUILD_PROJECT_NAME; // Odin main/initial package's directory name
+ Windows_Subsystem ODIN_WINDOWS_SUBSYSTEM; // .Console, .Windows
+ bool ODIN_DEBUG; // Odin in debug mode
+ bool ODIN_DISABLE_ASSERT; // Whether the default 'assert' et al is disabled in code or not
+ bool ODIN_DEFAULT_TO_NIL_ALLOCATOR; // Whether the default allocator is a "nil" allocator or not (i.e. it does nothing)
+ bool ODIN_DEFAULT_TO_PANIC_ALLOCATOR; // Whether the default allocator is a "panic" allocator or not (i.e. panics on any call to it)
bool ODIN_FOREIGN_ERROR_PROCEDURES;
bool ODIN_VALGRIND_SUPPORT;
@@ -423,6 +495,7 @@ struct BuildContext {
String extra_assembler_flags;
String microarch;
BuildModeKind build_mode;
+ bool keep_executable;
bool generate_docs;
bool custom_optimization_level;
i32 optimization_level;
@@ -441,6 +514,7 @@ struct BuildContext {
bool ignore_unknown_attributes;
bool no_bounds_check;
bool no_type_assert;
+ bool dynamic_literals; // Opt-in to `#+feature dynamic-literals` project-wide.
bool no_output_files;
bool no_crt;
bool no_rpath;
@@ -451,6 +525,8 @@ struct BuildContext {
bool keep_object_files;
bool disallow_do;
+ IntegerDivisionByZeroKind integer_division_by_zero_behaviour;
+
LinkerChoice linker_choice;
StringSet custom_attributes;
@@ -472,6 +548,8 @@ struct BuildContext {
bool ignore_microsoft_magic;
bool linker_map_file;
+ bool build_diagnostics;
+
bool use_single_module;
bool use_separate_modules;
bool module_per_file;
@@ -480,6 +558,8 @@ struct BuildContext {
bool internal_no_inline;
bool internal_by_value;
+ bool internal_weak_monomorphization;
+ bool internal_ignore_llvm_verification;
bool no_threaded_checker;
@@ -491,7 +571,7 @@ struct BuildContext {
bool dynamic_map_calls;
- bool obfuscate_source_code_locations;
+ SourceCodeLocationInfo source_code_location_info;
bool min_link_libs;
@@ -521,6 +601,21 @@ struct BuildContext {
String minimum_os_version_string;
bool minimum_os_version_string_given;
+
+
+ int ODIN_ANDROID_API_LEVEL;
+
+ String ODIN_ANDROID_SDK;
+
+ String ODIN_ANDROID_NDK;
+ String ODIN_ANDROID_NDK_TOOLCHAIN;
+ String ODIN_ANDROID_NDK_TOOLCHAIN_LIB;
+ String ODIN_ANDROID_NDK_TOOLCHAIN_LIB_LEVEL;
+ String ODIN_ANDROID_NDK_TOOLCHAIN_SYSROOT;
+
+ String android_keystore;
+ String android_keystore_alias;
+ String android_keystore_password;
};
gb_global BuildContext build_context = {0};
@@ -544,12 +639,6 @@ gb_internal isize MAX_ERROR_COLLECTOR_COUNT(void) {
return build_context.max_error_count;
}
-#if defined(GB_SYSTEM_WINDOWS)
- #include <llvm-c/Config/llvm-config.h>
-#else
- #include <llvm/Config/llvm-config.h>
-#endif
-
// NOTE: AMD64 targets had their alignment on 128 bit ints bumped from 8 to 16 (undocumented of course).
#if LLVM_VERSION_MAJOR >= 18
#define AMD64_MAX_ALIGNMENT 16
@@ -820,13 +909,47 @@ gb_global NamedTargetMetrics *selected_target_metrics;
gb_global Subtarget selected_subtarget;
-gb_internal TargetOsKind get_target_os_from_string(String str) {
+gb_internal TargetOsKind get_target_os_from_string(String str, Subtarget *subtarget_ = nullptr, String *subtarget_str = nullptr) {
+ String os_name = str;
+ String subtarget = {};
+ auto part = string_partition(str, str_lit(":"));
+ if (part.match.len == 1) {
+ os_name = part.head;
+ subtarget = part.tail;
+ }
+
+ TargetOsKind kind = TargetOs_Invalid;
+
for (isize i = 0; i < TargetOs_COUNT; i++) {
- if (str_eq_ignore_case(target_os_names[i], str)) {
- return cast(TargetOsKind)i;
+ if (str_eq_ignore_case(target_os_names[i], os_name)) {
+ kind = cast(TargetOsKind)i;
+ break;
+ }
+ }
+
+ if (subtarget_str) *subtarget_str = subtarget;
+
+ if (subtarget_) {
+ if (subtarget.len != 0) {
+ *subtarget_ = Subtarget_Invalid;
+
+ if (str_eq_ignore_case(subtarget, "generic") || str_eq_ignore_case(subtarget, "default")) {
+ *subtarget_ = Subtarget_Default;
+
+ } else {
+ for (isize i = 1; i < Subtarget_COUNT; i++) {
+ if (str_eq_ignore_case(subtarget_strings[i], subtarget)) {
+ *subtarget_ = cast(Subtarget)i;
+ break;
+ }
+ }
+ }
+ } else {
+ *subtarget_ = Subtarget_Default;
}
}
- return TargetOs_Invalid;
+
+ return kind;
}
gb_internal TargetArchKind get_target_arch_from_string(String str) {
@@ -847,14 +970,6 @@ gb_internal bool is_excluded_target_filename(String name) {
return true;
}
- if (build_context.command_kind != Command_test) {
- String test_suffix = str_lit("_test");
- if (string_ends_with(name, test_suffix) && name != test_suffix) {
- // Ignore *_test.odin files
- return true;
- }
- }
-
String str1 = {};
String str2 = {};
isize n = 0;
@@ -946,6 +1061,14 @@ gb_internal bool is_arch_x86(void) {
gb_global String const WIN32_SEPARATOR_STRING = {cast(u8 *)"\\", 1};
gb_global String const NIX_SEPARATOR_STRING = {cast(u8 *)"/", 1};
+gb_global String const SEPARATOR_STRING =
+#if defined(GB_SYSTEM_WINDOWS)
+ WIN32_SEPARATOR_STRING;
+#else
+ NIX_SEPARATOR_STRING;
+#endif
+
+
gb_global String const WASM_MODULE_NAME_SEPARATOR = str_lit("..");
gb_internal String internal_odin_root_dir(void);
@@ -1001,10 +1124,10 @@ gb_internal String internal_odin_root_dir(void) {
mutex_lock(&string_buffer_mutex);
defer (mutex_unlock(&string_buffer_mutex));
- text = gb_alloc_array(permanent_allocator(), wchar_t, len+1);
+ text = permanent_alloc_array<wchar_t>(len+1);
GetModuleFileNameW(nullptr, text, cast(int)len);
- path = string16_to_string(heap_allocator(), make_string16(text, len));
+ path = string16_to_string(heap_allocator(), make_string16(cast(u16 *)text, len));
for (i = path.len-1; i >= 0; i--) {
u8 c = path[i];
@@ -1038,8 +1161,8 @@ gb_internal String internal_odin_root_dir(void) {
return global_module_path;
}
- auto path_buf = array_make<char>(heap_allocator(), 300);
- defer (array_free(&path_buf));
+ TEMPORARY_ALLOCATOR_GUARD();
+ auto path_buf = array_make<char>(temporary_allocator(), 300);
len = 0;
for (;;) {
@@ -1056,7 +1179,7 @@ gb_internal String internal_odin_root_dir(void) {
mutex_lock(&string_buffer_mutex);
defer (mutex_unlock(&string_buffer_mutex));
- text = gb_alloc_array(permanent_allocator(), u8, len + 1);
+ text = permanent_alloc_array<u8>(len + 1);
gb_memmove(text, &path_buf[0], len);
path = path_to_fullpath(heap_allocator(), make_string(text, len), nullptr);
@@ -1108,7 +1231,7 @@ gb_internal String internal_odin_root_dir(void) {
mutex_lock(&string_buffer_mutex);
defer (mutex_unlock(&string_buffer_mutex));
- text = gb_alloc_array(permanent_allocator(), u8, len + 1);
+ text = permanent_alloc_array<u8>(len + 1);
gb_memmove(text, &path_buf[0], len);
path = path_to_fullpath(heap_allocator(), make_string(text, len), nullptr);
@@ -1269,7 +1392,7 @@ gb_internal String internal_odin_root_dir(void) {
mutex_lock(&string_buffer_mutex);
defer (mutex_unlock(&string_buffer_mutex));
- text = gb_alloc_array(permanent_allocator(), u8, len + 1);
+ text = permanent_alloc_array<u8>(len + 1);
gb_memmove(text, &path_buf[0], len);
@@ -1302,14 +1425,14 @@ gb_internal String path_to_fullpath(gbAllocator a, String s, bool *ok_) {
mutex_lock(&fullpath_mutex);
- len = GetFullPathNameW(&string16[0], 0, nullptr, nullptr);
+ len = GetFullPathNameW(cast(wchar_t *)&string16[0], 0, nullptr, nullptr);
if (len != 0) {
- wchar_t *text = gb_alloc_array(permanent_allocator(), wchar_t, len+1);
- GetFullPathNameW(&string16[0], len, text, nullptr);
+ wchar_t *text = permanent_alloc_array<wchar_t>(len+1);
+ GetFullPathNameW(cast(wchar_t *)&string16[0], len, text, nullptr);
mutex_unlock(&fullpath_mutex);
text[len] = 0;
- result = string16_to_string(a, make_string16(text, len));
+ result = string16_to_string(a, make_string16(cast(u16 *)text, len));
result = string_trim_whitespace(result);
// Replace Windows style separators
@@ -1461,6 +1584,94 @@ gb_internal bool has_ansi_terminal_colours(void) {
return build_context.has_ansi_terminal_colours && !json_errors();
}
+gb_internal void init_android_values(bool with_sdk) {
+ auto *bc = &build_context;
+ { // Android SDK/API Level
+ String default_level = str_lit("34");
+ if (!bc->minimum_os_version_string_given) {
+ bc->minimum_os_version_string = default_level;
+ }
+ BigInt level = {};
+ bool success = false;
+ big_int_from_string(&level, bc->minimum_os_version_string, &success);
+ if (!success) {
+ gb_printf_err("Warning: Invalid -minimum-os-version:%.*s for -subtarget:Android, defaulting to %.*s\n", LIT(bc->minimum_os_version_string), LIT(default_level));
+ bc->minimum_os_version_string = default_level;
+ big_int_from_string(&level, bc->minimum_os_version_string, &success);
+ GB_ASSERT(success);
+ }
+
+ i64 new_level = big_int_to_i64(&level);
+
+ if (new_level >= 21) {
+ bc->ODIN_ANDROID_API_LEVEL = cast(int)new_level;
+ } else {
+ gb_printf_err("Warning: Invalid -minimum-os-version:%.*s for -subtarget:Android, defaulting to %.*s\n", LIT(bc->minimum_os_version_string), LIT(default_level));
+ bc->ODIN_ANDROID_API_LEVEL = atoi(cast(char const *)default_level.text);
+ }
+ }
+ bc->ODIN_ANDROID_NDK = normalize_path(permanent_allocator(), make_string_c(gb_get_env("ODIN_ANDROID_NDK", permanent_allocator())), NIX_SEPARATOR_STRING);
+ bc->ODIN_ANDROID_NDK_TOOLCHAIN = normalize_path(permanent_allocator(), make_string_c(gb_get_env("ODIN_ANDROID_NDK_TOOLCHAIN", permanent_allocator())), NIX_SEPARATOR_STRING);
+ bc->ODIN_ANDROID_SDK = normalize_path(permanent_allocator(), make_string_c(gb_get_env("ODIN_ANDROID_SDK", permanent_allocator())), NIX_SEPARATOR_STRING);
+
+ #if defined(GB_SYSTEM_WINDOWS)
+ if (bc->ODIN_ANDROID_SDK.len == 0) {
+ bc->ODIN_ANDROID_SDK = normalize_path(permanent_allocator(),
+ path_to_fullpath(permanent_allocator(), str_lit("%LocalAppData%/Android/Sdk"), nullptr),
+ NIX_SEPARATOR_STRING);
+ }
+ #endif
+
+ if (bc->ODIN_ANDROID_NDK.len != 0 && bc->ODIN_ANDROID_NDK_TOOLCHAIN.len == 0) {
+ String arch = str_lit("x86_64");
+ #if defined (GB_CPU_ARM)
+ // TODO(bill): this is a complete guess
+ arch = str_lit("aarch64");
+ #endif
+ #if defined(GB_SYSTEM_WINDOWS)
+ bc->ODIN_ANDROID_NDK_TOOLCHAIN = concatenate4_strings(temporary_allocator(), bc->ODIN_ANDROID_NDK, str_lit("toolchains/llvm/prebuilt/"), str_lit("windows-"), arch);
+ #elif defined(GB_SYSTEM_OSX)
+ // TODO(bill): is this name even correct?
+ bc->ODIN_ANDROID_NDK_TOOLCHAIN = concatenate4_strings(temporary_allocator(), bc->ODIN_ANDROID_NDK, str_lit("toolchains/llvm/prebuilt/"), str_lit("darwin-"), arch);
+ #elif defined(GB_SYSTEM_LINUX)
+ bc->ODIN_ANDROID_NDK_TOOLCHAIN = concatenate4_strings(temporary_allocator(), bc->ODIN_ANDROID_NDK, str_lit("toolchains/llvm/prebuilt/"), str_lit("linux-"), arch);
+ #endif
+
+ bc->ODIN_ANDROID_NDK_TOOLCHAIN = normalize_path(permanent_allocator(), bc->ODIN_ANDROID_NDK_TOOLCHAIN, NIX_SEPARATOR_STRING);
+ }
+
+ if (bc->ODIN_ANDROID_NDK.len == 0 && !with_sdk) {
+ gb_printf_err("Error: ODIN_ANDROID_NDK not set");
+ gb_exit(1);
+
+ }
+
+ if (bc->ODIN_ANDROID_NDK_TOOLCHAIN.len == 0 && !with_sdk) {
+ gb_printf_err("Error: ODIN_ANDROID_NDK not set");
+ gb_exit(1);
+ }
+
+ bc->ODIN_ANDROID_NDK_TOOLCHAIN_LIB = concatenate_strings(permanent_allocator(), bc->ODIN_ANDROID_NDK_TOOLCHAIN, str_lit("sysroot/usr/lib/aarch64-linux-android/"));
+
+ char buf[32] = {};
+ gb_snprintf(buf, gb_size_of(buf), "%d/", bc->ODIN_ANDROID_API_LEVEL);
+ bc->ODIN_ANDROID_NDK_TOOLCHAIN_LIB_LEVEL = concatenate_strings(permanent_allocator(), bc->ODIN_ANDROID_NDK_TOOLCHAIN_LIB, make_string_c(buf));
+
+ bc->ODIN_ANDROID_NDK_TOOLCHAIN_SYSROOT = concatenate_strings(permanent_allocator(), bc->ODIN_ANDROID_NDK_TOOLCHAIN, str_lit("sysroot/"));
+
+
+ if (with_sdk) {
+ if (bc->ODIN_ANDROID_SDK.len == 0) {
+ gb_printf_err("Error: ODIN_ANDROID_SDK not set, which is required for -build-mode:executable for -subtarget:android");
+ gb_exit(1);
+ }
+ if (bc->android_keystore.len == 0) {
+ gb_printf_err("Error: -android-keystore:<string> has not been set\n");
+ gb_exit(1);
+ }
+ }
+}
+
gb_internal bool has_asm_extension(String const &path) {
String ext = path_extension(path);
if (ext == ".asm") {
@@ -1637,20 +1848,66 @@ gb_internal void init_build_context(TargetMetrics *cross_target, Subtarget subta
}
// Default to subsystem:CONSOLE on Windows targets
- if (bc->ODIN_WINDOWS_SUBSYSTEM == "" && bc->metrics.os == TargetOs_windows) {
- bc->ODIN_WINDOWS_SUBSYSTEM = windows_subsystem_names[Windows_Subsystem_CONSOLE];
+ if (bc->ODIN_WINDOWS_SUBSYSTEM == Windows_Subsystem_UNKNOWN && bc->metrics.os == TargetOs_windows) {
+ bc->ODIN_WINDOWS_SUBSYSTEM = Windows_Subsystem_CONSOLE;
+ }
+
+ if (subtarget == Subtarget_Android) {
+ switch (build_context.build_mode) {
+ case BuildMode_DynamicLibrary:
+ case BuildMode_Object:
+ case BuildMode_Assembly:
+ case BuildMode_LLVM_IR:
+ break;
+ default:
+ case BuildMode_Executable:
+ case BuildMode_StaticLibrary:
+ if ((build_context.command_kind & Command__does_build) != 0) {
+ gb_printf_err("Unsupported -build-mode for -subtarget:android\n");
+ gb_printf_err("\tCurrently only supporting: \n");
+ // gb_printf_err("\t\texe\n");
+ gb_printf_err("\t\tshared\n");
+ gb_printf_err("\t\tobject\n");
+ gb_printf_err("\t\tassembly\n");
+ gb_printf_err("\t\tllvm-ir\n");
+ gb_exit(1);
+ }
+ break;
+ }
}
- if (metrics->os == TargetOs_darwin && subtarget == Subtarget_iOS) {
+ if (metrics->os == TargetOs_darwin) {
+ switch (subtarget) {
+ case Subtarget_iPhone:
+ switch (metrics->arch) {
+ case TargetArch_arm64:
+ bc->metrics.target_triplet = str_lit("arm64-apple-ios");
+ break;
+ default:
+ GB_PANIC("Unknown architecture for -subtarget:iphone");
+ }
+ break;
+ case Subtarget_iPhoneSimulator:
+ switch (metrics->arch) {
+ case TargetArch_arm64:
+ bc->metrics.target_triplet = str_lit("arm64-apple-ios-simulator");
+ break;
+ case TargetArch_amd64:
+ bc->metrics.target_triplet = str_lit("x86_64-apple-ios-simulator");
+ break;
+ default:
+ GB_PANIC("Unknown architecture for -subtarget:iphonesimulator");
+ }
+ break;
+ }
+ } else if (metrics->os == TargetOs_linux && subtarget == Subtarget_Android) {
switch (metrics->arch) {
case TargetArch_arm64:
- bc->metrics.target_triplet = str_lit("arm64-apple-ios");
- break;
- case TargetArch_amd64:
- bc->metrics.target_triplet = str_lit("x86_64-apple-ios");
+ bc->metrics.target_triplet = str_lit("aarch64-none-linux-android");
+ bc->reloc_mode = RelocMode_PIC;
break;
default:
- GB_PANIC("Unknown architecture for darwin");
+ GB_PANIC("Unknown architecture for -subtarget:android");
}
}
@@ -1668,6 +1925,13 @@ gb_internal void init_build_context(TargetMetrics *cross_target, Subtarget subta
str_lit("-target "), bc->metrics.target_triplet, str_lit(" "));
} else if (is_arch_wasm()) {
gbString link_flags = gb_string_make(heap_allocator(), " ");
+
+ // NOTE(laytan): Put the stack first in the memory,
+ // causing a stack overflow to error immediately instead of corrupting globals.
+ link_flags = gb_string_appendc(link_flags, "--stack-first ");
+ // NOTE(laytan): default stack size is 64KiB, up to a more reasonable 1MiB.
+ link_flags = gb_string_appendc(link_flags, "-z stack-size=1048576 ");
+
// link_flags = gb_string_appendc(link_flags, "--export-all ");
// link_flags = gb_string_appendc(link_flags, "--export-table ");
// if (bc->metrics.arch == TargetArch_wasm64) {
@@ -1700,12 +1964,27 @@ gb_internal void init_build_context(TargetMetrics *cross_target, Subtarget subta
// does not annoy the user with version warnings.
if (metrics->os == TargetOs_darwin) {
if (!bc->minimum_os_version_string_given) {
- bc->minimum_os_version_string = str_lit("11.0.0");
+ if (subtarget == Subtarget_Default) {
+ bc->minimum_os_version_string = str_lit("11.0.0");
+ } else if (subtarget == Subtarget_iPhone || subtarget == Subtarget_iPhoneSimulator) {
+ // NOTE(harold): We default to 17.4 on iOS because that's when os_sync_wait_on_address was added and
+ // we'd like to avoid any potential App Store issues by using the private ulock_* there.
+ bc->minimum_os_version_string = str_lit("17.4");
+ }
}
- if (subtarget == Subtarget_Default) {
+ if (subtarget == Subtarget_iPhoneSimulator) {
+ // For the iPhoneSimulator subtarget, the version must be between 'ios' and '-simulator'.
+ String suffix = str_lit("-simulator");
+ GB_ASSERT(string_ends_with(bc->metrics.target_triplet, suffix));
+
+ String prefix = substring(bc->metrics.target_triplet, 0, bc->metrics.target_triplet.len - suffix.len);
+ bc->metrics.target_triplet = concatenate3_strings(permanent_allocator(), prefix, bc->minimum_os_version_string, suffix);
+ } else {
bc->metrics.target_triplet = concatenate_strings(permanent_allocator(), bc->metrics.target_triplet, bc->minimum_os_version_string);
}
+ } else if (selected_subtarget == Subtarget_Android) {
+ init_android_values(bc->build_mode == BuildMode_Executable && (bc->command_kind & Command__does_build) != 0);
}
if (!bc->custom_optimization_level) {
@@ -1731,12 +2010,6 @@ gb_internal void init_build_context(TargetMetrics *cross_target, Subtarget subta
}
- // TODO: Static map calls are bugged on `amd64sysv` abi.
- if (bc->metrics.os != TargetOs_windows && bc->metrics.arch == TargetArch_amd64) {
- // ENFORCE DYNAMIC MAP CALLS
- bc->dynamic_map_calls = true;
- }
-
bc->ODIN_VALGRIND_SUPPORT = false;
if (build_context.metrics.os != TargetOs_windows) {
switch (bc->metrics.arch) {
@@ -1842,6 +2115,39 @@ gb_internal bool check_target_feature_is_superset_of(String const &superset, Str
return true;
}
+gb_internal String infer_object_extension_from_build_context() {
+ String output_extension = {};
+ if (is_arch_wasm()) {
+ output_extension = STR_LIT("wasm.o");
+ } else {
+ switch (build_context.metrics.os) {
+ case TargetOs_windows:
+ output_extension = STR_LIT("obj");
+ break;
+ default:
+ case TargetOs_darwin:
+ case TargetOs_linux:
+ case TargetOs_essence:
+ output_extension = STR_LIT("o");
+ break;
+
+ case TargetOs_freestanding:
+ switch (build_context.metrics.abi) {
+ default:
+ case TargetABI_Default:
+ case TargetABI_SysV:
+ output_extension = STR_LIT("o");
+ break;
+ case TargetABI_Win64:
+ output_extension = STR_LIT("obj");
+ break;
+ }
+ break;
+ }
+ }
+ return output_extension;
+}
+
// NOTE(Jeroen): Set/create the output and other paths and report an error as appropriate.
// We've previously called `parse_build_flags`, so `out_filepath` should be set.
gb_internal bool init_build_paths(String init_filename) {
@@ -1885,10 +2191,6 @@ gb_internal bool init_build_paths(String init_filename) {
}
}
- if (bc->pdb_filepath.len > 0) {
- bc->build_paths[BuildPath_PDB] = path_from_string(ha, bc->pdb_filepath);
- }
-
if ((bc->command_kind & Command__does_build) && (!bc->ignore_microsoft_magic)) {
// NOTE(ic): It would be nice to extend this so that we could specify the Visual Studio version that we want instead of defaulting to the latest.
Find_Result find_result = find_visual_studio_and_windows_sdk();
@@ -1947,7 +2249,10 @@ gb_internal bool init_build_paths(String init_filename) {
output_extension = make_string(nullptr, 0);
String const single_file_extension = str_lit(".odin");
- if (build_context.metrics.os == TargetOs_windows) {
+ if (selected_subtarget == Subtarget_Android) {
+ // NOTE(bill): It's always shared!
+ output_extension = STR_LIT("so");
+ } else if (build_context.metrics.os == TargetOs_windows) {
output_extension = STR_LIT("exe");
} else if (build_context.cross_compiling && selected_target_metrics->metrics == &target_essence_amd64) {
// Do nothing: we don't want the .bin extension
@@ -1975,13 +2280,8 @@ gb_internal bool init_build_paths(String init_filename) {
if (build_context.metrics.os == TargetOs_windows) {
output_extension = STR_LIT("lib");
}
- }else if (build_context.build_mode == BuildMode_Object) {
- // By default use a .o object extension.
- output_extension = STR_LIT("o");
-
- if (build_context.metrics.os == TargetOs_windows) {
- output_extension = STR_LIT("obj");
- }
+ } else if (build_context.build_mode == BuildMode_Object) {
+ output_extension = infer_object_extension_from_build_context();
} else if (build_context.build_mode == BuildMode_Assembly) {
// By default use a .S asm extension.
output_extension = STR_LIT("S");
@@ -2001,7 +2301,7 @@ gb_internal bool init_build_paths(String init_filename) {
return false;
} else if (bc->build_paths[BuildPath_Output].ext.len == 0) {
gb_printf_err("Output path %.*s must have an appropriate extension.\n", LIT(output_file));
- return false;
+ return false;
}
}
} else {
@@ -2027,11 +2327,34 @@ gb_internal bool init_build_paths(String init_filename) {
while (output_name.len > 0 && (output_name[output_name.len-1] == '/' || output_name[output_name.len-1] == '\\')) {
output_name.len -= 1;
}
+ // Only trim the extension if it's an Odin source file.
+ // This lets people build folders with extensions or files beginning with dots.
+ if (path_extension(output_name) == ".odin" && !path_is_directory(output_name)) {
+ output_name = remove_extension_from_path(output_name);
+ }
output_name = remove_directory_from_path(output_name);
- output_name = remove_extension_from_path(output_name);
output_name = copy_string(ha, string_trim_whitespace(output_name));
- output_path = path_from_string(ha, output_name);
-
+ // This is `path_from_string` without the extension trimming.
+ Path res = {};
+ if (output_name.len > 0) {
+ String fullpath = path_to_full_path(ha, output_name);
+ defer (gb_free(ha, fullpath.text));
+
+ res.basename = directory_from_path(fullpath);
+ res.basename = copy_string(ha, res.basename);
+
+ if (path_is_directory(fullpath)) {
+ if (res.basename.len > 0 && res.basename.text[res.basename.len - 1] == '/') {
+ res.basename.len--;
+ }
+ } else {
+ isize name_start = (res.basename.len > 0) ? res.basename.len + 1 : res.basename.len;
+ res.name = substring(fullpath, name_start, fullpath.len);
+ res.name = copy_string(ha, res.name);
+ }
+ }
+ output_path = res;
+
// Note(Dragos): This is a fix for empty filenames
// Turn the trailing folder into the file name
if (output_path.name.len == 0) {
@@ -2061,6 +2384,26 @@ gb_internal bool init_build_paths(String init_filename) {
bc->build_paths[BuildPath_Output] = output_path;
}
+ if (build_context.ODIN_DEBUG) {
+ if (build_context.metrics.os == TargetOs_windows) {
+ if (bc->pdb_filepath.len > 0) {
+ bc->build_paths[BuildPath_Symbols] = path_from_string(ha, bc->pdb_filepath);
+ } else {
+ Path symbol_path;
+ symbol_path.basename = copy_string(ha, bc->build_paths[BuildPath_Output].basename);
+ symbol_path.name = copy_string(ha, bc->build_paths[BuildPath_Output].name);
+ symbol_path.ext = copy_string(ha, STR_LIT("pdb"));
+ bc->build_paths[BuildPath_Symbols] = symbol_path;
+ }
+ } else if (build_context.metrics.os == TargetOs_darwin) {
+ Path symbol_path;
+ symbol_path.basename = copy_string(ha, bc->build_paths[BuildPath_Output].basename);
+ symbol_path.name = copy_string(ha, bc->build_paths[BuildPath_Output].name);
+ symbol_path.ext = copy_string(ha, STR_LIT("dSYM"));
+ bc->build_paths[BuildPath_Symbols] = symbol_path;
+ }
+ }
+
// Do we have an extension? We might not if the output filename was supplied.
if (bc->build_paths[BuildPath_Output].ext.len == 0) {
if (build_context.metrics.os == TargetOs_windows || is_arch_wasm() || build_context.build_mode != BuildMode_Executable) {
@@ -2096,9 +2439,10 @@ gb_internal bool init_build_paths(String init_filename) {
case TargetOs_windows:
case TargetOs_linux:
case TargetOs_darwin:
+ case TargetOs_freebsd:
break;
default:
- gb_printf_err("-sanitize:address is only supported on windows, linux, and darwin\n");
+ gb_printf_err("-sanitize:address is only supported on Windows, Linux, Darwin, and FreeBSD\n");
return false;
}
}
@@ -2106,12 +2450,10 @@ gb_internal bool init_build_paths(String init_filename) {
if (build_context.sanitizer_flags & SanitizerFlag_Memory) {
switch (build_context.metrics.os) {
case TargetOs_linux:
+ case TargetOs_freebsd:
break;
default:
- gb_printf_err("-sanitize:memory is only supported on linux\n");
- return false;
- }
- if (build_context.metrics.os != TargetOs_linux) {
+ gb_printf_err("-sanitize:memory is only supported on Linux and FreeBSD\n");
return false;
}
}
@@ -2120,9 +2462,10 @@ gb_internal bool init_build_paths(String init_filename) {
switch (build_context.metrics.os) {
case TargetOs_linux:
case TargetOs_darwin:
+ case TargetOs_freebsd:
break;
default:
- gb_printf_err("-sanitize:thread is only supported on linux and darwin\n");
+ gb_printf_err("-sanitize:thread is only supported on Linux, Darwin, and FreeBSD\n");
return false;
}
}
diff --git a/src/build_settings_microarch.cpp b/src/build_settings_microarch.cpp
index 8f64d4026..16fb2196f 100644
--- a/src/build_settings_microarch.cpp
+++ b/src/build_settings_microarch.cpp
@@ -1,3 +1,561 @@
+#ifndef LLVM_VERSION_MAJOR
+ #error "LLVM_VERSION_MAJOR is not defined!"
+#endif
+
+#if LLVM_VERSION_MAJOR >= 20
+// Generated with the featuregen script in `misc/featuregen`
+gb_global String target_microarch_list[TargetArch_COUNT] = {
+ // TargetArch_Invalid:
+ str_lit(""),
+ // TargetArch_amd64:
+ str_lit("alderlake,amdfam10,arrowlake,arrowlake-s,arrowlake_s,athlon,athlon-4,athlon-fx,athlon-mp,athlon-tbird,athlon-xp,athlon64,athlon64-sse3,atom,atom_sse4_2,atom_sse4_2_movbe,barcelona,bdver1,bdver2,bdver3,bdver4,bonnell,broadwell,btver1,btver2,c3,c3-2,cannonlake,cascadelake,clearwaterforest,cooperlake,core-avx-i,core-avx2,core2,core_2_duo_sse4_1,core_2_duo_ssse3,core_2nd_gen_avx,core_3rd_gen_avx,core_4th_gen_avx,core_4th_gen_avx_tsx,core_5th_gen_avx,core_5th_gen_avx_tsx,core_aes_pclmulqdq,core_i7_sse4_2,corei7,corei7-avx,diamondrapids,emeraldrapids,generic,geode,goldmont,goldmont-plus,goldmont_plus,gracemont,grandridge,graniterapids,graniterapids-d,graniterapids_d,haswell,i386,i486,i586,i686,icelake-client,icelake-server,icelake_client,icelake_server,ivybridge,k6,k6-2,k6-3,k8,k8-sse3,knl,knm,lakemont,lunarlake,meteorlake,mic_avx512,nehalem,nocona,opteron,opteron-sse3,pantherlake,penryn,pentium,pentium-m,pentium-mmx,pentium2,pentium3,pentium3m,pentium4,pentium4m,pentium_4,pentium_4_sse3,pentium_ii,pentium_iii,pentium_iii_no_xmm_regs,pentium_m,pentium_mmx,pentium_pro,pentiumpro,prescott,raptorlake,rocketlake,sandybridge,sapphirerapids,sierraforest,silvermont,skx,skylake,skylake-avx512,skylake_avx512,slm,tigerlake,tremont,westmere,winchip-c6,winchip2,x86-64,x86-64-v2,x86-64-v3,x86-64-v4,yonah,znver1,znver2,znver3,znver4,znver5"),
+ // TargetArch_i386:
+ str_lit("alderlake,amdfam10,arrowlake,arrowlake-s,arrowlake_s,athlon,athlon-4,athlon-fx,athlon-mp,athlon-tbird,athlon-xp,athlon64,athlon64-sse3,atom,atom_sse4_2,atom_sse4_2_movbe,barcelona,bdver1,bdver2,bdver3,bdver4,bonnell,broadwell,btver1,btver2,c3,c3-2,cannonlake,cascadelake,clearwaterforest,cooperlake,core-avx-i,core-avx2,core2,core_2_duo_sse4_1,core_2_duo_ssse3,core_2nd_gen_avx,core_3rd_gen_avx,core_4th_gen_avx,core_4th_gen_avx_tsx,core_5th_gen_avx,core_5th_gen_avx_tsx,core_aes_pclmulqdq,core_i7_sse4_2,corei7,corei7-avx,diamondrapids,emeraldrapids,generic,geode,goldmont,goldmont-plus,goldmont_plus,gracemont,grandridge,graniterapids,graniterapids-d,graniterapids_d,haswell,i386,i486,i586,i686,icelake-client,icelake-server,icelake_client,icelake_server,ivybridge,k6,k6-2,k6-3,k8,k8-sse3,knl,knm,lakemont,lunarlake,meteorlake,mic_avx512,nehalem,nocona,opteron,opteron-sse3,pantherlake,penryn,pentium,pentium-m,pentium-mmx,pentium2,pentium3,pentium3m,pentium4,pentium4m,pentium_4,pentium_4_sse3,pentium_ii,pentium_iii,pentium_iii_no_xmm_regs,pentium_m,pentium_mmx,pentium_pro,pentiumpro,prescott,raptorlake,rocketlake,sandybridge,sapphirerapids,sierraforest,silvermont,skx,skylake,skylake-avx512,skylake_avx512,slm,tigerlake,tremont,westmere,winchip-c6,winchip2,x86-64,x86-64-v2,x86-64-v3,x86-64-v4,yonah,znver1,znver2,znver3,znver4,znver5"),
+ // TargetArch_arm32:
+ str_lit("arm1020e,arm1020t,arm1022e,arm10e,arm10tdmi,arm1136j-s,arm1136jf-s,arm1156t2-s,arm1156t2f-s,arm1176jz-s,arm1176jzf-s,arm710t,arm720t,arm7tdmi,arm7tdmi-s,arm8,arm810,arm9,arm920,arm920t,arm922t,arm926ej-s,arm940t,arm946e-s,arm966e-s,arm968e-s,arm9e,arm9tdmi,cortex-a12,cortex-a15,cortex-a17,cortex-a32,cortex-a35,cortex-a5,cortex-a510,cortex-a53,cortex-a55,cortex-a57,cortex-a7,cortex-a710,cortex-a72,cortex-a73,cortex-a75,cortex-a76,cortex-a76ae,cortex-a77,cortex-a78,cortex-a78ae,cortex-a78c,cortex-a8,cortex-a9,cortex-m0,cortex-m0plus,cortex-m1,cortex-m23,cortex-m3,cortex-m33,cortex-m35p,cortex-m4,cortex-m52,cortex-m55,cortex-m7,cortex-m85,cortex-r4,cortex-r4f,cortex-r5,cortex-r52,cortex-r52plus,cortex-r7,cortex-r8,cortex-x1,cortex-x1c,cyclone,ep9312,exynos-m3,exynos-m4,exynos-m5,generic,iwmmxt,krait,kryo,mpcore,mpcorenovfp,neoverse-n1,neoverse-n2,neoverse-v1,sc000,sc300,star-mc1,strongarm,strongarm110,strongarm1100,strongarm1110,swift,xscale"),
+ // TargetArch_arm64:
+ str_lit("a64fx,ampere1,ampere1a,ampere1b,apple-a10,apple-a11,apple-a12,apple-a13,apple-a14,apple-a15,apple-a16,apple-a17,apple-a7,apple-a8,apple-a9,apple-m1,apple-m2,apple-m3,apple-m4,apple-s4,apple-s5,carmel,cobalt-100,cortex-a34,cortex-a35,cortex-a510,cortex-a520,cortex-a520ae,cortex-a53,cortex-a55,cortex-a57,cortex-a65,cortex-a65ae,cortex-a710,cortex-a715,cortex-a72,cortex-a720,cortex-a720ae,cortex-a725,cortex-a73,cortex-a75,cortex-a76,cortex-a76ae,cortex-a77,cortex-a78,cortex-a78ae,cortex-a78c,cortex-r82,cortex-r82ae,cortex-x1,cortex-x1c,cortex-x2,cortex-x3,cortex-x4,cortex-x925,cyclone,exynos-m3,exynos-m4,exynos-m5,falkor,fujitsu-monaka,generic,grace,kryo,neoverse-512tvb,neoverse-e1,neoverse-n1,neoverse-n2,neoverse-n3,neoverse-v1,neoverse-v2,neoverse-v3,neoverse-v3ae,oryon-1,saphira,thunderx,thunderx2t99,thunderx3t110,thunderxt81,thunderxt83,thunderxt88,tsv110"),
+ // TargetArch_wasm32:
+ str_lit("bleeding-edge,generic,lime1,mvp"),
+ // TargetArch_wasm64p32:
+ str_lit("bleeding-edge,generic,lime1,mvp"),
+ // TargetArch_riscv64:
+ str_lit("generic,generic-rv32,generic-rv64,mips-p8700,rocket,rocket-rv32,rocket-rv64,rp2350-hazard3,sifive-7-series,sifive-e20,sifive-e21,sifive-e24,sifive-e31,sifive-e34,sifive-e76,sifive-p450,sifive-p470,sifive-p550,sifive-p670,sifive-s21,sifive-s51,sifive-s54,sifive-s76,sifive-u54,sifive-u74,sifive-x280,spacemit-x60,syntacore-scr1-base,syntacore-scr1-max,syntacore-scr3-rv32,syntacore-scr3-rv64,syntacore-scr4-rv32,syntacore-scr4-rv64,syntacore-scr5-rv32,syntacore-scr5-rv64,syntacore-scr7,tt-ascalon-d8,veyron-v1,xiangshan-nanhu"),
+};
+
+// Generated with the featuregen script in `misc/featuregen`
+gb_global String target_features_list[TargetArch_COUNT] = {
+ // TargetArch_Invalid:
+ str_lit(""),
+ // TargetArch_amd64:
+ str_lit("16bit-mode,32bit-mode,64bit,64bit-mode,adx,aes,allow-light-256-bit,amx-avx512,amx-bf16,amx-complex,amx-fp16,amx-fp8,amx-int8,amx-movrs,amx-tf32,amx-tile,amx-transpose,avx,avx10.1-256,avx10.1-512,avx10.2-256,avx10.2-512,avx2,avx512bf16,avx512bitalg,avx512bw,avx512cd,avx512dq,avx512f,avx512fp16,avx512ifma,avx512vbmi,avx512vbmi2,avx512vl,avx512vnni,avx512vp2intersect,avx512vpopcntdq,avxifma,avxneconvert,avxvnni,avxvnniint16,avxvnniint8,bmi,bmi2,branch-hint,branchfusion,ccmp,cf,cldemote,clflushopt,clwb,clzero,cmov,cmpccxadd,crc32,cx16,cx8,egpr,enqcmd,ermsb,evex512,f16c,false-deps-getmant,false-deps-lzcnt-tzcnt,false-deps-mulc,false-deps-mullq,false-deps-perm,false-deps-popcnt,false-deps-range,fast-11bytenop,fast-15bytenop,fast-7bytenop,fast-bextr,fast-dpwssd,fast-gather,fast-hops,fast-imm16,fast-lzcnt,fast-movbe,fast-scalar-fsqrt,fast-scalar-shift-masks,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fast-vector-shift-masks,faster-shift-than-shuffle,fma,fma4,fsgsbase,fsrm,fxsr,gfni,harden-sls-ijmp,harden-sls-ret,hreset,idivl-to-divb,idivq-to-divl,inline-asm-use-gpr32,invpcid,kl,lea-sp,lea-uses-ag,lvi-cfi,lvi-load-hardening,lwp,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,movrs,mwaitx,ndd,nf,no-bypass-delay,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pad-short-functions,pclmul,pconfig,pku,popcnt,ppx,prefer-128-bit,prefer-256-bit,prefer-mask-registers,prefer-movmsk-over-vtest,prefer-no-gather,prefer-no-scatter,prefetchi,prfchw,ptwrite,push2pop2,raoint,rdpid,rdpru,rdrnd,rdseed,retpoline,retpoline-external-thunk,retpoline-indirect-branches,retpoline-indirect-calls,rtm,sahf,sbb-dep-breaking,serialize,seses,sgx,sha,sha512,shstk,slow-3ops-lea,slow-incdec,slow-lea,slow-pmaddwd,slow-pmulld,slow-shld,slow-two-mem-ops,slow-unaligned-mem-16,slow-unaligned-mem-32,sm3,sm4,soft-float,sse,sse-unaligned-mem,sse2,sse3,sse4.1,sse4.2,sse4a,ssse3,tagged-globals,tbm,tsxldtrk,tuning-fast-imm-vector-shift,uintr,use-glm-div-sqrt-costs,use-slm-arith-costs,usermsr,vaes,vpclmulqdq,vzeroupper,waitpkg,wbnoinvd,widekl,x87,xop,xsave,xsavec,xsaveopt,xsaves,zu"),
+ // TargetArch_i386:
+ str_lit("16bit-mode,32bit-mode,64bit,64bit-mode,adx,aes,allow-light-256-bit,amx-avx512,amx-bf16,amx-complex,amx-fp16,amx-fp8,amx-int8,amx-movrs,amx-tf32,amx-tile,amx-transpose,avx,avx10.1-256,avx10.1-512,avx10.2-256,avx10.2-512,avx2,avx512bf16,avx512bitalg,avx512bw,avx512cd,avx512dq,avx512f,avx512fp16,avx512ifma,avx512vbmi,avx512vbmi2,avx512vl,avx512vnni,avx512vp2intersect,avx512vpopcntdq,avxifma,avxneconvert,avxvnni,avxvnniint16,avxvnniint8,bmi,bmi2,branch-hint,branchfusion,ccmp,cf,cldemote,clflushopt,clwb,clzero,cmov,cmpccxadd,crc32,cx16,cx8,egpr,enqcmd,ermsb,evex512,f16c,false-deps-getmant,false-deps-lzcnt-tzcnt,false-deps-mulc,false-deps-mullq,false-deps-perm,false-deps-popcnt,false-deps-range,fast-11bytenop,fast-15bytenop,fast-7bytenop,fast-bextr,fast-dpwssd,fast-gather,fast-hops,fast-imm16,fast-lzcnt,fast-movbe,fast-scalar-fsqrt,fast-scalar-shift-masks,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fast-vector-shift-masks,faster-shift-than-shuffle,fma,fma4,fsgsbase,fsrm,fxsr,gfni,harden-sls-ijmp,harden-sls-ret,hreset,idivl-to-divb,idivq-to-divl,inline-asm-use-gpr32,invpcid,kl,lea-sp,lea-uses-ag,lvi-cfi,lvi-load-hardening,lwp,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,movrs,mwaitx,ndd,nf,no-bypass-delay,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pad-short-functions,pclmul,pconfig,pku,popcnt,ppx,prefer-128-bit,prefer-256-bit,prefer-mask-registers,prefer-movmsk-over-vtest,prefer-no-gather,prefer-no-scatter,prefetchi,prfchw,ptwrite,push2pop2,raoint,rdpid,rdpru,rdrnd,rdseed,retpoline,retpoline-external-thunk,retpoline-indirect-branches,retpoline-indirect-calls,rtm,sahf,sbb-dep-breaking,serialize,seses,sgx,sha,sha512,shstk,slow-3ops-lea,slow-incdec,slow-lea,slow-pmaddwd,slow-pmulld,slow-shld,slow-two-mem-ops,slow-unaligned-mem-16,slow-unaligned-mem-32,sm3,sm4,soft-float,sse,sse-unaligned-mem,sse2,sse3,sse4.1,sse4.2,sse4a,ssse3,tagged-globals,tbm,tsxldtrk,tuning-fast-imm-vector-shift,uintr,use-glm-div-sqrt-costs,use-slm-arith-costs,usermsr,vaes,vpclmulqdq,vzeroupper,waitpkg,wbnoinvd,widekl,x87,xop,xsave,xsavec,xsaveopt,xsaves,zu"),
+ // TargetArch_arm32:
+ str_lit("32bit,8msecext,a12,a15,a17,a32,a35,a5,a53,a55,a57,a7,a72,a73,a75,a76,a77,a78c,a8,a9,aapcs-frame-chain,aclass,acquire-release,aes,armv4,armv4t,armv5t,armv5te,armv5tej,armv6,armv6-m,armv6j,armv6k,armv6kz,armv6s-m,armv6t2,armv7-a,armv7-m,armv7-r,armv7e-m,armv7k,armv7s,armv7ve,armv8-a,armv8-m.base,armv8-m.main,armv8-r,armv8.1-a,armv8.1-m.main,armv8.2-a,armv8.3-a,armv8.4-a,armv8.5-a,armv8.6-a,armv8.7-a,armv8.8-a,armv8.9-a,armv9-a,armv9.1-a,armv9.2-a,armv9.3-a,armv9.4-a,armv9.5-a,armv9.6-a,atomics-32,avoid-movs-shop,avoid-muls,avoid-partial-cpsr,bf16,big-endian-instructions,branch-align-64,cde,cdecp0,cdecp1,cdecp2,cdecp3,cdecp4,cdecp5,cdecp6,cdecp7,cheap-predicable-cpsr,clrbhb,cortex-a510,cortex-a710,cortex-a78,cortex-a78ae,cortex-x1,cortex-x1c,crc,crypto,d32,db,dfb,disable-postra-scheduler,dont-widen-vmovs,dotprod,dsp,execute-only,expand-fp-mlx,exynos,fix-cmse-cve-2021-35465,fix-cortex-a57-aes-1742098,fp-armv8,fp-armv8d16,fp-armv8d16sp,fp-armv8sp,fp16,fp16fml,fp64,fpao,fpregs,fpregs16,fpregs64,fullfp16,fuse-aes,fuse-literals,harden-sls-blr,harden-sls-nocomdat,harden-sls-retbr,hwdiv,hwdiv-arm,i8mm,iwmmxt,iwmmxt2,krait,kryo,lob,long-calls,loop-align,m3,m55,m7,m85,mclass,mp,muxed-units,mve,mve.fp,mve1beat,mve2beat,mve4beat,nacl-trap,neon,neon-fpmovs,neonfp,neoverse-v1,no-branch-predictor,no-bti-at-return-twice,no-movt,no-neg-immediates,noarm,nonpipelined-vfp,pacbti,perfmon,prefer-ishst,prefer-vmovsr,prof-unpr,r4,r5,r52,r52plus,r7,ras,rclass,read-tp-tpidrprw,read-tp-tpidruro,read-tp-tpidrurw,reserve-r9,ret-addr-stack,sb,sha2,slow-fp-brcc,slow-load-D-subreg,slow-odd-reg,slow-vdup32,slow-vgetlni32,slowfpvfmx,slowfpvmlx,soft-float,splat-vfp-neon,strict-align,swift,thumb-mode,thumb2,trustzone,use-mipipeliner,use-misched,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8,v8.1a,v8.1m.main,v8.2a,v8.3a,v8.4a,v8.5a,v8.6a,v8.7a,v8.8a,v8.9a,v8m,v8m.main,v9.1a,v9.2a,v9.3a,v9.4a,v9.5a,v9.6a,v9a,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,virtualization,vldn-align,vmlx-forwarding,vmlx-hazards,wide-stride-vfp,xscale,zcz"),
+ // TargetArch_arm64:
+ str_lit("CONTEXTIDREL2,a35,a510,a520,a520ae,a53,a55,a57,a64fx,a65,a710,a715,a72,a720,a720ae,a73,a75,a76,a77,a78,a78ae,a78c,addr-lsl-slow-14,aes,aggressive-fma,all,alternate-sextload-cvt-f32-pattern,altnzcv,alu-lsl-fast,am,ampere1,ampere1a,ampere1b,amvs,apple-a10,apple-a11,apple-a12,apple-a13,apple-a14,apple-a15,apple-a16,apple-a17,apple-a7,apple-m4,arith-bcc-fusion,arith-cbz-fusion,ascend-store-address,avoid-ldapur,balance-fp-ops,bf16,brbe,bti,call-saved-x10,call-saved-x11,call-saved-x12,call-saved-x13,call-saved-x14,call-saved-x15,call-saved-x18,call-saved-x8,call-saved-x9,carmel,ccdp,ccidx,ccpp,chk,clrbhb,cmp-bcc-fusion,cmpbr,complxnum,cortex-a725,cortex-r82,cortex-r82ae,cortex-x1,cortex-x2,cortex-x3,cortex-x4,cortex-x925,cpa,crc,crypto,cssc,d128,disable-latency-sched-heuristic,disable-ldp,disable-stp,dit,dotprod,ecv,el2vmsa,el3,enable-select-opt,ete,exynos-cheap-as-move,exynosm3,exynosm4,f32mm,f64mm,f8f16mm,f8f32mm,falkor,faminmax,fgt,fix-cortex-a53-835769,flagm,fmv,force-32bit-jump-tables,fp-armv8,fp16fml,fp8,fp8dot2,fp8dot4,fp8fma,fpac,fprcvt,fptoint,fujitsu-monaka,fullfp16,fuse-address,fuse-addsub-2reg-const1,fuse-adrp-add,fuse-aes,fuse-arith-logic,fuse-crypto-eor,fuse-csel,fuse-literals,gcs,harden-sls-blr,harden-sls-nocomdat,harden-sls-retbr,hbc,hcx,i8mm,ite,jsconv,kryo,ldp-aligned-only,lor,ls64,lse,lse128,lse2,lsfe,lsui,lut,mec,mops,mpam,mte,neon,neoverse512tvb,neoversee1,neoversen1,neoversen2,neoversen3,neoversev1,neoversev2,neoversev3,neoversev3AE,nmi,no-bti-at-return-twice,no-neg-immediates,no-sve-fp-ld1r,no-zcz-fp,nv,occmo,oryon-1,outline-atomics,pan,pan-rwv,pauth,pauth-lr,pcdphint,perfmon,pops,predictable-select-expensive,predres,prfm-slc-target,rand,ras,rasv2,rcpc,rcpc-immo,rcpc3,rdm,reserve-lr-for-ra,reserve-x1,reserve-x10,reserve-x11,reserve-x12,reserve-x13,reserve-x14,reserve-x15,reserve-x18,reserve-x2,reserve-x20,reserve-x21,reserve-x22,reserve-x23,reserve-x24,reserve-x25,reserve-x26,reserve-x27,reserve-x28,reserve-x3,reserve-x4,reserve-x5,reserve-x6,reserve-x7,reserve-x9,rme,saphira,sb,sel2,sha2,sha3,slow-misaligned-128store,slow-paired-128,slow-strqro-store,sm4,sme,sme-b16b16,sme-f16f16,sme-f64f64,sme-f8f16,sme-f8f32,sme-fa64,sme-i16i64,sme-lutv2,sme-mop4,sme-tmop,sme2,sme2p1,sme2p2,spe,spe-eef,specres2,specrestrict,ssbs,ssve-aes,ssve-bitperm,ssve-fp8dot2,ssve-fp8dot4,ssve-fp8fma,store-pair-suppress,stp-aligned-only,strict-align,sve,sve-aes,sve-aes2,sve-b16b16,sve-bfscale,sve-bitperm,sve-f16f32mm,sve2,sve2-aes,sve2-bitperm,sve2-sha3,sve2-sm4,sve2p1,sve2p2,tagged-globals,the,thunderx,thunderx2t99,thunderx3t110,thunderxt81,thunderxt83,thunderxt88,tlb-rmi,tlbiw,tme,tpidr-el1,tpidr-el2,tpidr-el3,tpidrro-el0,tracev8.4,trbe,tsv110,uaops,use-experimental-zeroing-pseudos,use-fixed-over-scalable-if-equal-cost,use-postra-scheduler,use-reciprocal-square-root,v8.1a,v8.2a,v8.3a,v8.4a,v8.5a,v8.6a,v8.7a,v8.8a,v8.9a,v8a,v8r,v9.1a,v9.2a,v9.3a,v9.4a,v9.5a,v9.6a,v9a,vh,wfxt,xs,zcm,zcz,zcz-fp-workaround,zcz-gp"),
+ // TargetArch_wasm32:
+ str_lit("atomics,bulk-memory,bulk-memory-opt,call-indirect-overlong,exception-handling,extended-const,fp16,multimemory,multivalue,mutable-globals,nontrapping-fptoint,reference-types,relaxed-simd,sign-ext,simd128,tail-call,wide-arithmetic"),
+ // TargetArch_wasm64p32:
+ str_lit("atomics,bulk-memory,bulk-memory-opt,call-indirect-overlong,exception-handling,extended-const,fp16,multimemory,multivalue,mutable-globals,nontrapping-fptoint,reference-types,relaxed-simd,sign-ext,simd128,tail-call,wide-arithmetic"),
+ // TargetArch_riscv64:
+ str_lit("32bit,64bit,a,auipc-addi-fusion,b,c,conditional-cmv-fusion,d,disable-latency-sched-heuristic,dlen-factor-2,e,experimental,experimental-rvm23u32,experimental-sdext,experimental-sdtrig,experimental-smctr,experimental-ssctr,experimental-svukte,experimental-xqcia,experimental-xqciac,experimental-xqcicli,experimental-xqcicm,experimental-xqcics,experimental-xqcicsr,experimental-xqciint,experimental-xqcilo,experimental-xqcilsm,experimental-xqcisls,experimental-zalasr,experimental-zicfilp,experimental-zicfiss,experimental-zvbc32e,experimental-zvkgs,f,forced-atomics,h,i,ld-add-fusion,lui-addi-fusion,m,mips-p8700,no-default-unroll,no-rvc-hints,no-sink-splat-operands,no-trailing-seq-cst-fence,optimized-nf2-segment-load-store,optimized-nf3-segment-load-store,optimized-nf4-segment-load-store,optimized-nf5-segment-load-store,optimized-nf6-segment-load-store,optimized-nf7-segment-load-store,optimized-nf8-segment-load-store,optimized-zero-stride-load,predictable-select-expensive,prefer-w-inst,relax,reserve-x1,reserve-x10,reserve-x11,reserve-x12,reserve-x13,reserve-x14,reserve-x15,reserve-x16,reserve-x17,reserve-x18,reserve-x19,reserve-x2,reserve-x20,reserve-x21,reserve-x22,reserve-x23,reserve-x24,reserve-x25,reserve-x26,reserve-x27,reserve-x28,reserve-x29,reserve-x3,reserve-x30,reserve-x31,reserve-x4,reserve-x5,reserve-x6,reserve-x7,reserve-x8,reserve-x9,rva20s64,rva20u64,rva22s64,rva22u64,rva23s64,rva23u64,rvb23s64,rvb23u64,rvi20u32,rvi20u64,save-restore,sha,shcounterenw,shgatpa,shifted-zextw-fusion,short-forward-branch-opt,shtvala,shvsatpa,shvstvala,shvstvecd,sifive7,smaia,smcdeleg,smcsrind,smdbltrp,smepmp,smmpm,smnpm,smrnmi,smstateen,ssaia,ssccfg,ssccptr,sscofpmf,sscounterenw,sscsrind,ssdbltrp,ssnpm,sspm,ssqosid,ssstateen,ssstrict,sstc,sstvala,sstvecd,ssu64xl,supm,svade,svadu,svbare,svinval,svnapot,svpbmt,svvptc,tagged-globals,unaligned-scalar-mem,unaligned-vector-mem,use-postra-scheduler,v,ventana-veyron,vxrm-pipeline-flush,xcvalu,xcvbi,xcvbitmanip,xcvelw,xcvmac,xcvmem,xcvsimd,xmipscmove,xmipslsp,xsfcease,xsfvcp,xsfvfnrclipxfqf,xsfvfwmaccqqq,xsfvqmaccdod,xsfvqmaccqoq,xsifivecdiscarddlone,xsifivecflushdlone,xtheadba,xtheadbb,xtheadbs,xtheadcmo,xtheadcondmov,xtheadfmemidx,xtheadmac,xtheadmemidx,xtheadmempair,xtheadsync,xtheadvdot,xventanacondops,xwchc,za128rs,za64rs,zaamo,zabha,zacas,zalrsc,zama16b,zawrs,zba,zbb,zbc,zbkb,zbkc,zbkx,zbs,zca,zcb,zcd,zce,zcf,zcmop,zcmp,zcmt,zdinx,zexth-fusion,zextw-fusion,zfa,zfbfmin,zfh,zfhmin,zfinx,zhinx,zhinxmin,zic64b,zicbom,zicbop,zicboz,ziccamoa,ziccif,zicclsm,ziccrse,zicntr,zicond,zicsr,zifencei,zihintntl,zihintpause,zihpm,zimop,zk,zkn,zknd,zkne,zknh,zkr,zks,zksed,zksh,zkt,zmmul,ztso,zvbb,zvbc,zve32f,zve32x,zve64d,zve64f,zve64x,zvfbfmin,zvfbfwma,zvfh,zvfhmin,zvkb,zvkg,zvkn,zvknc,zvkned,zvkng,zvknha,zvknhb,zvks,zvksc,zvksed,zvksg,zvksh,zvkt,zvl1024b,zvl128b,zvl16384b,zvl2048b,zvl256b,zvl32768b,zvl32b,zvl4096b,zvl512b,zvl64b,zvl65536b,zvl8192b"),
+};
+
+// Generated with the featuregen script in `misc/featuregen`
+gb_global int target_microarch_counts[TargetArch_COUNT] = {
+ // TargetArch_Invalid:
+ 0,
+ // TargetArch_amd64:
+ 129,
+ // TargetArch_i386:
+ 129,
+ // TargetArch_arm32:
+ 95,
+ // TargetArch_arm64:
+ 82,
+ // TargetArch_wasm32:
+ 4,
+ // TargetArch_wasm64p32:
+ 4,
+ // TargetArch_riscv64:
+ 39,
+};
+
+// Generated with the featuregen script in `misc/featuregen`
+gb_global MicroarchFeatureList microarch_features_list[] = {
+ // TargetArch_amd64:
+ { str_lit("alderlake"), str_lit("64bit,64bit-mode,adx,aes,allow-light-256-bit,avx,avx2,avxvnni,bmi,bmi2,cldemote,clflushopt,clwb,cmov,crc32,cx16,cx8,f16c,false-deps-perm,false-deps-popcnt,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fxsr,gfni,hreset,idivq-to-divl,invpcid,kl,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pconfig,pku,popcnt,prefer-movmsk-over-vtest,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,serialize,sha,shstk,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,vaes,vpclmulqdq,vzeroupper,waitpkg,widekl,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("amdfam10"), str_lit("64bit,64bit-mode,cmov,cx16,cx8,fast-scalar-shift-masks,fxsr,idivq-to-divl,lzcnt,nopl,popcnt,prfchw,sahf,sbb-dep-breaking,slow-shld,sse,sse2,sse3,sse4a,vzeroupper,x87") },
+ { str_lit("arrowlake"), str_lit("64bit,64bit-mode,adx,aes,allow-light-256-bit,avx,avx2,avxifma,avxneconvert,avxvnni,avxvnniint8,bmi,bmi2,cldemote,clflushopt,clwb,cmov,cmpccxadd,crc32,cx16,cx8,enqcmd,f16c,false-deps-perm,false-deps-popcnt,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fxsr,gfni,hreset,idivq-to-divl,invpcid,kl,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pconfig,pku,popcnt,prefer-movmsk-over-vtest,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,serialize,sha,shstk,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,uintr,vaes,vpclmulqdq,vzeroupper,waitpkg,widekl,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("arrowlake-s"), str_lit("64bit,64bit-mode,adx,aes,allow-light-256-bit,avx,avx2,avxifma,avxneconvert,avxvnni,avxvnniint16,avxvnniint8,bmi,bmi2,cldemote,clflushopt,clwb,cmov,cmpccxadd,crc32,cx16,cx8,enqcmd,f16c,false-deps-perm,false-deps-popcnt,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fxsr,gfni,hreset,idivq-to-divl,invpcid,kl,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pconfig,pku,popcnt,prefer-movmsk-over-vtest,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,serialize,sha,sha512,shstk,slow-3ops-lea,sm3,sm4,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,uintr,vaes,vpclmulqdq,vzeroupper,waitpkg,widekl,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("arrowlake_s"), str_lit("64bit,64bit-mode,adx,aes,allow-light-256-bit,avx,avx2,avxifma,avxneconvert,avxvnni,avxvnniint16,avxvnniint8,bmi,bmi2,cldemote,clflushopt,clwb,cmov,cmpccxadd,crc32,cx16,cx8,enqcmd,f16c,false-deps-perm,false-deps-popcnt,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fxsr,gfni,hreset,idivq-to-divl,invpcid,kl,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pconfig,pku,popcnt,prefer-movmsk-over-vtest,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,serialize,sha,sha512,shstk,slow-3ops-lea,sm3,sm4,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,uintr,vaes,vpclmulqdq,vzeroupper,waitpkg,widekl,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("athlon"), str_lit("64bit-mode,cmov,cx8,mmx,nopl,prfchw,slow-shld,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("athlon-4"), str_lit("64bit-mode,cmov,cx8,fxsr,mmx,nopl,prfchw,slow-shld,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("athlon-fx"), str_lit("64bit,64bit-mode,cmov,cx8,fast-scalar-shift-masks,fxsr,mmx,nopl,prfchw,sbb-dep-breaking,slow-shld,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("athlon-mp"), str_lit("64bit-mode,cmov,cx8,fxsr,mmx,nopl,prfchw,slow-shld,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("athlon-tbird"), str_lit("64bit-mode,cmov,cx8,mmx,nopl,prfchw,slow-shld,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("athlon-xp"), str_lit("64bit-mode,cmov,cx8,fxsr,mmx,nopl,prfchw,slow-shld,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("athlon64"), str_lit("64bit,64bit-mode,cmov,cx8,fast-scalar-shift-masks,fxsr,mmx,nopl,prfchw,sbb-dep-breaking,slow-shld,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("athlon64-sse3"), str_lit("64bit,64bit-mode,cmov,cx16,cx8,fast-scalar-shift-masks,fxsr,mmx,nopl,prfchw,sbb-dep-breaking,slow-shld,slow-unaligned-mem-16,sse,sse2,sse3,vzeroupper,x87") },
+ { str_lit("atom"), str_lit("64bit,64bit-mode,cmov,cx16,cx8,fast-imm16,fxsr,idivl-to-divb,idivq-to-divl,lea-sp,lea-uses-ag,mmx,movbe,no-bypass-delay,nopl,pad-short-functions,sahf,slow-two-mem-ops,slow-unaligned-mem-16,sse,sse2,sse3,ssse3,vzeroupper,x87") },
+ { str_lit("atom_sse4_2"), str_lit("64bit,64bit-mode,cmov,crc32,cx16,cx8,false-deps-popcnt,fast-7bytenop,fast-imm16,fast-movbe,fxsr,idivq-to-divl,mmx,movbe,no-bypass-delay,nopl,pclmul,popcnt,prfchw,rdrnd,sahf,slow-incdec,slow-lea,slow-pmulld,slow-two-mem-ops,sse,sse2,sse3,sse4.1,sse4.2,ssse3,use-slm-arith-costs,vzeroupper,x87") },
+ { str_lit("atom_sse4_2_movbe"), str_lit("64bit,64bit-mode,aes,clflushopt,cmov,crc32,cx16,cx8,false-deps-popcnt,fast-7bytenop,fast-imm16,fast-movbe,fsgsbase,fxsr,idivq-to-divl,mmx,movbe,no-bypass-delay,nopl,pclmul,popcnt,prfchw,rdrnd,rdseed,sahf,sha,slow-incdec,slow-lea,slow-pmulld,slow-two-mem-ops,sse,sse2,sse3,sse4.1,sse4.2,ssse3,use-slm-arith-costs,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("barcelona"), str_lit("64bit,64bit-mode,cmov,cx16,cx8,fast-scalar-shift-masks,fxsr,idivq-to-divl,lzcnt,nopl,popcnt,prfchw,sahf,sbb-dep-breaking,slow-shld,sse,sse2,sse3,sse4a,vzeroupper,x87") },
+ { str_lit("bdver1"), str_lit("64bit,64bit-mode,aes,avx,branchfusion,cmov,crc32,cx16,cx8,fast-11bytenop,fast-scalar-shift-masks,fma4,fxsr,idivq-to-divl,lwp,lzcnt,mmx,nopl,pclmul,popcnt,prfchw,sahf,sbb-dep-breaking,slow-shld,sse,sse2,sse3,sse4.1,sse4.2,sse4a,ssse3,vzeroupper,x87,xop,xsave") },
+ { str_lit("bdver2"), str_lit("64bit,64bit-mode,aes,avx,bmi,branchfusion,cmov,crc32,cx16,cx8,f16c,fast-11bytenop,fast-bextr,fast-movbe,fast-scalar-shift-masks,fma,fma4,fxsr,idivq-to-divl,lwp,lzcnt,mmx,nopl,pclmul,popcnt,prfchw,sahf,sbb-dep-breaking,slow-shld,sse,sse2,sse3,sse4.1,sse4.2,sse4a,ssse3,tbm,vzeroupper,x87,xop,xsave") },
+ { str_lit("bdver3"), str_lit("64bit,64bit-mode,aes,avx,bmi,branchfusion,cmov,crc32,cx16,cx8,f16c,fast-11bytenop,fast-bextr,fast-movbe,fast-scalar-shift-masks,fma,fma4,fsgsbase,fxsr,idivq-to-divl,lwp,lzcnt,mmx,nopl,pclmul,popcnt,prfchw,sahf,sbb-dep-breaking,slow-shld,sse,sse2,sse3,sse4.1,sse4.2,sse4a,ssse3,tbm,vzeroupper,x87,xop,xsave,xsaveopt") },
+ { str_lit("bdver4"), str_lit("64bit,64bit-mode,aes,avx,avx2,bmi,bmi2,branchfusion,cmov,crc32,cx16,cx8,f16c,fast-11bytenop,fast-bextr,fast-movbe,fast-scalar-shift-masks,fma,fma4,fsgsbase,fxsr,idivq-to-divl,lwp,lzcnt,mmx,movbe,mwaitx,nopl,pclmul,popcnt,prfchw,rdrnd,sahf,sbb-dep-breaking,slow-shld,sse,sse2,sse3,sse4.1,sse4.2,sse4a,ssse3,tbm,vzeroupper,x87,xop,xsave,xsaveopt") },
+ { str_lit("bonnell"), str_lit("64bit,64bit-mode,cmov,cx16,cx8,fast-imm16,fxsr,idivl-to-divb,idivq-to-divl,lea-sp,lea-uses-ag,mmx,movbe,no-bypass-delay,nopl,pad-short-functions,sahf,slow-two-mem-ops,slow-unaligned-mem-16,sse,sse2,sse3,ssse3,vzeroupper,x87") },
+ { str_lit("broadwell"), str_lit("64bit,64bit-mode,adx,allow-light-256-bit,avx,avx2,bmi,bmi2,cmov,crc32,cx16,cx8,ermsb,f16c,false-deps-lzcnt-tzcnt,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fma,fsgsbase,fxsr,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,popcnt,prfchw,rdrnd,rdseed,sahf,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87,xsave,xsaveopt") },
+ { str_lit("btver1"), str_lit("64bit,64bit-mode,cmov,cx16,cx8,fast-15bytenop,fast-imm16,fast-scalar-shift-masks,fast-vector-shift-masks,fxsr,idivq-to-divl,lzcnt,mmx,nopl,popcnt,prfchw,sahf,sbb-dep-breaking,slow-shld,sse,sse2,sse3,sse4a,ssse3,vzeroupper,x87") },
+ { str_lit("btver2"), str_lit("64bit,64bit-mode,aes,avx,bmi,cmov,crc32,cx16,cx8,f16c,fast-15bytenop,fast-bextr,fast-hops,fast-imm16,fast-lzcnt,fast-movbe,fast-scalar-shift-masks,fast-vector-shift-masks,fxsr,idivq-to-divl,lzcnt,mmx,movbe,nopl,pclmul,popcnt,prfchw,sahf,sbb-dep-breaking,slow-shld,sse,sse2,sse3,sse4.1,sse4.2,sse4a,ssse3,x87,xsave,xsaveopt") },
+ { str_lit("c3"), str_lit("64bit-mode,mmx,prfchw,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("c3-2"), str_lit("64bit-mode,cmov,cx8,fxsr,mmx,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("cannonlake"), str_lit("64bit,64bit-mode,adx,aes,allow-light-256-bit,avx,avx2,avx512bw,avx512cd,avx512dq,avx512f,avx512ifma,avx512vbmi,avx512vl,bmi,bmi2,clflushopt,cmov,crc32,cx16,cx8,ermsb,evex512,f16c,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fxsr,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pku,popcnt,prefer-256-bit,prfchw,rdrnd,rdseed,sahf,sha,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("cascadelake"), str_lit("64bit,64bit-mode,adx,aes,allow-light-256-bit,avx,avx2,avx512bw,avx512cd,avx512dq,avx512f,avx512vl,avx512vnni,bmi,bmi2,clflushopt,clwb,cmov,crc32,cx16,cx8,ermsb,evex512,f16c,false-deps-popcnt,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,faster-shift-than-shuffle,fma,fsgsbase,fxsr,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pku,popcnt,prefer-256-bit,prfchw,rdrnd,rdseed,sahf,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("clearwaterforest"), str_lit("64bit,64bit-mode,adx,aes,allow-light-256-bit,avx,avx2,avxifma,avxneconvert,avxvnni,avxvnniint16,avxvnniint8,bmi,bmi2,cldemote,clflushopt,clwb,cmov,cmpccxadd,crc32,cx16,cx8,enqcmd,f16c,false-deps-perm,false-deps-popcnt,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fxsr,gfni,hreset,idivq-to-divl,invpcid,kl,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pconfig,pku,popcnt,prefer-movmsk-over-vtest,prefetchi,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,serialize,sha,sha512,shstk,slow-3ops-lea,sm3,sm4,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,uintr,usermsr,vaes,vpclmulqdq,vzeroupper,waitpkg,widekl,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("cooperlake"), str_lit("64bit,64bit-mode,adx,aes,allow-light-256-bit,avx,avx2,avx512bf16,avx512bw,avx512cd,avx512dq,avx512f,avx512vl,avx512vnni,bmi,bmi2,clflushopt,clwb,cmov,crc32,cx16,cx8,ermsb,evex512,f16c,false-deps-popcnt,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,faster-shift-than-shuffle,fma,fsgsbase,fxsr,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pku,popcnt,prefer-256-bit,prfchw,rdrnd,rdseed,sahf,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("core-avx-i"), str_lit("64bit,64bit-mode,avx,cmov,crc32,cx16,cx8,f16c,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-shld-rotate,fsgsbase,fxsr,idivq-to-divl,macrofusion,mmx,no-bypass-delay-mov,nopl,pclmul,popcnt,rdrnd,sahf,slow-3ops-lea,slow-unaligned-mem-32,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87,xsave,xsaveopt") },
+ { str_lit("core-avx2"), str_lit("64bit,64bit-mode,allow-light-256-bit,avx,avx2,bmi,bmi2,cmov,crc32,cx16,cx8,ermsb,f16c,false-deps-lzcnt-tzcnt,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fma,fsgsbase,fxsr,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,popcnt,rdrnd,sahf,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87,xsave,xsaveopt") },
+ { str_lit("core2"), str_lit("64bit,64bit-mode,cmov,cx16,cx8,fxsr,macrofusion,mmx,nopl,sahf,slow-unaligned-mem-16,sse,sse2,sse3,ssse3,vzeroupper,x87") },
+ { str_lit("core_2_duo_sse4_1"), str_lit("64bit,64bit-mode,cmov,cx16,cx8,fxsr,macrofusion,mmx,nopl,sahf,slow-unaligned-mem-16,sse,sse2,sse3,sse4.1,ssse3,vzeroupper,x87") },
+ { str_lit("core_2_duo_ssse3"), str_lit("64bit,64bit-mode,cmov,cx16,cx8,fxsr,macrofusion,mmx,nopl,sahf,slow-unaligned-mem-16,sse,sse2,sse3,ssse3,vzeroupper,x87") },
+ { str_lit("core_2nd_gen_avx"), str_lit("64bit,64bit-mode,avx,cmov,crc32,cx16,cx8,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-shld-rotate,fxsr,idivq-to-divl,macrofusion,mmx,no-bypass-delay-mov,nopl,pclmul,popcnt,sahf,slow-3ops-lea,slow-unaligned-mem-32,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87,xsave,xsaveopt") },
+ { str_lit("core_3rd_gen_avx"), str_lit("64bit,64bit-mode,avx,cmov,crc32,cx16,cx8,f16c,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-shld-rotate,fsgsbase,fxsr,idivq-to-divl,macrofusion,mmx,no-bypass-delay-mov,nopl,pclmul,popcnt,rdrnd,sahf,slow-3ops-lea,slow-unaligned-mem-32,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87,xsave,xsaveopt") },
+ { str_lit("core_4th_gen_avx"), str_lit("64bit,64bit-mode,allow-light-256-bit,avx,avx2,bmi,bmi2,cmov,crc32,cx16,cx8,ermsb,f16c,false-deps-lzcnt-tzcnt,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fma,fsgsbase,fxsr,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,popcnt,rdrnd,sahf,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87,xsave,xsaveopt") },
+ { str_lit("core_4th_gen_avx_tsx"), str_lit("64bit,64bit-mode,allow-light-256-bit,avx,avx2,bmi,bmi2,cmov,crc32,cx16,cx8,ermsb,f16c,false-deps-lzcnt-tzcnt,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fma,fsgsbase,fxsr,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,popcnt,rdrnd,sahf,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87,xsave,xsaveopt") },
+ { str_lit("core_5th_gen_avx"), str_lit("64bit,64bit-mode,adx,allow-light-256-bit,avx,avx2,bmi,bmi2,cmov,crc32,cx16,cx8,ermsb,f16c,false-deps-lzcnt-tzcnt,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fma,fsgsbase,fxsr,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,popcnt,prfchw,rdrnd,rdseed,sahf,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87,xsave,xsaveopt") },
+ { str_lit("core_5th_gen_avx_tsx"), str_lit("64bit,64bit-mode,adx,allow-light-256-bit,avx,avx2,bmi,bmi2,cmov,crc32,cx16,cx8,ermsb,f16c,false-deps-lzcnt-tzcnt,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fma,fsgsbase,fxsr,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,popcnt,prfchw,rdrnd,rdseed,sahf,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87,xsave,xsaveopt") },
+ { str_lit("core_aes_pclmulqdq"), str_lit("64bit,64bit-mode,cmov,crc32,cx16,cx8,fxsr,idivq-to-divl,macrofusion,mmx,no-bypass-delay-mov,nopl,pclmul,popcnt,sahf,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87") },
+ { str_lit("core_i7_sse4_2"), str_lit("64bit,64bit-mode,cmov,crc32,cx16,cx8,fxsr,idivq-to-divl,macrofusion,mmx,no-bypass-delay-mov,nopl,popcnt,sahf,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87") },
+ { str_lit("corei7"), str_lit("64bit,64bit-mode,cmov,crc32,cx16,cx8,fxsr,idivq-to-divl,macrofusion,mmx,no-bypass-delay-mov,nopl,popcnt,sahf,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87") },
+ { str_lit("corei7-avx"), str_lit("64bit,64bit-mode,avx,cmov,crc32,cx16,cx8,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-shld-rotate,fxsr,idivq-to-divl,macrofusion,mmx,no-bypass-delay-mov,nopl,pclmul,popcnt,sahf,slow-3ops-lea,slow-unaligned-mem-32,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87,xsave,xsaveopt") },
+ { str_lit("diamondrapids"), str_lit("64bit,64bit-mode,adx,aes,allow-light-256-bit,amx-avx512,amx-bf16,amx-complex,amx-fp16,amx-fp8,amx-int8,amx-movrs,amx-tf32,amx-tile,amx-transpose,avx,avx10.1-256,avx10.1-512,avx10.2-256,avx10.2-512,avx2,avx512bf16,avx512bitalg,avx512bw,avx512cd,avx512dq,avx512f,avx512fp16,avx512ifma,avx512vbmi,avx512vbmi2,avx512vl,avx512vnni,avx512vpopcntdq,avxifma,avxneconvert,avxvnni,avxvnniint16,avxvnniint8,bmi,bmi2,branch-hint,ccmp,cf,cldemote,clflushopt,clwb,cmov,cmpccxadd,crc32,cx16,cx8,egpr,enqcmd,ermsb,evex512,f16c,false-deps-getmant,false-deps-mulc,false-deps-mullq,false-deps-perm,false-deps-range,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fsrm,fxsr,gfni,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,movrs,ndd,nf,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pconfig,pku,popcnt,ppx,prefer-256-bit,prefetchi,prfchw,ptwrite,push2pop2,rdpid,rdrnd,rdseed,sahf,serialize,sha,sha512,shstk,sm3,sm4,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tsxldtrk,tuning-fast-imm-vector-shift,uintr,usermsr,vaes,vpclmulqdq,vzeroupper,waitpkg,wbnoinvd,x87,xsave,xsavec,xsaveopt,xsaves,zu") },
+ { str_lit("emeraldrapids"), str_lit("64bit,64bit-mode,adx,aes,allow-light-256-bit,amx-bf16,amx-int8,amx-tile,avx,avx2,avx512bf16,avx512bitalg,avx512bw,avx512cd,avx512dq,avx512f,avx512fp16,avx512ifma,avx512vbmi,avx512vbmi2,avx512vl,avx512vnni,avx512vpopcntdq,avxvnni,bmi,bmi2,cldemote,clflushopt,clwb,cmov,crc32,cx16,cx8,enqcmd,ermsb,evex512,f16c,false-deps-getmant,false-deps-mulc,false-deps-mullq,false-deps-perm,false-deps-range,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fsrm,fxsr,gfni,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pconfig,pku,popcnt,prefer-256-bit,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,serialize,sha,shstk,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tsxldtrk,tuning-fast-imm-vector-shift,uintr,vaes,vpclmulqdq,vzeroupper,waitpkg,wbnoinvd,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("generic"), str_lit("64bit,64bit-mode,cx8,fast-15bytenop,fast-scalar-fsqrt,idivq-to-divl,macrofusion,slow-3ops-lea,sse,sse2,vzeroupper,x87") },
+ { str_lit("geode"), str_lit("64bit-mode,cx8,mmx,prfchw,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("goldmont"), str_lit("64bit,64bit-mode,aes,clflushopt,cmov,crc32,cx16,cx8,false-deps-popcnt,fast-imm16,fast-movbe,fsgsbase,fxsr,mmx,movbe,no-bypass-delay,nopl,pclmul,popcnt,prfchw,rdrnd,rdseed,sahf,sha,slow-incdec,slow-lea,slow-two-mem-ops,sse,sse2,sse3,sse4.1,sse4.2,ssse3,use-glm-div-sqrt-costs,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("goldmont-plus"), str_lit("64bit,64bit-mode,aes,clflushopt,cmov,crc32,cx16,cx8,fast-imm16,fast-movbe,fsgsbase,fxsr,mmx,movbe,no-bypass-delay,nopl,pclmul,popcnt,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,sha,slow-incdec,slow-lea,slow-two-mem-ops,sse,sse2,sse3,sse4.1,sse4.2,ssse3,use-glm-div-sqrt-costs,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("goldmont_plus"), str_lit("64bit,64bit-mode,aes,clflushopt,cmov,crc32,cx16,cx8,fast-imm16,fast-movbe,fsgsbase,fxsr,mmx,movbe,no-bypass-delay,nopl,pclmul,popcnt,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,sha,slow-incdec,slow-lea,slow-two-mem-ops,sse,sse2,sse3,sse4.1,sse4.2,ssse3,use-glm-div-sqrt-costs,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("gracemont"), str_lit("64bit,64bit-mode,adx,aes,avx,avx2,avxvnni,bmi,bmi2,cldemote,clflushopt,clwb,cmov,crc32,cx16,cx8,f16c,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fxsr,gfni,hreset,invpcid,kl,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,nopl,pclmul,pconfig,pku,popcnt,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,serialize,sha,shstk,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vaes,vpclmulqdq,vzeroupper,waitpkg,widekl,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("grandridge"), str_lit("64bit,64bit-mode,adx,aes,avx,avx2,avxifma,avxneconvert,avxvnni,avxvnniint8,bmi,bmi2,cldemote,clflushopt,clwb,cmov,cmpccxadd,crc32,cx16,cx8,enqcmd,f16c,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fxsr,gfni,hreset,invpcid,kl,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,nopl,pclmul,pconfig,pku,popcnt,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,serialize,sha,shstk,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,uintr,vaes,vpclmulqdq,vzeroupper,waitpkg,widekl,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("graniterapids"), str_lit("64bit,64bit-mode,adx,aes,allow-light-256-bit,amx-bf16,amx-fp16,amx-int8,amx-tile,avx,avx2,avx512bf16,avx512bitalg,avx512bw,avx512cd,avx512dq,avx512f,avx512fp16,avx512ifma,avx512vbmi,avx512vbmi2,avx512vl,avx512vnni,avx512vpopcntdq,avxvnni,bmi,bmi2,branch-hint,cldemote,clflushopt,clwb,cmov,crc32,cx16,cx8,enqcmd,ermsb,evex512,f16c,false-deps-getmant,false-deps-mulc,false-deps-mullq,false-deps-perm,false-deps-range,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fsrm,fxsr,gfni,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pconfig,pku,popcnt,prefer-256-bit,prefetchi,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,serialize,sha,shstk,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tsxldtrk,tuning-fast-imm-vector-shift,uintr,vaes,vpclmulqdq,vzeroupper,waitpkg,wbnoinvd,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("graniterapids-d"), str_lit("64bit,64bit-mode,adx,aes,allow-light-256-bit,amx-bf16,amx-complex,amx-fp16,amx-int8,amx-tile,avx,avx2,avx512bf16,avx512bitalg,avx512bw,avx512cd,avx512dq,avx512f,avx512fp16,avx512ifma,avx512vbmi,avx512vbmi2,avx512vl,avx512vnni,avx512vpopcntdq,avxvnni,bmi,bmi2,branch-hint,cldemote,clflushopt,clwb,cmov,crc32,cx16,cx8,enqcmd,ermsb,evex512,f16c,false-deps-getmant,false-deps-mulc,false-deps-mullq,false-deps-perm,false-deps-range,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fsrm,fxsr,gfni,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pconfig,pku,popcnt,prefer-256-bit,prefetchi,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,serialize,sha,shstk,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tsxldtrk,tuning-fast-imm-vector-shift,uintr,vaes,vpclmulqdq,vzeroupper,waitpkg,wbnoinvd,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("graniterapids_d"), str_lit("64bit,64bit-mode,adx,aes,allow-light-256-bit,amx-bf16,amx-complex,amx-fp16,amx-int8,amx-tile,avx,avx2,avx512bf16,avx512bitalg,avx512bw,avx512cd,avx512dq,avx512f,avx512fp16,avx512ifma,avx512vbmi,avx512vbmi2,avx512vl,avx512vnni,avx512vpopcntdq,avxvnni,bmi,bmi2,branch-hint,cldemote,clflushopt,clwb,cmov,crc32,cx16,cx8,enqcmd,ermsb,evex512,f16c,false-deps-getmant,false-deps-mulc,false-deps-mullq,false-deps-perm,false-deps-range,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fsrm,fxsr,gfni,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pconfig,pku,popcnt,prefer-256-bit,prefetchi,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,serialize,sha,shstk,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tsxldtrk,tuning-fast-imm-vector-shift,uintr,vaes,vpclmulqdq,vzeroupper,waitpkg,wbnoinvd,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("haswell"), str_lit("64bit,64bit-mode,allow-light-256-bit,avx,avx2,bmi,bmi2,cmov,crc32,cx16,cx8,ermsb,f16c,false-deps-lzcnt-tzcnt,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fma,fsgsbase,fxsr,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,popcnt,rdrnd,sahf,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87,xsave,xsaveopt") },
+ { str_lit("i386"), str_lit("64bit-mode,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("i486"), str_lit("64bit-mode,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("i586"), str_lit("64bit-mode,cx8,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("i686"), str_lit("64bit-mode,cmov,cx8,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("icelake-client"), str_lit("64bit,64bit-mode,adx,aes,allow-light-256-bit,avx,avx2,avx512bitalg,avx512bw,avx512cd,avx512dq,avx512f,avx512ifma,avx512vbmi,avx512vbmi2,avx512vl,avx512vnni,avx512vpopcntdq,bmi,bmi2,clflushopt,cmov,crc32,cx16,cx8,ermsb,evex512,f16c,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fsrm,fxsr,gfni,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pku,popcnt,prefer-256-bit,prfchw,rdpid,rdrnd,rdseed,sahf,sha,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,vaes,vpclmulqdq,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("icelake-server"), str_lit("64bit,64bit-mode,adx,aes,allow-light-256-bit,avx,avx2,avx512bitalg,avx512bw,avx512cd,avx512dq,avx512f,avx512ifma,avx512vbmi,avx512vbmi2,avx512vl,avx512vnni,avx512vpopcntdq,bmi,bmi2,clflushopt,clwb,cmov,crc32,cx16,cx8,ermsb,evex512,f16c,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fsrm,fxsr,gfni,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pconfig,pku,popcnt,prefer-256-bit,prfchw,rdpid,rdrnd,rdseed,sahf,sha,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,vaes,vpclmulqdq,vzeroupper,wbnoinvd,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("icelake_client"), str_lit("64bit,64bit-mode,adx,aes,allow-light-256-bit,avx,avx2,avx512bitalg,avx512bw,avx512cd,avx512dq,avx512f,avx512ifma,avx512vbmi,avx512vbmi2,avx512vl,avx512vnni,avx512vpopcntdq,bmi,bmi2,clflushopt,cmov,crc32,cx16,cx8,ermsb,evex512,f16c,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fsrm,fxsr,gfni,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pku,popcnt,prefer-256-bit,prfchw,rdpid,rdrnd,rdseed,sahf,sha,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,vaes,vpclmulqdq,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("icelake_server"), str_lit("64bit,64bit-mode,adx,aes,allow-light-256-bit,avx,avx2,avx512bitalg,avx512bw,avx512cd,avx512dq,avx512f,avx512ifma,avx512vbmi,avx512vbmi2,avx512vl,avx512vnni,avx512vpopcntdq,bmi,bmi2,clflushopt,clwb,cmov,crc32,cx16,cx8,ermsb,evex512,f16c,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fsrm,fxsr,gfni,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pconfig,pku,popcnt,prefer-256-bit,prfchw,rdpid,rdrnd,rdseed,sahf,sha,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,vaes,vpclmulqdq,vzeroupper,wbnoinvd,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("ivybridge"), str_lit("64bit,64bit-mode,avx,cmov,crc32,cx16,cx8,f16c,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-shld-rotate,fsgsbase,fxsr,idivq-to-divl,macrofusion,mmx,no-bypass-delay-mov,nopl,pclmul,popcnt,rdrnd,sahf,slow-3ops-lea,slow-unaligned-mem-32,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87,xsave,xsaveopt") },
+ { str_lit("k6"), str_lit("64bit-mode,cx8,mmx,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("k6-2"), str_lit("64bit-mode,cx8,mmx,prfchw,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("k6-3"), str_lit("64bit-mode,cx8,mmx,prfchw,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("k8"), str_lit("64bit,64bit-mode,cmov,cx8,fast-scalar-shift-masks,fxsr,mmx,nopl,prfchw,sbb-dep-breaking,slow-shld,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("k8-sse3"), str_lit("64bit,64bit-mode,cmov,cx16,cx8,fast-scalar-shift-masks,fxsr,mmx,nopl,prfchw,sbb-dep-breaking,slow-shld,slow-unaligned-mem-16,sse,sse2,sse3,vzeroupper,x87") },
+ { str_lit("knl"), str_lit("64bit,64bit-mode,adx,aes,avx,avx2,avx512cd,avx512f,bmi,bmi2,cmov,crc32,cx16,cx8,evex512,f16c,fast-gather,fast-imm16,fast-movbe,fma,fsgsbase,fxsr,idivq-to-divl,lzcnt,mmx,movbe,nopl,pclmul,popcnt,prefer-mask-registers,prfchw,rdrnd,rdseed,sahf,slow-3ops-lea,slow-incdec,slow-pmaddwd,slow-two-mem-ops,sse,sse2,sse3,sse4.1,sse4.2,ssse3,x87,xsave,xsaveopt") },
+ { str_lit("knm"), str_lit("64bit,64bit-mode,adx,aes,avx,avx2,avx512cd,avx512f,avx512vpopcntdq,bmi,bmi2,cmov,crc32,cx16,cx8,evex512,f16c,fast-gather,fast-imm16,fast-movbe,fma,fsgsbase,fxsr,idivq-to-divl,lzcnt,mmx,movbe,nopl,pclmul,popcnt,prefer-mask-registers,prfchw,rdrnd,rdseed,sahf,slow-3ops-lea,slow-incdec,slow-pmaddwd,slow-two-mem-ops,sse,sse2,sse3,sse4.1,sse4.2,ssse3,x87,xsave,xsaveopt") },
+ { str_lit("lakemont"), str_lit("64bit-mode,cx8,slow-unaligned-mem-16,sse,sse2,vzeroupper") },
+ { str_lit("lunarlake"), str_lit("64bit,64bit-mode,adx,aes,allow-light-256-bit,avx,avx2,avxifma,avxneconvert,avxvnni,avxvnniint16,avxvnniint8,bmi,bmi2,cldemote,clflushopt,clwb,cmov,cmpccxadd,crc32,cx16,cx8,enqcmd,f16c,false-deps-perm,false-deps-popcnt,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fxsr,gfni,hreset,idivq-to-divl,invpcid,kl,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pconfig,pku,popcnt,prefer-movmsk-over-vtest,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,serialize,sha,sha512,shstk,slow-3ops-lea,sm3,sm4,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,uintr,vaes,vpclmulqdq,vzeroupper,waitpkg,widekl,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("meteorlake"), str_lit("64bit,64bit-mode,adx,aes,allow-light-256-bit,avx,avx2,avxvnni,bmi,bmi2,cldemote,clflushopt,clwb,cmov,crc32,cx16,cx8,f16c,false-deps-perm,false-deps-popcnt,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fxsr,gfni,hreset,idivq-to-divl,invpcid,kl,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pconfig,pku,popcnt,prefer-movmsk-over-vtest,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,serialize,sha,shstk,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,vaes,vpclmulqdq,vzeroupper,waitpkg,widekl,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("mic_avx512"), str_lit("64bit,64bit-mode,adx,aes,avx,avx2,avx512cd,avx512f,bmi,bmi2,cmov,crc32,cx16,cx8,evex512,f16c,fast-gather,fast-imm16,fast-movbe,fma,fsgsbase,fxsr,idivq-to-divl,lzcnt,mmx,movbe,nopl,pclmul,popcnt,prefer-mask-registers,prfchw,rdrnd,rdseed,sahf,slow-3ops-lea,slow-incdec,slow-pmaddwd,slow-two-mem-ops,sse,sse2,sse3,sse4.1,sse4.2,ssse3,x87,xsave,xsaveopt") },
+ { str_lit("nehalem"), str_lit("64bit,64bit-mode,cmov,crc32,cx16,cx8,fxsr,idivq-to-divl,macrofusion,mmx,no-bypass-delay-mov,nopl,popcnt,sahf,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87") },
+ { str_lit("nocona"), str_lit("64bit,64bit-mode,cmov,cx16,cx8,fxsr,mmx,nopl,slow-unaligned-mem-16,sse,sse2,sse3,vzeroupper,x87") },
+ { str_lit("opteron"), str_lit("64bit,64bit-mode,cmov,cx8,fast-scalar-shift-masks,fxsr,mmx,nopl,prfchw,sbb-dep-breaking,slow-shld,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("opteron-sse3"), str_lit("64bit,64bit-mode,cmov,cx16,cx8,fast-scalar-shift-masks,fxsr,mmx,nopl,prfchw,sbb-dep-breaking,slow-shld,slow-unaligned-mem-16,sse,sse2,sse3,vzeroupper,x87") },
+ { str_lit("pantherlake"), str_lit("64bit,64bit-mode,adx,aes,allow-light-256-bit,avx,avx2,avxifma,avxneconvert,avxvnni,avxvnniint16,avxvnniint8,bmi,bmi2,cldemote,clflushopt,clwb,cmov,cmpccxadd,crc32,cx16,cx8,enqcmd,f16c,false-deps-perm,false-deps-popcnt,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fxsr,gfni,hreset,idivq-to-divl,invpcid,kl,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pconfig,pku,popcnt,prefer-movmsk-over-vtest,prefetchi,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,serialize,sha,sha512,shstk,slow-3ops-lea,sm3,sm4,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,uintr,vaes,vpclmulqdq,vzeroupper,waitpkg,widekl,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("penryn"), str_lit("64bit,64bit-mode,cmov,cx16,cx8,fxsr,macrofusion,mmx,nopl,sahf,slow-unaligned-mem-16,sse,sse2,sse3,sse4.1,ssse3,vzeroupper,x87") },
+ { str_lit("pentium"), str_lit("64bit-mode,cx8,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("pentium-m"), str_lit("64bit-mode,cmov,cx8,fxsr,mmx,nopl,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("pentium-mmx"), str_lit("64bit-mode,cx8,mmx,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("pentium2"), str_lit("64bit-mode,cmov,cx8,fxsr,mmx,nopl,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("pentium3"), str_lit("64bit-mode,cmov,cx8,fxsr,mmx,nopl,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("pentium3m"), str_lit("64bit-mode,cmov,cx8,fxsr,mmx,nopl,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("pentium4"), str_lit("64bit-mode,cmov,cx8,fxsr,mmx,nopl,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("pentium4m"), str_lit("64bit-mode,cmov,cx8,fxsr,mmx,nopl,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("pentium_4"), str_lit("64bit-mode,cmov,cx8,fxsr,mmx,nopl,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("pentium_4_sse3"), str_lit("64bit-mode,cmov,cx8,fxsr,mmx,nopl,slow-unaligned-mem-16,sse,sse2,sse3,vzeroupper,x87") },
+ { str_lit("pentium_ii"), str_lit("64bit-mode,cmov,cx8,fxsr,mmx,nopl,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("pentium_iii"), str_lit("64bit-mode,cmov,cx8,fxsr,mmx,nopl,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("pentium_iii_no_xmm_regs"), str_lit("64bit-mode,cmov,cx8,fxsr,mmx,nopl,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("pentium_m"), str_lit("64bit-mode,cmov,cx8,fxsr,mmx,nopl,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("pentium_mmx"), str_lit("64bit-mode,cx8,mmx,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("pentium_pro"), str_lit("64bit-mode,cmov,cx8,nopl,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("pentiumpro"), str_lit("64bit-mode,cmov,cx8,nopl,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("prescott"), str_lit("64bit-mode,cmov,cx8,fxsr,mmx,nopl,slow-unaligned-mem-16,sse,sse2,sse3,vzeroupper,x87") },
+ { str_lit("raptorlake"), str_lit("64bit,64bit-mode,adx,aes,allow-light-256-bit,avx,avx2,avxvnni,bmi,bmi2,cldemote,clflushopt,clwb,cmov,crc32,cx16,cx8,f16c,false-deps-perm,false-deps-popcnt,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fxsr,gfni,hreset,idivq-to-divl,invpcid,kl,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pconfig,pku,popcnt,prefer-movmsk-over-vtest,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,serialize,sha,shstk,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,vaes,vpclmulqdq,vzeroupper,waitpkg,widekl,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("rocketlake"), str_lit("64bit,64bit-mode,adx,aes,allow-light-256-bit,avx,avx2,avx512bitalg,avx512bw,avx512cd,avx512dq,avx512f,avx512ifma,avx512vbmi,avx512vbmi2,avx512vl,avx512vnni,avx512vpopcntdq,bmi,bmi2,clflushopt,cmov,crc32,cx16,cx8,ermsb,evex512,f16c,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fsrm,fxsr,gfni,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pku,popcnt,prefer-256-bit,prfchw,rdpid,rdrnd,rdseed,sahf,sha,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,vaes,vpclmulqdq,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("sandybridge"), str_lit("64bit,64bit-mode,avx,cmov,crc32,cx16,cx8,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-shld-rotate,fxsr,idivq-to-divl,macrofusion,mmx,no-bypass-delay-mov,nopl,pclmul,popcnt,sahf,slow-3ops-lea,slow-unaligned-mem-32,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87,xsave,xsaveopt") },
+ { str_lit("sapphirerapids"), str_lit("64bit,64bit-mode,adx,aes,allow-light-256-bit,amx-bf16,amx-int8,amx-tile,avx,avx2,avx512bf16,avx512bitalg,avx512bw,avx512cd,avx512dq,avx512f,avx512fp16,avx512ifma,avx512vbmi,avx512vbmi2,avx512vl,avx512vnni,avx512vpopcntdq,avxvnni,bmi,bmi2,cldemote,clflushopt,clwb,cmov,crc32,cx16,cx8,enqcmd,ermsb,evex512,f16c,false-deps-getmant,false-deps-mulc,false-deps-mullq,false-deps-perm,false-deps-range,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fsrm,fxsr,gfni,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pconfig,pku,popcnt,prefer-256-bit,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,serialize,sha,shstk,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tsxldtrk,tuning-fast-imm-vector-shift,uintr,vaes,vpclmulqdq,vzeroupper,waitpkg,wbnoinvd,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("sierraforest"), str_lit("64bit,64bit-mode,adx,aes,avx,avx2,avxifma,avxneconvert,avxvnni,avxvnniint8,bmi,bmi2,cldemote,clflushopt,clwb,cmov,cmpccxadd,crc32,cx16,cx8,enqcmd,f16c,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fxsr,gfni,hreset,invpcid,kl,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,nopl,pclmul,pconfig,pku,popcnt,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,serialize,sha,shstk,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,uintr,vaes,vpclmulqdq,vzeroupper,waitpkg,widekl,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("silvermont"), str_lit("64bit,64bit-mode,cmov,crc32,cx16,cx8,false-deps-popcnt,fast-7bytenop,fast-imm16,fast-movbe,fxsr,idivq-to-divl,mmx,movbe,no-bypass-delay,nopl,pclmul,popcnt,prfchw,rdrnd,sahf,slow-incdec,slow-lea,slow-pmulld,slow-two-mem-ops,sse,sse2,sse3,sse4.1,sse4.2,ssse3,use-slm-arith-costs,vzeroupper,x87") },
+ { str_lit("skx"), str_lit("64bit,64bit-mode,adx,aes,allow-light-256-bit,avx,avx2,avx512bw,avx512cd,avx512dq,avx512f,avx512vl,bmi,bmi2,clflushopt,clwb,cmov,crc32,cx16,cx8,ermsb,evex512,f16c,false-deps-popcnt,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,faster-shift-than-shuffle,fma,fsgsbase,fxsr,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pku,popcnt,prefer-256-bit,prfchw,rdrnd,rdseed,sahf,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("skylake"), str_lit("64bit,64bit-mode,adx,aes,allow-light-256-bit,avx,avx2,bmi,bmi2,clflushopt,cmov,crc32,cx16,cx8,ermsb,f16c,false-deps-popcnt,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fxsr,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,popcnt,prfchw,rdrnd,rdseed,sahf,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("skylake-avx512"), str_lit("64bit,64bit-mode,adx,aes,allow-light-256-bit,avx,avx2,avx512bw,avx512cd,avx512dq,avx512f,avx512vl,bmi,bmi2,clflushopt,clwb,cmov,crc32,cx16,cx8,ermsb,evex512,f16c,false-deps-popcnt,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,faster-shift-than-shuffle,fma,fsgsbase,fxsr,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pku,popcnt,prefer-256-bit,prfchw,rdrnd,rdseed,sahf,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("skylake_avx512"), str_lit("64bit,64bit-mode,adx,aes,allow-light-256-bit,avx,avx2,avx512bw,avx512cd,avx512dq,avx512f,avx512vl,bmi,bmi2,clflushopt,clwb,cmov,crc32,cx16,cx8,ermsb,evex512,f16c,false-deps-popcnt,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,faster-shift-than-shuffle,fma,fsgsbase,fxsr,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pku,popcnt,prefer-256-bit,prfchw,rdrnd,rdseed,sahf,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("slm"), str_lit("64bit,64bit-mode,cmov,crc32,cx16,cx8,false-deps-popcnt,fast-7bytenop,fast-imm16,fast-movbe,fxsr,idivq-to-divl,mmx,movbe,no-bypass-delay,nopl,pclmul,popcnt,prfchw,rdrnd,sahf,slow-incdec,slow-lea,slow-pmulld,slow-two-mem-ops,sse,sse2,sse3,sse4.1,sse4.2,ssse3,use-slm-arith-costs,vzeroupper,x87") },
+ { str_lit("tigerlake"), str_lit("64bit,64bit-mode,adx,aes,allow-light-256-bit,avx,avx2,avx512bitalg,avx512bw,avx512cd,avx512dq,avx512f,avx512ifma,avx512vbmi,avx512vbmi2,avx512vl,avx512vnni,avx512vp2intersect,avx512vpopcntdq,bmi,bmi2,clflushopt,clwb,cmov,crc32,cx16,cx8,ermsb,evex512,f16c,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fsrm,fxsr,gfni,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pku,popcnt,prefer-256-bit,prfchw,rdpid,rdrnd,rdseed,sahf,sha,shstk,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,vaes,vpclmulqdq,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("tremont"), str_lit("64bit,64bit-mode,aes,clflushopt,clwb,cmov,crc32,cx16,cx8,fast-imm16,fast-movbe,fsgsbase,fxsr,gfni,mmx,movbe,no-bypass-delay,nopl,pclmul,popcnt,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,sha,slow-incdec,slow-lea,slow-two-mem-ops,sse,sse2,sse3,sse4.1,sse4.2,ssse3,use-glm-div-sqrt-costs,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("westmere"), str_lit("64bit,64bit-mode,cmov,crc32,cx16,cx8,fxsr,idivq-to-divl,macrofusion,mmx,no-bypass-delay-mov,nopl,pclmul,popcnt,sahf,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87") },
+ { str_lit("winchip-c6"), str_lit("64bit-mode,mmx,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("winchip2"), str_lit("64bit-mode,mmx,prfchw,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("x86-64"), str_lit("64bit,64bit-mode,cmov,cx8,fxsr,idivq-to-divl,macrofusion,mmx,nopl,slow-3ops-lea,slow-incdec,sse,sse2,vzeroupper,x87") },
+ { str_lit("x86-64-v2"), str_lit("64bit,64bit-mode,cmov,crc32,cx16,cx8,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-shld-rotate,fxsr,idivq-to-divl,macrofusion,mmx,nopl,popcnt,sahf,slow-3ops-lea,slow-unaligned-mem-32,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87") },
+ { str_lit("x86-64-v3"), str_lit("64bit,64bit-mode,allow-light-256-bit,avx,avx2,bmi,bmi2,cmov,crc32,cx16,cx8,f16c,false-deps-lzcnt-tzcnt,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fma,fxsr,idivq-to-divl,lzcnt,macrofusion,mmx,movbe,nopl,popcnt,sahf,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87,xsave") },
+ { str_lit("x86-64-v4"), str_lit("64bit,64bit-mode,allow-light-256-bit,avx,avx2,avx512bw,avx512cd,avx512dq,avx512f,avx512vl,bmi,bmi2,cmov,crc32,cx16,cx8,evex512,f16c,false-deps-popcnt,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fxsr,idivq-to-divl,lzcnt,macrofusion,mmx,movbe,nopl,popcnt,prefer-256-bit,sahf,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87,xsave") },
+ { str_lit("yonah"), str_lit("64bit-mode,cmov,cx8,fxsr,mmx,nopl,slow-unaligned-mem-16,sse,sse2,sse3,vzeroupper,x87") },
+ { str_lit("znver1"), str_lit("64bit,64bit-mode,adx,aes,allow-light-256-bit,avx,avx2,bmi,bmi2,branchfusion,clflushopt,clzero,cmov,crc32,cx16,cx8,f16c,fast-15bytenop,fast-bextr,fast-imm16,fast-lzcnt,fast-movbe,fast-scalar-fsqrt,fast-scalar-shift-masks,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fxsr,idivq-to-divl,lzcnt,mmx,movbe,mwaitx,nopl,pclmul,popcnt,prfchw,rdrnd,rdseed,sahf,sbb-dep-breaking,sha,slow-shld,sse,sse2,sse3,sse4.1,sse4.2,sse4a,ssse3,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("znver2"), str_lit("64bit,64bit-mode,adx,aes,allow-light-256-bit,avx,avx2,bmi,bmi2,branchfusion,clflushopt,clwb,clzero,cmov,crc32,cx16,cx8,f16c,fast-15bytenop,fast-bextr,fast-imm16,fast-lzcnt,fast-movbe,fast-scalar-fsqrt,fast-scalar-shift-masks,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fxsr,idivq-to-divl,lzcnt,mmx,movbe,mwaitx,nopl,pclmul,popcnt,prfchw,rdpid,rdpru,rdrnd,rdseed,sahf,sbb-dep-breaking,sha,slow-shld,sse,sse2,sse3,sse4.1,sse4.2,sse4a,ssse3,vzeroupper,wbnoinvd,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("znver3"), str_lit("64bit,64bit-mode,adx,aes,allow-light-256-bit,avx,avx2,bmi,bmi2,branchfusion,clflushopt,clwb,clzero,cmov,crc32,cx16,cx8,f16c,fast-15bytenop,fast-bextr,fast-imm16,fast-lzcnt,fast-movbe,fast-scalar-fsqrt,fast-scalar-shift-masks,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fsrm,fxsr,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,mwaitx,nopl,pclmul,pku,popcnt,prfchw,rdpid,rdpru,rdrnd,rdseed,sahf,sbb-dep-breaking,sha,slow-shld,sse,sse2,sse3,sse4.1,sse4.2,sse4a,ssse3,vaes,vpclmulqdq,vzeroupper,wbnoinvd,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("znver4"), str_lit("64bit,64bit-mode,adx,aes,allow-light-256-bit,avx,avx2,avx512bf16,avx512bitalg,avx512bw,avx512cd,avx512dq,avx512f,avx512ifma,avx512vbmi,avx512vbmi2,avx512vl,avx512vnni,avx512vpopcntdq,bmi,bmi2,branchfusion,clflushopt,clwb,clzero,cmov,crc32,cx16,cx8,evex512,f16c,fast-15bytenop,fast-bextr,fast-dpwssd,fast-imm16,fast-lzcnt,fast-movbe,fast-scalar-fsqrt,fast-scalar-shift-masks,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fsrm,fxsr,gfni,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,mwaitx,nopl,pclmul,pku,popcnt,prfchw,rdpid,rdpru,rdrnd,rdseed,sahf,sbb-dep-breaking,sha,shstk,slow-shld,sse,sse2,sse3,sse4.1,sse4.2,sse4a,ssse3,vaes,vpclmulqdq,vzeroupper,wbnoinvd,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("znver5"), str_lit("64bit,64bit-mode,adx,aes,allow-light-256-bit,avx,avx2,avx512bf16,avx512bitalg,avx512bw,avx512cd,avx512dq,avx512f,avx512ifma,avx512vbmi,avx512vbmi2,avx512vl,avx512vnni,avx512vp2intersect,avx512vpopcntdq,avxvnni,bmi,bmi2,branchfusion,clflushopt,clwb,clzero,cmov,crc32,cx16,cx8,evex512,f16c,fast-15bytenop,fast-bextr,fast-dpwssd,fast-imm16,fast-lzcnt,fast-movbe,fast-scalar-fsqrt,fast-scalar-shift-masks,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fsrm,fxsr,gfni,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,mwaitx,nopl,pclmul,pku,popcnt,prefetchi,prfchw,rdpid,rdpru,rdrnd,rdseed,sahf,sbb-dep-breaking,sha,shstk,slow-shld,sse,sse2,sse3,sse4.1,sse4.2,sse4a,ssse3,vaes,vpclmulqdq,vzeroupper,wbnoinvd,x87,xsave,xsavec,xsaveopt,xsaves") },
+ // TargetArch_i386:
+ { str_lit("alderlake"), str_lit("32bit-mode,64bit,adx,aes,allow-light-256-bit,avx,avx2,avxvnni,bmi,bmi2,cldemote,clflushopt,clwb,cmov,crc32,cx16,cx8,f16c,false-deps-perm,false-deps-popcnt,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fxsr,gfni,hreset,idivq-to-divl,invpcid,kl,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pconfig,pku,popcnt,prefer-movmsk-over-vtest,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,serialize,sha,shstk,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,vaes,vpclmulqdq,vzeroupper,waitpkg,widekl,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("amdfam10"), str_lit("32bit-mode,64bit,cmov,cx16,cx8,fast-scalar-shift-masks,fxsr,idivq-to-divl,lzcnt,nopl,popcnt,prfchw,sahf,sbb-dep-breaking,slow-shld,sse,sse2,sse3,sse4a,vzeroupper,x87") },
+ { str_lit("arrowlake"), str_lit("32bit-mode,64bit,adx,aes,allow-light-256-bit,avx,avx2,avxifma,avxneconvert,avxvnni,avxvnniint8,bmi,bmi2,cldemote,clflushopt,clwb,cmov,cmpccxadd,crc32,cx16,cx8,enqcmd,f16c,false-deps-perm,false-deps-popcnt,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fxsr,gfni,hreset,idivq-to-divl,invpcid,kl,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pconfig,pku,popcnt,prefer-movmsk-over-vtest,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,serialize,sha,shstk,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,uintr,vaes,vpclmulqdq,vzeroupper,waitpkg,widekl,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("arrowlake-s"), str_lit("32bit-mode,64bit,adx,aes,allow-light-256-bit,avx,avx2,avxifma,avxneconvert,avxvnni,avxvnniint16,avxvnniint8,bmi,bmi2,cldemote,clflushopt,clwb,cmov,cmpccxadd,crc32,cx16,cx8,enqcmd,f16c,false-deps-perm,false-deps-popcnt,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fxsr,gfni,hreset,idivq-to-divl,invpcid,kl,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pconfig,pku,popcnt,prefer-movmsk-over-vtest,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,serialize,sha,sha512,shstk,slow-3ops-lea,sm3,sm4,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,uintr,vaes,vpclmulqdq,vzeroupper,waitpkg,widekl,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("arrowlake_s"), str_lit("32bit-mode,64bit,adx,aes,allow-light-256-bit,avx,avx2,avxifma,avxneconvert,avxvnni,avxvnniint16,avxvnniint8,bmi,bmi2,cldemote,clflushopt,clwb,cmov,cmpccxadd,crc32,cx16,cx8,enqcmd,f16c,false-deps-perm,false-deps-popcnt,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fxsr,gfni,hreset,idivq-to-divl,invpcid,kl,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pconfig,pku,popcnt,prefer-movmsk-over-vtest,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,serialize,sha,sha512,shstk,slow-3ops-lea,sm3,sm4,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,uintr,vaes,vpclmulqdq,vzeroupper,waitpkg,widekl,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("athlon"), str_lit("32bit-mode,cmov,cx8,mmx,nopl,prfchw,slow-shld,slow-unaligned-mem-16,vzeroupper,x87") },
+ { str_lit("athlon-4"), str_lit("32bit-mode,cmov,cx8,fxsr,mmx,nopl,prfchw,slow-shld,slow-unaligned-mem-16,sse,vzeroupper,x87") },
+ { str_lit("athlon-fx"), str_lit("32bit-mode,64bit,cmov,cx8,fast-scalar-shift-masks,fxsr,mmx,nopl,prfchw,sbb-dep-breaking,slow-shld,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("athlon-mp"), str_lit("32bit-mode,cmov,cx8,fxsr,mmx,nopl,prfchw,slow-shld,slow-unaligned-mem-16,sse,vzeroupper,x87") },
+ { str_lit("athlon-tbird"), str_lit("32bit-mode,cmov,cx8,mmx,nopl,prfchw,slow-shld,slow-unaligned-mem-16,vzeroupper,x87") },
+ { str_lit("athlon-xp"), str_lit("32bit-mode,cmov,cx8,fxsr,mmx,nopl,prfchw,slow-shld,slow-unaligned-mem-16,sse,vzeroupper,x87") },
+ { str_lit("athlon64"), str_lit("32bit-mode,64bit,cmov,cx8,fast-scalar-shift-masks,fxsr,mmx,nopl,prfchw,sbb-dep-breaking,slow-shld,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("athlon64-sse3"), str_lit("32bit-mode,64bit,cmov,cx16,cx8,fast-scalar-shift-masks,fxsr,mmx,nopl,prfchw,sbb-dep-breaking,slow-shld,slow-unaligned-mem-16,sse,sse2,sse3,vzeroupper,x87") },
+ { str_lit("atom"), str_lit("32bit-mode,64bit,cmov,cx16,cx8,fast-imm16,fxsr,idivl-to-divb,idivq-to-divl,lea-sp,lea-uses-ag,mmx,movbe,no-bypass-delay,nopl,pad-short-functions,sahf,slow-two-mem-ops,slow-unaligned-mem-16,sse,sse2,sse3,ssse3,vzeroupper,x87") },
+ { str_lit("atom_sse4_2"), str_lit("32bit-mode,64bit,cmov,crc32,cx16,cx8,false-deps-popcnt,fast-7bytenop,fast-imm16,fast-movbe,fxsr,idivq-to-divl,mmx,movbe,no-bypass-delay,nopl,pclmul,popcnt,prfchw,rdrnd,sahf,slow-incdec,slow-lea,slow-pmulld,slow-two-mem-ops,sse,sse2,sse3,sse4.1,sse4.2,ssse3,use-slm-arith-costs,vzeroupper,x87") },
+ { str_lit("atom_sse4_2_movbe"), str_lit("32bit-mode,64bit,aes,clflushopt,cmov,crc32,cx16,cx8,false-deps-popcnt,fast-7bytenop,fast-imm16,fast-movbe,fsgsbase,fxsr,idivq-to-divl,mmx,movbe,no-bypass-delay,nopl,pclmul,popcnt,prfchw,rdrnd,rdseed,sahf,sha,slow-incdec,slow-lea,slow-pmulld,slow-two-mem-ops,sse,sse2,sse3,sse4.1,sse4.2,ssse3,use-slm-arith-costs,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("barcelona"), str_lit("32bit-mode,64bit,cmov,cx16,cx8,fast-scalar-shift-masks,fxsr,idivq-to-divl,lzcnt,nopl,popcnt,prfchw,sahf,sbb-dep-breaking,slow-shld,sse,sse2,sse3,sse4a,vzeroupper,x87") },
+ { str_lit("bdver1"), str_lit("32bit-mode,64bit,aes,avx,branchfusion,cmov,crc32,cx16,cx8,fast-11bytenop,fast-scalar-shift-masks,fma4,fxsr,idivq-to-divl,lwp,lzcnt,mmx,nopl,pclmul,popcnt,prfchw,sahf,sbb-dep-breaking,slow-shld,sse,sse2,sse3,sse4.1,sse4.2,sse4a,ssse3,vzeroupper,x87,xop,xsave") },
+ { str_lit("bdver2"), str_lit("32bit-mode,64bit,aes,avx,bmi,branchfusion,cmov,crc32,cx16,cx8,f16c,fast-11bytenop,fast-bextr,fast-movbe,fast-scalar-shift-masks,fma,fma4,fxsr,idivq-to-divl,lwp,lzcnt,mmx,nopl,pclmul,popcnt,prfchw,sahf,sbb-dep-breaking,slow-shld,sse,sse2,sse3,sse4.1,sse4.2,sse4a,ssse3,tbm,vzeroupper,x87,xop,xsave") },
+ { str_lit("bdver3"), str_lit("32bit-mode,64bit,aes,avx,bmi,branchfusion,cmov,crc32,cx16,cx8,f16c,fast-11bytenop,fast-bextr,fast-movbe,fast-scalar-shift-masks,fma,fma4,fsgsbase,fxsr,idivq-to-divl,lwp,lzcnt,mmx,nopl,pclmul,popcnt,prfchw,sahf,sbb-dep-breaking,slow-shld,sse,sse2,sse3,sse4.1,sse4.2,sse4a,ssse3,tbm,vzeroupper,x87,xop,xsave,xsaveopt") },
+ { str_lit("bdver4"), str_lit("32bit-mode,64bit,aes,avx,avx2,bmi,bmi2,branchfusion,cmov,crc32,cx16,cx8,f16c,fast-11bytenop,fast-bextr,fast-movbe,fast-scalar-shift-masks,fma,fma4,fsgsbase,fxsr,idivq-to-divl,lwp,lzcnt,mmx,movbe,mwaitx,nopl,pclmul,popcnt,prfchw,rdrnd,sahf,sbb-dep-breaking,slow-shld,sse,sse2,sse3,sse4.1,sse4.2,sse4a,ssse3,tbm,vzeroupper,x87,xop,xsave,xsaveopt") },
+ { str_lit("bonnell"), str_lit("32bit-mode,64bit,cmov,cx16,cx8,fast-imm16,fxsr,idivl-to-divb,idivq-to-divl,lea-sp,lea-uses-ag,mmx,movbe,no-bypass-delay,nopl,pad-short-functions,sahf,slow-two-mem-ops,slow-unaligned-mem-16,sse,sse2,sse3,ssse3,vzeroupper,x87") },
+ { str_lit("broadwell"), str_lit("32bit-mode,64bit,adx,allow-light-256-bit,avx,avx2,bmi,bmi2,cmov,crc32,cx16,cx8,ermsb,f16c,false-deps-lzcnt-tzcnt,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fma,fsgsbase,fxsr,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,popcnt,prfchw,rdrnd,rdseed,sahf,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87,xsave,xsaveopt") },
+ { str_lit("btver1"), str_lit("32bit-mode,64bit,cmov,cx16,cx8,fast-15bytenop,fast-imm16,fast-scalar-shift-masks,fast-vector-shift-masks,fxsr,idivq-to-divl,lzcnt,mmx,nopl,popcnt,prfchw,sahf,sbb-dep-breaking,slow-shld,sse,sse2,sse3,sse4a,ssse3,vzeroupper,x87") },
+ { str_lit("btver2"), str_lit("32bit-mode,64bit,aes,avx,bmi,cmov,crc32,cx16,cx8,f16c,fast-15bytenop,fast-bextr,fast-hops,fast-imm16,fast-lzcnt,fast-movbe,fast-scalar-shift-masks,fast-vector-shift-masks,fxsr,idivq-to-divl,lzcnt,mmx,movbe,nopl,pclmul,popcnt,prfchw,sahf,sbb-dep-breaking,slow-shld,sse,sse2,sse3,sse4.1,sse4.2,sse4a,ssse3,x87,xsave,xsaveopt") },
+ { str_lit("c3"), str_lit("32bit-mode,mmx,prfchw,slow-unaligned-mem-16,vzeroupper,x87") },
+ { str_lit("c3-2"), str_lit("32bit-mode,cmov,cx8,fxsr,mmx,slow-unaligned-mem-16,sse,vzeroupper,x87") },
+ { str_lit("cannonlake"), str_lit("32bit-mode,64bit,adx,aes,allow-light-256-bit,avx,avx2,avx512bw,avx512cd,avx512dq,avx512f,avx512ifma,avx512vbmi,avx512vl,bmi,bmi2,clflushopt,cmov,crc32,cx16,cx8,ermsb,evex512,f16c,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fxsr,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pku,popcnt,prefer-256-bit,prfchw,rdrnd,rdseed,sahf,sha,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("cascadelake"), str_lit("32bit-mode,64bit,adx,aes,allow-light-256-bit,avx,avx2,avx512bw,avx512cd,avx512dq,avx512f,avx512vl,avx512vnni,bmi,bmi2,clflushopt,clwb,cmov,crc32,cx16,cx8,ermsb,evex512,f16c,false-deps-popcnt,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,faster-shift-than-shuffle,fma,fsgsbase,fxsr,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pku,popcnt,prefer-256-bit,prfchw,rdrnd,rdseed,sahf,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("clearwaterforest"), str_lit("32bit-mode,64bit,adx,aes,allow-light-256-bit,avx,avx2,avxifma,avxneconvert,avxvnni,avxvnniint16,avxvnniint8,bmi,bmi2,cldemote,clflushopt,clwb,cmov,cmpccxadd,crc32,cx16,cx8,enqcmd,f16c,false-deps-perm,false-deps-popcnt,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fxsr,gfni,hreset,idivq-to-divl,invpcid,kl,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pconfig,pku,popcnt,prefer-movmsk-over-vtest,prefetchi,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,serialize,sha,sha512,shstk,slow-3ops-lea,sm3,sm4,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,uintr,usermsr,vaes,vpclmulqdq,vzeroupper,waitpkg,widekl,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("cooperlake"), str_lit("32bit-mode,64bit,adx,aes,allow-light-256-bit,avx,avx2,avx512bf16,avx512bw,avx512cd,avx512dq,avx512f,avx512vl,avx512vnni,bmi,bmi2,clflushopt,clwb,cmov,crc32,cx16,cx8,ermsb,evex512,f16c,false-deps-popcnt,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,faster-shift-than-shuffle,fma,fsgsbase,fxsr,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pku,popcnt,prefer-256-bit,prfchw,rdrnd,rdseed,sahf,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("core-avx-i"), str_lit("32bit-mode,64bit,avx,cmov,crc32,cx16,cx8,f16c,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-shld-rotate,fsgsbase,fxsr,idivq-to-divl,macrofusion,mmx,no-bypass-delay-mov,nopl,pclmul,popcnt,rdrnd,sahf,slow-3ops-lea,slow-unaligned-mem-32,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87,xsave,xsaveopt") },
+ { str_lit("core-avx2"), str_lit("32bit-mode,64bit,allow-light-256-bit,avx,avx2,bmi,bmi2,cmov,crc32,cx16,cx8,ermsb,f16c,false-deps-lzcnt-tzcnt,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fma,fsgsbase,fxsr,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,popcnt,rdrnd,sahf,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87,xsave,xsaveopt") },
+ { str_lit("core2"), str_lit("32bit-mode,64bit,cmov,cx16,cx8,fxsr,macrofusion,mmx,nopl,sahf,slow-unaligned-mem-16,sse,sse2,sse3,ssse3,vzeroupper,x87") },
+ { str_lit("core_2_duo_sse4_1"), str_lit("32bit-mode,64bit,cmov,cx16,cx8,fxsr,macrofusion,mmx,nopl,sahf,slow-unaligned-mem-16,sse,sse2,sse3,sse4.1,ssse3,vzeroupper,x87") },
+ { str_lit("core_2_duo_ssse3"), str_lit("32bit-mode,64bit,cmov,cx16,cx8,fxsr,macrofusion,mmx,nopl,sahf,slow-unaligned-mem-16,sse,sse2,sse3,ssse3,vzeroupper,x87") },
+ { str_lit("core_2nd_gen_avx"), str_lit("32bit-mode,64bit,avx,cmov,crc32,cx16,cx8,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-shld-rotate,fxsr,idivq-to-divl,macrofusion,mmx,no-bypass-delay-mov,nopl,pclmul,popcnt,sahf,slow-3ops-lea,slow-unaligned-mem-32,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87,xsave,xsaveopt") },
+ { str_lit("core_3rd_gen_avx"), str_lit("32bit-mode,64bit,avx,cmov,crc32,cx16,cx8,f16c,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-shld-rotate,fsgsbase,fxsr,idivq-to-divl,macrofusion,mmx,no-bypass-delay-mov,nopl,pclmul,popcnt,rdrnd,sahf,slow-3ops-lea,slow-unaligned-mem-32,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87,xsave,xsaveopt") },
+ { str_lit("core_4th_gen_avx"), str_lit("32bit-mode,64bit,allow-light-256-bit,avx,avx2,bmi,bmi2,cmov,crc32,cx16,cx8,ermsb,f16c,false-deps-lzcnt-tzcnt,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fma,fsgsbase,fxsr,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,popcnt,rdrnd,sahf,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87,xsave,xsaveopt") },
+ { str_lit("core_4th_gen_avx_tsx"), str_lit("32bit-mode,64bit,allow-light-256-bit,avx,avx2,bmi,bmi2,cmov,crc32,cx16,cx8,ermsb,f16c,false-deps-lzcnt-tzcnt,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fma,fsgsbase,fxsr,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,popcnt,rdrnd,sahf,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87,xsave,xsaveopt") },
+ { str_lit("core_5th_gen_avx"), str_lit("32bit-mode,64bit,adx,allow-light-256-bit,avx,avx2,bmi,bmi2,cmov,crc32,cx16,cx8,ermsb,f16c,false-deps-lzcnt-tzcnt,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fma,fsgsbase,fxsr,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,popcnt,prfchw,rdrnd,rdseed,sahf,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87,xsave,xsaveopt") },
+ { str_lit("core_5th_gen_avx_tsx"), str_lit("32bit-mode,64bit,adx,allow-light-256-bit,avx,avx2,bmi,bmi2,cmov,crc32,cx16,cx8,ermsb,f16c,false-deps-lzcnt-tzcnt,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fma,fsgsbase,fxsr,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,popcnt,prfchw,rdrnd,rdseed,sahf,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87,xsave,xsaveopt") },
+ { str_lit("core_aes_pclmulqdq"), str_lit("32bit-mode,64bit,cmov,crc32,cx16,cx8,fxsr,idivq-to-divl,macrofusion,mmx,no-bypass-delay-mov,nopl,pclmul,popcnt,sahf,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87") },
+ { str_lit("core_i7_sse4_2"), str_lit("32bit-mode,64bit,cmov,crc32,cx16,cx8,fxsr,idivq-to-divl,macrofusion,mmx,no-bypass-delay-mov,nopl,popcnt,sahf,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87") },
+ { str_lit("corei7"), str_lit("32bit-mode,64bit,cmov,crc32,cx16,cx8,fxsr,idivq-to-divl,macrofusion,mmx,no-bypass-delay-mov,nopl,popcnt,sahf,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87") },
+ { str_lit("corei7-avx"), str_lit("32bit-mode,64bit,avx,cmov,crc32,cx16,cx8,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-shld-rotate,fxsr,idivq-to-divl,macrofusion,mmx,no-bypass-delay-mov,nopl,pclmul,popcnt,sahf,slow-3ops-lea,slow-unaligned-mem-32,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87,xsave,xsaveopt") },
+ { str_lit("diamondrapids"), str_lit("32bit-mode,64bit,adx,aes,allow-light-256-bit,amx-avx512,amx-bf16,amx-complex,amx-fp16,amx-fp8,amx-int8,amx-movrs,amx-tf32,amx-tile,amx-transpose,avx,avx10.1-256,avx10.1-512,avx10.2-256,avx10.2-512,avx2,avx512bf16,avx512bitalg,avx512bw,avx512cd,avx512dq,avx512f,avx512fp16,avx512ifma,avx512vbmi,avx512vbmi2,avx512vl,avx512vnni,avx512vpopcntdq,avxifma,avxneconvert,avxvnni,avxvnniint16,avxvnniint8,bmi,bmi2,branch-hint,ccmp,cf,cldemote,clflushopt,clwb,cmov,cmpccxadd,crc32,cx16,cx8,egpr,enqcmd,ermsb,evex512,f16c,false-deps-getmant,false-deps-mulc,false-deps-mullq,false-deps-perm,false-deps-range,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fsrm,fxsr,gfni,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,movrs,ndd,nf,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pconfig,pku,popcnt,ppx,prefer-256-bit,prefetchi,prfchw,ptwrite,push2pop2,rdpid,rdrnd,rdseed,sahf,serialize,sha,sha512,shstk,sm3,sm4,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tsxldtrk,tuning-fast-imm-vector-shift,uintr,usermsr,vaes,vpclmulqdq,vzeroupper,waitpkg,wbnoinvd,x87,xsave,xsavec,xsaveopt,xsaves,zu") },
+ { str_lit("emeraldrapids"), str_lit("32bit-mode,64bit,adx,aes,allow-light-256-bit,amx-bf16,amx-int8,amx-tile,avx,avx2,avx512bf16,avx512bitalg,avx512bw,avx512cd,avx512dq,avx512f,avx512fp16,avx512ifma,avx512vbmi,avx512vbmi2,avx512vl,avx512vnni,avx512vpopcntdq,avxvnni,bmi,bmi2,cldemote,clflushopt,clwb,cmov,crc32,cx16,cx8,enqcmd,ermsb,evex512,f16c,false-deps-getmant,false-deps-mulc,false-deps-mullq,false-deps-perm,false-deps-range,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fsrm,fxsr,gfni,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pconfig,pku,popcnt,prefer-256-bit,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,serialize,sha,shstk,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tsxldtrk,tuning-fast-imm-vector-shift,uintr,vaes,vpclmulqdq,vzeroupper,waitpkg,wbnoinvd,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("generic"), str_lit("32bit-mode,64bit,cx8,fast-15bytenop,fast-scalar-fsqrt,idivq-to-divl,macrofusion,slow-3ops-lea,vzeroupper,x87") },
+ { str_lit("geode"), str_lit("32bit-mode,cx8,mmx,prfchw,slow-unaligned-mem-16,vzeroupper,x87") },
+ { str_lit("goldmont"), str_lit("32bit-mode,64bit,aes,clflushopt,cmov,crc32,cx16,cx8,false-deps-popcnt,fast-imm16,fast-movbe,fsgsbase,fxsr,mmx,movbe,no-bypass-delay,nopl,pclmul,popcnt,prfchw,rdrnd,rdseed,sahf,sha,slow-incdec,slow-lea,slow-two-mem-ops,sse,sse2,sse3,sse4.1,sse4.2,ssse3,use-glm-div-sqrt-costs,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("goldmont-plus"), str_lit("32bit-mode,64bit,aes,clflushopt,cmov,crc32,cx16,cx8,fast-imm16,fast-movbe,fsgsbase,fxsr,mmx,movbe,no-bypass-delay,nopl,pclmul,popcnt,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,sha,slow-incdec,slow-lea,slow-two-mem-ops,sse,sse2,sse3,sse4.1,sse4.2,ssse3,use-glm-div-sqrt-costs,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("goldmont_plus"), str_lit("32bit-mode,64bit,aes,clflushopt,cmov,crc32,cx16,cx8,fast-imm16,fast-movbe,fsgsbase,fxsr,mmx,movbe,no-bypass-delay,nopl,pclmul,popcnt,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,sha,slow-incdec,slow-lea,slow-two-mem-ops,sse,sse2,sse3,sse4.1,sse4.2,ssse3,use-glm-div-sqrt-costs,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("gracemont"), str_lit("32bit-mode,64bit,adx,aes,avx,avx2,avxvnni,bmi,bmi2,cldemote,clflushopt,clwb,cmov,crc32,cx16,cx8,f16c,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fxsr,gfni,hreset,invpcid,kl,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,nopl,pclmul,pconfig,pku,popcnt,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,serialize,sha,shstk,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vaes,vpclmulqdq,vzeroupper,waitpkg,widekl,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("grandridge"), str_lit("32bit-mode,64bit,adx,aes,avx,avx2,avxifma,avxneconvert,avxvnni,avxvnniint8,bmi,bmi2,cldemote,clflushopt,clwb,cmov,cmpccxadd,crc32,cx16,cx8,enqcmd,f16c,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fxsr,gfni,hreset,invpcid,kl,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,nopl,pclmul,pconfig,pku,popcnt,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,serialize,sha,shstk,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,uintr,vaes,vpclmulqdq,vzeroupper,waitpkg,widekl,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("graniterapids"), str_lit("32bit-mode,64bit,adx,aes,allow-light-256-bit,amx-bf16,amx-fp16,amx-int8,amx-tile,avx,avx2,avx512bf16,avx512bitalg,avx512bw,avx512cd,avx512dq,avx512f,avx512fp16,avx512ifma,avx512vbmi,avx512vbmi2,avx512vl,avx512vnni,avx512vpopcntdq,avxvnni,bmi,bmi2,branch-hint,cldemote,clflushopt,clwb,cmov,crc32,cx16,cx8,enqcmd,ermsb,evex512,f16c,false-deps-getmant,false-deps-mulc,false-deps-mullq,false-deps-perm,false-deps-range,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fsrm,fxsr,gfni,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pconfig,pku,popcnt,prefer-256-bit,prefetchi,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,serialize,sha,shstk,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tsxldtrk,tuning-fast-imm-vector-shift,uintr,vaes,vpclmulqdq,vzeroupper,waitpkg,wbnoinvd,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("graniterapids-d"), str_lit("32bit-mode,64bit,adx,aes,allow-light-256-bit,amx-bf16,amx-complex,amx-fp16,amx-int8,amx-tile,avx,avx2,avx512bf16,avx512bitalg,avx512bw,avx512cd,avx512dq,avx512f,avx512fp16,avx512ifma,avx512vbmi,avx512vbmi2,avx512vl,avx512vnni,avx512vpopcntdq,avxvnni,bmi,bmi2,branch-hint,cldemote,clflushopt,clwb,cmov,crc32,cx16,cx8,enqcmd,ermsb,evex512,f16c,false-deps-getmant,false-deps-mulc,false-deps-mullq,false-deps-perm,false-deps-range,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fsrm,fxsr,gfni,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pconfig,pku,popcnt,prefer-256-bit,prefetchi,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,serialize,sha,shstk,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tsxldtrk,tuning-fast-imm-vector-shift,uintr,vaes,vpclmulqdq,vzeroupper,waitpkg,wbnoinvd,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("graniterapids_d"), str_lit("32bit-mode,64bit,adx,aes,allow-light-256-bit,amx-bf16,amx-complex,amx-fp16,amx-int8,amx-tile,avx,avx2,avx512bf16,avx512bitalg,avx512bw,avx512cd,avx512dq,avx512f,avx512fp16,avx512ifma,avx512vbmi,avx512vbmi2,avx512vl,avx512vnni,avx512vpopcntdq,avxvnni,bmi,bmi2,branch-hint,cldemote,clflushopt,clwb,cmov,crc32,cx16,cx8,enqcmd,ermsb,evex512,f16c,false-deps-getmant,false-deps-mulc,false-deps-mullq,false-deps-perm,false-deps-range,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fsrm,fxsr,gfni,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pconfig,pku,popcnt,prefer-256-bit,prefetchi,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,serialize,sha,shstk,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tsxldtrk,tuning-fast-imm-vector-shift,uintr,vaes,vpclmulqdq,vzeroupper,waitpkg,wbnoinvd,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("haswell"), str_lit("32bit-mode,64bit,allow-light-256-bit,avx,avx2,bmi,bmi2,cmov,crc32,cx16,cx8,ermsb,f16c,false-deps-lzcnt-tzcnt,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fma,fsgsbase,fxsr,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,popcnt,rdrnd,sahf,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87,xsave,xsaveopt") },
+ { str_lit("i386"), str_lit("32bit-mode,slow-unaligned-mem-16,vzeroupper,x87") },
+ { str_lit("i486"), str_lit("32bit-mode,slow-unaligned-mem-16,vzeroupper,x87") },
+ { str_lit("i586"), str_lit("32bit-mode,cx8,slow-unaligned-mem-16,vzeroupper,x87") },
+ { str_lit("i686"), str_lit("32bit-mode,cmov,cx8,slow-unaligned-mem-16,vzeroupper,x87") },
+ { str_lit("icelake-client"), str_lit("32bit-mode,64bit,adx,aes,allow-light-256-bit,avx,avx2,avx512bitalg,avx512bw,avx512cd,avx512dq,avx512f,avx512ifma,avx512vbmi,avx512vbmi2,avx512vl,avx512vnni,avx512vpopcntdq,bmi,bmi2,clflushopt,cmov,crc32,cx16,cx8,ermsb,evex512,f16c,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fsrm,fxsr,gfni,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pku,popcnt,prefer-256-bit,prfchw,rdpid,rdrnd,rdseed,sahf,sha,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,vaes,vpclmulqdq,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("icelake-server"), str_lit("32bit-mode,64bit,adx,aes,allow-light-256-bit,avx,avx2,avx512bitalg,avx512bw,avx512cd,avx512dq,avx512f,avx512ifma,avx512vbmi,avx512vbmi2,avx512vl,avx512vnni,avx512vpopcntdq,bmi,bmi2,clflushopt,clwb,cmov,crc32,cx16,cx8,ermsb,evex512,f16c,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fsrm,fxsr,gfni,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pconfig,pku,popcnt,prefer-256-bit,prfchw,rdpid,rdrnd,rdseed,sahf,sha,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,vaes,vpclmulqdq,vzeroupper,wbnoinvd,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("icelake_client"), str_lit("32bit-mode,64bit,adx,aes,allow-light-256-bit,avx,avx2,avx512bitalg,avx512bw,avx512cd,avx512dq,avx512f,avx512ifma,avx512vbmi,avx512vbmi2,avx512vl,avx512vnni,avx512vpopcntdq,bmi,bmi2,clflushopt,cmov,crc32,cx16,cx8,ermsb,evex512,f16c,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fsrm,fxsr,gfni,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pku,popcnt,prefer-256-bit,prfchw,rdpid,rdrnd,rdseed,sahf,sha,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,vaes,vpclmulqdq,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("icelake_server"), str_lit("32bit-mode,64bit,adx,aes,allow-light-256-bit,avx,avx2,avx512bitalg,avx512bw,avx512cd,avx512dq,avx512f,avx512ifma,avx512vbmi,avx512vbmi2,avx512vl,avx512vnni,avx512vpopcntdq,bmi,bmi2,clflushopt,clwb,cmov,crc32,cx16,cx8,ermsb,evex512,f16c,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fsrm,fxsr,gfni,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pconfig,pku,popcnt,prefer-256-bit,prfchw,rdpid,rdrnd,rdseed,sahf,sha,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,vaes,vpclmulqdq,vzeroupper,wbnoinvd,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("ivybridge"), str_lit("32bit-mode,64bit,avx,cmov,crc32,cx16,cx8,f16c,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-shld-rotate,fsgsbase,fxsr,idivq-to-divl,macrofusion,mmx,no-bypass-delay-mov,nopl,pclmul,popcnt,rdrnd,sahf,slow-3ops-lea,slow-unaligned-mem-32,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87,xsave,xsaveopt") },
+ { str_lit("k6"), str_lit("32bit-mode,cx8,mmx,slow-unaligned-mem-16,vzeroupper,x87") },
+ { str_lit("k6-2"), str_lit("32bit-mode,cx8,mmx,prfchw,slow-unaligned-mem-16,vzeroupper,x87") },
+ { str_lit("k6-3"), str_lit("32bit-mode,cx8,mmx,prfchw,slow-unaligned-mem-16,vzeroupper,x87") },
+ { str_lit("k8"), str_lit("32bit-mode,64bit,cmov,cx8,fast-scalar-shift-masks,fxsr,mmx,nopl,prfchw,sbb-dep-breaking,slow-shld,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("k8-sse3"), str_lit("32bit-mode,64bit,cmov,cx16,cx8,fast-scalar-shift-masks,fxsr,mmx,nopl,prfchw,sbb-dep-breaking,slow-shld,slow-unaligned-mem-16,sse,sse2,sse3,vzeroupper,x87") },
+ { str_lit("knl"), str_lit("32bit-mode,64bit,adx,aes,avx,avx2,avx512cd,avx512f,bmi,bmi2,cmov,crc32,cx16,cx8,evex512,f16c,fast-gather,fast-imm16,fast-movbe,fma,fsgsbase,fxsr,idivq-to-divl,lzcnt,mmx,movbe,nopl,pclmul,popcnt,prefer-mask-registers,prfchw,rdrnd,rdseed,sahf,slow-3ops-lea,slow-incdec,slow-pmaddwd,slow-two-mem-ops,sse,sse2,sse3,sse4.1,sse4.2,ssse3,x87,xsave,xsaveopt") },
+ { str_lit("knm"), str_lit("32bit-mode,64bit,adx,aes,avx,avx2,avx512cd,avx512f,avx512vpopcntdq,bmi,bmi2,cmov,crc32,cx16,cx8,evex512,f16c,fast-gather,fast-imm16,fast-movbe,fma,fsgsbase,fxsr,idivq-to-divl,lzcnt,mmx,movbe,nopl,pclmul,popcnt,prefer-mask-registers,prfchw,rdrnd,rdseed,sahf,slow-3ops-lea,slow-incdec,slow-pmaddwd,slow-two-mem-ops,sse,sse2,sse3,sse4.1,sse4.2,ssse3,x87,xsave,xsaveopt") },
+ { str_lit("lakemont"), str_lit("32bit-mode,cx8,slow-unaligned-mem-16,vzeroupper") },
+ { str_lit("lunarlake"), str_lit("32bit-mode,64bit,adx,aes,allow-light-256-bit,avx,avx2,avxifma,avxneconvert,avxvnni,avxvnniint16,avxvnniint8,bmi,bmi2,cldemote,clflushopt,clwb,cmov,cmpccxadd,crc32,cx16,cx8,enqcmd,f16c,false-deps-perm,false-deps-popcnt,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fxsr,gfni,hreset,idivq-to-divl,invpcid,kl,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pconfig,pku,popcnt,prefer-movmsk-over-vtest,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,serialize,sha,sha512,shstk,slow-3ops-lea,sm3,sm4,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,uintr,vaes,vpclmulqdq,vzeroupper,waitpkg,widekl,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("meteorlake"), str_lit("32bit-mode,64bit,adx,aes,allow-light-256-bit,avx,avx2,avxvnni,bmi,bmi2,cldemote,clflushopt,clwb,cmov,crc32,cx16,cx8,f16c,false-deps-perm,false-deps-popcnt,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fxsr,gfni,hreset,idivq-to-divl,invpcid,kl,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pconfig,pku,popcnt,prefer-movmsk-over-vtest,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,serialize,sha,shstk,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,vaes,vpclmulqdq,vzeroupper,waitpkg,widekl,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("mic_avx512"), str_lit("32bit-mode,64bit,adx,aes,avx,avx2,avx512cd,avx512f,bmi,bmi2,cmov,crc32,cx16,cx8,evex512,f16c,fast-gather,fast-imm16,fast-movbe,fma,fsgsbase,fxsr,idivq-to-divl,lzcnt,mmx,movbe,nopl,pclmul,popcnt,prefer-mask-registers,prfchw,rdrnd,rdseed,sahf,slow-3ops-lea,slow-incdec,slow-pmaddwd,slow-two-mem-ops,sse,sse2,sse3,sse4.1,sse4.2,ssse3,x87,xsave,xsaveopt") },
+ { str_lit("nehalem"), str_lit("32bit-mode,64bit,cmov,crc32,cx16,cx8,fxsr,idivq-to-divl,macrofusion,mmx,no-bypass-delay-mov,nopl,popcnt,sahf,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87") },
+ { str_lit("nocona"), str_lit("32bit-mode,64bit,cmov,cx16,cx8,fxsr,mmx,nopl,slow-unaligned-mem-16,sse,sse2,sse3,vzeroupper,x87") },
+ { str_lit("opteron"), str_lit("32bit-mode,64bit,cmov,cx8,fast-scalar-shift-masks,fxsr,mmx,nopl,prfchw,sbb-dep-breaking,slow-shld,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("opteron-sse3"), str_lit("32bit-mode,64bit,cmov,cx16,cx8,fast-scalar-shift-masks,fxsr,mmx,nopl,prfchw,sbb-dep-breaking,slow-shld,slow-unaligned-mem-16,sse,sse2,sse3,vzeroupper,x87") },
+ { str_lit("pantherlake"), str_lit("32bit-mode,64bit,adx,aes,allow-light-256-bit,avx,avx2,avxifma,avxneconvert,avxvnni,avxvnniint16,avxvnniint8,bmi,bmi2,cldemote,clflushopt,clwb,cmov,cmpccxadd,crc32,cx16,cx8,enqcmd,f16c,false-deps-perm,false-deps-popcnt,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fxsr,gfni,hreset,idivq-to-divl,invpcid,kl,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pconfig,pku,popcnt,prefer-movmsk-over-vtest,prefetchi,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,serialize,sha,sha512,shstk,slow-3ops-lea,sm3,sm4,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,uintr,vaes,vpclmulqdq,vzeroupper,waitpkg,widekl,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("penryn"), str_lit("32bit-mode,64bit,cmov,cx16,cx8,fxsr,macrofusion,mmx,nopl,sahf,slow-unaligned-mem-16,sse,sse2,sse3,sse4.1,ssse3,vzeroupper,x87") },
+ { str_lit("pentium"), str_lit("32bit-mode,cx8,slow-unaligned-mem-16,vzeroupper,x87") },
+ { str_lit("pentium-m"), str_lit("32bit-mode,cmov,cx8,fxsr,mmx,nopl,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("pentium-mmx"), str_lit("32bit-mode,cx8,mmx,slow-unaligned-mem-16,vzeroupper,x87") },
+ { str_lit("pentium2"), str_lit("32bit-mode,cmov,cx8,fxsr,mmx,nopl,slow-unaligned-mem-16,vzeroupper,x87") },
+ { str_lit("pentium3"), str_lit("32bit-mode,cmov,cx8,fxsr,mmx,nopl,slow-unaligned-mem-16,sse,vzeroupper,x87") },
+ { str_lit("pentium3m"), str_lit("32bit-mode,cmov,cx8,fxsr,mmx,nopl,slow-unaligned-mem-16,sse,vzeroupper,x87") },
+ { str_lit("pentium4"), str_lit("32bit-mode,cmov,cx8,fxsr,mmx,nopl,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("pentium4m"), str_lit("32bit-mode,cmov,cx8,fxsr,mmx,nopl,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("pentium_4"), str_lit("32bit-mode,cmov,cx8,fxsr,mmx,nopl,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("pentium_4_sse3"), str_lit("32bit-mode,cmov,cx8,fxsr,mmx,nopl,slow-unaligned-mem-16,sse,sse2,sse3,vzeroupper,x87") },
+ { str_lit("pentium_ii"), str_lit("32bit-mode,cmov,cx8,fxsr,mmx,nopl,slow-unaligned-mem-16,vzeroupper,x87") },
+ { str_lit("pentium_iii"), str_lit("32bit-mode,cmov,cx8,fxsr,mmx,nopl,slow-unaligned-mem-16,sse,vzeroupper,x87") },
+ { str_lit("pentium_iii_no_xmm_regs"), str_lit("32bit-mode,cmov,cx8,fxsr,mmx,nopl,slow-unaligned-mem-16,sse,vzeroupper,x87") },
+ { str_lit("pentium_m"), str_lit("32bit-mode,cmov,cx8,fxsr,mmx,nopl,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("pentium_mmx"), str_lit("32bit-mode,cx8,mmx,slow-unaligned-mem-16,vzeroupper,x87") },
+ { str_lit("pentium_pro"), str_lit("32bit-mode,cmov,cx8,nopl,slow-unaligned-mem-16,vzeroupper,x87") },
+ { str_lit("pentiumpro"), str_lit("32bit-mode,cmov,cx8,nopl,slow-unaligned-mem-16,vzeroupper,x87") },
+ { str_lit("prescott"), str_lit("32bit-mode,cmov,cx8,fxsr,mmx,nopl,slow-unaligned-mem-16,sse,sse2,sse3,vzeroupper,x87") },
+ { str_lit("raptorlake"), str_lit("32bit-mode,64bit,adx,aes,allow-light-256-bit,avx,avx2,avxvnni,bmi,bmi2,cldemote,clflushopt,clwb,cmov,crc32,cx16,cx8,f16c,false-deps-perm,false-deps-popcnt,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fxsr,gfni,hreset,idivq-to-divl,invpcid,kl,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pconfig,pku,popcnt,prefer-movmsk-over-vtest,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,serialize,sha,shstk,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,vaes,vpclmulqdq,vzeroupper,waitpkg,widekl,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("rocketlake"), str_lit("32bit-mode,64bit,adx,aes,allow-light-256-bit,avx,avx2,avx512bitalg,avx512bw,avx512cd,avx512dq,avx512f,avx512ifma,avx512vbmi,avx512vbmi2,avx512vl,avx512vnni,avx512vpopcntdq,bmi,bmi2,clflushopt,cmov,crc32,cx16,cx8,ermsb,evex512,f16c,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fsrm,fxsr,gfni,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pku,popcnt,prefer-256-bit,prfchw,rdpid,rdrnd,rdseed,sahf,sha,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,vaes,vpclmulqdq,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("sandybridge"), str_lit("32bit-mode,64bit,avx,cmov,crc32,cx16,cx8,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-shld-rotate,fxsr,idivq-to-divl,macrofusion,mmx,no-bypass-delay-mov,nopl,pclmul,popcnt,sahf,slow-3ops-lea,slow-unaligned-mem-32,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87,xsave,xsaveopt") },
+ { str_lit("sapphirerapids"), str_lit("32bit-mode,64bit,adx,aes,allow-light-256-bit,amx-bf16,amx-int8,amx-tile,avx,avx2,avx512bf16,avx512bitalg,avx512bw,avx512cd,avx512dq,avx512f,avx512fp16,avx512ifma,avx512vbmi,avx512vbmi2,avx512vl,avx512vnni,avx512vpopcntdq,avxvnni,bmi,bmi2,cldemote,clflushopt,clwb,cmov,crc32,cx16,cx8,enqcmd,ermsb,evex512,f16c,false-deps-getmant,false-deps-mulc,false-deps-mullq,false-deps-perm,false-deps-range,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fsrm,fxsr,gfni,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pconfig,pku,popcnt,prefer-256-bit,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,serialize,sha,shstk,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tsxldtrk,tuning-fast-imm-vector-shift,uintr,vaes,vpclmulqdq,vzeroupper,waitpkg,wbnoinvd,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("sierraforest"), str_lit("32bit-mode,64bit,adx,aes,avx,avx2,avxifma,avxneconvert,avxvnni,avxvnniint8,bmi,bmi2,cldemote,clflushopt,clwb,cmov,cmpccxadd,crc32,cx16,cx8,enqcmd,f16c,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fxsr,gfni,hreset,invpcid,kl,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,nopl,pclmul,pconfig,pku,popcnt,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,serialize,sha,shstk,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,uintr,vaes,vpclmulqdq,vzeroupper,waitpkg,widekl,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("silvermont"), str_lit("32bit-mode,64bit,cmov,crc32,cx16,cx8,false-deps-popcnt,fast-7bytenop,fast-imm16,fast-movbe,fxsr,idivq-to-divl,mmx,movbe,no-bypass-delay,nopl,pclmul,popcnt,prfchw,rdrnd,sahf,slow-incdec,slow-lea,slow-pmulld,slow-two-mem-ops,sse,sse2,sse3,sse4.1,sse4.2,ssse3,use-slm-arith-costs,vzeroupper,x87") },
+ { str_lit("skx"), str_lit("32bit-mode,64bit,adx,aes,allow-light-256-bit,avx,avx2,avx512bw,avx512cd,avx512dq,avx512f,avx512vl,bmi,bmi2,clflushopt,clwb,cmov,crc32,cx16,cx8,ermsb,evex512,f16c,false-deps-popcnt,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,faster-shift-than-shuffle,fma,fsgsbase,fxsr,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pku,popcnt,prefer-256-bit,prfchw,rdrnd,rdseed,sahf,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("skylake"), str_lit("32bit-mode,64bit,adx,aes,allow-light-256-bit,avx,avx2,bmi,bmi2,clflushopt,cmov,crc32,cx16,cx8,ermsb,f16c,false-deps-popcnt,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fxsr,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,popcnt,prfchw,rdrnd,rdseed,sahf,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("skylake-avx512"), str_lit("32bit-mode,64bit,adx,aes,allow-light-256-bit,avx,avx2,avx512bw,avx512cd,avx512dq,avx512f,avx512vl,bmi,bmi2,clflushopt,clwb,cmov,crc32,cx16,cx8,ermsb,evex512,f16c,false-deps-popcnt,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,faster-shift-than-shuffle,fma,fsgsbase,fxsr,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pku,popcnt,prefer-256-bit,prfchw,rdrnd,rdseed,sahf,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("skylake_avx512"), str_lit("32bit-mode,64bit,adx,aes,allow-light-256-bit,avx,avx2,avx512bw,avx512cd,avx512dq,avx512f,avx512vl,bmi,bmi2,clflushopt,clwb,cmov,crc32,cx16,cx8,ermsb,evex512,f16c,false-deps-popcnt,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,faster-shift-than-shuffle,fma,fsgsbase,fxsr,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pku,popcnt,prefer-256-bit,prfchw,rdrnd,rdseed,sahf,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("slm"), str_lit("32bit-mode,64bit,cmov,crc32,cx16,cx8,false-deps-popcnt,fast-7bytenop,fast-imm16,fast-movbe,fxsr,idivq-to-divl,mmx,movbe,no-bypass-delay,nopl,pclmul,popcnt,prfchw,rdrnd,sahf,slow-incdec,slow-lea,slow-pmulld,slow-two-mem-ops,sse,sse2,sse3,sse4.1,sse4.2,ssse3,use-slm-arith-costs,vzeroupper,x87") },
+ { str_lit("tigerlake"), str_lit("32bit-mode,64bit,adx,aes,allow-light-256-bit,avx,avx2,avx512bitalg,avx512bw,avx512cd,avx512dq,avx512f,avx512ifma,avx512vbmi,avx512vbmi2,avx512vl,avx512vnni,avx512vp2intersect,avx512vpopcntdq,bmi,bmi2,clflushopt,clwb,cmov,crc32,cx16,cx8,ermsb,evex512,f16c,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fsrm,fxsr,gfni,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pku,popcnt,prefer-256-bit,prfchw,rdpid,rdrnd,rdseed,sahf,sha,shstk,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,vaes,vpclmulqdq,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("tremont"), str_lit("32bit-mode,64bit,aes,clflushopt,clwb,cmov,crc32,cx16,cx8,fast-imm16,fast-movbe,fsgsbase,fxsr,gfni,mmx,movbe,no-bypass-delay,nopl,pclmul,popcnt,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,sha,slow-incdec,slow-lea,slow-two-mem-ops,sse,sse2,sse3,sse4.1,sse4.2,ssse3,use-glm-div-sqrt-costs,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("westmere"), str_lit("32bit-mode,64bit,cmov,crc32,cx16,cx8,fxsr,idivq-to-divl,macrofusion,mmx,no-bypass-delay-mov,nopl,pclmul,popcnt,sahf,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87") },
+ { str_lit("winchip-c6"), str_lit("32bit-mode,mmx,slow-unaligned-mem-16,vzeroupper,x87") },
+ { str_lit("winchip2"), str_lit("32bit-mode,mmx,prfchw,slow-unaligned-mem-16,vzeroupper,x87") },
+ { str_lit("x86-64"), str_lit("32bit-mode,64bit,cmov,cx8,fxsr,idivq-to-divl,macrofusion,mmx,nopl,slow-3ops-lea,slow-incdec,sse,sse2,vzeroupper,x87") },
+ { str_lit("x86-64-v2"), str_lit("32bit-mode,64bit,cmov,crc32,cx16,cx8,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-shld-rotate,fxsr,idivq-to-divl,macrofusion,mmx,nopl,popcnt,sahf,slow-3ops-lea,slow-unaligned-mem-32,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87") },
+ { str_lit("x86-64-v3"), str_lit("32bit-mode,64bit,allow-light-256-bit,avx,avx2,bmi,bmi2,cmov,crc32,cx16,cx8,f16c,false-deps-lzcnt-tzcnt,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fma,fxsr,idivq-to-divl,lzcnt,macrofusion,mmx,movbe,nopl,popcnt,sahf,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87,xsave") },
+ { str_lit("x86-64-v4"), str_lit("32bit-mode,64bit,allow-light-256-bit,avx,avx2,avx512bw,avx512cd,avx512dq,avx512f,avx512vl,bmi,bmi2,cmov,crc32,cx16,cx8,evex512,f16c,false-deps-popcnt,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fxsr,idivq-to-divl,lzcnt,macrofusion,mmx,movbe,nopl,popcnt,prefer-256-bit,sahf,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87,xsave") },
+ { str_lit("yonah"), str_lit("32bit-mode,cmov,cx8,fxsr,mmx,nopl,slow-unaligned-mem-16,sse,sse2,sse3,vzeroupper,x87") },
+ { str_lit("znver1"), str_lit("32bit-mode,64bit,adx,aes,allow-light-256-bit,avx,avx2,bmi,bmi2,branchfusion,clflushopt,clzero,cmov,crc32,cx16,cx8,f16c,fast-15bytenop,fast-bextr,fast-imm16,fast-lzcnt,fast-movbe,fast-scalar-fsqrt,fast-scalar-shift-masks,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fxsr,idivq-to-divl,lzcnt,mmx,movbe,mwaitx,nopl,pclmul,popcnt,prfchw,rdrnd,rdseed,sahf,sbb-dep-breaking,sha,slow-shld,sse,sse2,sse3,sse4.1,sse4.2,sse4a,ssse3,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("znver2"), str_lit("32bit-mode,64bit,adx,aes,allow-light-256-bit,avx,avx2,bmi,bmi2,branchfusion,clflushopt,clwb,clzero,cmov,crc32,cx16,cx8,f16c,fast-15bytenop,fast-bextr,fast-imm16,fast-lzcnt,fast-movbe,fast-scalar-fsqrt,fast-scalar-shift-masks,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fxsr,idivq-to-divl,lzcnt,mmx,movbe,mwaitx,nopl,pclmul,popcnt,prfchw,rdpid,rdpru,rdrnd,rdseed,sahf,sbb-dep-breaking,sha,slow-shld,sse,sse2,sse3,sse4.1,sse4.2,sse4a,ssse3,vzeroupper,wbnoinvd,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("znver3"), str_lit("32bit-mode,64bit,adx,aes,allow-light-256-bit,avx,avx2,bmi,bmi2,branchfusion,clflushopt,clwb,clzero,cmov,crc32,cx16,cx8,f16c,fast-15bytenop,fast-bextr,fast-imm16,fast-lzcnt,fast-movbe,fast-scalar-fsqrt,fast-scalar-shift-masks,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fsrm,fxsr,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,mwaitx,nopl,pclmul,pku,popcnt,prfchw,rdpid,rdpru,rdrnd,rdseed,sahf,sbb-dep-breaking,sha,slow-shld,sse,sse2,sse3,sse4.1,sse4.2,sse4a,ssse3,vaes,vpclmulqdq,vzeroupper,wbnoinvd,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("znver4"), str_lit("32bit-mode,64bit,adx,aes,allow-light-256-bit,avx,avx2,avx512bf16,avx512bitalg,avx512bw,avx512cd,avx512dq,avx512f,avx512ifma,avx512vbmi,avx512vbmi2,avx512vl,avx512vnni,avx512vpopcntdq,bmi,bmi2,branchfusion,clflushopt,clwb,clzero,cmov,crc32,cx16,cx8,evex512,f16c,fast-15bytenop,fast-bextr,fast-dpwssd,fast-imm16,fast-lzcnt,fast-movbe,fast-scalar-fsqrt,fast-scalar-shift-masks,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fsrm,fxsr,gfni,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,mwaitx,nopl,pclmul,pku,popcnt,prfchw,rdpid,rdpru,rdrnd,rdseed,sahf,sbb-dep-breaking,sha,shstk,slow-shld,sse,sse2,sse3,sse4.1,sse4.2,sse4a,ssse3,vaes,vpclmulqdq,vzeroupper,wbnoinvd,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("znver5"), str_lit("32bit-mode,64bit,adx,aes,allow-light-256-bit,avx,avx2,avx512bf16,avx512bitalg,avx512bw,avx512cd,avx512dq,avx512f,avx512ifma,avx512vbmi,avx512vbmi2,avx512vl,avx512vnni,avx512vp2intersect,avx512vpopcntdq,avxvnni,bmi,bmi2,branchfusion,clflushopt,clwb,clzero,cmov,crc32,cx16,cx8,evex512,f16c,fast-15bytenop,fast-bextr,fast-dpwssd,fast-imm16,fast-lzcnt,fast-movbe,fast-scalar-fsqrt,fast-scalar-shift-masks,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fsrm,fxsr,gfni,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,mwaitx,nopl,pclmul,pku,popcnt,prefetchi,prfchw,rdpid,rdpru,rdrnd,rdseed,sahf,sbb-dep-breaking,sha,shstk,slow-shld,sse,sse2,sse3,sse4.1,sse4.2,sse4a,ssse3,vaes,vpclmulqdq,vzeroupper,wbnoinvd,x87,xsave,xsavec,xsaveopt,xsaves") },
+ // TargetArch_arm32:
+ { str_lit("arm1020e"), str_lit("armv5te,v4t,v5t,v5te") },
+ { str_lit("arm1020t"), str_lit("armv5t,v4t,v5t") },
+ { str_lit("arm1022e"), str_lit("armv5te,v4t,v5t,v5te") },
+ { str_lit("arm10e"), str_lit("armv5te,v4t,v5t,v5te") },
+ { str_lit("arm10tdmi"), str_lit("armv5t,v4t,v5t") },
+ { str_lit("arm1136j-s"), str_lit("armv6,dsp,v4t,v5t,v5te,v6") },
+ { str_lit("arm1136jf-s"), str_lit("armv6,dsp,fp64,fpregs,fpregs64,slowfpvmlx,v4t,v5t,v5te,v6,vfp2,vfp2sp") },
+ { str_lit("arm1156t2-s"), str_lit("armv6t2,dsp,thumb2,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v8m") },
+ { str_lit("arm1156t2f-s"), str_lit("armv6t2,dsp,fp64,fpregs,fpregs64,slowfpvmlx,thumb2,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v8m,vfp2,vfp2sp") },
+ { str_lit("arm1176jz-s"), str_lit("armv6kz,trustzone,v4t,v5t,v5te,v6,v6k") },
+ { str_lit("arm1176jzf-s"), str_lit("armv6kz,fp64,fpregs,fpregs64,slowfpvmlx,trustzone,v4t,v5t,v5te,v6,v6k,vfp2,vfp2sp") },
+ { str_lit("arm710t"), str_lit("armv4t,v4t") },
+ { str_lit("arm720t"), str_lit("armv4t,v4t") },
+ { str_lit("arm7tdmi"), str_lit("armv4t,v4t") },
+ { str_lit("arm7tdmi-s"), str_lit("armv4t,v4t") },
+ { str_lit("arm8"), str_lit("armv4") },
+ { str_lit("arm810"), str_lit("armv4") },
+ { str_lit("arm9"), str_lit("armv4t,v4t") },
+ { str_lit("arm920"), str_lit("armv4t,v4t") },
+ { str_lit("arm920t"), str_lit("armv4t,v4t") },
+ { str_lit("arm922t"), str_lit("armv4t,v4t") },
+ { str_lit("arm926ej-s"), str_lit("armv5te,v4t,v5t,v5te") },
+ { str_lit("arm940t"), str_lit("armv4t,v4t") },
+ { str_lit("arm946e-s"), str_lit("armv5te,v4t,v5t,v5te") },
+ { str_lit("arm966e-s"), str_lit("armv5te,v4t,v5t,v5te") },
+ { str_lit("arm968e-s"), str_lit("armv5te,v4t,v5t,v5te") },
+ { str_lit("arm9e"), str_lit("armv5te,v4t,v5t,v5te") },
+ { str_lit("arm9tdmi"), str_lit("armv4t,v4t") },
+ { str_lit("cortex-a12"), str_lit("a12,aclass,armv7-a,avoid-partial-cpsr,d32,db,dsp,fp16,fp64,fpregs,fpregs64,hwdiv,hwdiv-arm,mp,neon,perfmon,ret-addr-stack,thumb2,trustzone,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8m,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,virtualization,vmlx-forwarding") },
+ { str_lit("cortex-a15"), str_lit("a15,aclass,armv7-a,avoid-partial-cpsr,d32,db,dont-widen-vmovs,dsp,fp16,fp64,fpregs,fpregs64,hwdiv,hwdiv-arm,mp,muxed-units,neon,perfmon,ret-addr-stack,splat-vfp-neon,thumb2,trustzone,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8m,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,virtualization,vldn-align") },
+ { str_lit("cortex-a17"), str_lit("a17,aclass,armv7-a,avoid-partial-cpsr,d32,db,dsp,fp16,fp64,fpregs,fpregs64,hwdiv,hwdiv-arm,mp,neon,perfmon,ret-addr-stack,thumb2,trustzone,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8m,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,virtualization,vmlx-forwarding") },
+ { str_lit("cortex-a32"), str_lit("aclass,acquire-release,aes,armv8-a,crc,crypto,d32,db,dsp,fp-armv8,fp-armv8d16,fp-armv8d16sp,fp-armv8sp,fp16,fp64,fpregs,fpregs64,hwdiv,hwdiv-arm,mp,neon,perfmon,sha2,thumb2,trustzone,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8,v8m,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,virtualization") },
+ { str_lit("cortex-a35"), str_lit("a35,aclass,acquire-release,aes,armv8-a,crc,crypto,d32,db,dsp,fp-armv8,fp-armv8d16,fp-armv8d16sp,fp-armv8sp,fp16,fp64,fpregs,fpregs64,hwdiv,hwdiv-arm,mp,neon,perfmon,sha2,thumb2,trustzone,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8,v8m,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,virtualization") },
+ { str_lit("cortex-a5"), str_lit("a5,aclass,armv7-a,d32,db,dsp,fp16,fp64,fpregs,fpregs64,mp,neon,perfmon,ret-addr-stack,slow-fp-brcc,slowfpvfmx,slowfpvmlx,thumb2,trustzone,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8m,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,vmlx-forwarding") },
+ { str_lit("cortex-a510"), str_lit("aclass,acquire-release,armv9-a,bf16,cortex-a710,crc,d32,db,dotprod,dsp,fp-armv8,fp-armv8d16,fp-armv8d16sp,fp-armv8sp,fp16,fp16fml,fp64,fpregs,fpregs16,fpregs64,fullfp16,hwdiv,hwdiv-arm,i8mm,mp,neon,perfmon,ras,sb,thumb2,trustzone,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8,v8.1a,v8.2a,v8.3a,v8.4a,v8.5a,v8m,v9a,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,virtualization") },
+ { str_lit("cortex-a53"), str_lit("a53,aclass,acquire-release,aes,armv8-a,crc,crypto,d32,db,dsp,fp-armv8,fp-armv8d16,fp-armv8d16sp,fp-armv8sp,fp16,fp64,fpao,fpregs,fpregs64,hwdiv,hwdiv-arm,mp,neon,perfmon,sha2,thumb2,trustzone,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8,v8m,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,virtualization") },
+ { str_lit("cortex-a55"), str_lit("a55,aclass,acquire-release,aes,armv8.2-a,crc,crypto,d32,db,dotprod,dsp,fp-armv8,fp-armv8d16,fp-armv8d16sp,fp-armv8sp,fp16,fp64,fpregs,fpregs64,hwdiv,hwdiv-arm,mp,neon,perfmon,ras,sha2,thumb2,trustzone,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8,v8.1a,v8.2a,v8m,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,virtualization") },
+ { str_lit("cortex-a57"), str_lit("a57,aclass,acquire-release,aes,armv8-a,avoid-partial-cpsr,cheap-predicable-cpsr,crc,crypto,d32,db,dsp,fix-cortex-a57-aes-1742098,fp-armv8,fp-armv8d16,fp-armv8d16sp,fp-armv8sp,fp16,fp64,fpao,fpregs,fpregs64,hwdiv,hwdiv-arm,mp,neon,perfmon,sha2,thumb2,trustzone,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8,v8m,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,virtualization") },
+ { str_lit("cortex-a7"), str_lit("a7,aclass,armv7-a,d32,db,dsp,fp16,fp64,fpregs,fpregs64,hwdiv,hwdiv-arm,mp,neon,perfmon,ret-addr-stack,slow-fp-brcc,slowfpvfmx,slowfpvmlx,thumb2,trustzone,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8m,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,virtualization,vmlx-forwarding,vmlx-hazards") },
+ { str_lit("cortex-a710"), str_lit("aclass,acquire-release,armv9-a,bf16,cortex-a710,crc,d32,db,dotprod,dsp,fp-armv8,fp-armv8d16,fp-armv8d16sp,fp-armv8sp,fp16,fp16fml,fp64,fpregs,fpregs16,fpregs64,fullfp16,hwdiv,hwdiv-arm,i8mm,mp,neon,perfmon,ras,sb,thumb2,trustzone,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8,v8.1a,v8.2a,v8.3a,v8.4a,v8.5a,v8m,v9a,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,virtualization") },
+ { str_lit("cortex-a72"), str_lit("a72,aclass,acquire-release,aes,armv8-a,crc,crypto,d32,db,dsp,fix-cortex-a57-aes-1742098,fp-armv8,fp-armv8d16,fp-armv8d16sp,fp-armv8sp,fp16,fp64,fpregs,fpregs64,hwdiv,hwdiv-arm,mp,neon,perfmon,sha2,thumb2,trustzone,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8,v8m,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,virtualization") },
+ { str_lit("cortex-a73"), str_lit("a73,aclass,acquire-release,aes,armv8-a,crc,crypto,d32,db,dsp,fp-armv8,fp-armv8d16,fp-armv8d16sp,fp-armv8sp,fp16,fp64,fpregs,fpregs64,hwdiv,hwdiv-arm,mp,neon,perfmon,sha2,thumb2,trustzone,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8,v8m,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,virtualization") },
+ { str_lit("cortex-a75"), str_lit("a75,aclass,acquire-release,aes,armv8.2-a,crc,crypto,d32,db,dotprod,dsp,fp-armv8,fp-armv8d16,fp-armv8d16sp,fp-armv8sp,fp16,fp64,fpregs,fpregs64,hwdiv,hwdiv-arm,mp,neon,perfmon,ras,sha2,thumb2,trustzone,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8,v8.1a,v8.2a,v8m,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,virtualization") },
+ { str_lit("cortex-a76"), str_lit("a76,aclass,acquire-release,aes,armv8.2-a,crc,crypto,d32,db,dotprod,dsp,fp-armv8,fp-armv8d16,fp-armv8d16sp,fp-armv8sp,fp16,fp64,fpregs,fpregs16,fpregs64,fullfp16,hwdiv,hwdiv-arm,mp,neon,perfmon,ras,sha2,thumb2,trustzone,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8,v8.1a,v8.2a,v8m,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,virtualization") },
+ { str_lit("cortex-a76ae"), str_lit("a76,aclass,acquire-release,aes,armv8.2-a,crc,crypto,d32,db,dotprod,dsp,fp-armv8,fp-armv8d16,fp-armv8d16sp,fp-armv8sp,fp16,fp64,fpregs,fpregs16,fpregs64,fullfp16,hwdiv,hwdiv-arm,mp,neon,perfmon,ras,sha2,thumb2,trustzone,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8,v8.1a,v8.2a,v8m,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,virtualization") },
+ { str_lit("cortex-a77"), str_lit("a77,aclass,acquire-release,aes,armv8.2-a,crc,crypto,d32,db,dotprod,dsp,fp-armv8,fp-armv8d16,fp-armv8d16sp,fp-armv8sp,fp16,fp64,fpregs,fpregs16,fpregs64,fullfp16,hwdiv,hwdiv-arm,mp,neon,perfmon,ras,sha2,thumb2,trustzone,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8,v8.1a,v8.2a,v8m,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,virtualization") },
+ { str_lit("cortex-a78"), str_lit("aclass,acquire-release,aes,armv8.2-a,cortex-a78,crc,crypto,d32,db,dotprod,dsp,fp-armv8,fp-armv8d16,fp-armv8d16sp,fp-armv8sp,fp16,fp64,fpregs,fpregs16,fpregs64,fullfp16,hwdiv,hwdiv-arm,mp,neon,perfmon,ras,sha2,thumb2,trustzone,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8,v8.1a,v8.2a,v8m,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,virtualization") },
+ { str_lit("cortex-a78ae"), str_lit("aclass,acquire-release,aes,armv8.2-a,cortex-a78ae,crc,crypto,d32,db,dotprod,dsp,fp-armv8,fp-armv8d16,fp-armv8d16sp,fp-armv8sp,fp16,fp64,fpregs,fpregs16,fpregs64,fullfp16,hwdiv,hwdiv-arm,mp,neon,perfmon,ras,sha2,thumb2,trustzone,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8,v8.1a,v8.2a,v8m,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,virtualization") },
+ { str_lit("cortex-a78c"), str_lit("a78c,aclass,acquire-release,aes,armv8.2-a,crc,crypto,d32,db,dotprod,dsp,fp-armv8,fp-armv8d16,fp-armv8d16sp,fp-armv8sp,fp16,fp64,fpregs,fpregs16,fpregs64,fullfp16,hwdiv,hwdiv-arm,mp,neon,perfmon,ras,sha2,thumb2,trustzone,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8,v8.1a,v8.2a,v8m,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,virtualization") },
+ { str_lit("cortex-a8"), str_lit("a8,aclass,armv7-a,d32,db,dsp,fp64,fpregs,fpregs64,neon,nonpipelined-vfp,perfmon,ret-addr-stack,slow-fp-brcc,slowfpvfmx,slowfpvmlx,thumb2,trustzone,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8m,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vmlx-forwarding,vmlx-hazards") },
+ { str_lit("cortex-a9"), str_lit("a9,aclass,armv7-a,avoid-partial-cpsr,d32,db,dsp,expand-fp-mlx,fp16,fp64,fpregs,fpregs64,mp,muxed-units,neon,neon-fpmovs,perfmon,prefer-vmovsr,ret-addr-stack,thumb2,trustzone,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8m,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vldn-align,vmlx-forwarding,vmlx-hazards") },
+ { str_lit("cortex-m0"), str_lit("armv6-m,db,mclass,no-branch-predictor,noarm,strict-align,thumb-mode,v4t,v5t,v5te,v6,v6m") },
+ { str_lit("cortex-m0plus"), str_lit("armv6-m,db,mclass,no-branch-predictor,noarm,strict-align,thumb-mode,v4t,v5t,v5te,v6,v6m") },
+ { str_lit("cortex-m1"), str_lit("armv6-m,db,mclass,no-branch-predictor,noarm,strict-align,thumb-mode,v4t,v5t,v5te,v6,v6m") },
+ { str_lit("cortex-m23"), str_lit("8msecext,acquire-release,armv8-m.base,db,hwdiv,mclass,no-branch-predictor,no-movt,noarm,strict-align,thumb-mode,v4t,v5t,v5te,v6,v6m,v7clrex,v8m") },
+ { str_lit("cortex-m3"), str_lit("armv7-m,db,hwdiv,loop-align,m3,mclass,no-branch-predictor,noarm,thumb-mode,thumb2,use-misched,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8m") },
+ { str_lit("cortex-m33"), str_lit("8msecext,acquire-release,armv8-m.main,avoid-muls,db,dsp,fix-cmse-cve-2021-35465,fp-armv8d16sp,fp16,fpregs,hwdiv,loop-align,mclass,no-branch-predictor,noarm,slowfpvfmx,slowfpvmlx,thumb-mode,thumb2,use-misched,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8m,v8m.main,vfp2sp,vfp3d16sp,vfp4d16sp") },
+ { str_lit("cortex-m35p"), str_lit("8msecext,acquire-release,armv8-m.main,db,dsp,fix-cmse-cve-2021-35465,fp-armv8d16sp,fp16,fpregs,hwdiv,loop-align,mclass,no-branch-predictor,noarm,slowfpvfmx,slowfpvmlx,thumb-mode,thumb2,use-misched,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8m,v8m.main,vfp2sp,vfp3d16sp,vfp4d16sp") },
+ { str_lit("cortex-m4"), str_lit("armv7e-m,db,dsp,fp16,fpregs,hwdiv,loop-align,mclass,no-branch-predictor,noarm,slowfpvfmx,slowfpvmlx,thumb-mode,thumb2,use-misched,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8m,vfp2sp,vfp3d16sp,vfp4d16sp") },
+ { str_lit("cortex-m52"), str_lit("8msecext,acquire-release,armv8.1-m.main,db,dsp,fp-armv8d16,fp-armv8d16sp,fp16,fp64,fpregs,fpregs16,fpregs64,fullfp16,hwdiv,lob,loop-align,mclass,mve,mve.fp,mve1beat,no-branch-predictor,noarm,pacbti,ras,slowfpvmlx,thumb-mode,thumb2,use-misched,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8.1m.main,v8m,v8m.main,vfp2,vfp2sp,vfp3d16,vfp3d16sp,vfp4d16,vfp4d16sp") },
+ { str_lit("cortex-m55"), str_lit("8msecext,acquire-release,armv8.1-m.main,db,dsp,fix-cmse-cve-2021-35465,fp-armv8d16,fp-armv8d16sp,fp16,fp64,fpregs,fpregs16,fpregs64,fullfp16,hwdiv,lob,loop-align,m55,mclass,mve,mve.fp,no-branch-predictor,noarm,ras,slowfpvmlx,thumb-mode,thumb2,use-misched,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8.1m.main,v8m,v8m.main,vfp2,vfp2sp,vfp3d16,vfp3d16sp,vfp4d16,vfp4d16sp") },
+ { str_lit("cortex-m7"), str_lit("armv7e-m,branch-align-64,db,dsp,fp-armv8d16,fp-armv8d16sp,fp16,fp64,fpregs,fpregs64,hwdiv,m7,mclass,noarm,thumb-mode,thumb2,use-mipipeliner,use-misched,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8m,vfp2,vfp2sp,vfp3d16,vfp3d16sp,vfp4d16,vfp4d16sp") },
+ { str_lit("cortex-m85"), str_lit("8msecext,acquire-release,armv8.1-m.main,branch-align-64,db,dsp,fp-armv8d16,fp-armv8d16sp,fp16,fp64,fpregs,fpregs16,fpregs64,fullfp16,hwdiv,lob,m85,mclass,mve,mve.fp,noarm,pacbti,ras,thumb-mode,thumb2,use-misched,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8.1m.main,v8m,v8m.main,vfp2,vfp2sp,vfp3d16,vfp3d16sp,vfp4d16,vfp4d16sp") },
+ { str_lit("cortex-r4"), str_lit("armv7-r,avoid-partial-cpsr,db,dsp,hwdiv,perfmon,r4,rclass,ret-addr-stack,thumb2,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8m") },
+ { str_lit("cortex-r4f"), str_lit("armv7-r,avoid-partial-cpsr,db,dsp,fp64,fpregs,fpregs64,hwdiv,perfmon,r4,rclass,ret-addr-stack,slow-fp-brcc,slowfpvfmx,slowfpvmlx,thumb2,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8m,vfp2,vfp2sp,vfp3d16,vfp3d16sp") },
+ { str_lit("cortex-r5"), str_lit("armv7-r,avoid-partial-cpsr,db,dsp,fp64,fpregs,fpregs64,hwdiv,hwdiv-arm,perfmon,r5,rclass,ret-addr-stack,slow-fp-brcc,slowfpvfmx,slowfpvmlx,thumb2,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8m,vfp2,vfp2sp,vfp3d16,vfp3d16sp") },
+ { str_lit("cortex-r52"), str_lit("acquire-release,armv8-r,crc,d32,db,dfb,dsp,fp-armv8,fp-armv8d16,fp-armv8d16sp,fp-armv8sp,fp16,fp64,fpao,fpregs,fpregs64,hwdiv,hwdiv-arm,mp,neon,perfmon,r52,rclass,thumb2,use-misched,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8,v8m,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,virtualization") },
+ { str_lit("cortex-r52plus"), str_lit("acquire-release,armv8-r,crc,d32,db,dfb,dsp,fp-armv8,fp-armv8d16,fp-armv8d16sp,fp-armv8sp,fp16,fp64,fpao,fpregs,fpregs64,hwdiv,hwdiv-arm,mp,neon,perfmon,r52plus,rclass,thumb2,use-misched,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8,v8m,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,virtualization") },
+ { str_lit("cortex-r7"), str_lit("armv7-r,avoid-partial-cpsr,db,dsp,fp16,fp64,fpregs,fpregs64,hwdiv,hwdiv-arm,mp,perfmon,r7,rclass,ret-addr-stack,slow-fp-brcc,slowfpvfmx,slowfpvmlx,thumb2,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8m,vfp2,vfp2sp,vfp3d16,vfp3d16sp") },
+ { str_lit("cortex-r8"), str_lit("armv7-r,avoid-partial-cpsr,db,dsp,fp16,fp64,fpregs,fpregs64,hwdiv,hwdiv-arm,mp,perfmon,rclass,ret-addr-stack,slow-fp-brcc,slowfpvfmx,slowfpvmlx,thumb2,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8m,vfp2,vfp2sp,vfp3d16,vfp3d16sp") },
+ { str_lit("cortex-x1"), str_lit("aclass,acquire-release,aes,armv8.2-a,cortex-x1,crc,crypto,d32,db,dotprod,dsp,fp-armv8,fp-armv8d16,fp-armv8d16sp,fp-armv8sp,fp16,fp64,fpregs,fpregs16,fpregs64,fullfp16,hwdiv,hwdiv-arm,mp,neon,perfmon,ras,sha2,thumb2,trustzone,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8,v8.1a,v8.2a,v8m,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,virtualization") },
+ { str_lit("cortex-x1c"), str_lit("aclass,acquire-release,aes,armv8.2-a,cortex-x1c,crc,crypto,d32,db,dotprod,dsp,fp-armv8,fp-armv8d16,fp-armv8d16sp,fp-armv8sp,fp16,fp64,fpregs,fpregs16,fpregs64,fullfp16,hwdiv,hwdiv-arm,mp,neon,perfmon,ras,sha2,thumb2,trustzone,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8,v8.1a,v8.2a,v8m,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,virtualization") },
+ { str_lit("cyclone"), str_lit("aclass,acquire-release,aes,armv8-a,avoid-movs-shop,avoid-partial-cpsr,crc,crypto,d32,db,disable-postra-scheduler,dsp,fp-armv8,fp-armv8d16,fp-armv8d16sp,fp-armv8sp,fp16,fp64,fpregs,fpregs64,hwdiv,hwdiv-arm,mp,neon,neonfp,perfmon,ret-addr-stack,sha2,slowfpvfmx,slowfpvmlx,swift,thumb2,trustzone,use-misched,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8,v8m,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,virtualization,zcz") },
+ { str_lit("ep9312"), str_lit("armv4t,v4t") },
+ { str_lit("exynos-m3"), str_lit("aclass,acquire-release,aes,armv8-a,crc,crypto,d32,db,dont-widen-vmovs,dsp,expand-fp-mlx,exynos,fp-armv8,fp-armv8d16,fp-armv8d16sp,fp-armv8sp,fp16,fp64,fpregs,fpregs64,fuse-aes,fuse-literals,hwdiv,hwdiv-arm,mp,neon,perfmon,prof-unpr,ret-addr-stack,sha2,slow-fp-brcc,slow-vdup32,slow-vgetlni32,slowfpvfmx,slowfpvmlx,splat-vfp-neon,thumb2,trustzone,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8,v8m,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,virtualization,wide-stride-vfp,zcz") },
+ { str_lit("exynos-m4"), str_lit("aclass,acquire-release,aes,armv8.2-a,crc,crypto,d32,db,dont-widen-vmovs,dotprod,dsp,expand-fp-mlx,exynos,fp-armv8,fp-armv8d16,fp-armv8d16sp,fp-armv8sp,fp16,fp64,fpregs,fpregs16,fpregs64,fullfp16,fuse-aes,fuse-literals,hwdiv,hwdiv-arm,mp,neon,perfmon,prof-unpr,ras,ret-addr-stack,sha2,slow-fp-brcc,slow-vdup32,slow-vgetlni32,slowfpvfmx,slowfpvmlx,splat-vfp-neon,thumb2,trustzone,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8,v8.1a,v8.2a,v8m,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,virtualization,wide-stride-vfp,zcz") },
+ { str_lit("exynos-m5"), str_lit("aclass,acquire-release,aes,armv8.2-a,crc,crypto,d32,db,dont-widen-vmovs,dotprod,dsp,expand-fp-mlx,exynos,fp-armv8,fp-armv8d16,fp-armv8d16sp,fp-armv8sp,fp16,fp64,fpregs,fpregs16,fpregs64,fullfp16,fuse-aes,fuse-literals,hwdiv,hwdiv-arm,mp,neon,perfmon,prof-unpr,ras,ret-addr-stack,sha2,slow-fp-brcc,slow-vdup32,slow-vgetlni32,slowfpvfmx,slowfpvmlx,splat-vfp-neon,thumb2,trustzone,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8,v8.1a,v8.2a,v8m,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,virtualization,wide-stride-vfp,zcz") },
+ { str_lit("generic"), str_lit("") },
+ { str_lit("iwmmxt"), str_lit("armv5te,v4t,v5t,v5te") },
+ { str_lit("krait"), str_lit("aclass,armv7-a,avoid-partial-cpsr,d32,db,dsp,fp16,fp64,fpregs,fpregs64,hwdiv,hwdiv-arm,krait,muxed-units,neon,perfmon,ret-addr-stack,thumb2,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8m,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,vldn-align,vmlx-forwarding") },
+ { str_lit("kryo"), str_lit("aclass,acquire-release,aes,armv8-a,crc,crypto,d32,db,dsp,fp-armv8,fp-armv8d16,fp-armv8d16sp,fp-armv8sp,fp16,fp64,fpregs,fpregs64,hwdiv,hwdiv-arm,kryo,mp,neon,perfmon,sha2,thumb2,trustzone,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8,v8m,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,virtualization") },
+ { str_lit("mpcore"), str_lit("armv6k,fp64,fpregs,fpregs64,slowfpvmlx,v4t,v5t,v5te,v6,v6k,vfp2,vfp2sp") },
+ { str_lit("mpcorenovfp"), str_lit("armv6k,v4t,v5t,v5te,v6,v6k") },
+ { str_lit("neoverse-n1"), str_lit("aclass,acquire-release,aes,armv8.2-a,crc,crypto,d32,db,dotprod,dsp,fp-armv8,fp-armv8d16,fp-armv8d16sp,fp-armv8sp,fp16,fp64,fpregs,fpregs64,hwdiv,hwdiv-arm,mp,neon,perfmon,ras,sha2,thumb2,trustzone,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8,v8.1a,v8.2a,v8m,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,virtualization") },
+ { str_lit("neoverse-n2"), str_lit("aclass,acquire-release,armv9-a,bf16,crc,d32,db,dotprod,dsp,fp-armv8,fp-armv8d16,fp-armv8d16sp,fp-armv8sp,fp16,fp16fml,fp64,fpregs,fpregs16,fpregs64,fullfp16,hwdiv,hwdiv-arm,i8mm,mp,neon,perfmon,ras,sb,thumb2,trustzone,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8,v8.1a,v8.2a,v8.3a,v8.4a,v8.5a,v8m,v9a,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,virtualization") },
+ { str_lit("neoverse-v1"), str_lit("aclass,acquire-release,aes,armv8.4-a,bf16,crc,crypto,d32,db,dotprod,dsp,fp-armv8,fp-armv8d16,fp-armv8d16sp,fp-armv8sp,fp16,fp64,fpregs,fpregs16,fpregs64,fullfp16,hwdiv,hwdiv-arm,i8mm,mp,neon,perfmon,ras,sha2,thumb2,trustzone,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8,v8.1a,v8.2a,v8.3a,v8.4a,v8m,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,virtualization") },
+ { str_lit("sc000"), str_lit("armv6-m,db,mclass,no-branch-predictor,noarm,strict-align,thumb-mode,v4t,v5t,v5te,v6,v6m") },
+ { str_lit("sc300"), str_lit("armv7-m,db,hwdiv,m3,mclass,no-branch-predictor,noarm,thumb-mode,thumb2,use-misched,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8m") },
+ { str_lit("star-mc1"), str_lit("8msecext,acquire-release,armv8-m.main,db,dsp,fix-cmse-cve-2021-35465,fp-armv8d16sp,fp16,fpregs,hwdiv,loop-align,mclass,no-branch-predictor,noarm,slowfpvfmx,slowfpvmlx,thumb-mode,thumb2,use-misched,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8m,v8m.main,vfp2sp,vfp3d16sp,vfp4d16sp") },
+ { str_lit("strongarm"), str_lit("armv4") },
+ { str_lit("strongarm110"), str_lit("armv4") },
+ { str_lit("strongarm1100"), str_lit("armv4") },
+ { str_lit("strongarm1110"), str_lit("armv4") },
+ { str_lit("swift"), str_lit("aclass,armv7-a,avoid-movs-shop,avoid-partial-cpsr,d32,db,disable-postra-scheduler,dsp,fp16,fp64,fpregs,fpregs64,hwdiv,hwdiv-arm,mp,neon,neonfp,perfmon,prefer-ishst,prof-unpr,ret-addr-stack,slow-load-D-subreg,slow-odd-reg,slow-vdup32,slow-vgetlni32,slowfpvfmx,slowfpvmlx,swift,thumb2,use-misched,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8m,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,vmlx-hazards,wide-stride-vfp") },
+ { str_lit("xscale"), str_lit("armv5te,v4t,v5t,v5te") },
+ // TargetArch_arm64:
+ { str_lit("a64fx"), str_lit("CONTEXTIDREL2,a64fx,aes,aggressive-fma,arith-bcc-fusion,ccpp,complxnum,crc,el2vmsa,el3,fp-armv8,fullfp16,lor,lse,neon,pan,pan-rwv,perfmon,predictable-select-expensive,ras,rdm,sha2,store-pair-suppress,sve,uaops,use-postra-scheduler,v8.1a,v8.2a,v8a,vh") },
+ { str_lit("ampere1"), str_lit("CONTEXTIDREL2,aes,aggressive-fma,altnzcv,alu-lsl-fast,am,ampere1,amvs,arith-bcc-fusion,bf16,bti,ccdp,ccidx,ccpp,cmp-bcc-fusion,complxnum,crc,dit,dotprod,ecv,el2vmsa,el3,fgt,flagm,fp-armv8,fptoint,fullfp16,fuse-address,fuse-adrp-add,fuse-aes,fuse-literals,i8mm,jsconv,ldp-aligned-only,lor,lse,lse2,mpam,neon,nv,pan,pan-rwv,pauth,perfmon,predres,rand,ras,rcpc,rcpc-immo,rdm,sb,sel2,sha2,sha3,specrestrict,ssbs,store-pair-suppress,stp-aligned-only,tlb-rmi,tracev8.4,uaops,use-postra-scheduler,v8.1a,v8.2a,v8.3a,v8.4a,v8.5a,v8.6a,v8a,vh") },
+ { str_lit("ampere1a"), str_lit("CONTEXTIDREL2,aes,aggressive-fma,altnzcv,alu-lsl-fast,am,ampere1a,amvs,arith-bcc-fusion,bf16,bti,ccdp,ccidx,ccpp,cmp-bcc-fusion,complxnum,crc,dit,dotprod,ecv,el2vmsa,el3,fgt,flagm,fp-armv8,fptoint,fullfp16,fuse-address,fuse-addsub-2reg-const1,fuse-adrp-add,fuse-aes,fuse-literals,i8mm,jsconv,ldp-aligned-only,lor,lse,lse2,mpam,mte,neon,nv,pan,pan-rwv,pauth,perfmon,predres,rand,ras,rcpc,rcpc-immo,rdm,sb,sel2,sha2,sha3,sm4,specrestrict,ssbs,store-pair-suppress,stp-aligned-only,tlb-rmi,tracev8.4,uaops,use-postra-scheduler,v8.1a,v8.2a,v8.3a,v8.4a,v8.5a,v8.6a,v8a,vh") },
+ { str_lit("ampere1b"), str_lit("CONTEXTIDREL2,aes,aggressive-fma,altnzcv,alu-lsl-fast,am,ampere1b,amvs,arith-bcc-fusion,bf16,bti,ccdp,ccidx,ccpp,cmp-bcc-fusion,complxnum,crc,cssc,dit,dotprod,ecv,el2vmsa,el3,enable-select-opt,fgt,flagm,fp-armv8,fptoint,fullfp16,fuse-address,fuse-adrp-add,fuse-aes,fuse-literals,hcx,i8mm,jsconv,ldp-aligned-only,lor,lse,lse2,mpam,mte,neon,nv,pan,pan-rwv,pauth,perfmon,predictable-select-expensive,predres,rand,ras,rcpc,rcpc-immo,rdm,sb,sel2,sha2,sha3,sm4,specrestrict,ssbs,store-pair-suppress,stp-aligned-only,tlb-rmi,tracev8.4,uaops,use-postra-scheduler,v8.1a,v8.2a,v8.3a,v8.4a,v8.5a,v8.6a,v8.7a,v8a,vh,wfxt,xs") },
+ { str_lit("apple-a10"), str_lit("CONTEXTIDREL2,aes,alternate-sextload-cvt-f32-pattern,apple-a10,arith-bcc-fusion,arith-cbz-fusion,crc,disable-latency-sched-heuristic,el2vmsa,el3,fp-armv8,fuse-aes,fuse-crypto-eor,lor,neon,pan,perfmon,rdm,sha2,store-pair-suppress,v8a,vh,zcm,zcz,zcz-gp") },
+ { str_lit("apple-a11"), str_lit("CONTEXTIDREL2,aes,alternate-sextload-cvt-f32-pattern,apple-a11,arith-bcc-fusion,arith-cbz-fusion,ccpp,crc,disable-latency-sched-heuristic,el2vmsa,el3,fp-armv8,fullfp16,fuse-aes,fuse-crypto-eor,lor,lse,neon,pan,pan-rwv,perfmon,ras,rdm,sha2,store-pair-suppress,uaops,v8.1a,v8.2a,v8a,vh,zcm,zcz,zcz-gp") },
+ { str_lit("apple-a12"), str_lit("CONTEXTIDREL2,aes,alternate-sextload-cvt-f32-pattern,apple-a12,arith-bcc-fusion,arith-cbz-fusion,ccpp,complxnum,crc,disable-latency-sched-heuristic,el2vmsa,el3,fp-armv8,fullfp16,fuse-aes,fuse-crypto-eor,jsconv,lor,lse,neon,pan,pan-rwv,pauth,perfmon,ras,rcpc,rdm,sha2,store-pair-suppress,uaops,v8.1a,v8.2a,v8.3a,v8a,vh,zcm,zcz,zcz-gp") },
+ { str_lit("apple-a13"), str_lit("CONTEXTIDREL2,aes,alternate-sextload-cvt-f32-pattern,am,apple-a13,arith-bcc-fusion,arith-cbz-fusion,ccpp,complxnum,crc,disable-latency-sched-heuristic,dit,dotprod,el2vmsa,el3,flagm,fp-armv8,fp16fml,fullfp16,fuse-aes,fuse-crypto-eor,jsconv,lor,lse,lse2,mpam,neon,nv,pan,pan-rwv,pauth,perfmon,ras,rcpc,rcpc-immo,rdm,sel2,sha2,sha3,store-pair-suppress,tlb-rmi,tracev8.4,uaops,v8.1a,v8.2a,v8.3a,v8.4a,v8a,vh,zcm,zcz,zcz-gp") },
+ { str_lit("apple-a14"), str_lit("CONTEXTIDREL2,aes,aggressive-fma,alternate-sextload-cvt-f32-pattern,altnzcv,am,apple-a14,arith-bcc-fusion,arith-cbz-fusion,ccdp,ccpp,complxnum,crc,disable-latency-sched-heuristic,dit,dotprod,el2vmsa,el3,flagm,fp-armv8,fp16fml,fptoint,fullfp16,fuse-address,fuse-aes,fuse-arith-logic,fuse-crypto-eor,fuse-csel,fuse-literals,jsconv,lor,lse,lse2,mpam,neon,nv,pan,pan-rwv,pauth,perfmon,predres,ras,rcpc,rcpc-immo,rdm,sb,sel2,sha2,sha3,specrestrict,ssbs,store-pair-suppress,tlb-rmi,tracev8.4,uaops,v8.1a,v8.2a,v8.3a,v8.4a,v8a,vh,zcm,zcz,zcz-gp") },
+ { str_lit("apple-a15"), str_lit("CONTEXTIDREL2,aes,alternate-sextload-cvt-f32-pattern,altnzcv,am,amvs,apple-a15,arith-bcc-fusion,arith-cbz-fusion,bf16,bti,ccdp,ccpp,complxnum,crc,disable-latency-sched-heuristic,dit,dotprod,ecv,el2vmsa,el3,fgt,flagm,fp-armv8,fp16fml,fpac,fptoint,fullfp16,fuse-address,fuse-adrp-add,fuse-aes,fuse-arith-logic,fuse-crypto-eor,fuse-csel,fuse-literals,i8mm,jsconv,lor,lse,lse2,mpam,neon,nv,pan,pan-rwv,pauth,perfmon,predres,ras,rcpc,rcpc-immo,rdm,sb,sel2,sha2,sha3,specrestrict,ssbs,store-pair-suppress,tlb-rmi,tracev8.4,uaops,v8.1a,v8.2a,v8.3a,v8.4a,v8.5a,v8.6a,v8a,vh,zcm,zcz,zcz-gp") },
+ { str_lit("apple-a16"), str_lit("CONTEXTIDREL2,aes,alternate-sextload-cvt-f32-pattern,altnzcv,am,amvs,apple-a16,arith-bcc-fusion,arith-cbz-fusion,bf16,bti,ccdp,ccpp,complxnum,crc,disable-latency-sched-heuristic,dit,dotprod,ecv,el2vmsa,el3,fgt,flagm,fp-armv8,fp16fml,fpac,fptoint,fullfp16,fuse-address,fuse-adrp-add,fuse-aes,fuse-arith-logic,fuse-crypto-eor,fuse-csel,fuse-literals,hcx,i8mm,jsconv,lor,lse,lse2,mpam,neon,nv,pan,pan-rwv,pauth,perfmon,predres,ras,rcpc,rcpc-immo,rdm,sb,sel2,sha2,sha3,specrestrict,ssbs,store-pair-suppress,tlb-rmi,tracev8.4,uaops,v8.1a,v8.2a,v8.3a,v8.4a,v8.5a,v8.6a,v8a,vh,zcm,zcz,zcz-gp") },
+ { str_lit("apple-a17"), str_lit("CONTEXTIDREL2,aes,alternate-sextload-cvt-f32-pattern,altnzcv,am,amvs,apple-a17,arith-bcc-fusion,arith-cbz-fusion,bf16,bti,ccdp,ccpp,complxnum,crc,disable-latency-sched-heuristic,dit,dotprod,ecv,el2vmsa,el3,fgt,flagm,fp-armv8,fp16fml,fpac,fptoint,fullfp16,fuse-address,fuse-adrp-add,fuse-aes,fuse-arith-logic,fuse-crypto-eor,fuse-csel,fuse-literals,hcx,i8mm,jsconv,lor,lse,lse2,mpam,neon,nv,pan,pan-rwv,pauth,perfmon,predres,ras,rcpc,rcpc-immo,rdm,sb,sel2,sha2,sha3,specrestrict,ssbs,store-pair-suppress,tlb-rmi,tracev8.4,uaops,v8.1a,v8.2a,v8.3a,v8.4a,v8.5a,v8.6a,v8a,vh,zcm,zcz,zcz-gp") },
+ { str_lit("apple-a7"), str_lit("aes,alternate-sextload-cvt-f32-pattern,apple-a7,arith-bcc-fusion,arith-cbz-fusion,disable-latency-sched-heuristic,el2vmsa,el3,fp-armv8,fuse-aes,fuse-crypto-eor,neon,perfmon,sha2,store-pair-suppress,v8a,zcm,zcz,zcz-fp-workaround,zcz-gp") },
+ { str_lit("apple-a8"), str_lit("aes,alternate-sextload-cvt-f32-pattern,apple-a7,arith-bcc-fusion,arith-cbz-fusion,disable-latency-sched-heuristic,el2vmsa,el3,fp-armv8,fuse-aes,fuse-crypto-eor,neon,perfmon,sha2,store-pair-suppress,v8a,zcm,zcz,zcz-fp-workaround,zcz-gp") },
+ { str_lit("apple-a9"), str_lit("aes,alternate-sextload-cvt-f32-pattern,apple-a7,arith-bcc-fusion,arith-cbz-fusion,disable-latency-sched-heuristic,el2vmsa,el3,fp-armv8,fuse-aes,fuse-crypto-eor,neon,perfmon,sha2,store-pair-suppress,v8a,zcm,zcz,zcz-fp-workaround,zcz-gp") },
+ { str_lit("apple-m1"), str_lit("CONTEXTIDREL2,aes,aggressive-fma,alternate-sextload-cvt-f32-pattern,altnzcv,am,apple-a14,arith-bcc-fusion,arith-cbz-fusion,ccdp,ccpp,complxnum,crc,disable-latency-sched-heuristic,dit,dotprod,el2vmsa,el3,flagm,fp-armv8,fp16fml,fptoint,fullfp16,fuse-address,fuse-aes,fuse-arith-logic,fuse-crypto-eor,fuse-csel,fuse-literals,jsconv,lor,lse,lse2,mpam,neon,nv,pan,pan-rwv,pauth,perfmon,predres,ras,rcpc,rcpc-immo,rdm,sb,sel2,sha2,sha3,specrestrict,ssbs,store-pair-suppress,tlb-rmi,tracev8.4,uaops,v8.1a,v8.2a,v8.3a,v8.4a,v8a,vh,zcm,zcz,zcz-gp") },
+ { str_lit("apple-m2"), str_lit("CONTEXTIDREL2,aes,alternate-sextload-cvt-f32-pattern,altnzcv,am,amvs,apple-a15,arith-bcc-fusion,arith-cbz-fusion,bf16,bti,ccdp,ccpp,complxnum,crc,disable-latency-sched-heuristic,dit,dotprod,ecv,el2vmsa,el3,fgt,flagm,fp-armv8,fp16fml,fpac,fptoint,fullfp16,fuse-address,fuse-adrp-add,fuse-aes,fuse-arith-logic,fuse-crypto-eor,fuse-csel,fuse-literals,i8mm,jsconv,lor,lse,lse2,mpam,neon,nv,pan,pan-rwv,pauth,perfmon,predres,ras,rcpc,rcpc-immo,rdm,sb,sel2,sha2,sha3,specrestrict,ssbs,store-pair-suppress,tlb-rmi,tracev8.4,uaops,v8.1a,v8.2a,v8.3a,v8.4a,v8.5a,v8.6a,v8a,vh,zcm,zcz,zcz-gp") },
+ { str_lit("apple-m3"), str_lit("CONTEXTIDREL2,aes,alternate-sextload-cvt-f32-pattern,altnzcv,am,amvs,apple-a16,arith-bcc-fusion,arith-cbz-fusion,bf16,bti,ccdp,ccpp,complxnum,crc,disable-latency-sched-heuristic,dit,dotprod,ecv,el2vmsa,el3,fgt,flagm,fp-armv8,fp16fml,fpac,fptoint,fullfp16,fuse-address,fuse-adrp-add,fuse-aes,fuse-arith-logic,fuse-crypto-eor,fuse-csel,fuse-literals,hcx,i8mm,jsconv,lor,lse,lse2,mpam,neon,nv,pan,pan-rwv,pauth,perfmon,predres,ras,rcpc,rcpc-immo,rdm,sb,sel2,sha2,sha3,specrestrict,ssbs,store-pair-suppress,tlb-rmi,tracev8.4,uaops,v8.1a,v8.2a,v8.3a,v8.4a,v8.5a,v8.6a,v8a,vh,zcm,zcz,zcz-gp") },
+ { str_lit("apple-m4"), str_lit("CONTEXTIDREL2,aes,alternate-sextload-cvt-f32-pattern,altnzcv,am,amvs,apple-m4,arith-bcc-fusion,arith-cbz-fusion,bf16,bti,ccdp,ccpp,complxnum,crc,disable-latency-sched-heuristic,dit,dotprod,ecv,el2vmsa,el3,fgt,flagm,fp-armv8,fp16fml,fpac,fptoint,fullfp16,fuse-address,fuse-adrp-add,fuse-aes,fuse-arith-logic,fuse-crypto-eor,fuse-csel,fuse-literals,hcx,i8mm,jsconv,lor,lse,lse2,mpam,neon,nv,pan,pan-rwv,pauth,perfmon,predres,ras,rcpc,rcpc-immo,rdm,sb,sel2,sha2,sha3,sme,sme-f64f64,sme-i16i64,sme2,specrestrict,tlb-rmi,tracev8.4,uaops,v8.1a,v8.2a,v8.3a,v8.4a,v8.5a,v8.6a,v8.7a,v8a,vh,wfxt,xs,zcm,zcz,zcz-gp") },
+ { str_lit("apple-s4"), str_lit("CONTEXTIDREL2,aes,alternate-sextload-cvt-f32-pattern,apple-a12,arith-bcc-fusion,arith-cbz-fusion,ccpp,complxnum,crc,disable-latency-sched-heuristic,el2vmsa,el3,fp-armv8,fullfp16,fuse-aes,fuse-crypto-eor,jsconv,lor,lse,neon,pan,pan-rwv,pauth,perfmon,ras,rcpc,rdm,sha2,store-pair-suppress,uaops,v8.1a,v8.2a,v8.3a,v8a,vh,zcm,zcz,zcz-gp") },
+ { str_lit("apple-s5"), str_lit("CONTEXTIDREL2,aes,alternate-sextload-cvt-f32-pattern,apple-a12,arith-bcc-fusion,arith-cbz-fusion,ccpp,complxnum,crc,disable-latency-sched-heuristic,el2vmsa,el3,fp-armv8,fullfp16,fuse-aes,fuse-crypto-eor,jsconv,lor,lse,neon,pan,pan-rwv,pauth,perfmon,ras,rcpc,rdm,sha2,store-pair-suppress,uaops,v8.1a,v8.2a,v8.3a,v8a,vh,zcm,zcz,zcz-gp") },
+ { str_lit("carmel"), str_lit("CONTEXTIDREL2,aes,carmel,ccpp,crc,el2vmsa,el3,fp-armv8,fullfp16,lor,lse,neon,pan,pan-rwv,ras,rdm,sha2,uaops,v8.1a,v8.2a,v8a,vh") },
+ { str_lit("cobalt-100"), str_lit("CONTEXTIDREL2,altnzcv,alu-lsl-fast,am,bf16,bti,ccdp,ccidx,ccpp,complxnum,crc,dit,dotprod,el2vmsa,el3,enable-select-opt,ete,flagm,fp-armv8,fp16fml,fptoint,fullfp16,fuse-adrp-add,fuse-aes,i8mm,jsconv,lor,lse,lse2,mpam,mte,neon,neoversen2,nv,pan,pan-rwv,pauth,perfmon,predictable-select-expensive,predres,ras,rcpc,rcpc-immo,rdm,sb,sel2,specrestrict,ssbs,sve,sve-bitperm,sve2,tlb-rmi,tracev8.4,trbe,uaops,use-postra-scheduler,v8.1a,v8.2a,v8.3a,v8.4a,v8.5a,v8a,v9a,vh") },
+ { str_lit("cortex-a34"), str_lit("a35,aes,crc,el2vmsa,el3,fp-armv8,neon,perfmon,sha2,v8a") },
+ { str_lit("cortex-a35"), str_lit("a35,aes,crc,el2vmsa,el3,fp-armv8,neon,perfmon,sha2,v8a") },
+ { str_lit("cortex-a510"), str_lit("CONTEXTIDREL2,a510,altnzcv,am,bf16,bti,ccdp,ccidx,ccpp,complxnum,crc,dit,dotprod,el2vmsa,el3,ete,flagm,fp-armv8,fp16fml,fptoint,fullfp16,fuse-adrp-add,fuse-aes,i8mm,jsconv,lor,lse,lse2,mpam,mte,neon,nv,pan,pan-rwv,pauth,perfmon,predres,ras,rcpc,rcpc-immo,rdm,sb,sel2,specrestrict,ssbs,sve,sve-bitperm,sve2,tlb-rmi,tracev8.4,trbe,uaops,use-postra-scheduler,v8.1a,v8.2a,v8.3a,v8.4a,v8.5a,v8a,v9a,vh") },
+ { str_lit("cortex-a520"), str_lit("CONTEXTIDREL2,a520,altnzcv,am,amvs,bf16,bti,ccdp,ccidx,ccpp,complxnum,crc,dit,dotprod,ecv,el2vmsa,el3,ete,fgt,flagm,fp-armv8,fp16fml,fptoint,fullfp16,fuse-adrp-add,fuse-aes,hcx,i8mm,jsconv,lor,lse,lse2,mpam,mte,neon,nv,pan,pan-rwv,pauth,perfmon,predres,ras,rcpc,rcpc-immo,rdm,sb,sel2,specrestrict,ssbs,sve,sve-bitperm,sve2,tlb-rmi,tracev8.4,trbe,uaops,use-postra-scheduler,v8.1a,v8.2a,v8.3a,v8.4a,v8.5a,v8.6a,v8.7a,v8a,v9.1a,v9.2a,v9a,vh,wfxt,xs") },
+ { str_lit("cortex-a520ae"), str_lit("CONTEXTIDREL2,a520ae,altnzcv,am,amvs,bf16,bti,ccdp,ccidx,ccpp,complxnum,crc,dit,dotprod,ecv,el2vmsa,el3,ete,fgt,flagm,fp-armv8,fp16fml,fptoint,fullfp16,fuse-adrp-add,fuse-aes,hcx,i8mm,jsconv,lor,lse,lse2,mpam,mte,neon,nv,pan,pan-rwv,pauth,perfmon,predres,ras,rcpc,rcpc-immo,rdm,sb,sel2,specrestrict,ssbs,sve,sve-bitperm,sve2,tlb-rmi,tracev8.4,trbe,uaops,use-postra-scheduler,v8.1a,v8.2a,v8.3a,v8.4a,v8.5a,v8.6a,v8.7a,v8a,v9.1a,v9.2a,v9a,vh,wfxt,xs") },
+ { str_lit("cortex-a53"), str_lit("a53,aes,balance-fp-ops,crc,el2vmsa,el3,fp-armv8,fuse-adrp-add,fuse-aes,neon,perfmon,sha2,use-postra-scheduler,v8a") },
+ { str_lit("cortex-a55"), str_lit("CONTEXTIDREL2,a55,aes,ccpp,crc,dotprod,el2vmsa,el3,fp-armv8,fullfp16,fuse-address,fuse-adrp-add,fuse-aes,lor,lse,neon,pan,pan-rwv,perfmon,ras,rcpc,rdm,sha2,uaops,use-postra-scheduler,v8.1a,v8.2a,v8a,vh") },
+ { str_lit("cortex-a57"), str_lit("a57,addr-lsl-slow-14,aes,balance-fp-ops,crc,el2vmsa,el3,enable-select-opt,fp-armv8,fuse-adrp-add,fuse-aes,fuse-literals,neon,perfmon,predictable-select-expensive,sha2,use-postra-scheduler,v8a") },
+ { str_lit("cortex-a65"), str_lit("CONTEXTIDREL2,a65,aes,ccpp,crc,dotprod,el2vmsa,el3,enable-select-opt,fp-armv8,fullfp16,fuse-address,fuse-adrp-add,fuse-aes,fuse-literals,lor,lse,neon,pan,pan-rwv,perfmon,predictable-select-expensive,ras,rcpc,rdm,sha2,ssbs,uaops,v8.1a,v8.2a,v8a,vh") },
+ { str_lit("cortex-a65ae"), str_lit("CONTEXTIDREL2,a65,aes,ccpp,crc,dotprod,el2vmsa,el3,enable-select-opt,fp-armv8,fullfp16,fuse-address,fuse-adrp-add,fuse-aes,fuse-literals,lor,lse,neon,pan,pan-rwv,perfmon,predictable-select-expensive,ras,rcpc,rdm,sha2,ssbs,uaops,v8.1a,v8.2a,v8a,vh") },
+ { str_lit("cortex-a710"), str_lit("CONTEXTIDREL2,a710,altnzcv,alu-lsl-fast,am,bf16,bti,ccdp,ccidx,ccpp,cmp-bcc-fusion,complxnum,crc,dit,dotprod,el2vmsa,el3,enable-select-opt,ete,flagm,fp-armv8,fp16fml,fptoint,fullfp16,fuse-adrp-add,fuse-aes,i8mm,jsconv,lor,lse,lse2,mpam,mte,neon,nv,pan,pan-rwv,pauth,perfmon,predictable-select-expensive,predres,ras,rcpc,rcpc-immo,rdm,sb,sel2,specrestrict,ssbs,sve,sve-bitperm,sve2,tlb-rmi,tracev8.4,trbe,uaops,use-postra-scheduler,v8.1a,v8.2a,v8.3a,v8.4a,v8.5a,v8a,v9a,vh") },
+ { str_lit("cortex-a715"), str_lit("CONTEXTIDREL2,a715,altnzcv,alu-lsl-fast,am,bf16,bti,ccdp,ccidx,ccpp,cmp-bcc-fusion,complxnum,crc,dit,dotprod,el2vmsa,el3,enable-select-opt,ete,flagm,fp-armv8,fp16fml,fptoint,fullfp16,fuse-adrp-add,fuse-aes,i8mm,jsconv,lor,lse,lse2,mpam,mte,neon,nv,pan,pan-rwv,pauth,perfmon,predictable-select-expensive,predres,ras,rcpc,rcpc-immo,rdm,sb,sel2,spe,specrestrict,ssbs,sve,sve-bitperm,sve2,tlb-rmi,tracev8.4,trbe,uaops,use-postra-scheduler,v8.1a,v8.2a,v8.3a,v8.4a,v8.5a,v8a,v9a,vh") },
+ { str_lit("cortex-a72"), str_lit("a72,addr-lsl-slow-14,aes,crc,el2vmsa,el3,enable-select-opt,fp-armv8,fuse-adrp-add,fuse-aes,fuse-literals,neon,perfmon,predictable-select-expensive,sha2,v8a") },
+ { str_lit("cortex-a720"), str_lit("CONTEXTIDREL2,a720,altnzcv,alu-lsl-fast,am,amvs,bf16,bti,ccdp,ccidx,ccpp,cmp-bcc-fusion,complxnum,crc,dit,dotprod,ecv,el2vmsa,el3,enable-select-opt,ete,fgt,flagm,fp-armv8,fp16fml,fptoint,fullfp16,fuse-adrp-add,fuse-aes,hcx,i8mm,jsconv,lor,lse,lse2,mpam,mte,neon,nv,pan,pan-rwv,pauth,perfmon,predictable-select-expensive,predres,ras,rcpc,rcpc-immo,rdm,sb,sel2,spe,spe-eef,specrestrict,ssbs,sve,sve-bitperm,sve2,tlb-rmi,tracev8.4,trbe,uaops,use-postra-scheduler,v8.1a,v8.2a,v8.3a,v8.4a,v8.5a,v8.6a,v8.7a,v8a,v9.1a,v9.2a,v9a,vh,wfxt,xs") },
+ { str_lit("cortex-a720ae"), str_lit("CONTEXTIDREL2,a720ae,altnzcv,alu-lsl-fast,am,amvs,bf16,bti,ccdp,ccidx,ccpp,cmp-bcc-fusion,complxnum,crc,dit,dotprod,ecv,el2vmsa,el3,enable-select-opt,ete,fgt,flagm,fp-armv8,fp16fml,fptoint,fullfp16,fuse-adrp-add,fuse-aes,hcx,i8mm,jsconv,lor,lse,lse2,mpam,mte,neon,nv,pan,pan-rwv,pauth,perfmon,predictable-select-expensive,predres,ras,rcpc,rcpc-immo,rdm,sb,sel2,spe,spe-eef,specrestrict,ssbs,sve,sve-bitperm,sve2,tlb-rmi,tracev8.4,trbe,uaops,use-postra-scheduler,v8.1a,v8.2a,v8.3a,v8.4a,v8.5a,v8.6a,v8.7a,v8a,v9.1a,v9.2a,v9a,vh,wfxt,xs") },
+ { str_lit("cortex-a725"), str_lit("CONTEXTIDREL2,altnzcv,alu-lsl-fast,am,amvs,bf16,bti,ccdp,ccidx,ccpp,cmp-bcc-fusion,complxnum,cortex-a725,crc,dit,dotprod,ecv,el2vmsa,el3,enable-select-opt,ete,fgt,flagm,fp-armv8,fp16fml,fptoint,fullfp16,fuse-adrp-add,fuse-aes,hcx,i8mm,jsconv,lor,lse,lse2,mpam,mte,neon,nv,pan,pan-rwv,pauth,perfmon,predictable-select-expensive,predres,ras,rcpc,rcpc-immo,rdm,sb,sel2,spe,spe-eef,specrestrict,ssbs,sve,sve-bitperm,sve2,tlb-rmi,tracev8.4,trbe,uaops,use-postra-scheduler,v8.1a,v8.2a,v8.3a,v8.4a,v8.5a,v8.6a,v8.7a,v8a,v9.1a,v9.2a,v9a,vh,wfxt,xs") },
+ { str_lit("cortex-a73"), str_lit("a73,addr-lsl-slow-14,aes,crc,el2vmsa,el3,enable-select-opt,fp-armv8,fuse-adrp-add,fuse-aes,neon,perfmon,predictable-select-expensive,sha2,v8a") },
+ { str_lit("cortex-a75"), str_lit("CONTEXTIDREL2,a75,addr-lsl-slow-14,aes,ccpp,crc,dotprod,el2vmsa,el3,enable-select-opt,fp-armv8,fullfp16,fuse-adrp-add,fuse-aes,lor,lse,neon,pan,pan-rwv,perfmon,predictable-select-expensive,ras,rcpc,rdm,sha2,uaops,v8.1a,v8.2a,v8a,vh") },
+ { str_lit("cortex-a76"), str_lit("CONTEXTIDREL2,a76,addr-lsl-slow-14,aes,alu-lsl-fast,ccpp,crc,dotprod,el2vmsa,el3,enable-select-opt,fp-armv8,fullfp16,fuse-adrp-add,fuse-aes,lor,lse,neon,pan,pan-rwv,perfmon,predictable-select-expensive,ras,rcpc,rdm,sha2,ssbs,uaops,v8.1a,v8.2a,v8a,vh") },
+ { str_lit("cortex-a76ae"), str_lit("CONTEXTIDREL2,a76,addr-lsl-slow-14,aes,alu-lsl-fast,ccpp,crc,dotprod,el2vmsa,el3,enable-select-opt,fp-armv8,fullfp16,fuse-adrp-add,fuse-aes,lor,lse,neon,pan,pan-rwv,perfmon,predictable-select-expensive,ras,rcpc,rdm,sha2,ssbs,uaops,v8.1a,v8.2a,v8a,vh") },
+ { str_lit("cortex-a77"), str_lit("CONTEXTIDREL2,a77,addr-lsl-slow-14,aes,alu-lsl-fast,ccpp,cmp-bcc-fusion,crc,dotprod,el2vmsa,el3,enable-select-opt,fp-armv8,fullfp16,fuse-adrp-add,fuse-aes,lor,lse,neon,pan,pan-rwv,perfmon,predictable-select-expensive,ras,rcpc,rdm,sha2,ssbs,uaops,v8.1a,v8.2a,v8a,vh") },
+ { str_lit("cortex-a78"), str_lit("CONTEXTIDREL2,a78,addr-lsl-slow-14,aes,alu-lsl-fast,ccpp,cmp-bcc-fusion,crc,dotprod,el2vmsa,el3,enable-select-opt,fp-armv8,fullfp16,fuse-adrp-add,fuse-aes,lor,lse,neon,pan,pan-rwv,perfmon,predictable-select-expensive,ras,rcpc,rdm,sha2,spe,ssbs,uaops,use-postra-scheduler,v8.1a,v8.2a,v8a,vh") },
+ { str_lit("cortex-a78ae"), str_lit("CONTEXTIDREL2,a78ae,addr-lsl-slow-14,aes,alu-lsl-fast,ccpp,cmp-bcc-fusion,crc,dotprod,el2vmsa,el3,enable-select-opt,fp-armv8,fullfp16,fuse-adrp-add,fuse-aes,lor,lse,neon,pan,pan-rwv,perfmon,predictable-select-expensive,ras,rcpc,rdm,sha2,spe,ssbs,uaops,use-postra-scheduler,v8.1a,v8.2a,v8a,vh") },
+ { str_lit("cortex-a78c"), str_lit("CONTEXTIDREL2,a78c,addr-lsl-slow-14,aes,alu-lsl-fast,ccpp,cmp-bcc-fusion,crc,dotprod,el2vmsa,el3,enable-select-opt,flagm,fp-armv8,fullfp16,fuse-adrp-add,fuse-aes,lor,lse,neon,pan,pan-rwv,pauth,perfmon,predictable-select-expensive,ras,rcpc,rdm,sha2,spe,ssbs,uaops,use-postra-scheduler,v8.1a,v8.2a,v8a,vh") },
+ { str_lit("cortex-r82"), str_lit("CONTEXTIDREL2,ccdp,ccpp,complxnum,cortex-r82,crc,dit,dotprod,flagm,fp-armv8,fp16fml,fullfp16,jsconv,lse,neon,pan,pan-rwv,pauth,perfmon,predres,ras,rcpc,rcpc-immo,rdm,sb,sel2,specrestrict,ssbs,tlb-rmi,tracev8.4,uaops,use-postra-scheduler,v8r") },
+ { str_lit("cortex-r82ae"), str_lit("CONTEXTIDREL2,ccdp,ccpp,complxnum,cortex-r82ae,crc,dit,dotprod,flagm,fp-armv8,fp16fml,fullfp16,jsconv,lse,neon,pan,pan-rwv,pauth,perfmon,predres,ras,rcpc,rcpc-immo,rdm,sb,sel2,specrestrict,ssbs,tlb-rmi,tracev8.4,uaops,use-postra-scheduler,v8r") },
+ { str_lit("cortex-x1"), str_lit("CONTEXTIDREL2,addr-lsl-slow-14,aes,alu-lsl-fast,ccpp,cmp-bcc-fusion,cortex-x1,crc,dotprod,el2vmsa,el3,enable-select-opt,fp-armv8,fullfp16,fuse-adrp-add,fuse-aes,lor,lse,neon,pan,pan-rwv,perfmon,predictable-select-expensive,ras,rcpc,rdm,sha2,spe,ssbs,uaops,use-postra-scheduler,v8.1a,v8.2a,v8a,vh") },
+ { str_lit("cortex-x1c"), str_lit("CONTEXTIDREL2,addr-lsl-slow-14,aes,alu-lsl-fast,ccpp,cmp-bcc-fusion,cortex-x1,crc,dotprod,el2vmsa,el3,enable-select-opt,flagm,fp-armv8,fullfp16,fuse-adrp-add,fuse-aes,lor,lse,lse2,neon,pan,pan-rwv,pauth,perfmon,predictable-select-expensive,ras,rcpc,rcpc-immo,rdm,sha2,spe,ssbs,uaops,use-postra-scheduler,v8.1a,v8.2a,v8a,vh") },
+ { str_lit("cortex-x2"), str_lit("CONTEXTIDREL2,altnzcv,alu-lsl-fast,am,bf16,bti,ccdp,ccidx,ccpp,cmp-bcc-fusion,complxnum,cortex-x2,crc,dit,dotprod,el2vmsa,el3,enable-select-opt,ete,flagm,fp-armv8,fp16fml,fptoint,fullfp16,fuse-adrp-add,fuse-aes,i8mm,jsconv,lor,lse,lse2,mpam,mte,neon,nv,pan,pan-rwv,pauth,perfmon,predictable-select-expensive,predres,ras,rcpc,rcpc-immo,rdm,sb,sel2,specrestrict,ssbs,sve,sve-bitperm,sve2,tlb-rmi,tracev8.4,trbe,uaops,use-fixed-over-scalable-if-equal-cost,use-postra-scheduler,v8.1a,v8.2a,v8.3a,v8.4a,v8.5a,v8a,v9a,vh") },
+ { str_lit("cortex-x3"), str_lit("CONTEXTIDREL2,altnzcv,alu-lsl-fast,am,avoid-ldapur,bf16,bti,ccdp,ccidx,ccpp,complxnum,cortex-x3,crc,dit,dotprod,el2vmsa,el3,enable-select-opt,ete,flagm,fp-armv8,fp16fml,fptoint,fullfp16,fuse-adrp-add,fuse-aes,i8mm,jsconv,lor,lse,lse2,mpam,mte,neon,nv,pan,pan-rwv,pauth,perfmon,predictable-select-expensive,predres,ras,rcpc,rcpc-immo,rdm,sb,sel2,spe,specrestrict,ssbs,sve,sve-bitperm,sve2,tlb-rmi,tracev8.4,trbe,uaops,use-fixed-over-scalable-if-equal-cost,use-postra-scheduler,v8.1a,v8.2a,v8.3a,v8.4a,v8.5a,v8a,v9a,vh") },
+ { str_lit("cortex-x4"), str_lit("CONTEXTIDREL2,altnzcv,alu-lsl-fast,am,amvs,avoid-ldapur,bf16,bti,ccdp,ccidx,ccpp,complxnum,cortex-x4,crc,dit,dotprod,ecv,el2vmsa,el3,enable-select-opt,ete,fgt,flagm,fp-armv8,fp16fml,fptoint,fullfp16,fuse-adrp-add,fuse-aes,hcx,i8mm,jsconv,lor,lse,lse2,mpam,mte,neon,nv,pan,pan-rwv,pauth,perfmon,predictable-select-expensive,predres,ras,rcpc,rcpc-immo,rdm,sb,sel2,spe,spe-eef,specrestrict,ssbs,sve,sve-bitperm,sve2,tlb-rmi,tracev8.4,trbe,uaops,use-fixed-over-scalable-if-equal-cost,use-postra-scheduler,v8.1a,v8.2a,v8.3a,v8.4a,v8.5a,v8.6a,v8.7a,v8a,v9.1a,v9.2a,v9a,vh,wfxt,xs") },
+ { str_lit("cortex-x925"), str_lit("CONTEXTIDREL2,altnzcv,alu-lsl-fast,am,amvs,avoid-ldapur,bf16,bti,ccdp,ccidx,ccpp,complxnum,cortex-x925,crc,dit,dotprod,ecv,el2vmsa,el3,enable-select-opt,ete,fgt,flagm,fp-armv8,fp16fml,fptoint,fullfp16,fuse-adrp-add,fuse-aes,hcx,i8mm,jsconv,lor,lse,lse2,mpam,mte,neon,nv,pan,pan-rwv,pauth,perfmon,predictable-select-expensive,predres,ras,rcpc,rcpc-immo,rdm,sb,sel2,spe,spe-eef,specrestrict,ssbs,sve,sve-bitperm,sve2,tlb-rmi,tracev8.4,trbe,uaops,use-fixed-over-scalable-if-equal-cost,use-postra-scheduler,v8.1a,v8.2a,v8.3a,v8.4a,v8.5a,v8.6a,v8.7a,v8a,v9.1a,v9.2a,v9a,vh,wfxt,xs") },
+ { str_lit("cyclone"), str_lit("aes,alternate-sextload-cvt-f32-pattern,apple-a7,arith-bcc-fusion,arith-cbz-fusion,disable-latency-sched-heuristic,el2vmsa,el3,fp-armv8,fuse-aes,fuse-crypto-eor,neon,perfmon,sha2,store-pair-suppress,v8a,zcm,zcz,zcz-fp-workaround,zcz-gp") },
+ { str_lit("exynos-m3"), str_lit("aes,alu-lsl-fast,crc,el2vmsa,el3,exynos-cheap-as-move,exynosm3,force-32bit-jump-tables,fp-armv8,fuse-address,fuse-adrp-add,fuse-aes,fuse-csel,fuse-literals,neon,perfmon,predictable-select-expensive,sha2,store-pair-suppress,use-postra-scheduler,v8a") },
+ { str_lit("exynos-m4"), str_lit("CONTEXTIDREL2,aes,alu-lsl-fast,arith-bcc-fusion,arith-cbz-fusion,ccpp,crc,dotprod,el2vmsa,el3,exynos-cheap-as-move,exynosm4,force-32bit-jump-tables,fp-armv8,fullfp16,fuse-address,fuse-adrp-add,fuse-aes,fuse-arith-logic,fuse-csel,fuse-literals,lor,lse,neon,pan,pan-rwv,perfmon,ras,rdm,sha2,store-pair-suppress,uaops,use-postra-scheduler,v8.1a,v8.2a,v8a,vh,zcz,zcz-gp") },
+ { str_lit("exynos-m5"), str_lit("CONTEXTIDREL2,aes,alu-lsl-fast,arith-bcc-fusion,arith-cbz-fusion,ccpp,crc,dotprod,el2vmsa,el3,exynos-cheap-as-move,exynosm4,force-32bit-jump-tables,fp-armv8,fullfp16,fuse-address,fuse-adrp-add,fuse-aes,fuse-arith-logic,fuse-csel,fuse-literals,lor,lse,neon,pan,pan-rwv,perfmon,ras,rdm,sha2,store-pair-suppress,uaops,use-postra-scheduler,v8.1a,v8.2a,v8a,vh,zcz,zcz-gp") },
+ { str_lit("falkor"), str_lit("aes,alu-lsl-fast,crc,el2vmsa,el3,falkor,fp-armv8,neon,perfmon,predictable-select-expensive,rdm,sha2,slow-strqro-store,store-pair-suppress,use-postra-scheduler,v8a,zcz,zcz-gp") },
+ { str_lit("fujitsu-monaka"), str_lit("CONTEXTIDREL2,aes,altnzcv,am,amvs,arith-bcc-fusion,bf16,bti,ccdp,ccidx,ccpp,clrbhb,complxnum,crc,dit,dotprod,ecv,el2vmsa,el3,enable-select-opt,ete,faminmax,fgt,flagm,fp-armv8,fp16fml,fp8,fp8dot2,fpac,fptoint,fujitsu-monaka,fullfp16,hbc,hcx,i8mm,jsconv,lor,ls64,lse,lse2,lut,mec,mops,mpam,neon,nmi,nv,pan,pan-rwv,pauth,perfmon,predictable-select-expensive,predres,rand,ras,rcpc,rcpc-immo,rdm,rme,sb,sel2,sha2,sha3,sm4,specres2,specrestrict,ssbs,sve,sve-aes,sve-bitperm,sve2,sve2-sha3,sve2-sm4,tlb-rmi,tracev8.4,trbe,uaops,use-postra-scheduler,v8.1a,v8.2a,v8.3a,v8.4a,v8.5a,v8.6a,v8.7a,v8.8a,v8a,v9.1a,v9.2a,v9.3a,v9a,vh,wfxt,xs") },
+ { str_lit("generic"), str_lit("enable-select-opt,ete,fp-armv8,fuse-adrp-add,fuse-aes,neon,trbe,use-postra-scheduler") },
+ { str_lit("grace"), str_lit("CONTEXTIDREL2,altnzcv,alu-lsl-fast,am,avoid-ldapur,bf16,bti,ccdp,ccidx,ccpp,cmp-bcc-fusion,complxnum,crc,dit,dotprod,el2vmsa,el3,enable-select-opt,ete,flagm,fp-armv8,fp16fml,fptoint,fullfp16,fuse-adrp-add,fuse-aes,i8mm,jsconv,lor,lse,lse2,mpam,mte,neon,neoversev2,nv,pan,pan-rwv,pauth,perfmon,predictable-select-expensive,predres,rand,ras,rcpc,rcpc-immo,rdm,sb,sel2,spe,specrestrict,ssbs,sve,sve-bitperm,sve2,tlb-rmi,tracev8.4,trbe,uaops,use-fixed-over-scalable-if-equal-cost,use-postra-scheduler,v8.1a,v8.2a,v8.3a,v8.4a,v8.5a,v8a,v9a,vh") },
+ { str_lit("kryo"), str_lit("aes,alu-lsl-fast,crc,el2vmsa,el3,fp-armv8,kryo,neon,perfmon,predictable-select-expensive,sha2,store-pair-suppress,use-postra-scheduler,v8a,zcz,zcz-gp") },
+ { str_lit("neoverse-512tvb"), str_lit("CONTEXTIDREL2,aes,alu-lsl-fast,am,bf16,ccdp,ccidx,ccpp,complxnum,crc,dit,dotprod,el2vmsa,el3,enable-select-opt,flagm,fp-armv8,fp16fml,fullfp16,fuse-adrp-add,fuse-aes,i8mm,jsconv,lor,lse,lse2,mpam,neon,neoverse512tvb,nv,pan,pan-rwv,pauth,perfmon,predictable-select-expensive,rand,ras,rcpc,rcpc-immo,rdm,sel2,sha2,sha3,sm4,spe,ssbs,sve,tlb-rmi,tracev8.4,uaops,use-postra-scheduler,v8.1a,v8.2a,v8.3a,v8.4a,v8a,vh") },
+ { str_lit("neoverse-e1"), str_lit("CONTEXTIDREL2,aes,ccpp,crc,dotprod,el2vmsa,el3,fp-armv8,fullfp16,fuse-adrp-add,fuse-aes,lor,lse,neon,neoversee1,pan,pan-rwv,perfmon,ras,rcpc,rdm,sha2,ssbs,uaops,use-postra-scheduler,v8.1a,v8.2a,v8a,vh") },
+ { str_lit("neoverse-n1"), str_lit("CONTEXTIDREL2,addr-lsl-slow-14,aes,alu-lsl-fast,ccpp,crc,dotprod,el2vmsa,el3,enable-select-opt,fp-armv8,fullfp16,fuse-adrp-add,fuse-aes,lor,lse,neon,neoversen1,pan,pan-rwv,perfmon,predictable-select-expensive,ras,rcpc,rdm,sha2,spe,ssbs,uaops,use-postra-scheduler,v8.1a,v8.2a,v8a,vh") },
+ { str_lit("neoverse-n2"), str_lit("CONTEXTIDREL2,altnzcv,alu-lsl-fast,am,bf16,bti,ccdp,ccidx,ccpp,complxnum,crc,dit,dotprod,el2vmsa,el3,enable-select-opt,ete,flagm,fp-armv8,fp16fml,fptoint,fullfp16,fuse-adrp-add,fuse-aes,i8mm,jsconv,lor,lse,lse2,mpam,mte,neon,neoversen2,nv,pan,pan-rwv,pauth,perfmon,predictable-select-expensive,predres,ras,rcpc,rcpc-immo,rdm,sb,sel2,specrestrict,ssbs,sve,sve-bitperm,sve2,tlb-rmi,tracev8.4,trbe,uaops,use-postra-scheduler,v8.1a,v8.2a,v8.3a,v8.4a,v8.5a,v8a,v9a,vh") },
+ { str_lit("neoverse-n3"), str_lit("CONTEXTIDREL2,altnzcv,alu-lsl-fast,am,amvs,bf16,bti,ccdp,ccidx,ccpp,complxnum,crc,dit,dotprod,ecv,el2vmsa,el3,enable-select-opt,ete,fgt,flagm,fp-armv8,fp16fml,fptoint,fullfp16,fuse-adrp-add,fuse-aes,hcx,i8mm,jsconv,lor,lse,lse2,mpam,mte,neon,neoversen3,nv,pan,pan-rwv,pauth,perfmon,predictable-select-expensive,predres,rand,ras,rcpc,rcpc-immo,rdm,sb,sel2,spe,spe-eef,specrestrict,ssbs,sve,sve-bitperm,sve2,tlb-rmi,tracev8.4,trbe,uaops,use-postra-scheduler,v8.1a,v8.2a,v8.3a,v8.4a,v8.5a,v8.6a,v8.7a,v8a,v9.1a,v9.2a,v9a,vh,wfxt,xs") },
+ { str_lit("neoverse-v1"), str_lit("CONTEXTIDREL2,addr-lsl-slow-14,aes,alu-lsl-fast,am,bf16,ccdp,ccidx,ccpp,complxnum,crc,dit,dotprod,el2vmsa,el3,enable-select-opt,flagm,fp-armv8,fp16fml,fullfp16,fuse-adrp-add,fuse-aes,i8mm,jsconv,lor,lse,lse2,mpam,neon,neoversev1,no-sve-fp-ld1r,nv,pan,pan-rwv,pauth,perfmon,predictable-select-expensive,rand,ras,rcpc,rcpc-immo,rdm,sel2,sha2,sha3,sm4,spe,ssbs,sve,tlb-rmi,tracev8.4,uaops,use-postra-scheduler,v8.1a,v8.2a,v8.3a,v8.4a,v8a,vh") },
+ { str_lit("neoverse-v2"), str_lit("CONTEXTIDREL2,altnzcv,alu-lsl-fast,am,avoid-ldapur,bf16,bti,ccdp,ccidx,ccpp,cmp-bcc-fusion,complxnum,crc,dit,dotprod,el2vmsa,el3,enable-select-opt,ete,flagm,fp-armv8,fp16fml,fptoint,fullfp16,fuse-adrp-add,fuse-aes,i8mm,jsconv,lor,lse,lse2,mpam,mte,neon,neoversev2,nv,pan,pan-rwv,pauth,perfmon,predictable-select-expensive,predres,rand,ras,rcpc,rcpc-immo,rdm,sb,sel2,spe,specrestrict,ssbs,sve,sve-bitperm,sve2,tlb-rmi,tracev8.4,trbe,uaops,use-fixed-over-scalable-if-equal-cost,use-postra-scheduler,v8.1a,v8.2a,v8.3a,v8.4a,v8.5a,v8a,v9a,vh") },
+ { str_lit("neoverse-v3"), str_lit("CONTEXTIDREL2,altnzcv,alu-lsl-fast,am,amvs,avoid-ldapur,bf16,brbe,bti,ccdp,ccidx,ccpp,complxnum,crc,dit,dotprod,ecv,el2vmsa,el3,enable-select-opt,ete,fgt,flagm,fp-armv8,fp16fml,fptoint,fullfp16,fuse-adrp-add,fuse-aes,hcx,i8mm,jsconv,lor,ls64,lse,lse2,mpam,mte,neon,neoversev3,nv,pan,pan-rwv,pauth,perfmon,predictable-select-expensive,predres,rand,ras,rcpc,rcpc-immo,rdm,rme,sb,sel2,spe,spe-eef,specrestrict,ssbs,sve,sve-bitperm,sve2,tlb-rmi,tracev8.4,trbe,uaops,use-postra-scheduler,v8.1a,v8.2a,v8.3a,v8.4a,v8.5a,v8.6a,v8.7a,v8a,v9.1a,v9.2a,v9a,vh,wfxt,xs") },
+ { str_lit("neoverse-v3ae"), str_lit("CONTEXTIDREL2,altnzcv,alu-lsl-fast,am,amvs,avoid-ldapur,bf16,brbe,bti,ccdp,ccidx,ccpp,complxnum,crc,dit,dotprod,ecv,el2vmsa,el3,enable-select-opt,ete,fgt,flagm,fp-armv8,fp16fml,fptoint,fullfp16,fuse-adrp-add,fuse-aes,hcx,i8mm,jsconv,lor,ls64,lse,lse2,mpam,mte,neon,neoversev3AE,nv,pan,pan-rwv,pauth,perfmon,predictable-select-expensive,predres,rand,ras,rcpc,rcpc-immo,rdm,rme,sb,sel2,spe,spe-eef,specrestrict,ssbs,sve,sve-bitperm,sve2,tlb-rmi,tracev8.4,trbe,uaops,use-postra-scheduler,v8.1a,v8.2a,v8.3a,v8.4a,v8.5a,v8.6a,v8.7a,v8a,v9.1a,v9.2a,v9a,vh,wfxt,xs") },
+ { str_lit("oryon-1"), str_lit("CONTEXTIDREL2,aes,altnzcv,am,amvs,bf16,bti,ccdp,ccidx,ccpp,complxnum,crc,dit,dotprod,ecv,el2vmsa,el3,enable-select-opt,fgt,flagm,fp-armv8,fp16fml,fptoint,fullfp16,fuse-address,fuse-adrp-add,fuse-aes,fuse-crypto-eor,i8mm,jsconv,lor,lse,lse2,mpam,neon,nv,oryon-1,pan,pan-rwv,pauth,perfmon,predres,rand,ras,rcpc,rcpc-immo,rdm,sb,sel2,sha2,sha3,sm4,spe,specrestrict,ssbs,tlb-rmi,tracev8.4,uaops,use-postra-scheduler,v8.1a,v8.2a,v8.3a,v8.4a,v8.5a,v8.6a,v8a,vh") },
+ { str_lit("saphira"), str_lit("CONTEXTIDREL2,aes,alu-lsl-fast,am,ccidx,ccpp,complxnum,crc,dit,dotprod,el2vmsa,el3,flagm,fp-armv8,jsconv,lor,lse,lse2,mpam,neon,nv,pan,pan-rwv,pauth,perfmon,predictable-select-expensive,ras,rcpc,rcpc-immo,rdm,saphira,sel2,sha2,spe,store-pair-suppress,tlb-rmi,tracev8.4,uaops,use-postra-scheduler,v8.1a,v8.2a,v8.3a,v8.4a,v8a,vh,zcz,zcz-gp") },
+ { str_lit("thunderx"), str_lit("aes,crc,el2vmsa,el3,fp-armv8,neon,perfmon,predictable-select-expensive,sha2,store-pair-suppress,thunderx,use-postra-scheduler,v8a") },
+ { str_lit("thunderx2t99"), str_lit("CONTEXTIDREL2,aes,aggressive-fma,arith-bcc-fusion,crc,el2vmsa,el3,fp-armv8,lor,lse,neon,pan,predictable-select-expensive,rdm,sha2,store-pair-suppress,thunderx2t99,use-postra-scheduler,v8.1a,v8a,vh") },
+ { str_lit("thunderx3t110"), str_lit("CONTEXTIDREL2,aes,aggressive-fma,arith-bcc-fusion,balance-fp-ops,ccidx,ccpp,complxnum,crc,el2vmsa,el3,fp-armv8,jsconv,lor,lse,neon,pan,pan-rwv,pauth,perfmon,predictable-select-expensive,ras,rcpc,rdm,sha2,store-pair-suppress,strict-align,thunderx3t110,uaops,use-postra-scheduler,v8.1a,v8.2a,v8.3a,v8a,vh") },
+ { str_lit("thunderxt81"), str_lit("aes,crc,el2vmsa,el3,fp-armv8,neon,perfmon,predictable-select-expensive,sha2,store-pair-suppress,thunderxt81,use-postra-scheduler,v8a") },
+ { str_lit("thunderxt83"), str_lit("aes,crc,el2vmsa,el3,fp-armv8,neon,perfmon,predictable-select-expensive,sha2,store-pair-suppress,thunderxt83,use-postra-scheduler,v8a") },
+ { str_lit("thunderxt88"), str_lit("aes,crc,el2vmsa,el3,fp-armv8,neon,perfmon,predictable-select-expensive,sha2,store-pair-suppress,thunderxt88,use-postra-scheduler,v8a") },
+ { str_lit("tsv110"), str_lit("CONTEXTIDREL2,aes,ccpp,complxnum,crc,dotprod,el2vmsa,el3,fp-armv8,fp16fml,fullfp16,fuse-aes,jsconv,lor,lse,neon,pan,pan-rwv,perfmon,ras,rdm,sha2,spe,store-pair-suppress,tsv110,uaops,use-postra-scheduler,v8.1a,v8.2a,v8a,vh") },
+ // TargetArch_wasm32:
+ { str_lit("bleeding-edge"), str_lit("atomics,bulk-memory,bulk-memory-opt,call-indirect-overlong,exception-handling,extended-const,fp16,multimemory,multivalue,mutable-globals,nontrapping-fptoint,reference-types,relaxed-simd,sign-ext,simd128,tail-call") },
+ { str_lit("generic"), str_lit("bulk-memory,bulk-memory-opt,call-indirect-overlong,multivalue,mutable-globals,nontrapping-fptoint,reference-types,sign-ext") },
+ { str_lit("lime1"), str_lit("bulk-memory-opt,call-indirect-overlong,extended-const,multivalue,mutable-globals,nontrapping-fptoint,sign-ext") },
+ { str_lit("mvp"), str_lit("") },
+ // TargetArch_wasm64p32:
+ { str_lit("bleeding-edge"), str_lit("atomics,bulk-memory,bulk-memory-opt,call-indirect-overlong,exception-handling,extended-const,fp16,multimemory,multivalue,mutable-globals,nontrapping-fptoint,reference-types,relaxed-simd,sign-ext,simd128,tail-call") },
+ { str_lit("generic"), str_lit("bulk-memory,bulk-memory-opt,call-indirect-overlong,multivalue,mutable-globals,nontrapping-fptoint,reference-types,sign-ext") },
+ { str_lit("lime1"), str_lit("bulk-memory-opt,call-indirect-overlong,extended-const,multivalue,mutable-globals,nontrapping-fptoint,sign-ext") },
+ { str_lit("mvp"), str_lit("") },
+ // TargetArch_riscv64:
+ { str_lit("generic"), str_lit("64bit,i,optimized-nf2-segment-load-store") },
+ { str_lit("generic-rv32"), str_lit("32bit,i,optimized-nf2-segment-load-store") },
+ { str_lit("generic-rv64"), str_lit("64bit,i,optimized-nf2-segment-load-store") },
+ { str_lit("mips-p8700"), str_lit("64bit,a,c,d,f,i,m,mips-p8700,xmipscmove,xmipslsp,zaamo,zalrsc,zba,zbb,zicsr,zifencei,zmmul") },
+ { str_lit("rocket"), str_lit("") },
+ { str_lit("rocket-rv32"), str_lit("32bit,i,zicsr,zifencei") },
+ { str_lit("rocket-rv64"), str_lit("64bit,i,zicsr,zifencei") },
+ { str_lit("rp2350-hazard3"), str_lit("32bit,a,c,i,m,zaamo,zalrsc,zba,zbb,zbkb,zbs,zca,zcb,zcmp,zicsr,zifencei,zmmul") },
+ { str_lit("sifive-7-series"), str_lit("no-default-unroll,short-forward-branch-opt,sifive7,use-postra-scheduler") },
+ { str_lit("sifive-e20"), str_lit("32bit,c,i,m,zicsr,zifencei,zmmul") },
+ { str_lit("sifive-e21"), str_lit("32bit,a,c,i,m,zaamo,zalrsc,zicsr,zifencei,zmmul") },
+ { str_lit("sifive-e24"), str_lit("32bit,a,c,f,i,m,zaamo,zalrsc,zicsr,zifencei,zmmul") },
+ { str_lit("sifive-e31"), str_lit("32bit,a,c,i,m,zaamo,zalrsc,zicsr,zifencei,zmmul") },
+ { str_lit("sifive-e34"), str_lit("32bit,a,c,f,i,m,zaamo,zalrsc,zicsr,zifencei,zmmul") },
+ { str_lit("sifive-e76"), str_lit("32bit,a,c,f,i,m,no-default-unroll,short-forward-branch-opt,sifive7,use-postra-scheduler,zaamo,zalrsc,zicsr,zifencei,zmmul") },
+ { str_lit("sifive-p450"), str_lit("64bit,a,auipc-addi-fusion,b,c,conditional-cmv-fusion,d,f,i,lui-addi-fusion,m,no-default-unroll,unaligned-scalar-mem,unaligned-vector-mem,use-postra-scheduler,za64rs,zaamo,zalrsc,zba,zbb,zbs,zfhmin,zic64b,zicbom,zicbop,zicboz,ziccamoa,ziccif,zicclsm,ziccrse,zicntr,zicsr,zifencei,zihintntl,zihintpause,zihpm,zkt,zmmul") },
+ { str_lit("sifive-p470"), str_lit("64bit,a,auipc-addi-fusion,b,c,conditional-cmv-fusion,d,f,i,lui-addi-fusion,m,no-default-unroll,no-sink-splat-operands,unaligned-scalar-mem,unaligned-vector-mem,use-postra-scheduler,v,vxrm-pipeline-flush,xsifivecdiscarddlone,xsifivecflushdlone,za64rs,zaamo,zalrsc,zba,zbb,zbs,zfhmin,zic64b,zicbom,zicbop,zicboz,ziccamoa,ziccif,zicclsm,ziccrse,zicntr,zicsr,zifencei,zihintntl,zihintpause,zihpm,zkt,zmmul,zvbb,zvbc,zve32f,zve32x,zve64d,zve64f,zve64x,zvkb,zvkg,zvkn,zvknc,zvkned,zvkng,zvknhb,zvks,zvksc,zvksed,zvksg,zvksh,zvkt,zvl128b,zvl32b,zvl64b") },
+ { str_lit("sifive-p550"), str_lit("64bit,a,auipc-addi-fusion,c,conditional-cmv-fusion,d,f,i,lui-addi-fusion,m,no-default-unroll,use-postra-scheduler,zaamo,zalrsc,zba,zbb,zicsr,zifencei,zmmul") },
+ { str_lit("sifive-p670"), str_lit("64bit,a,auipc-addi-fusion,b,c,conditional-cmv-fusion,d,f,i,lui-addi-fusion,m,no-default-unroll,no-sink-splat-operands,unaligned-scalar-mem,unaligned-vector-mem,use-postra-scheduler,v,vxrm-pipeline-flush,za64rs,zaamo,zalrsc,zba,zbb,zbs,zfhmin,zic64b,zicbom,zicbop,zicboz,ziccamoa,ziccif,zicclsm,ziccrse,zicntr,zicsr,zifencei,zihintntl,zihintpause,zihpm,zkt,zmmul,zvbb,zvbc,zve32f,zve32x,zve64d,zve64f,zve64x,zvkb,zvkg,zvkn,zvknc,zvkned,zvkng,zvknhb,zvks,zvksc,zvksed,zvksg,zvksh,zvkt,zvl128b,zvl32b,zvl64b") },
+ { str_lit("sifive-s21"), str_lit("64bit,a,c,i,m,zaamo,zalrsc,zicsr,zifencei,zmmul") },
+ { str_lit("sifive-s51"), str_lit("64bit,a,c,i,m,zaamo,zalrsc,zicsr,zifencei,zmmul") },
+ { str_lit("sifive-s54"), str_lit("64bit,a,c,d,f,i,m,zaamo,zalrsc,zicsr,zifencei,zmmul") },
+ { str_lit("sifive-s76"), str_lit("64bit,a,c,d,f,i,m,no-default-unroll,short-forward-branch-opt,sifive7,use-postra-scheduler,zaamo,zalrsc,zicsr,zifencei,zihintpause,zmmul") },
+ { str_lit("sifive-u54"), str_lit("64bit,a,c,d,f,i,m,zaamo,zalrsc,zicsr,zifencei,zmmul") },
+ { str_lit("sifive-u74"), str_lit("64bit,a,c,d,f,i,m,no-default-unroll,short-forward-branch-opt,sifive7,use-postra-scheduler,zaamo,zalrsc,zicsr,zifencei,zmmul") },
+ { str_lit("sifive-x280"), str_lit("64bit,a,c,d,dlen-factor-2,f,i,m,no-default-unroll,optimized-nf2-segment-load-store,optimized-zero-stride-load,short-forward-branch-opt,sifive7,use-postra-scheduler,v,zaamo,zalrsc,zba,zbb,zfh,zfhmin,zicsr,zifencei,zmmul,zve32f,zve32x,zve64d,zve64f,zve64x,zvfh,zvfhmin,zvl128b,zvl256b,zvl32b,zvl512b,zvl64b") },
+ { str_lit("spacemit-x60"), str_lit("64bit,a,b,c,d,dlen-factor-2,f,i,m,optimized-nf2-segment-load-store,optimized-nf3-segment-load-store,optimized-nf4-segment-load-store,ssccptr,sscofpmf,sscounterenw,sstc,sstvala,sstvecd,svade,svbare,svinval,svnapot,svpbmt,unaligned-scalar-mem,v,vxrm-pipeline-flush,za64rs,zaamo,zalrsc,zba,zbb,zbc,zbkc,zbs,zfh,zfhmin,zic64b,zicbom,zicbop,zicboz,ziccamoa,ziccif,zicclsm,ziccrse,zicntr,zicond,zicsr,zifencei,zihintpause,zihpm,zkt,zmmul,zve32f,zve32x,zve64d,zve64f,zve64x,zvfh,zvfhmin,zvkt,zvl128b,zvl256b,zvl32b,zvl64b") },
+ { str_lit("syntacore-scr1-base"), str_lit("32bit,c,i,no-default-unroll,zicsr,zifencei") },
+ { str_lit("syntacore-scr1-max"), str_lit("32bit,c,i,m,no-default-unroll,zicsr,zifencei,zmmul") },
+ { str_lit("syntacore-scr3-rv32"), str_lit("32bit,c,i,m,no-default-unroll,use-postra-scheduler,zicsr,zifencei,zmmul") },
+ { str_lit("syntacore-scr3-rv64"), str_lit("64bit,a,c,i,m,no-default-unroll,use-postra-scheduler,zaamo,zalrsc,zicsr,zifencei,zmmul") },
+ { str_lit("syntacore-scr4-rv32"), str_lit("32bit,c,d,f,i,m,no-default-unroll,use-postra-scheduler,zicsr,zifencei,zmmul") },
+ { str_lit("syntacore-scr4-rv64"), str_lit("64bit,a,c,d,f,i,m,no-default-unroll,use-postra-scheduler,zaamo,zalrsc,zicsr,zifencei,zmmul") },
+ { str_lit("syntacore-scr5-rv32"), str_lit("32bit,a,c,d,f,i,m,no-default-unroll,use-postra-scheduler,zaamo,zalrsc,zicsr,zifencei,zmmul") },
+ { str_lit("syntacore-scr5-rv64"), str_lit("64bit,a,c,d,f,i,m,no-default-unroll,use-postra-scheduler,zaamo,zalrsc,zicsr,zifencei,zmmul") },
+ { str_lit("syntacore-scr7"), str_lit("64bit,a,c,d,f,i,m,no-default-unroll,use-postra-scheduler,v,zaamo,zalrsc,zba,zbb,zbc,zbkb,zbkc,zbkx,zbs,zicsr,zifencei,zkn,zknd,zkne,zknh,zmmul,zve32f,zve32x,zve64d,zve64f,zve64x,zvl128b,zvl32b,zvl64b") },
+ { str_lit("tt-ascalon-d8"), str_lit("64bit,a,b,c,d,f,h,i,m,no-default-unroll,optimized-zero-stride-load,sha,shcounterenw,shgatpa,shtvala,shvsatpa,shvstvala,shvstvecd,smaia,ssaia,ssccptr,sscofpmf,sscounterenw,ssnpm,ssstateen,ssstrict,sstc,sstvala,sstvecd,ssu64xl,supm,svade,svbare,svinval,svnapot,svpbmt,unaligned-scalar-mem,unaligned-vector-mem,use-postra-scheduler,v,za64rs,zaamo,zalrsc,zawrs,zba,zbb,zbs,zca,zcb,zcmop,zfa,zfbfmin,zfh,zfhmin,zic64b,zicbom,zicbop,zicboz,ziccamoa,ziccif,zicclsm,ziccrse,zicntr,zicond,zicsr,zifencei,zihintntl,zihintpause,zihpm,zimop,zkt,zmmul,zvbb,zvbc,zve32f,zve32x,zve64d,zve64f,zve64x,zvfbfmin,zvfbfwma,zvfh,zvfhmin,zvkb,zvkg,zvkn,zvkned,zvkng,zvknhb,zvkt,zvl128b,zvl256b,zvl32b,zvl64b") },
+ { str_lit("veyron-v1"), str_lit("64bit,a,auipc-addi-fusion,c,d,f,i,ld-add-fusion,lui-addi-fusion,m,shifted-zextw-fusion,ventana-veyron,xventanacondops,zaamo,zalrsc,zba,zbb,zbc,zbs,zexth-fusion,zextw-fusion,zicbom,zicbop,zicboz,zicntr,zicsr,zifencei,zihintpause,zihpm,zmmul") },
+ { str_lit("xiangshan-nanhu"), str_lit("64bit,a,c,d,f,i,m,no-default-unroll,shifted-zextw-fusion,svinval,zaamo,zalrsc,zba,zbb,zbc,zbkb,zbkc,zbkx,zbs,zexth-fusion,zextw-fusion,zicbom,zicboz,zicsr,zifencei,zkn,zknd,zkne,zknh,zksed,zksh,zmmul") },
+};
+#else
// Generated with the featuregen script in `misc/featuregen`
gb_global String target_microarch_list[TargetArch_COUNT] = {
// TargetArch_Invalid:
@@ -514,3 +1072,4 @@ gb_global MicroarchFeatureList microarch_features_list[] = {
{ str_lit("veyron-v1"), str_lit("64bit,a,auipc-addi-fusion,c,d,f,ld-add-fusion,lui-addi-fusion,m,shifted-zextw-fusion,ventana-veyron,xventanacondops,zba,zbb,zbc,zbs,zexth-fusion,zextw-fusion,zicbom,zicbop,zicboz,zicntr,zicsr,zifencei,zihintpause,zihpm") },
{ str_lit("xiangshan-nanhu"), str_lit("64bit,a,c,d,f,m,svinval,zba,zbb,zbc,zbkb,zbkc,zbkx,zbs,zicbom,zicboz,zicsr,zifencei,zkn,zknd,zkne,zknh,zksed,zksh") },
};
+#endif
diff --git a/src/bundle_command.cpp b/src/bundle_command.cpp
new file mode 100644
index 000000000..7abd48104
--- /dev/null
+++ b/src/bundle_command.cpp
@@ -0,0 +1,212 @@
+i32 bundle_android(String init_directory);
+
+i32 bundle(String init_directory) {
+ switch (build_context.command_kind) {
+ case Command_bundle_android:
+ return bundle_android(init_directory);
+ }
+ gb_printf_err("Unknown odin package <platform>\n");
+ return 1;
+}
+
+
+i32 bundle_android(String original_init_directory) {
+ TEMPORARY_ALLOCATOR_GUARD();
+
+ i32 result = 0;
+ init_android_values(/*with_sdk*/true);
+
+ bool init_directory_ok = false;
+ String init_directory = path_to_fullpath(temporary_allocator(), original_init_directory, &init_directory_ok);
+ if (!init_directory_ok) {
+ gb_printf_err("Error: '%.*s' is not a valid directory", LIT(original_init_directory));
+ return 1;
+ }
+ init_directory = normalize_path(temporary_allocator(), init_directory, NIX_SEPARATOR_STRING);
+
+ int const ODIN_ANDROID_API_LEVEL = build_context.ODIN_ANDROID_API_LEVEL;
+
+ String android_sdk_build_tools = concatenate3_strings(temporary_allocator(),
+ build_context.ODIN_ANDROID_SDK, str_lit("build-tools"), NIX_SEPARATOR_STRING);
+
+ Array<FileInfo> list = {};
+ ReadDirectoryError rd_err = read_directory(android_sdk_build_tools, &list);
+ defer (array_free(&list));
+
+ switch (rd_err) {
+ case ReadDirectory_InvalidPath:
+ gb_printf_err("Invalid path: %.*s\n", LIT(android_sdk_build_tools));
+ return 1;
+ case ReadDirectory_NotExists:
+ gb_printf_err("Path does not exist: %.*s\n", LIT(android_sdk_build_tools));
+ return 1;
+ case ReadDirectory_Permission:
+ gb_printf_err("Unknown error whilst reading path %.*s\n", LIT(android_sdk_build_tools));
+ return 1;
+ case ReadDirectory_NotDir:
+ gb_printf_err("Expected a directory for a package, got a file: %.*s\n", LIT(android_sdk_build_tools));
+ return 1;
+ case ReadDirectory_Empty:
+ gb_printf_err("Empty directory: %.*s\n", LIT(android_sdk_build_tools));
+ return 1;
+ case ReadDirectory_Unknown:
+ gb_printf_err("Unknown error whilst reading path %.*s\n", LIT(android_sdk_build_tools));
+ return 1;
+ }
+
+ auto possible_valid_dirs = array_make<FileInfo>(heap_allocator(), 0, list.count);
+ defer (array_free(&possible_valid_dirs));
+
+
+ for (FileInfo fi : list) if (fi.is_dir) {
+ bool all_numbers = true;
+ for (isize i = 0; i < fi.name.len; i++) {
+ u8 c = fi.name[i];
+ if ('0' <= c && c <= '9') {
+ // true
+ } else if (i == 0) {
+ all_numbers = false;
+ } else if (c == '.') {
+ break;
+ } else {
+ all_numbers = false;
+ }
+ }
+
+ if (all_numbers) {
+ array_add(&possible_valid_dirs, fi);
+ }
+ }
+
+ if (possible_valid_dirs.count == 0) {
+ gb_printf_err("Unable to find any Android SDK/API Level in %.*s\n", LIT(android_sdk_build_tools));
+ return 1;
+ }
+
+ int *dir_numbers = temporary_alloc_array<int>(possible_valid_dirs.count);
+
+ char buf[1024] = {};
+ for_array(i, possible_valid_dirs) {
+ FileInfo fi = possible_valid_dirs[i];
+ isize n = gb_min(gb_size_of(buf)-1, fi.name.len);
+ memcpy(buf, fi.name.text, n);
+ buf[n] = 0;
+
+ dir_numbers[i] = atoi(buf);
+ }
+
+ isize closest_number_idx = -1;
+ for (isize i = 0; i < possible_valid_dirs.count; i++) {
+ if (dir_numbers[i] >= ODIN_ANDROID_API_LEVEL) {
+ if (closest_number_idx < 0) {
+ closest_number_idx = i;
+ } else if (dir_numbers[i] < dir_numbers[closest_number_idx]) {
+ closest_number_idx = i;
+ }
+ }
+ }
+
+ if (closest_number_idx < 0) {
+ gb_printf_err("Unable to find any Android SDK/API Level in %.*s meeting the minimum API level of %d\n", LIT(android_sdk_build_tools), ODIN_ANDROID_API_LEVEL);
+ return 1;
+ }
+
+ String api_number = possible_valid_dirs[closest_number_idx].name;
+
+ android_sdk_build_tools = concatenate_strings(temporary_allocator(), android_sdk_build_tools, api_number);
+ String android_sdk_platforms = concatenate_strings(temporary_allocator(),
+ build_context.ODIN_ANDROID_SDK,
+ make_string_c(gb_bprintf("platforms/android-%d/", dir_numbers[closest_number_idx]))
+ );
+
+ android_sdk_build_tools = normalize_path(temporary_allocator(), android_sdk_build_tools, NIX_SEPARATOR_STRING);
+ android_sdk_platforms = normalize_path(temporary_allocator(), android_sdk_platforms, NIX_SEPARATOR_STRING);
+
+ gbString cmd = gb_string_make(heap_allocator(), "");
+ defer (gb_string_free(cmd));
+
+
+ String output_filename = str_lit("test");
+ String output_apk = path_remove_extension(output_filename);
+
+ TIME_SECTION("Android aapt");
+ {
+ TEMPORARY_ALLOCATOR_GUARD();
+ gb_string_clear(cmd);
+
+ String manifest = concatenate_strings(temporary_allocator(), init_directory, str_lit("AndroidManifest.xml"));
+
+ cmd = gb_string_append_length(cmd, android_sdk_build_tools.text, android_sdk_build_tools.len);
+ cmd = gb_string_appendc(cmd, "aapt");
+ cmd = gb_string_appendc(cmd, " package -f");
+ cmd = gb_string_append_fmt(cmd, " -M \"%.*s\"", LIT(manifest));
+ cmd = gb_string_append_fmt(cmd, " -I \"%.*sandroid.jar\"", LIT(android_sdk_platforms));
+ cmd = gb_string_append_fmt(cmd, " -F \"%.*s.apk-build\"", LIT(output_apk));
+
+ String resources_dir = concatenate_strings(temporary_allocator(), init_directory, str_lit("res"));
+ if (gb_file_exists((const char *)resources_dir.text)) {
+ cmd = gb_string_append_fmt(cmd, " -S \"%.*s\"", LIT(resources_dir));
+ }
+
+ String assets_dir = concatenate_strings(temporary_allocator(), init_directory, str_lit("assets"));
+ if (gb_file_exists((const char *)assets_dir.text)) {
+ cmd = gb_string_append_fmt(cmd, " -A \"%.*s\"", LIT(assets_dir));
+ }
+
+ String lib_dir = concatenate_strings(temporary_allocator(), init_directory, str_lit("lib"));
+ if (gb_file_exists((const char *)lib_dir.text)) {
+ cmd = gb_string_append_fmt(cmd, " \"%.*s\"", LIT(lib_dir));
+ }
+
+ result = system_exec_command_line_app("android-aapt", cmd);
+ if (result) {
+ return result;
+ }
+ }
+
+ TIME_SECTION("Android zipalign");
+ {
+ TEMPORARY_ALLOCATOR_GUARD();
+ gb_string_clear(cmd);
+
+ cmd = gb_string_append_length(cmd, android_sdk_build_tools.text, android_sdk_build_tools.len);
+ cmd = gb_string_appendc(cmd, "zipalign");
+ cmd = gb_string_appendc(cmd, " -f 4");
+ cmd = gb_string_append_fmt(cmd, " \"%.*s.apk-build\" \"%.*s.apk\"", LIT(output_apk), LIT(output_apk));
+
+ result = system_exec_command_line_app("android-zipalign", cmd);
+ if (result) {
+ return result;
+ }
+ }
+
+ TIME_SECTION("Android apksigner");
+ {
+ TEMPORARY_ALLOCATOR_GUARD();
+ gb_string_clear(cmd);
+
+ cmd = gb_string_append_length(cmd, android_sdk_build_tools.text, android_sdk_build_tools.len);
+ cmd = gb_string_appendc(cmd, "apksigner");
+ cmd = gb_string_appendc(cmd, " sign");
+
+ String keystore = normalize_path(temporary_allocator(), build_context.android_keystore, NIX_SEPARATOR_STRING);
+ keystore = substring(keystore, 0, keystore.len - 1);
+ cmd = gb_string_append_fmt(cmd, " --ks \"%.*s\"", LIT(keystore));
+
+ if (build_context.android_keystore_alias.len != 0) {
+ cmd = gb_string_append_fmt(cmd, " --ks-key-alias \"%.*s\"", LIT(build_context.android_keystore_alias));
+ }
+ if (build_context.android_keystore_password.len != 0) {
+ cmd = gb_string_append_fmt(cmd, " --ks-pass pass:\"%.*s\"", LIT(build_context.android_keystore_password));
+ }
+
+ cmd = gb_string_append_fmt(cmd, " \"%.*s.apk\"", LIT(output_apk));
+
+ result = system_exec_command_line_app("android-apksigner", cmd);
+ if (result) {
+ return result;
+ }
+ }
+
+ return 0;
+}
diff --git a/src/cached.cpp b/src/cached.cpp
index efdadce7b..61b5d01b4 100644
--- a/src/cached.cpp
+++ b/src/cached.cpp
@@ -231,7 +231,7 @@ Array<String> cache_gather_envs() {
wchar_t *curr_string = strings;
while (curr_string && *curr_string) {
- String16 wstr = make_string16_c(curr_string);
+ String16 wstr = make_string16_c(cast(u16 *)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="))) {
diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp
index 023aeff73..f142f04b7 100644
--- a/src/check_builtin.cpp
+++ b/src/check_builtin.cpp
@@ -1,5 +1,14 @@
typedef bool (BuiltinTypeIsProc)(Type *t);
+gb_internal int enum_constant_entity_cmp(void const* a, void const* b) {
+ Entity const *ea = *(cast(Entity const **)a);
+ Entity const *eb = *(cast(Entity const **)b);
+ GB_ASSERT(ea->kind == Entity_Constant && eb->kind == Entity_Constant);
+ GB_ASSERT(ea->Constant.value.kind == ExactValue_Integer && eb->Constant.value.kind == ExactValue_Integer);
+
+ return big_int_cmp(&ea->Constant.value.value_integer, &eb->Constant.value.value_integer);
+}
+
gb_global BuiltinTypeIsProc *builtin_type_is_procs[BuiltinProc__type_simple_boolean_end - BuiltinProc__type_simple_boolean_begin] = {
nullptr, // BuiltinProc__type_simple_boolean_begin
@@ -10,6 +19,7 @@ gb_global BuiltinTypeIsProc *builtin_type_is_procs[BuiltinProc__type_simple_bool
is_type_complex,
is_type_quaternion,
is_type_string,
+ is_type_string16,
is_type_typeid,
is_type_any,
is_type_endian_platform,
@@ -23,6 +33,7 @@ gb_global BuiltinTypeIsProc *builtin_type_is_procs[BuiltinProc__type_simple_bool
is_type_sliceable,
is_type_comparable,
is_type_simple_compare,
+ is_type_nearly_simple_compare,
is_type_dereferenceable,
is_type_valid_for_keys,
is_type_valid_for_matrix_elems,
@@ -41,6 +52,7 @@ gb_global BuiltinTypeIsProc *builtin_type_is_procs[BuiltinProc__type_simple_bool
is_type_enum,
is_type_proc,
is_type_bit_set,
+ is_type_bit_field,
is_type_simd_vector,
is_type_matrix,
@@ -147,6 +159,11 @@ gb_internal bool does_require_msgSend_stret(Type *return_type) {
if (return_type == nullptr) {
return false;
}
+
+ if (build_context.metrics.os != TargetOs_darwin) {
+ return false;
+ }
+
if (build_context.metrics.arch == TargetArch_i386 || build_context.metrics.arch == TargetArch_amd64) {
i64 struct_limit = type_size_of(t_uintptr) << 1;
return type_size_of(return_type) > struct_limit;
@@ -193,7 +210,7 @@ gb_internal ObjcMsgKind get_objc_proc_kind(Type *return_type) {
return ObjcMsg_normal;
}
-gb_internal void add_objc_proc_type(CheckerContext *c, Ast *call, Type *return_type, Slice<Type *> param_types) {
+void add_objc_proc_type(CheckerContext *c, Ast *call, Type *return_type, Slice<Type *> param_types) {
ObjcMsgKind kind = get_objc_proc_kind(return_type);
Scope *scope = create_scope(c->info, nullptr);
@@ -223,14 +240,20 @@ gb_internal void add_objc_proc_type(CheckerContext *c, Ast *call, Type *return_t
data.kind = kind;
data.proc_type = alloc_type_proc(scope, params, param_types.count, results, results->Tuple.variables.count, false, ProcCC_CDecl);
- mutex_lock(&c->info->objc_types_mutex);
+ mutex_lock(&c->info->objc_objc_msgSend_mutex);
map_set(&c->info->objc_msgSend_types, call, data);
- mutex_unlock(&c->info->objc_types_mutex);
+ mutex_unlock(&c->info->objc_objc_msgSend_mutex);
try_to_add_package_dependency(c, "runtime", "objc_msgSend");
try_to_add_package_dependency(c, "runtime", "objc_msgSend_fpret");
try_to_add_package_dependency(c, "runtime", "objc_msgSend_fp2ret");
try_to_add_package_dependency(c, "runtime", "objc_msgSend_stret");
+
+ Slice<Ast *> args = call->CallExpr.args;
+ if (args.count > 0 && args[0]->tav.objc_super_target) {
+ try_to_add_package_dependency(c, "runtime", "objc_msgSendSuper2");
+ try_to_add_package_dependency(c, "runtime", "objc_msgSendSuper2_stret");
+ }
}
gb_internal bool is_constant_string(CheckerContext *c, String const &builtin_name, Ast *expr, String *name_) {
@@ -337,7 +360,7 @@ gb_internal bool check_builtin_objc_procedure(CheckerContext *c, Operand *operan
}
isize const arg_offset = 1;
- auto param_types = slice_make<Type *>(permanent_allocator(), ce->args.count-arg_offset);
+ auto param_types = permanent_slice_make<Type *>(ce->args.count-arg_offset);
param_types[0] = t_objc_id;
param_types[1] = sel_type;
@@ -387,6 +410,328 @@ gb_internal bool check_builtin_objc_procedure(CheckerContext *c, Operand *operan
try_to_add_package_dependency(c, "runtime", "objc_allocateClassPair");
return true;
} break;
+
+ case BuiltinProc_objc_ivar_get:
+ {
+ Type *self_type = nullptr;
+
+ Operand self = {};
+ check_expr_or_type(c, &self, ce->args[0]);
+
+ if (!is_operand_value(self) || !check_is_assignable_to(c, &self, t_objc_id)) {
+ gbString e = expr_to_string(self.expr);
+ gbString t = type_to_string(self.type);
+ error(self.expr, "'%.*s' expected a type or value derived from intrinsics.objc_object, got '%s' of type %s", LIT(builtin_name), e, t);
+ gb_string_free(t);
+ gb_string_free(e);
+ return false;
+ } else if (!is_type_pointer(self.type)) {
+ gbString e = expr_to_string(self.expr);
+ gbString t = type_to_string(self.type);
+ error(self.expr, "'%.*s' expected a pointer of a value derived from intrinsics.objc_object, got '%s' of type %s", LIT(builtin_name), e, t);
+ gb_string_free(t);
+ gb_string_free(e);
+ return false;
+ }
+
+ self_type = type_deref(self.type);
+
+ if (!(self_type->kind == Type_Named &&
+ self_type->Named.type_name != nullptr &&
+ self_type->Named.type_name->TypeName.objc_class_name != "")) {
+ gbString t = type_to_string(self_type);
+ error(self.expr, "'%.*s' expected a named type with the attribute @(objc_class=<string>) , got type %s", LIT(builtin_name), t);
+ gb_string_free(t);
+ return false;
+ }
+
+ Type *ivar_type = self_type->Named.type_name->TypeName.objc_ivar;
+ if (ivar_type == nullptr) {
+ gbString t = type_to_string(self_type);
+ error(self.expr, "'%.*s' requires that type %s have the attribute @(objc_ivar=<ivar_type_name>).", LIT(builtin_name), t);
+ gb_string_free(t);
+ return false;
+ }
+
+ if (type_hint != nullptr && type_hint->kind == Type_Pointer && type_hint->Pointer.elem == ivar_type) {
+ operand->type = type_hint;
+ } else {
+ operand->type = alloc_type_pointer(ivar_type);
+ }
+
+ operand->mode = Addressing_Value;
+ return true;
+
+ } break;
+
+ case BuiltinProc_objc_block:
+ {
+ // NOTE(harold): The last argument specified in the call is the handler proc,
+ // any other arguments before it are capture by-copy arguments.
+ auto param_operands = permanent_slice_make<Operand>(ce->args.count);
+
+ isize capture_arg_count = ce->args.count - 1;
+
+ // NOTE(harold): The first argument is already checked at check_builtin_procedure().
+ // Checking again would invalidate the Entity -> Value map for direct arguments if it's the handler proc.
+ param_operands[0] = *operand;
+
+ for (isize i = 0; i < ce->args.count-1; i++) {
+ Operand x = {};
+ check_expr(c, &x, ce->args[i]);
+
+ switch (x.mode) {
+ case Addressing_Value:
+ case Addressing_Context:
+ case Addressing_Variable:
+ case Addressing_Constant:
+ param_operands[i] = x;
+ break;
+
+ default:
+ gbString e = expr_to_string(x.expr);
+ gbString t = type_to_string(x.type);
+ error(x.expr, "'%.*s' capture arguments must be values, but got %s of type %s", LIT(builtin_name), e, t);
+ gb_string_free(t);
+ gb_string_free(e);
+ return false;
+ }
+ }
+
+ // Validate handler proc
+ Operand handler = {};
+
+ if (capture_arg_count == 0) {
+ // It's already been checked and assigned
+ handler = param_operands[0];
+ } else {
+ check_expr_or_type(c, &handler, ce->args[capture_arg_count]);
+ param_operands[capture_arg_count] = handler;
+ }
+
+ if (!is_operand_value(handler) || handler.type->kind != Type_Proc) {
+ gbString e = expr_to_string(handler.expr);
+ gbString t = type_to_string(handler.type);
+ error(handler.expr, "'%.*s' expected a procedure, but got '%s' of type %s", LIT(builtin_name), e, t);
+ gb_string_free(t);
+ gb_string_free(e);
+ return false;
+ }
+
+ Ast *handler_node = unparen_expr(handler.expr);
+
+ // Only direct reference to procs are allowed
+ switch (handler_node->kind) {
+ case Ast_ProcLit: break; // ok
+ case Ast_Ident: {
+ auto& ident = handler_node->Ident;
+
+ if (ident.entity == nullptr) {
+ error(handler.expr, "'%.*s' failed to resolve entity from expression", LIT(builtin_name));
+ return false;
+ }
+
+ if (ident.entity->kind != Entity_Procedure) {
+ gbString e = expr_to_string(handler_node);
+
+ ERROR_BLOCK();
+ error(handler.expr, "'%.*s' expected a direct reference to a procedure", LIT(builtin_name));
+ if(ident.entity->kind == Entity_Variable) {
+ error_line("\tSuggestion: Variables referencing a procedure are not allowed, they are not a direct procedure reference.");
+ } else {
+ error_line("\tSuggestion: Ensure '%s' is not a runtime-evaluated expression.", e); // NOTE(harold): Is this case possible to hit?
+ }
+ error_line("\n\t Refer to a procedure directly by its name or declare it anonymously: %.*s(proc(){})", LIT(builtin_name));
+
+ gb_string_free(e);
+ return false;
+ }
+ } break;
+
+ default: {
+ gbString e = expr_to_string(handler_node);
+ ERROR_BLOCK();
+ error(handler.expr, "'%.*s' expected a direct reference to a procedure", LIT(builtin_name));
+ if( handler_node->kind == Ast_CallExpr) {
+ error_line("\tSuggestion: Do not use a procedure returned from another procedure.");
+ } else {
+ error_line("\tSuggestion: Ensure '%s' is not a runtime-evaluated expression.", e);
+ }
+ error_line("\n\t Refer to a procedure directly by its name or declare it anonymously: %.*s(proc(){})", LIT(builtin_name));
+
+ gb_string_free(e);
+ } return false;
+ } // End switch
+
+ auto& handler_type_proc = handler.type->Proc;
+
+ if (capture_arg_count > handler_type_proc.param_count) {
+ error(handler.expr, "'%.*s' captured arguments exceeded the handler's parameter count", LIT(builtin_name));
+ return false;
+ }
+
+ // If the handler proc is odin calling convention, but there must be a context defined in this scope.
+ if (handler_type_proc.calling_convention == ProcCC_Odin) {
+ if ((c->scope->flags & ScopeFlag_ContextDefined) == 0) {
+ ERROR_BLOCK();
+ error(handler.expr, "The handler procedure for '%.*s' requires a context, but no context is defined in the current scope", LIT(builtin_name));
+ error_line("\tSuggestion: 'context = runtime.default_context()', or use the \"c\" calling convention for the handler procedure");
+ return false;
+ }
+ }
+
+ // At most a single return value is supported
+ if (handler_type_proc.result_count > 1) {
+ error(handler_type_proc.node->ProcType.results, "Handler procedures for '%.*s' cannot have multiple return values", LIT(builtin_name));
+ return false;
+ }
+
+ // Ensure that captured args are assignable to the handler's corresponding capture params
+ if (handler_type_proc.param_count > 0) {
+ auto& handler_param_types = handler.type->Proc.params->Tuple.variables;
+ Slice<Entity *> handler_capture_param_types = slice(handler_param_types, handler_param_types.count - capture_arg_count, handler_param_types.count);
+
+ for (isize i = 0; i < capture_arg_count; i++) {
+ Operand op = param_operands[i];
+ if (!check_is_assignable_to(c, &op, handler_capture_param_types[i]->type)) {
+ gbString e = expr_to_string(op.expr);
+ gbString src = type_to_string(op.type);
+ gbString dst = type_to_string(handler_capture_param_types[i]->type);
+ error(op.expr, "'%.*s' captured value '%s' of type '%s' is not assignable to type '%s'", LIT(builtin_name), e, src, dst);
+ gb_string_free(e);
+ gb_string_free(src);
+ gb_string_free(dst);
+ return false;
+ }
+ }
+ }
+
+ ProcCallingConvention cc = handler_type_proc.calling_convention;
+ switch (cc) {
+ case ProcCC_Odin:
+ case ProcCC_Contextless:
+ case ProcCC_CDecl:
+ break; // ok
+ default:
+ ERROR_BLOCK();
+
+ error(handler.expr, "'%.*s' Invalid calling convention for block procedure.", LIT(builtin_name));
+ error_line("\tSuggestion: Do not specify a calling convention ot else use \"c\" or \"cotextless\"");
+ return false;
+ }
+
+ if (handler_type_proc.is_polymorphic) {
+ error(handler.expr, "'%.*s' Unspecialized polymorphic procedures are not allowed.", LIT(builtin_name));
+ return false;
+ }
+
+ // Create the specialized Objc_Block type that this intrinsic will return
+ Token ident = {};
+ ident.kind = Token_Ident;
+ ident.string = str_lit("Objc_Block");
+ ident.pos = ast_token(call).pos;
+
+ Token l_paren = {};
+ l_paren.kind = Token_OpenParen;
+ l_paren.string = str_lit("(");
+ l_paren.pos = ident.pos;
+
+ Token r_paren = {};
+ r_paren.kind = Token_CloseParen;
+ l_paren.string = str_lit(")");
+ r_paren.pos = ident.pos;
+
+ // Remove the capture args from the resulting Objc_Block type signature
+ Ast* handler_proc_type_copy = clone_ast(handler_type_proc.node);
+ handler_proc_type_copy->ProcType.params->FieldList.list.count -= capture_arg_count;
+
+ // Make sure the Objc_Block's specialized proc is always "c" calling conv,
+ // even if we have a context, as the invoker is always "c".
+ // This allows us to have compatibility with the target block types with either calling convention used.
+ handler_proc_type_copy->ProcType.calling_convention = ProcCC_CDecl;
+
+ Array<Ast *> poly_args = {};
+ array_init(&poly_args, permanent_allocator(), 1, 1);
+ poly_args[0] = handler_proc_type_copy;
+
+
+ Type *t_Objc_Block = find_core_type(c->checker, str_lit("Objc_Block"));
+ Operand poly_op = {};
+ poly_op.type = t_Objc_Block;
+ poly_op.mode = Addressing_Type;
+
+ Ast *poly_call = ast_call_expr(nullptr, ast_ident(nullptr, ident), poly_args, l_paren, r_paren, {});
+
+ auto err = check_polymorphic_record_type(c, &poly_op, poly_call);
+
+ if (err != 0) {
+ operand->mode = Addressing_Invalid;
+ operand->type = t_invalid;
+ error(handler.expr, "'%.*s' failed to determine resulting Objc_Block handler procedure", LIT(builtin_name));
+ return false;
+ }
+
+ GB_ASSERT(poly_op.type != t_Objc_Block);
+ GB_ASSERT(poly_op.mode == Addressing_Type);
+
+ bool is_global_block = capture_arg_count == 0 && handler_type_proc.calling_convention != ProcCC_Odin;
+ if (is_global_block) {
+ try_to_add_package_dependency(c, "runtime", "_NSConcreteGlobalBlock");
+ } else {
+ try_to_add_package_dependency(c, "runtime", "_NSConcreteStackBlock");
+ }
+
+ *operand = poly_op;
+ operand->type = alloc_type_pointer(operand->type);
+ operand->mode = Addressing_Value;
+ return true;
+ } break;
+
+ case BuiltinProc_objc_super:
+ {
+ // Must be a pointer to an Objective-C object.
+ Type *objc_obj = operand->type;
+ if (!is_type_objc_ptr_to_object(objc_obj)) {
+ gbString e = expr_to_string(operand->expr);
+ gbString t = type_to_string(objc_obj);
+ error(operand->expr, "'%.*s' expected a pointer to an Objective-C object, but got '%s' of type %s", LIT(builtin_name), e, t);
+ gb_string_free(t);
+ gb_string_free(e);
+ return false;
+ }
+
+ if (operand->mode != Addressing_Value && operand->mode != Addressing_Variable) {
+ gbString e = expr_to_string(operand->expr);
+ gbString t = type_to_string(operand->type);
+ error(operand->expr, "'%.*s' expression '%s', of type %s, must be a value or variable.", LIT(builtin_name), e, t);
+ gb_string_free(t);
+ gb_string_free(e);
+ return false;
+ }
+
+ Type *obj_type = type_deref(objc_obj);
+ GB_ASSERT(obj_type->kind == Type_Named);
+
+ // NOTE(harold) Track original type before transforming it to the superclass.
+ // This is needed because objc_msgSendSuper2 must start its search on the subclass, not the superclass.
+ call->tav.objc_super_target = obj_type;
+
+ // The superclass type must be known at compile time. We require this so that the selector method expressions
+ // methods are resolved to the superclass's methods instead of the subclass's.
+ Type *superclass = obj_type->Named.type_name->TypeName.objc_superclass;
+ if (superclass == nullptr) {
+ gbString t = type_to_string(obj_type);
+ error(operand->expr, "'%.*s' target object '%.*s' does not have an Objective-C superclass. One must be set via the @(objc_superclass) attribute", LIT(builtin_name), t);
+ gb_string_free(t);
+ return false;
+ }
+
+ GB_ASSERT(superclass->Named.type_name->TypeName.objc_class_name.len > 0);
+
+ operand->type = alloc_type_pointer(superclass);
+ return true;
+
+ } break;
}
}
@@ -645,6 +990,13 @@ gb_internal bool check_builtin_simd_operation(CheckerContext *c, Operand *operan
break;
}
+ if (!are_types_identical(x.type, y.type)) {
+ gbString tx = type_to_string(x.type);
+ gbString ty = type_to_string(y.type);
+ error(call, "Mismatched types to '%.*s', '%s' vs '%s'", LIT(builtin_name), tx, ty);
+ gb_string_free(ty);
+ gb_string_free(tx);
+ }
Type *vt = base_type(x.type);
GB_ASSERT(vt->kind == Type_SimdVector);
@@ -753,6 +1105,36 @@ gb_internal bool check_builtin_simd_operation(CheckerContext *c, Operand *operan
return true;
}
+ case BuiltinProc_simd_indices:
+ {
+ Operand x = {};
+ check_expr_or_type(c, &x, ce->args[0], nullptr);
+ if (x.mode == Addressing_Invalid) return false;
+ if (x.mode != Addressing_Type) {
+ gbString s = expr_to_string(x.expr);
+ error(x.expr, "'%.*s' expected a simd vector type, got '%s'", LIT(builtin_name), s);
+ gb_string_free(s);
+ return false;
+ }
+ if (!is_type_simd_vector(x.type)) {
+ gbString s = type_to_string(x.type);
+ error(x.expr, "'%.*s' expected a simd vector type, got '%s'", LIT(builtin_name), s);
+ gb_string_free(s);
+ return false;
+ }
+
+ Type *elem = base_array_type(x.type);
+ if (!is_type_numeric(elem)) {
+ gbString s = type_to_string(x.type);
+ error(x.expr, "'%.*s' expected a simd vector type with a numeric element type, got '%s'", LIT(builtin_name), s);
+ gb_string_free(s);
+ }
+
+ operand->mode = Addressing_Value;
+ operand->type = x.type;
+ return true;
+ }
+
case BuiltinProc_simd_extract:
{
Operand x = {};
@@ -816,8 +1198,12 @@ gb_internal bool check_builtin_simd_operation(CheckerContext *c, Operand *operan
}
break;
+ case BuiltinProc_simd_reduce_add_bisect:
+ case BuiltinProc_simd_reduce_mul_bisect:
case BuiltinProc_simd_reduce_add_ordered:
case BuiltinProc_simd_reduce_mul_ordered:
+ case BuiltinProc_simd_reduce_add_pairs:
+ case BuiltinProc_simd_reduce_mul_pairs:
case BuiltinProc_simd_reduce_min:
case BuiltinProc_simd_reduce_max:
{
@@ -1050,6 +1436,58 @@ gb_internal bool check_builtin_simd_operation(CheckerContext *c, Operand *operan
return true;
}
+ case BuiltinProc_simd_runtime_swizzle:
+ {
+ if (ce->args.count != 2) {
+ error(call, "'%.*s' expected 2 arguments, got %td", LIT(builtin_name), ce->args.count);
+ return false;
+ }
+
+ Operand src = {};
+ Operand indices = {};
+ check_expr(c, &src, ce->args[0]); if (src.mode == Addressing_Invalid) return false;
+ check_expr_with_type_hint(c, &indices, ce->args[1], src.type); if (indices.mode == Addressing_Invalid) return false;
+
+ if (!is_type_simd_vector(src.type)) {
+ error(src.expr, "'%.*s' expected first argument to be a simd vector", LIT(builtin_name));
+ return false;
+ }
+ if (!is_type_simd_vector(indices.type)) {
+ error(indices.expr, "'%.*s' expected second argument (indices) to be a simd vector", LIT(builtin_name));
+ return false;
+ }
+
+ Type *src_elem = base_array_type(src.type);
+ Type *indices_elem = base_array_type(indices.type);
+
+ if (!is_type_integer(src_elem)) {
+ gbString src_str = type_to_string(src.type);
+ error(src.expr, "'%.*s' expected first argument to be a simd vector of integers, got '%s'", LIT(builtin_name), src_str);
+ gb_string_free(src_str);
+ return false;
+ }
+
+ if (!is_type_integer(indices_elem)) {
+ gbString indices_str = type_to_string(indices.type);
+ error(indices.expr, "'%.*s' expected indices to be a simd vector of integers, got '%s'", LIT(builtin_name), indices_str);
+ gb_string_free(indices_str);
+ return false;
+ }
+
+ if (!are_types_identical(src.type, indices.type)) {
+ gbString src_str = type_to_string(src.type);
+ gbString indices_str = type_to_string(indices.type);
+ error(indices.expr, "'%.*s' expected both arguments to have the same type, got '%s' vs '%s'", LIT(builtin_name), src_str, indices_str);
+ gb_string_free(indices_str);
+ gb_string_free(src_str);
+ return false;
+ }
+
+ operand->mode = Addressing_Value;
+ operand->type = src.type;
+ return true;
+ }
+
case BuiltinProc_simd_ceil:
case BuiltinProc_simd_floor:
case BuiltinProc_simd_trunc:
@@ -1675,12 +2113,16 @@ gb_internal bool check_builtin_procedure_directive(CheckerContext *c, Operand *o
}
if (ce->args.count > 0) {
Ast *arg = ce->args[0];
- Operand o = {};
- Entity *e = check_ident(c, &o, arg, nullptr, nullptr, true);
- if (e == nullptr || (e->flags & EntityFlag_Param) == 0) {
- error(ce->args[0], "'#caller_expression' expected a valid earlier parameter name");
+ if (arg->kind != Ast_Ident) {
+ error(arg, "'#caller_expression' expected an identifier");
+ } else {
+ Operand o = {};
+ Entity *e = check_ident(c, &o, arg, nullptr, nullptr, true);
+ if (e == nullptr || (e->flags & EntityFlag_Param) == 0) {
+ error(arg, "'#caller_expression' expected a valid earlier parameter name");
+ }
+ arg->Ident.entity = e;
}
- arg->Ident.entity = e;
}
operand->type = t_string;
@@ -2048,6 +2490,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
case BuiltinProc_atomic_type_is_lock_free:
case BuiltinProc_has_target_feature:
case BuiltinProc_procedure_of:
+ case BuiltinProc_simd_indices:
// NOTE(bill): The first arg may be a Type, this will be checked case by case
break;
@@ -2121,7 +2564,10 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
case BuiltinProc_objc_find_selector:
case BuiltinProc_objc_find_class:
case BuiltinProc_objc_register_selector:
- case BuiltinProc_objc_register_class:
+ case BuiltinProc_objc_register_class:
+ case BuiltinProc_objc_ivar_get:
+ case BuiltinProc_objc_block:
+ case BuiltinProc_objc_super:
return check_builtin_objc_procedure(c, operand, call, id, type_hint);
case BuiltinProc___entry_point:
@@ -2160,13 +2606,23 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
if (is_type_string(op_type) && id == BuiltinProc_len) {
if (operand->mode == Addressing_Constant) {
mode = Addressing_Constant;
- String str = operand->value.value_string;
- value = exact_value_i64(str.len);
+
+ if (operand->value.kind == ExactValue_String) {
+ String str = operand->value.value_string;
+ value = exact_value_i64(str.len);
+ } else if (operand->value.kind == ExactValue_String16) {
+ String16 str = operand->value.value_string16;
+ value = exact_value_i64(str.len);
+ } else {
+ GB_PANIC("Unhandled value kind: %d", operand->value.kind);
+ }
type = t_untyped_integer;
} else {
mode = Addressing_Value;
if (is_type_cstring(op_type)) {
add_package_dependency(c, "runtime", "cstring_len");
+ } else if (is_type_cstring16(op_type)) {
+ add_package_dependency(c, "runtime", "cstring16_len");
}
}
} else if (is_type_array(op_type)) {
@@ -2218,7 +2674,11 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
if (mode == Addressing_Invalid) {
gbString t = type_to_string(operand->type);
- error(call, "'%.*s' is not supported for '%s'", LIT(builtin_name), t);
+ if (is_type_bit_set(op_type) && id == BuiltinProc_len) {
+ error(call, "'%.*s' is not supported for '%s', did you mean 'card'?", LIT(builtin_name), t);
+ } else {
+ error(call, "'%.*s' is not supported for '%s'", LIT(builtin_name), t);
+ }
return false;
}
@@ -2771,8 +3231,6 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
// quaternion :: proc(imag, jmag, kmag, real: float_type) -> complex_type
Operand xyzw[4] = {};
- u32 first_index = 0;
-
// NOTE(bill): Invalid will be the default till fixed
operand->type = t_invalid;
operand->mode = Addressing_Invalid;
@@ -2817,6 +3275,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
if (fields_set[*index]) {
error(field->field, "Previously assigned field: '%.*s'", LIT(name));
+ return false;
}
fields_set[*index] = style;
@@ -2833,7 +3292,6 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
if (!ok || index < 0) {
return false;
}
- first_index = cast(u32)index;
*refs[index] = o;
}
@@ -2858,12 +3316,17 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
}
- for (u32 i = 0; i < 4; i++ ){
- u32 j = (i + first_index) % 4;
- if (j == first_index) {
- convert_to_typed(c, &xyzw[j], xyzw[(first_index+1)%4].type); if (xyzw[j].mode == Addressing_Invalid) return false;
- } else {
- convert_to_typed(c, &xyzw[j], xyzw[first_index].type); if (xyzw[j].mode == Addressing_Invalid) return false;
+ // The first typed value found, if any exist, will dictate the type for all untyped values.
+ for (u32 i = 0; i < 4; i++) {
+ if (is_type_typed(xyzw[i].type)) {
+ for (u32 j = 0; j < 4; j++) {
+ // `convert_to_typed` should check if it is typed already.
+ convert_to_typed(c, &xyzw[j], xyzw[i].type);
+ if (xyzw[j].mode == Addressing_Invalid) {
+ return false;
+ }
+ }
+ break;
}
}
if (xyzw[0].mode == Addressing_Constant &&
@@ -2887,7 +3350,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
gbString ty = type_to_string(xyzw[1].type);
gbString tz = type_to_string(xyzw[2].type);
gbString tw = type_to_string(xyzw[3].type);
- error(call, "Mismatched types to 'quaternion', 'x=%s' vs 'y=%s' vs 'z=%s' vs 'w=%s'", tx, ty, tz, tw);
+ error(call, "Mismatched types to 'quaternion', 'w=%s' vs 'x=%s' vs 'y=%s' vs 'z=%s'", tw, tx, ty, tz);
gb_string_free(tw);
gb_string_free(tz);
gb_string_free(ty);
@@ -2923,7 +3386,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
operand->mode = Addressing_Constant;
}
- BasicKind kind = core_type(xyzw[first_index].type)->Basic.kind;
+ BasicKind kind = core_type(xyzw[0].type)->Basic.kind;
switch (kind) {
case Basic_f16: operand->type = t_quaternion64; break;
case Basic_f32: operand->type = t_quaternion128; break;
@@ -3143,6 +3606,194 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
break;
}
+ case BuiltinProc_compress_values: {
+ Operand *ops = temporary_alloc_array<Operand>(ce->args.count);
+
+ isize value_count = 0;
+
+ for_array(i, ce->args) {
+ Ast *arg = ce->args[i];
+ Operand *op = &ops[i];
+ check_multi_expr(c, op, arg);
+ if (op->mode == Addressing_Invalid) {
+ return false;
+ }
+
+ if (op->type == nullptr || op->type == t_invalid) {
+ gbString s = expr_to_string(op->expr);
+ error(op->expr, "Invalid expression to '%.*s', got %s", LIT(builtin_name), s);
+ gb_string_free(s);
+ }
+ if (is_type_tuple(op->type)) {
+ value_count += op->type->Tuple.variables.count;
+ } else {
+ value_count += 1;
+ }
+ }
+
+ GB_ASSERT(value_count >= 1);
+
+ if (value_count == 1) {
+ *operand = ops[0];
+ break;
+ }
+
+ if (type_hint != nullptr) {
+ Type *th = base_type(type_hint);
+ if (th->kind == Type_Struct) {
+ if (value_count == th->Struct.fields.count) {
+ isize index = 0;
+ for_array(i, ce->args) {
+ Operand *op = &ops[i];
+ if (is_type_tuple(op->type)) {
+ for (Entity *v : op->type->Tuple.variables) {
+ Operand x = {};
+ x.mode = Addressing_Value;
+ x.type = v->type;
+ check_assignment(c, &x, th->Struct.fields[index++]->type, builtin_name);
+ if (x.mode == Addressing_Invalid) {
+ return false;
+ }
+ }
+ } else {
+ check_assignment(c, op, th->Struct.fields[index++]->type, builtin_name);
+ if (op->mode == Addressing_Invalid) {
+ return false;
+ }
+ }
+ }
+
+ operand->type = type_hint;
+ operand->mode = Addressing_Value;
+ break;
+ }
+ } else if (is_type_array_like(th)) {
+ if (cast(i64)value_count == get_array_type_count(th)) {
+ Type *elem = base_array_type(th);
+ for_array(i, ce->args) {
+ Operand *op = &ops[i];
+ if (is_type_tuple(op->type)) {
+ for (Entity *v : op->type->Tuple.variables) {
+ Operand x = {};
+ x.mode = Addressing_Value;
+ x.type = v->type;
+ check_assignment(c, &x, elem, builtin_name);
+ if (x.mode == Addressing_Invalid) {
+ return false;
+ }
+ }
+ } else {
+ check_assignment(c, op, elem, builtin_name);
+ if (op->mode == Addressing_Invalid) {
+ return false;
+ }
+ }
+ }
+
+ operand->type = type_hint;
+ operand->mode = Addressing_Value;
+ break;
+ }
+ }
+ }
+
+ bool all_types_the_same = true;
+ Type *last_type = nullptr;
+ for_array(i, ce->args) {
+ Operand *op = &ops[i];
+ if (is_type_tuple(op->type)) {
+ if (last_type == nullptr) {
+ op->type->Tuple.variables[0]->type;
+ }
+ for (Entity *v : op->type->Tuple.variables) {
+ if (!are_types_identical(last_type, v->type)) {
+ all_types_the_same = false;
+ break;
+ }
+ last_type = v->type;
+ }
+ } else {
+ if (last_type == nullptr) {
+ last_type = op->type;
+ } else {
+ if (!are_types_identical(last_type, op->type)) {
+ all_types_the_same = false;
+ break;
+ }
+ last_type = op->type;
+ }
+ }
+ }
+
+ if (all_types_the_same) {
+ Type *elem_type = default_type(last_type);
+ if (is_type_untyped(elem_type)) {
+ gbString s = expr_to_string(call);
+ error(call, "Invalid use of '%s' in '%.*s'", s, LIT(builtin_name));
+ gb_string_free(s);
+ return false;
+ }
+
+ operand->type = alloc_type_array(elem_type, value_count);
+ operand->mode = Addressing_Value;
+ } else {
+ Type *st = alloc_type_struct_complete();
+ st->Struct.fields = permanent_slice_make<Entity *>(value_count);
+ st->Struct.tags = permanent_alloc_array<String>(value_count);
+ st->Struct.offsets = permanent_alloc_array<i64>(value_count);
+
+ Scope *scope = create_scope(c->info, nullptr);
+
+ Token token = {};
+ token.kind = Token_Ident;
+ token.pos = ast_token(call).pos;
+
+ isize index = 0;
+ for_array(i, ce->args) {
+ Operand *op = &ops[i];
+ if (is_type_tuple(op->type)) {
+ for (Entity *v : op->type->Tuple.variables) {
+ Type *t = default_type(v->type);
+ if (is_type_untyped(t)) {
+ gbString s = expr_to_string(op->expr);
+ error(op->expr, "Invalid use of '%s' in '%.*s'", s, LIT(builtin_name));
+ gb_string_free(s);
+ return false;
+ }
+
+ gbString s = gb_string_make_reserve(permanent_allocator(), 32);
+ s = gb_string_append_fmt(s, "v%lld", cast(long long)index);
+ token.string = make_string_c(s);
+ Entity *e = alloc_entity_field(scope, token, t, false, cast(i32)index, EntityState_Resolved);
+ st->Struct.fields[index++] = e;
+ }
+ } else {
+ Type *t = default_type(op->type);
+ if (is_type_untyped(t)) {
+ gbString s = expr_to_string(op->expr);
+ error(op->expr, "Invalid use of '%s' in '%.*s'", s, LIT(builtin_name));
+ gb_string_free(s);
+ return false;
+ }
+
+ gbString s = gb_string_make_reserve(permanent_allocator(), 32);
+ s = gb_string_append_fmt(s, "v%lld", cast(long long)index);
+ token.string = make_string_c(s);
+ Entity *e = alloc_entity_field(scope, token, t, false, cast(i32)index, EntityState_Resolved);
+ st->Struct.fields[index++] = e;
+ }
+ }
+
+
+ gb_unused(type_size_of(st));
+
+ operand->type = st;
+ operand->mode = Addressing_Value;
+ }
+ break;
+ }
+
+
case BuiltinProc_min: {
// min :: proc($T: typeid) -> ordered
// min :: proc(a: ..ordered) -> ordered
@@ -3789,7 +4440,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
elem = alloc_type_struct();
elem->Struct.scope = s;
elem->Struct.fields = slice_from_array(fields);
- elem->Struct.tags = gb_alloc_array(permanent_allocator(), String, fields.count);
+ elem->Struct.tags = permanent_alloc_array<String>(fields.count);
elem->Struct.node = dummy_node_struct;
type_set_offsets(elem);
wait_signal_set(&elem->Struct.fields_wait_signal);
@@ -3821,7 +4472,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
gb_string_free(s);
return false;
}
- auto types = slice_make<Type *>(permanent_allocator(), t->Struct.fields.count-1);
+ auto types = permanent_slice_make<Type *>(t->Struct.fields.count-1);
for_array(i, types) {
Entity *f = t->Struct.fields[i];
GB_ASSERT(f->type->kind == Type_MultiPointer);
@@ -4157,8 +4808,8 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
if (is_type_array(elem)) {
Type *old_array = base_type(elem);
soa_struct = alloc_type_struct();
- soa_struct->Struct.fields = slice_make<Entity *>(heap_allocator(), cast(isize)old_array->Array.count);
- soa_struct->Struct.tags = gb_alloc_array(permanent_allocator(), String, cast(isize)old_array->Array.count);
+ soa_struct->Struct.fields = permanent_slice_make<Entity *>(cast(isize)old_array->Array.count);
+ soa_struct->Struct.tags = permanent_alloc_array<String>(cast(isize)old_array->Array.count);
soa_struct->Struct.node = operand->expr;
soa_struct->Struct.soa_kind = StructSoa_Fixed;
soa_struct->Struct.soa_elem = elem;
@@ -4190,8 +4841,8 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
Type *old_struct = base_type(elem);
soa_struct = alloc_type_struct();
- soa_struct->Struct.fields = slice_make<Entity *>(heap_allocator(), old_struct->Struct.fields.count);
- soa_struct->Struct.tags = gb_alloc_array(permanent_allocator(), String, old_struct->Struct.fields.count);
+ soa_struct->Struct.fields = permanent_slice_make<Entity *>(old_struct->Struct.fields.count);
+ soa_struct->Struct.tags = permanent_alloc_array<String>(old_struct->Struct.fields.count);
soa_struct->Struct.node = operand->expr;
soa_struct->Struct.soa_kind = StructSoa_Fixed;
soa_struct->Struct.soa_elem = elem;
@@ -4321,7 +4972,9 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
break;
case Type_Basic:
if (t->Basic.kind == Basic_string) {
- operand->type = alloc_type_multi_pointer(t_u8);
+ operand->type = t_u8_multi_ptr;
+ } else if (t->Basic.kind == Basic_string16) {
+ operand->type = t_u16_multi_ptr;
}
break;
case Type_Pointer:
@@ -4351,6 +5004,15 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
}
break;
+ case BuiltinProc_read_cycle_counter_frequency:
+ if (build_context.metrics.arch != TargetArch_arm64) {
+ error(call, "'%.*s' is only allowed on arm64 targets", LIT(builtin_name));
+ return false;
+ }
+ operand->mode = Addressing_Value;
+ operand->type = t_i64;
+ break;
+
case BuiltinProc_read_cycle_counter:
operand->mode = Addressing_Value;
operand->type = t_i64;
@@ -5301,7 +5963,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
}
if (sz >= 64) {
- if (is_type_unsigned(x.type)) {
+ if (is_type_unsigned(x.type) || is_type_unsigned(y.type)) {
add_package_dependency(c, "runtime", "umodti3", true);
add_package_dependency(c, "runtime", "udivti3", true);
} else {
@@ -5539,6 +6201,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
case Type_EnumeratedArray: operand->type = bt->EnumeratedArray.elem; break;
case Type_Slice: operand->type = bt->Slice.elem; break;
case Type_DynamicArray: operand->type = bt->DynamicArray.elem; break;
+ case Type_SimdVector: operand->type = bt->SimdVector.elem; break;
}
}
operand->mode = Addressing_Type;
@@ -5571,7 +6234,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
}
Type *new_type = alloc_type_union();
- auto variants = slice_make<Type *>(permanent_allocator(), bt->Union.variants.count);
+ auto variants = permanent_slice_make<Type *>(bt->Union.variants.count);
for_array(i, bt->Union.variants) {
variants[i] = alloc_type_pointer(bt->Union.variants[i]);
}
@@ -5588,6 +6251,87 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
}
operand->mode = Addressing_Type;
break;
+ case BuiltinProc_type_integer_to_unsigned:
+ if (operand->mode != Addressing_Type) {
+ error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name));
+ return false;
+ }
+
+ if (is_type_polymorphic(operand->type)) {
+ gbString t = type_to_string(operand->type);
+ error(operand->expr, "Expected a non-polymorphic type for '%.*s', got %s", LIT(builtin_name), t);
+ gb_string_free(t);
+ return false;
+ }
+
+ {
+ Type *bt = base_type(operand->type);
+
+ if (bt->kind != Type_Basic ||
+ (bt->Basic.flags & BasicFlag_Unsigned) != 0 ||
+ (bt->Basic.flags & BasicFlag_Integer) == 0) {
+ gbString t = type_to_string(operand->type);
+ error(operand->expr, "Expected a signed integer type for '%.*s', got %s", LIT(builtin_name), t);
+ gb_string_free(t);
+ return false;
+ }
+
+ if ((bt->Basic.flags & BasicFlag_Untyped) != 0) {
+ gbString t = type_to_string(operand->type);
+ error(operand->expr, "Expected a non-untyped integer type for '%.*s', got %s", LIT(builtin_name), t);
+ gb_string_free(t);
+ return false;
+ }
+
+ Type *u_type = &basic_types[bt->Basic.kind + 1];
+
+ operand->type = u_type;
+ }
+ break;
+ case BuiltinProc_type_integer_to_signed:
+ if (operand->mode != Addressing_Type) {
+ error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name));
+ return false;
+ }
+
+ if (is_type_polymorphic(operand->type)) {
+ gbString t = type_to_string(operand->type);
+ error(operand->expr, "Expected a non-polymorphic type for '%.*s', got %s", LIT(builtin_name), t);
+ gb_string_free(t);
+ return false;
+ }
+
+ {
+ Type *bt = base_type(operand->type);
+
+ if (bt->kind != Type_Basic ||
+ (bt->Basic.flags & BasicFlag_Unsigned) == 0 ||
+ (bt->Basic.flags & BasicFlag_Integer) == 0) {
+ gbString t = type_to_string(operand->type);
+ error(operand->expr, "Expected an unsigned integer type for '%.*s', got %s", LIT(builtin_name), t);
+ gb_string_free(t);
+ return false;
+ }
+
+ if ((bt->Basic.flags & BasicFlag_Untyped) != 0) {
+ gbString t = type_to_string(operand->type);
+ error(operand->expr, "Expected a non-untyped integer type for '%.*s', got %s", LIT(builtin_name), t);
+ gb_string_free(t);
+ return false;
+ }
+
+ if (bt->Basic.kind == Basic_uintptr) {
+ gbString t = type_to_string(operand->type);
+ error(operand->expr, "Type %s does not have a signed integer mapping for '%.*s'", t, LIT(builtin_name));
+ gb_string_free(t);
+ return false;
+ }
+
+ Type *u_type = &basic_types[bt->Basic.kind - 1];
+
+ operand->type = u_type;
+ }
+ break;
case BuiltinProc_type_merge:
{
operand->mode = Addressing_Type;
@@ -5679,6 +6423,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
case BuiltinProc_type_is_complex:
case BuiltinProc_type_is_quaternion:
case BuiltinProc_type_is_string:
+ case BuiltinProc_type_is_string16:
case BuiltinProc_type_is_typeid:
case BuiltinProc_type_is_any:
case BuiltinProc_type_is_endian_platform:
@@ -5692,6 +6437,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
case BuiltinProc_type_is_sliceable:
case BuiltinProc_type_is_comparable:
case BuiltinProc_type_is_simple_compare:
+ case BuiltinProc_type_is_nearly_simple_compare:
case BuiltinProc_type_is_dereferenceable:
case BuiltinProc_type_is_valid_map_key:
case BuiltinProc_type_is_valid_matrix_elements:
@@ -5708,6 +6454,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
case BuiltinProc_type_is_enum:
case BuiltinProc_type_is_proc:
case BuiltinProc_type_is_bit_set:
+ case BuiltinProc_type_is_bit_field:
case BuiltinProc_type_is_simd_vector:
case BuiltinProc_type_is_matrix:
case BuiltinProc_type_is_specialized_polymorphic_record:
@@ -5989,12 +6736,13 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
// NOTE(jakubtomsu): forces calculation of variant_block_size
type_size_of(u);
- i64 tag_offset = u->Union.variant_block_size;
- GB_ASSERT(tag_offset > 0);
+ // NOTE(Jeroen): A tag offset of zero is perfectly fine if all members of the union are empty structs.
+ // What matters is that the tag size is > 0.
+ GB_ASSERT(u->Union.tag_size > 0);
operand->mode = Addressing_Constant;
operand->type = t_untyped_integer;
- operand->value = exact_value_i64(tag_offset);
+ operand->value = exact_value_i64(u->Union.variant_block_size);
}
break;
@@ -6538,6 +7286,50 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
break;
}
+ case BuiltinProc_type_enum_is_contiguous:
+ {
+ Operand op = {};
+ Type *bt = check_type(c, ce->args[0]);
+ Type *type = base_type(bt);
+ if (type == nullptr || type == t_invalid) {
+ error(ce->args[0], "Expected a type for '%.*s'", LIT(builtin_name));
+ return false;
+ }
+ if (!is_type_enum(type)) {
+ gbString t = type_to_string(type);
+ error(ce->args[0], "Expected an enum type for '%.*s', got %s", LIT(builtin_name), t);
+ gb_string_free(t);
+ return false;
+ }
+
+ auto enum_constants = array_make<Entity *>(temporary_allocator(), type->Enum.fields.count);
+ array_copy(&enum_constants, type->Enum.fields, 0);
+ array_sort(enum_constants, enum_constant_entity_cmp);
+
+ BigInt minus_one = big_int_make_i64(-1);
+ defer (big_int_dealloc(&minus_one));
+ BigInt diff = {};
+
+ bool contiguous = true;
+ operand->mode = Addressing_Constant;
+ operand->type = t_untyped_bool;
+
+ for (isize i = 0; i < enum_constants.count - 1; i++) {
+ BigInt curr = enum_constants[i]->Constant.value.value_integer;
+ BigInt next = enum_constants[i + 1]->Constant.value.value_integer;
+ big_int_sub(&diff, &curr, &next);
+ defer (big_int_dealloc(&diff));
+
+ if (!big_int_is_zero(&diff) && big_int_cmp(&diff, &minus_one) != 0) {
+ contiguous = false;
+ break;
+ }
+ }
+
+ operand->value = exact_value_bool(contiguous);
+ break;
+ }
+
case BuiltinProc_type_equal_proc:
{
Operand op = {};
@@ -6619,6 +7411,22 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
break;
}
+ case BuiltinProc_type_canonical_name:
+ {
+ Operand op = {};
+ Type *type = check_type(c, ce->args[0]);
+ Type *bt = base_type(type);
+ if (bt == nullptr || bt == t_invalid) {
+ error(ce->args[0], "Expected a type for '%.*s'", LIT(builtin_name));
+ return false;
+ }
+
+ operand->mode = Addressing_Constant;
+ operand->type = t_untyped_string;
+ operand->value = exact_value_string(type_to_canonical_string(permanent_allocator(), type));
+ break;
+ }
+
case BuiltinProc_procedure_of:
{
Ast *call_expr = unparen_expr(ce->args[0]);
@@ -6671,7 +7479,11 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
return false;
}
operand->mode = Addressing_Value;
- operand->type = alloc_type_multi_pointer(t_u16);
+ if (type_hint != nullptr && is_type_cstring16(type_hint)) {
+ operand->type = type_hint;
+ } else {
+ operand->type = alloc_type_multi_pointer(t_u16);
+ }
operand->value = {};
break;
}
diff --git a/src/check_decl.cpp b/src/check_decl.cpp
index 5607ea725..fa4eade0f 100644
--- a/src/check_decl.cpp
+++ b/src/check_decl.cpp
@@ -145,13 +145,6 @@ gb_internal void check_init_variables(CheckerContext *ctx, Entity **lhs, isize l
if (d != nullptr) {
d->init_expr = o->expr;
}
-
- if (o->type && is_type_no_copy(o->type)) {
- ERROR_BLOCK();
- if (check_no_copy_assignment(*o, str_lit("initialization"))) {
- error_line("\tInitialization of a #no_copy type must be either implicitly zero, a constant literal, or a return value from a call expression");
- }
- }
}
if (rhs_count > 0 && lhs_count != rhs_count) {
error(lhs[0]->token, "Assignment count mismatch '%td' = '%td'", lhs_count, rhs_count);
@@ -169,8 +162,6 @@ gb_internal void override_entity_in_scope(Entity *original_entity, Entity *new_e
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
@@ -179,7 +170,9 @@ gb_internal void override_entity_in_scope(Entity *original_entity, Entity *new_e
// 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
+ rw_mutex_lock(&found_scope->mutex);
string_map_set(&found_scope->elements, original_name, new_entity);
+ rw_mutex_unlock(&found_scope->mutex);
original_entity->flags |= EntityFlag_Overridden;
original_entity->type = new_entity->type;
@@ -468,6 +461,10 @@ gb_internal void check_type_decl(CheckerContext *ctx, Entity *e, Ast *init_expr,
}
e->type = named;
+ if (!is_distinct) {
+ e->TypeName.is_type_alias = true;
+ }
+
check_type_path_push(ctx, e);
Type *bt = check_type_expr(ctx, te, named);
check_type_path_pop(ctx);
@@ -502,9 +499,9 @@ gb_internal void check_type_decl(CheckerContext *ctx, Entity *e, Ast *init_expr,
if (!is_distinct) {
e->type = bt;
named->Named.base = bt;
- e->TypeName.is_type_alias = true;
}
+ e->TypeName.is_type_alias = !is_distinct;
if (decl->type_expr != nullptr) {
Type *t = check_type(ctx, decl->type_expr);
@@ -520,12 +517,95 @@ gb_internal void check_type_decl(CheckerContext *ctx, Entity *e, Ast *init_expr,
if (decl != nullptr) {
AttributeContext ac = {};
check_decl_attributes(ctx, decl->attributes, type_decl_attribute, &ac);
+
if (e->kind == Entity_TypeName && ac.objc_class != "") {
+
e->TypeName.objc_class_name = ac.objc_class;
+ if (ac.objc_is_implementation) {
+ e->TypeName.objc_is_implementation = ac.objc_is_implementation;
+ e->TypeName.objc_superclass = ac.objc_superclass;
+ e->TypeName.objc_ivar = ac.objc_ivar;
+ e->TypeName.objc_context_provider = ac.objc_context_provider;
+
+ mutex_lock(&ctx->info->objc_class_name_mutex);
+ bool class_exists = string_set_update(&ctx->info->obcj_class_name_set, ac.objc_class);
+ mutex_unlock(&ctx->info->objc_class_name_mutex);
+ if (class_exists) {
+ error(e->token, "@(objc_class) name '%.*s' has already been used elsewhere", LIT(ac.objc_class));
+ }
+
+ mpsc_enqueue(&ctx->info->objc_class_implementations, e);
+
+ GB_ASSERT(e->TypeName.objc_ivar == nullptr || e->TypeName.objc_ivar->kind == Type_Named);
+
+ // Enqueue the contex_provider proc to be checked after it is resolved
+ if (e->TypeName.objc_context_provider != nullptr) {
+ mpsc_enqueue(&ctx->checker->procs_with_objc_context_provider_to_check, e);
+ }
+
+ // TODO(harold): I think there's a Check elsewhere in the checker for checking cycles.
+ // See about moving this to the right location.
+ // Ensure superclass hierarchy are all Objective-C classes and does not cycle
+
+ // NOTE(harold): We check for superclass unconditionally (before checking if super is null)
+ // because this should be the case 99.99% of the time. Not subclassing something that
+ // is, or is the child of, NSObject means the objc runtime messaging will not properly work on this type.
+ TypeSet super_set{};
+ type_set_init(&super_set, 8);
+ defer (type_set_destroy(&super_set));
+
+ type_set_update(&super_set, e->type);
+
+ Type *super = ac.objc_superclass;
+ while (super != nullptr) {
+ if (type_set_update(&super_set, super)) {
+ error(e->token, "@(objc_superclass) Superclass hierarchy cycle encountered");
+ break;
+ }
+
+ check_single_global_entity(ctx->checker, super->Named.type_name, super->Named.type_name->decl_info);
+
+ if (super->kind != Type_Named) {
+ error(e->token, "@(objc_superclass) Referenced type must be a named struct");
+ break;
+ }
+
+ Type* named_type = base_named_type(super);
+ GB_ASSERT(named_type->kind == Type_Named);
+
+ if (!is_type_objc_object(named_type)) {
+ error(e->token, "@(objc_superclass) Superclass '%.*s' must be an Objective-C class", LIT(named_type->Named.name));
+ break;
+ }
+
+ if (named_type->Named.type_name->TypeName.objc_class_name == "") {
+ error(e->token, "@(objc_superclass) Superclass '%.*s' must have a valid @(objc_class) attribute", LIT(named_type->Named.name));
+ break;
+ }
+
+ super = named_type->Named.type_name->TypeName.objc_superclass;
+ }
+ } else {
+ if (ac.objc_ivar != nullptr) {
+ error(e->token, "@(objc_ivar) may only be applied when the @(obj_implement) attribute is also applied");
+ } else if (ac.objc_context_provider != nullptr) {
+ error(e->token, "@(objc_context_provider) may only be applied when the @(obj_implement) attribute is also applied");
+ }
+ }
+
if (type_size_of(e->type) > 0) {
error(e->token, "@(objc_class) marked type must be of zero size");
}
+ } else if (ac.objc_is_implementation) {
+ error(e->token, "@(objc_implement) may only be applied when the @(objc_class) attribute is also applied");
+ }
+
+ if (ac.raddbg_type_view) {
+ RaddbgTypeView type_view = {};
+ type_view.type = e->type;
+ type_view.view = ac.raddbg_type_view_string;
+ mpsc_enqueue(&ctx->info->raddbg_type_views_queue, type_view);
}
}
@@ -628,6 +708,10 @@ gb_internal void check_const_decl(CheckerContext *ctx, Entity *e, Ast *type_expr
Operand x = {};
x.type = entity->type;
x.mode = Addressing_Variable;
+ if (entity->kind == Entity_Constant) {
+ x.mode = Addressing_Constant;
+ x.value = entity->Constant.value;
+ }
if (!check_is_assignable_to(ctx, &x, e->type)) {
gbString expr_str = expr_to_string(init);
gbString op_type_str = type_to_string(entity->type);
@@ -736,6 +820,12 @@ gb_internal bool signature_parameter_similar_enough(Type *x, Type *y) {
if (sig_compare(is_type_cstring, is_type_u8_multi_ptr, x, y)) {
return true;
}
+ if (sig_compare(is_type_cstring16, is_type_u16_ptr, x, y)) {
+ return true;
+ }
+ if (sig_compare(is_type_cstring16, is_type_u16_multi_ptr, x, y)) {
+ return true;
+ }
if (sig_compare(is_type_uintptr, is_type_rawptr, x, y)) {
return true;
@@ -759,6 +849,50 @@ gb_internal bool signature_parameter_similar_enough(Type *x, Type *y) {
}
}
+ Type *x_base = base_type(x);
+ Type *y_base = base_type(y);
+
+ if (x_base == y_base) {
+ return true;
+ }
+
+ if (x_base->kind == y_base->kind &&
+ x_base->kind == Type_Struct) {
+ i64 xs = type_size_of(x_base);
+ i64 ys = type_size_of(y_base);
+
+ i64 xa = type_align_of(x_base);
+ i64 ya = type_align_of(y_base);
+
+
+ if (x_base->Struct.is_raw_union == y_base->Struct.is_raw_union &&
+ xs == ys && xa == ya) {
+ if (xs > 16) {
+ // @@ABI NOTE(bill): Just allow anything over 16-bytes to be allowed, because on all current ABIs
+ // it will be passed by point
+ // NOTE(bill): this must be changed when ABI changes
+ return true;
+ }
+ if (x_base->Struct.is_raw_union) {
+ return true;
+ }
+ if (x->Struct.fields.count == y->Struct.fields.count) {
+ for (isize i = 0; i < x->Struct.fields.count; i++) {
+ Entity *a = x->Struct.fields[i];
+ Entity *b = y->Struct.fields[i];
+ bool similar = signature_parameter_similar_enough(a->type, b->type);
+ if (!similar) {
+ // NOTE(bill): If the fields are not similar enough, then stop.
+ goto end;
+ }
+ }
+ }
+ // HACK NOTE(bill): Allow this for the time begin until it actually becomes a practical problem
+ return true;
+ }
+ }
+
+end:;
return are_types_identical(x, y);
}
@@ -856,7 +990,7 @@ gb_internal Entity *init_entity_foreign_library(CheckerContext *ctx, Entity *e)
error(ident, "foreign library names must be an identifier");
} else {
String name = ident->Ident.token.string;
- Entity *found = scope_lookup(ctx->scope, name);
+ Entity *found = scope_lookup(ctx->scope, name, ident->Ident.hash);
if (found == nullptr) {
if (is_blank_ident(name)) {
@@ -914,61 +1048,167 @@ gb_internal String handle_link_name(CheckerContext *ctx, Token token, String lin
}
-gb_internal void check_objc_methods(CheckerContext *ctx, Entity *e, AttributeContext const &ac) {
- if (!(ac.objc_name.len || ac.objc_is_class_method || ac.objc_type)) {
+gb_internal void check_objc_methods(CheckerContext *ctx, Entity *e, AttributeContext &ac) {
+ if (!ac.objc_type) {
return;
}
- if (ac.objc_name.len == 0 && ac.objc_is_class_method) {
- error(e->token, "@(objc_name) is required with @(objc_is_class_method)");
- } else if (ac.objc_type == nullptr) {
- error(e->token, "@(objc_name) requires that @(objc_type) to be set");
- } else if (ac.objc_name.len == 0 && ac.objc_type) {
- error(e->token, "@(objc_name) is required with @(objc_type)");
- } else {
- Type *t = ac.objc_type;
- if (t->kind == Type_Named) {
- Entity *tn = t->Named.type_name;
- GB_ASSERT(tn->kind == Entity_TypeName);
+ Type *t = ac.objc_type;
+ GB_ASSERT(t->kind == Type_Named); // NOTE(harold): This is already checked for at the attribute resolution stage.
- if (tn->scope != e->scope) {
- error(e->token, "@(objc_name) attribute may only be applied to procedures and types within the same scope");
- } else {
- mutex_lock(&global_type_name_objc_metadata_mutex);
- defer (mutex_unlock(&global_type_name_objc_metadata_mutex));
+ // Attempt to infer th objc_name automatically if the proc name contains
+ // the type name objc_type's name, followed by an underscore, as a prefix.
+ if (ac.objc_name.len == 0) {
+ String proc_name = e->token.string;
+ String type_name = t->Named.name;
- if (!tn->TypeName.objc_metadata) {
- tn->TypeName.objc_metadata = create_type_name_obj_c_metadata();
- }
- auto *md = tn->TypeName.objc_metadata;
- mutex_lock(md->mutex);
- defer (mutex_unlock(md->mutex));
-
- if (!ac.objc_is_class_method) {
- bool ok = true;
- for (TypeNameObjCMetadataEntry const &entry : md->value_entries) {
- if (entry.name == ac.objc_name) {
- error(e->token, "Previous declaration of @(objc_name=\"%.*s\")", LIT(ac.objc_name));
- ok = false;
- break;
- }
- }
- if (ok) {
- array_add(&md->value_entries, TypeNameObjCMetadataEntry{ac.objc_name, e});
- }
+ if (proc_name.len > type_name.len + 1 &&
+ proc_name[type_name.len] == '_' &&
+ str_eq(type_name, substring(proc_name, 0, type_name.len))
+ ) {
+ ac.objc_name = substring(proc_name, type_name.len+1, proc_name.len);
+ } else {
+ error(e->token, "@(objc_name) requires that @(objc_type) be set or inferred "
+ "by prefixing the proc name with the type and underscore: MyObjcType_myProcName :: proc().");
+ }
+ }
+
+ Entity *tn = t->Named.type_name;
+ GB_ASSERT(tn->kind == Entity_TypeName);
+
+ if (tn->scope != e->scope) {
+ error(e->token, "@(objc_name) attribute may only be applied to procedures and types within the same scope");
+ } else {
+ // Enable implementation by default if the class is an implementer too and
+ // @objc_implement was not set to false explicitly in this proc.
+ bool implement = tn->TypeName.objc_is_implementation;
+ if( ac.objc_is_implementation && !tn->TypeName.objc_is_implementation ) {
+ error(e->token, "Cannot apply @(objc_is_implement) to a procedure whose type does not also have @(objc_is_implement) set");
+ }
+
+ if (ac.objc_is_disabled_implement) {
+ implement = false;
+ }
+
+ String objc_selector = ac.objc_selector != "" ? ac.objc_selector : ac.objc_name;
+
+ if (e->kind == Entity_Procedure) {
+ bool has_body = e->decl_info->proc_lit->ProcLit.body != nullptr;
+ e->Procedure.is_objc_impl_or_import = implement || !has_body;
+ e->Procedure.is_objc_class_method = ac.objc_is_class_method;
+ e->Procedure.objc_selector_name = objc_selector;
+ e->Procedure.objc_class = tn;
+
+ auto &proc = e->type->Proc;
+ Type *first_param = proc.param_count > 0 ? proc.params->Tuple.variables[0]->type : t_untyped_nil;
+
+ if (implement) {
+ if( !has_body ) {
+ error(e->token, "Procedures with @(objc_is_implement) must have a body");
+ } else if (!tn->TypeName.objc_is_implementation) {
+ error(e->token, "@(objc_is_implement) attribute may only be applied to procedures whose class also have @(objc_is_implement) applied");
+ } else if (!ac.objc_is_class_method && !(first_param->kind == Type_Pointer && internal_check_is_assignable_to(t, first_param->Pointer.elem))) {
+ error(e->token, "Objective-C instance methods implementations require the first parameter to be a pointer to the class type set by @(objc_type)");
+ } else if (proc.calling_convention == ProcCC_Odin && !tn->TypeName.objc_context_provider) {
+ error(e->token, "Objective-C methods with Odin calling convention can only be used with classes that have @(objc_context_provider) set");
+ } else if (ac.objc_is_class_method && proc.calling_convention != ProcCC_CDecl) {
+ error(e->token, "Objective-C class methods (objc_is_class_method=true) that have @objc_is_implementation can only use \"c\" calling convention");
+ } else if (proc.result_count > 1) {
+ error(e->token, "Objective-C method implementations may return at most 1 value");
} else {
- bool ok = true;
- for (TypeNameObjCMetadataEntry const &entry : md->type_entries) {
- if (entry.name == ac.objc_name) {
- error(e->token, "Previous declaration of @(objc_name=\"%.*s\")", LIT(ac.objc_name));
- ok = false;
- break;
- }
+ // Always export unconditionally
+ // NOTE(harold): This means check_objc_methods() MUST be called before
+ // e->Procedure.is_export is set in check_proc_decl()!
+ if (ac.is_export) {
+ error(e->token, "Explicit export not allowed when @(objc_implement) is set. It set exported implicitly");
}
- if (ok) {
- array_add(&md->type_entries, TypeNameObjCMetadataEntry{ac.objc_name, e});
+ if (ac.link_name != "") {
+ error(e->token, "Explicit linkage not allowed when @(objc_implement) is set. It set to \"strong\" implicitly");
+ }
+
+ ac.is_export = true;
+ ac.linkage = STR_LIT("strong");
+
+ auto method = ObjcMethodData{ ac, e };
+ method.ac.objc_selector = objc_selector;
+
+ CheckerInfo *info = ctx->info;
+ mutex_lock(&info->objc_method_mutex);
+ defer (mutex_unlock(&info->objc_method_mutex));
+
+ Array<ObjcMethodData>* method_list = map_get(&info->objc_method_implementations, t);
+ if (method_list) {
+ array_add(method_list, method);
+ } else {
+ auto list = array_make<ObjcMethodData>(permanent_allocator(), 1, 8);
+ list[0] = method;
+
+ map_set(&info->objc_method_implementations, t, list);
}
}
+ } else if (!has_body) {
+ if (ac.objc_selector == "The @(objc_selector) attribute is required for imported Objective-C methods.") {
+ return;
+ } else if (proc.calling_convention != ProcCC_CDecl) {
+ error(e->token, "Imported Objective-C methods must use the \"c\" calling convention");
+ return;
+ } else if (tn->TypeName.objc_context_provider) {
+ error(e->token, "Imported Objective-C class '%.*s' must not declare context providers.", tn->type->Named.name);
+ return;
+ } else if (tn->TypeName.objc_is_implementation) {
+ error(e->token, "Imported Objective-C methods used in a class with @(objc_implement) is not allowed.");
+ return;
+ } else if (!ac.objc_is_class_method && !(first_param->kind == Type_Pointer && internal_check_is_assignable_to(t, first_param->Pointer.elem))) {
+ error(e->token, "Objective-C instance methods require the first parameter to be a pointer to the class type set by @(objc_type)");
+ return;
+ }
+ }
+ else if(ac.objc_selector != "") {
+ error(e->token, "@(objc_selector) may only be applied to procedures that are Objective-C method implementations or are imported.");
+ return;
+ }
+ } else {
+ GB_ASSERT(e->kind == Entity_ProcGroup);
+ if (tn->TypeName.objc_is_implementation) {
+ error(e->token, "Objective-C procedure groups cannot use the @(objc_implement) attribute.");
+ return;
+ }
+ }
+
+
+ mutex_lock(&global_type_name_objc_metadata_mutex);
+ defer (mutex_unlock(&global_type_name_objc_metadata_mutex));
+
+ if (!tn->TypeName.objc_metadata) {
+ tn->TypeName.objc_metadata = create_type_name_obj_c_metadata();
+ }
+ auto *md = tn->TypeName.objc_metadata;
+ mutex_lock(md->mutex);
+ defer (mutex_unlock(md->mutex));
+
+ if (!ac.objc_is_class_method) {
+ bool ok = true;
+ for (TypeNameObjCMetadataEntry const &entry : md->value_entries) {
+ if (entry.name == ac.objc_name) {
+ error(e->token, "Previous declaration of @(objc_name=\"%.*s\")", LIT(ac.objc_name));
+ ok = false;
+ break;
+ }
+ }
+ if (ok) {
+ array_add(&md->value_entries, TypeNameObjCMetadataEntry{ac.objc_name, e});
+ }
+ } else {
+ bool ok = true;
+ for (TypeNameObjCMetadataEntry const &entry : md->type_entries) {
+ if (entry.name == ac.objc_name) {
+ error(e->token, "Previous declaration of @(objc_name=\"%.*s\")", LIT(ac.objc_name));
+ ok = false;
+ break;
+ }
+ }
+ if (ok) {
+ array_add(&md->type_entries, TypeNameObjCMetadataEntry{ac.objc_name, e});
}
}
}
@@ -1137,6 +1377,9 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
break;
}
+ // NOTE(harold): For Objective-C method implementations, this must happen after
+ // check_objc_methods() is called as it re-sets ac.is_export to true unconditionally.
+ // The same is true for the linkage, set below.
e->Procedure.entry_point_only = ac.entry_point_only;
e->Procedure.is_export = ac.is_export;
@@ -1186,12 +1429,16 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
has_instrumentation = false;
e->flags |= EntityFlag_Require;
} else if (ac.instrumentation_enter) {
+ init_core_source_code_location(ctx->checker);
if (!is_valid_instrumentation_call(e->type)) {
init_core_source_code_location(ctx->checker);
gbString s = type_to_string(e->type);
error(e->token, "@(instrumentation_enter) procedures must have the type '%s', got %s", instrumentation_proc_type_str, s);
gb_string_free(s);
}
+ if ((e->scope->flags & (ScopeFlag_File|ScopeFlag_Pkg)) == 0) {
+ error(e->token, "@(instrumentation_enter) procedures must be declared at the file scope");
+ }
MUTEX_GUARD(&ctx->info->instrumentation_mutex);
if (ctx->info->instrumentation_enter_entity != nullptr) {
error(e->token, "@(instrumentation_enter) has already been set");
@@ -1208,6 +1455,9 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
error(e->token, "@(instrumentation_exit) procedures must have the type '%s', got %s", instrumentation_proc_type_str, s);
gb_string_free(s);
}
+ if ((e->scope->flags & (ScopeFlag_File|ScopeFlag_Pkg)) == 0) {
+ error(e->token, "@(instrumentation_exit) procedures must be declared at the file scope");
+ }
MUTEX_GUARD(&ctx->info->instrumentation_mutex);
if (ctx->info->instrumentation_exit_entity != nullptr) {
error(e->token, "@(instrumentation_exit) has already been set");
@@ -1221,6 +1471,8 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
e->Procedure.has_instrumentation = has_instrumentation;
+ e->Procedure.no_sanitize_address = ac.no_sanitize_address;
+ e->Procedure.no_sanitize_memory = ac.no_sanitize_memory;
e->deprecated_message = ac.deprecated_message;
e->warning_message = ac.warning_message;
@@ -1236,6 +1488,7 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
}
}
+ // NOTE(harold): See export/linkage note above(where is_export is assigned) regarding Objective-C method implementations
bool is_foreign = e->Procedure.is_foreign;
bool is_export = e->Procedure.is_export;
@@ -1307,7 +1560,7 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
if (!pt->is_polymorphic) {
check_procedure_later(ctx->checker, ctx->file, e->token, d, proc_type, pl->body, pl->tags);
}
- } else if (!is_foreign) {
+ } else if (!is_foreign && !e->Procedure.is_objc_impl_or_import) {
if (e->Procedure.is_export) {
error(e->token, "Foreign export procedures must have a body");
} else {
@@ -1355,6 +1608,7 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
// NOTE(bill): this must be delayed because the foreign import paths might not be evaluated yet until much later
mpsc_enqueue(&ctx->info->foreign_decls_to_check, e);
} else {
+ // TODO(harold): Check if it's an objective-C foreign, if so, I don't think we need to check it.
check_foreign_procedure(ctx, e, d);
}
} else {
@@ -1377,7 +1631,7 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
"\tother at %s",
LIT(name), token_pos_to_string(pos));
} else if (name == "main") {
- if (d->entity->pkg->kind != Package_Runtime) {
+ if (d->entity.load()->pkg->kind != Package_Runtime) {
error(d->proc_lit, "The link name 'main' is reserved for internal use");
}
} else {
@@ -1393,7 +1647,7 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
}
}
-gb_internal void check_global_variable_decl(CheckerContext *ctx, Entity *&e, Ast *type_expr, Ast *init_expr) {
+gb_internal void check_global_variable_decl(CheckerContext *ctx, Entity *e, Ast *type_expr, Ast *init_expr) {
GB_ASSERT(e->type == nullptr);
GB_ASSERT(e->kind == Entity_Variable);
@@ -1514,7 +1768,28 @@ gb_internal void check_global_variable_decl(CheckerContext *ctx, Entity *&e, Ast
check_expr_with_type_hint(ctx, &o, init_expr, e->type);
check_init_variable(ctx, e, &o, str_lit("variable declaration"));
if (e->Variable.is_rodata && o.mode != Addressing_Constant) {
+ ERROR_BLOCK();
error(o.expr, "Variables declared with @(rodata) must have constant initialization");
+ Ast *expr = unparen_expr(o.expr);
+ if (is_type_struct(e->type) && expr && expr->kind == Ast_CompoundLit) {
+ ast_node(cl, CompoundLit, expr);
+ for (Ast *elem_ : cl->elems) {
+ Ast *elem = elem_;
+ if (elem->kind == Ast_FieldValue) {
+ elem = elem->FieldValue.value;
+ }
+ elem = unparen_expr(elem);
+
+ Entity *e = entity_of_node(elem);
+ if (elem->tav.mode != Addressing_Constant && e == nullptr && elem->kind != Ast_ProcLit) {
+ Token tok = ast_token(elem);
+ TokenPos pos = tok.pos;
+ gbString s = type_to_string(type_of_expr(elem));
+ error_line("%s Element is not constant, which is required for @(rodata), of type %s\n", token_pos_to_string(pos), s);
+ gb_string_free(s);
+ }
+ }
+ }
}
check_rtti_type_disallowed(e->token, e->type, "A variable declaration is using a type, %s, which has been disallowed");
@@ -1686,6 +1961,17 @@ gb_internal void check_entity_decl(CheckerContext *ctx, Entity *e, DeclInfo *d,
c.scope = d->scope;
c.decl = d;
c.type_level = 0;
+ c.curr_proc_calling_convention = ProcCC_Contextless;
+
+ auto prev_flags = c.scope->flags;
+ defer (c.scope->flags = prev_flags);
+
+ if (check_feature_flags(ctx, d->decl_node) & OptInFeatureFlag_GlobalContext) {
+ c.scope->flags |= ScopeFlag_ContextDefined;
+ } else {
+ c.scope->flags &= ~ScopeFlag_ContextDefined;
+ }
+
e->parent_proc_decl = c.curr_proc_decl;
e->state = EntityState_InProgress;
@@ -1732,7 +2018,7 @@ gb_internal void add_deps_from_child_to_parent(DeclInfo *decl) {
rw_mutex_shared_lock(&decl->deps_mutex);
rw_mutex_lock(&decl->parent->deps_mutex);
- for (Entity *e : decl->deps) {
+ FOR_PTR_SET(e, decl->deps) {
ptr_set_add(&decl->parent->deps, e);
}
@@ -1784,8 +2070,8 @@ gb_internal bool check_proc_body(CheckerContext *ctx_, Token token, DeclInfo *de
ctx->curr_proc_sig = type;
ctx->curr_proc_calling_convention = type->Proc.calling_convention;
- if (decl->parent && decl->entity && decl->parent->entity) {
- decl->entity->parent_proc_decl = decl->parent;
+ if (decl->parent && decl->entity.load() && decl->parent->entity) {
+ decl->entity.load()->parent_proc_decl = decl->parent;
}
if (ctx->pkg->name != "runtime") {
@@ -1798,9 +2084,9 @@ gb_internal bool check_proc_body(CheckerContext *ctx_, Token token, DeclInfo *de
ast_node(bs, BlockStmt, body);
+ TEMPORARY_ALLOCATOR_GUARD();
Array<ProcUsingVar> using_entities = {};
- using_entities.allocator = heap_allocator();
- defer (array_free(&using_entities));
+ using_entities.allocator = temporary_allocator();
{
if (type->Proc.param_count > 0) {
@@ -1889,7 +2175,7 @@ gb_internal bool check_proc_body(CheckerContext *ctx_, Token token, DeclInfo *de
GB_ASSERT(decl->proc_checked_state != ProcCheckedState_Checked);
if (decl->defer_use_checked) {
GB_ASSERT(is_type_polymorphic(type, true));
- error(token, "Defer Use Checked: %.*s", LIT(decl->entity->token.string));
+ error(token, "Defer Use Checked: %.*s", LIT(decl->entity.load()->token.string));
GB_ASSERT(decl->defer_use_checked == false);
}
diff --git a/src/check_expr.cpp b/src/check_expr.cpp
index bd1c34044..251f196a5 100644
--- a/src/check_expr.cpp
+++ b/src/check_expr.cpp
@@ -129,6 +129,8 @@ gb_internal bool check_is_castable_to(CheckerContext *c, Operand *operand, Type
gb_internal bool is_exact_value_zero(ExactValue const &v);
+gb_internal IntegerDivisionByZeroKind check_for_integer_division_by_zero(CheckerContext *c, Ast *node);
+
enum LoadDirectiveResult {
LoadDirective_Success = 0,
LoadDirective_Error = 1,
@@ -263,6 +265,9 @@ gb_internal void check_did_you_mean_scope(String const &name, Scope *scope, char
gb_internal Entity *entity_from_expr(Ast *expr) {
expr = unparen_expr(expr);
+ if (expr == nullptr) {
+ return nullptr;
+ }
switch (expr->kind) {
case Ast_Ident:
return expr->Ident.entity;
@@ -582,6 +587,7 @@ gb_internal bool find_or_generate_polymorphic_procedure(CheckerContext *old_c, E
d->proc_lit = proc_lit;
d->proc_checked_state = ProcCheckedState_Unchecked;
d->defer_use_checked = false;
+ d->para_poly_original = old_decl->entity;
Entity *entity = alloc_entity_procedure(nullptr, token, final_proc_type, tags);
entity->state.store(EntityState_Resolved);
@@ -603,7 +609,7 @@ gb_internal bool find_or_generate_polymorphic_procedure(CheckerContext *old_c, E
entity->flags |= EntityFlag_Disabled;
}
- d->entity = entity;
+ d->entity.store(entity);
AstFile *file = nullptr;
{
@@ -643,7 +649,7 @@ gb_internal bool find_or_generate_polymorphic_procedure(CheckerContext *old_c, E
gb_internal bool check_polymorphic_procedure_assignment(CheckerContext *c, Operand *operand, Type *type, Ast *poly_def_node, PolyProcData *poly_proc_data) {
if (operand->expr == nullptr) return false;
- Entity *base_entity = entity_of_node(operand->expr);
+ Entity *base_entity = entity_from_expr(operand->expr);
if (base_entity == nullptr) return false;
return find_or_generate_polymorphic_procedure(c, base_entity, type, nullptr, poly_def_node, poly_proc_data);
}
@@ -816,9 +822,11 @@ gb_internal i64 check_distance_between_types(CheckerContext *c, Operand *operand
}
}
- if (is_type_enum(dst) && are_types_identical(dst->Enum.base_type, operand->type)) {
- if (c->in_enum_type) {
- return 3;
+ if (c != nullptr) {
+ if (is_type_enum(dst) && are_types_identical(dst->Enum.base_type, operand->type)) {
+ if (c->in_enum_type) {
+ return 3;
+ }
}
}
@@ -863,6 +871,11 @@ gb_internal i64 check_distance_between_types(CheckerContext *c, Operand *operand
if (are_types_identical(vt, s)) {
return 1;
}
+ if (is_type_proc(vt)) {
+ if (are_types_identical(base_type(vt), src)) {
+ return 1;
+ }
+ }
}
if (dst->Union.variants.count == 1) {
@@ -990,14 +1003,34 @@ gb_internal i64 assign_score_function(i64 distance, bool is_variadic=false) {
gb_internal bool check_is_assignable_to_with_score(CheckerContext *c, Operand *operand, Type *type, i64 *score_, bool is_variadic=false, bool allow_array_programming=true) {
- i64 score = 0;
- i64 distance = check_distance_between_types(c, operand, type, allow_array_programming);
- bool ok = distance >= 0;
- if (ok) {
- score = assign_score_function(distance, is_variadic);
+ if (c == nullptr) {
+ GB_ASSERT(operand->mode == Addressing_Value);
+ GB_ASSERT(is_type_typed(operand->type));
}
- if (score_) *score_ = score;
- return ok;
+ if (operand->mode == Addressing_Invalid || type == t_invalid) {
+ if (score_) *score_ = 0;
+ return false;
+ }
+
+ // Handle polymorphic procedure used as default parameter
+ if (operand->mode == Addressing_Value && is_type_proc(type) && is_type_proc(operand->type)) {
+ Entity *e = entity_from_expr(operand->expr);
+ if (e != nullptr && e->kind == Entity_Procedure && is_type_polymorphic(e->type) && !is_type_polymorphic(type)) {
+ // Special case: Allow a polymorphic procedure to be used as default value for concrete proc type
+ // during the initial check. It will be properly instantiated when actually used.
+ if (score_) *score_ = assign_score_function(1);
+ return true;
+ }
+ }
+
+ i64 score = check_distance_between_types(c, operand, type, allow_array_programming);
+ if (score >= 0) {
+ if (score_) *score_ = assign_score_function(score, is_variadic);
+ return true;
+ }
+
+ if (score_) *score_ = 0;
+ return false;
}
@@ -1708,7 +1741,7 @@ gb_internal Entity *check_ident(CheckerContext *c, Operand *o, Ast *n, Type *nam
o->expr = n;
String name = n->Ident.token.string;
- Entity *e = scope_lookup(c->scope, name);
+ Entity *e = scope_lookup(c->scope, name, n->Ident.hash);
if (e == nullptr) {
if (is_blank_ident(name)) {
error(n, "'_' cannot be used as a value");
@@ -1849,7 +1882,10 @@ gb_internal Entity *check_ident(CheckerContext *c, Operand *o, Ast *n, Type *nam
o->type = t_invalid;
}
if (o->type != nullptr && o->type->kind == Type_Named && o->type->Named.type_name->TypeName.is_type_alias) {
- o->type = base_type(o->type);
+ Type *bt = base_type(o->type);
+ if (bt != nullptr) {
+ o->type = bt;
+ }
}
break;
@@ -2075,6 +2111,9 @@ gb_internal bool check_representable_as_constant(CheckerContext *c, ExactValue i
} else if (is_type_boolean(type)) {
return in_value.kind == ExactValue_Bool;
} else if (is_type_string(type)) {
+ if (in_value.kind == ExactValue_String16) {
+ return is_type_string16(type) || is_type_cstring16(type);
+ }
return in_value.kind == ExactValue_String;
} else if (is_type_integer(type) || is_type_rune(type)) {
if (in_value.kind == ExactValue_Bool) {
@@ -2289,11 +2328,28 @@ gb_internal bool check_representable_as_constant(CheckerContext *c, ExactValue i
if (in_value.kind == ExactValue_String) {
return false;
}
+ if (in_value.kind == ExactValue_String16) {
+ return false;
+ }
if (out_value) *out_value = in_value;
} else if (is_type_bit_set(type)) {
if (in_value.kind == ExactValue_Integer) {
return true;
}
+ } else if (is_type_typeid(type)) {
+
+ if (in_value.kind == ExactValue_Compound) {
+ ast_node(cl, CompoundLit, in_value.value_compound);
+ if (cl->elems.count == 0) {
+ in_value = exact_value_typeid(nullptr);
+ } else {
+ return false;
+ }
+ }
+ if (in_value.kind == ExactValue_Typeid) {
+ if (out_value) *out_value = in_value;
+ return true;
+ }
}
return false;
@@ -2393,27 +2449,27 @@ gb_internal void check_assignment_error_suggestion(CheckerContext *c, Operand *o
Type *s = src->Array.elem;
Type *d = dst->Slice.elem;
if (are_types_identical(s, d)) {
- error_line("\tSuggestion: the array expression may be sliced with %s[:]\n", a);
+ error_line("\tSuggestion: The array expression may be sliced with %s[:]\n", a);
}
} else if (is_type_dynamic_array(src) && is_type_slice(dst)) {
Type *s = src->DynamicArray.elem;
Type *d = dst->Slice.elem;
if (are_types_identical(s, d)) {
- error_line("\tSuggestion: the dynamic array expression may be sliced with %s[:]\n", a);
+ error_line("\tSuggestion: The dynamic array expression may be sliced with %s[:]\n", a);
}
}else if (are_types_identical(src, dst) && !are_types_identical(o->type, type)) {
- error_line("\tSuggestion: the expression may be directly casted to type %s\n", b);
+ error_line("\tSuggestion: The expression may be directly casted to type %s\n", b);
} else if (are_types_identical(src, t_string) && is_type_u8_slice(dst)) {
- error_line("\tSuggestion: a string may be transmuted to %s\n", b);
- error_line("\t This is an UNSAFE operation as string data is assumed to be immutable, \n");
+ error_line("\tSuggestion: A string may be transmuted to %s\n", b);
+ error_line("\t This is an UNSAFE operation as string data is assumed to be immutable,\n");
error_line("\t whereas slices in general are assumed to be mutable.\n");
} else if (is_type_u8_slice(src) && are_types_identical(dst, t_string) && o->mode != Addressing_Constant) {
- error_line("\tSuggestion: the expression may be casted to %s\n", b);
+ error_line("\tSuggestion: The expression may be casted to %s\n", b);
} else if (check_integer_exceed_suggestion(c, o, type, max_bit_size)) {
return;
} else if (is_expr_inferred_fixed_array(c->type_hint_expr) && is_type_array_like(type) && is_type_array_like(o->type)) {
gbString s = expr_to_string(c->type_hint_expr);
- error_line("\tSuggestion: make sure that `%s` is attached to the compound literal directly\n", s);
+ error_line("\tSuggestion: Make sure that `%s` is attached to the compound literal directly\n", s);
gb_string_free(s);
} else if (is_type_pointer(type) &&
o->mode == Addressing_Variable &&
@@ -2424,7 +2480,8 @@ gb_internal void check_assignment_error_suggestion(CheckerContext *c, Operand *o
} else if (is_type_pointer(o->type) &&
are_types_identical(type_deref(o->type), type)) {
gbString s = expr_to_string(o->expr);
- error_line("\tSuggestion: Did you mean `%s^`\n", s);
+ if (s[0] == '&') error_line("\tSuggestion: Did you mean `%s`\n", &s[1]);
+ else error_line("\tSuggestion: Did you mean `%s^`\n", s);
gb_string_free(s);
}
}
@@ -2599,9 +2656,8 @@ gb_internal ExactValue exact_bit_set_all_set_mask(Type *type) {
continue;
}
- BigInt shift_amount = f->Constant.value.value_integer;
- big_int_sub_eq(&shift_amount, &b_lower_base);
-
+ BigInt shift_amount = {};
+ big_int_sub(&shift_amount, &f->Constant.value.value_integer, &b_lower_base);
BigInt value = {};
big_int_shl(&value, &one, &shift_amount);
@@ -2832,6 +2888,14 @@ gb_internal void add_comparison_procedures_for_fields(CheckerContext *c, Type *t
add_package_dependency(c, "runtime", "string_eq");
add_package_dependency(c, "runtime", "string_ne");
break;
+ case Basic_cstring16:
+ add_package_dependency(c, "runtime", "cstring16_eq");
+ add_package_dependency(c, "runtime", "cstring16_ne");
+ break;
+ case Basic_string16:
+ add_package_dependency(c, "runtime", "string16_eq");
+ add_package_dependency(c, "runtime", "string16_ne");
+ break;
}
break;
case Type_Struct:
@@ -2903,9 +2967,20 @@ gb_internal void check_comparison(CheckerContext *c, Ast *node, Operand *x, Oper
if (!defined) {
gbString xs = type_to_string(x->type, temporary_allocator());
gbString ys = type_to_string(y->type, temporary_allocator());
- err_str = gb_string_make(temporary_allocator(),
- gb_bprintf("operator '%.*s' not defined between the types '%s' and '%s'", LIT(token_strings[op]), xs, ys)
- );
+
+ if (!is_type_comparable(x->type)) {
+ err_str = gb_string_make(temporary_allocator(),
+ gb_bprintf("Type '%s' is not simply comparable, so operator '%.*s' is not defined for it", xs, LIT(token_strings[op]))
+ );
+ } else if (!is_type_comparable(y->type)) {
+ err_str = gb_string_make(temporary_allocator(),
+ gb_bprintf("Type '%s' is not simply comparable, so operator '%.*s' is not defined for it", ys, LIT(token_strings[op]))
+ );
+ } else {
+ err_str = gb_string_make(temporary_allocator(),
+ gb_bprintf("Operator '%.*s' not defined between the types '%s' and '%s'", LIT(token_strings[op]), xs, ys)
+ );
+ }
} else {
Type *comparison_type = x->type;
if (x->type == err_type && is_operand_nil(*x)) {
@@ -2926,11 +3001,11 @@ gb_internal void check_comparison(CheckerContext *c, Ast *node, Operand *x, Oper
} else {
yt = type_to_string(y->type);
}
- err_str = gb_string_make(temporary_allocator(), gb_bprintf("mismatched types '%s' and '%s'", xt, yt));
+ err_str = gb_string_make(temporary_allocator(), gb_bprintf("Mismatched types '%s' and '%s'", xt, yt));
}
if (err_str != nullptr) {
- error(node, "Cannot compare expression, %s", err_str);
+ error(node, "Cannot compare expression. %s.", err_str);
x->type = t_untyped_bool;
} else {
if (x->mode == Addressing_Constant &&
@@ -2994,6 +3069,24 @@ gb_internal void check_comparison(CheckerContext *c, Ast *node, Operand *x, Oper
case Token_LtEq: add_package_dependency(c, "runtime", "cstring_le"); break;
case Token_GtEq: add_package_dependency(c, "runtime", "cstring_gt"); break;
}
+ } else if (is_type_cstring16(x->type) && is_type_cstring16(y->type)) {
+ switch (op) {
+ case Token_CmpEq: add_package_dependency(c, "runtime", "cstring16_eq"); break;
+ case Token_NotEq: add_package_dependency(c, "runtime", "cstring16_ne"); break;
+ case Token_Lt: add_package_dependency(c, "runtime", "cstring16_lt"); break;
+ case Token_Gt: add_package_dependency(c, "runtime", "cstring16_gt"); break;
+ case Token_LtEq: add_package_dependency(c, "runtime", "cstring16_le"); break;
+ case Token_GtEq: add_package_dependency(c, "runtime", "cstring16_gt"); break;
+ }
+ } else if (is_type_string16(x->type) || is_type_string16(y->type)) {
+ switch (op) {
+ case Token_CmpEq: add_package_dependency(c, "runtime", "string16_eq"); break;
+ case Token_NotEq: add_package_dependency(c, "runtime", "string16_ne"); break;
+ case Token_Lt: add_package_dependency(c, "runtime", "string16_lt"); break;
+ case Token_Gt: add_package_dependency(c, "runtime", "string16_gt"); break;
+ case Token_LtEq: add_package_dependency(c, "runtime", "string16_le"); break;
+ case Token_GtEq: add_package_dependency(c, "runtime", "string16_gt"); break;
+ }
} else if (is_type_string(x->type) || is_type_string(y->type)) {
switch (op) {
case Token_CmpEq: add_package_dependency(c, "runtime", "string_eq"); break;
@@ -3045,126 +3138,106 @@ gb_internal void check_shift(CheckerContext *c, Operand *x, Operand *y, Ast *nod
GB_ASSERT(node->kind == Ast_BinaryExpr);
ast_node(be, BinaryExpr, node);
- ExactValue x_val = {};
- if (x->mode == Addressing_Constant) {
- x_val = exact_value_to_integer(x->value);
+ bool y_is_untyped = is_type_untyped(y->type);
+ if (y_is_untyped) {
+ convert_to_typed(c, y, t_untyped_integer);
+ if (y->mode == Addressing_Invalid) {
+ x->mode = Addressing_Invalid;
+ return;
+ }
+ } else if (!is_type_unsigned(y->type)) {
+ gbString y_str = expr_to_string(y->expr);
+ error(y->expr, "Shift amount '%s' must be an unsigned integer", y_str);
+ gb_string_free(y_str);
+ x->mode = Addressing_Invalid;
+ return;
}
bool x_is_untyped = is_type_untyped(x->type);
- if (!(is_type_integer(x->type) || (x_is_untyped && x_val.kind == ExactValue_Integer))) {
- gbString err_str = expr_to_string(x->expr);
- error(node, "Shifted operand '%s' must be an integer", err_str);
- gb_string_free(err_str);
+ if (!(x_is_untyped || is_type_integer(x->type))) {
+ gbString x_str = expr_to_string(x->expr);
+ error(x->expr, "Shifted operand '%s' must be an integer", x_str);
+ gb_string_free(x_str);
x->mode = Addressing_Invalid;
return;
}
- if (is_type_unsigned(y->type)) {
-
- } else if (is_type_untyped(y->type)) {
- convert_to_typed(c, y, t_untyped_integer);
- if (y->mode == Addressing_Invalid) {
+ if (y->mode == Addressing_Constant) {
+ if (big_int_is_neg(&y->value.value_integer)) {
+ gbString y_str = expr_to_string(y->expr);
+ error(y->expr, "Shift amount '%s' cannot be negative", y_str);
+ gb_string_free(y_str);
x->mode = Addressing_Invalid;
return;
}
- } else {
- gbString err_str = expr_to_string(y->expr);
- error(node, "Shift amount '%s' must be an unsigned integer", err_str);
- gb_string_free(err_str);
- x->mode = Addressing_Invalid;
- return;
- }
+ BigInt max_shift = {};
+ big_int_from_u64(&max_shift, MAX_BIG_INT_SHIFT);
- if (x->mode == Addressing_Constant) {
- if (y->mode == Addressing_Constant) {
- ExactValue y_val = exact_value_to_integer(y->value);
- if (y_val.kind != ExactValue_Integer) {
- gbString err_str = expr_to_string(y->expr);
- error(node, "Shift amount '%s' must be an unsigned integer", err_str);
- gb_string_free(err_str);
- x->mode = Addressing_Invalid;
- return;
- }
+ if (big_int_cmp(&y->value.value_integer, &max_shift) > 0) {
+ gbString y_str = expr_to_string(y->expr);
+ error(y->expr, "Shift amount '%s' must be <= %u", y_str, MAX_BIG_INT_SHIFT);
+ gb_string_free(y_str);
+ x->mode = Addressing_Invalid;
+ return;
+ }
- BigInt max_shift = {};
- big_int_from_u64(&max_shift, MAX_BIG_INT_SHIFT);
+ if (x->mode == Addressing_Constant) {
+ if (x_is_untyped) {
+ convert_to_typed(c, x, t_untyped_integer);
+ if (x->mode == Addressing_Invalid) {
+ return;
+ }
- if (big_int_cmp(&y_val.value_integer, &max_shift) > 0) {
- gbString err_str = expr_to_string(y->expr);
- error(node, "Shift amount too large: '%s'", err_str);
- gb_string_free(err_str);
- x->mode = Addressing_Invalid;
- return;
- }
+ x->expr = node;
+ x->value = exact_value_shift(be->op.kind, exact_value_to_integer(x->value), exact_value_to_integer(y->value));
- if (!is_type_integer(x->type)) {
- // NOTE(bill): It could be an untyped float but still representable
- // as an integer
- x->type = t_untyped_integer;
+ return;
}
x->expr = node;
- x->value = exact_value_shift(be->op.kind, x_val, y_val);
+ x->value = exact_value_shift(be->op.kind, x->value, y->value);
+ check_is_expressible(c, x, x->type);
- if (is_type_typed(x->type)) {
- check_is_expressible(c, x, x->type);
- }
return;
}
- TokenPos pos = ast_token(x->expr).pos;
+ if (y_is_untyped) {
+ convert_to_typed(c, y, t_uint);
+ }
+
+ return;
+ }
+
+ if (x->mode == Addressing_Constant) {
if (x_is_untyped) {
- if (x->expr != nullptr) {
- x->expr->tav.is_lhs = true;
- }
- x->mode = Addressing_Value;
if (type_hint) {
if (is_type_integer(type_hint)) {
convert_to_typed(c, x, type_hint);
+ } else if (is_type_any(type_hint)) {
+ convert_to_typed(c, x, default_type(t_untyped_integer));
} else {
gbString x_str = expr_to_string(x->expr);
- gbString to_type = type_to_string(type_hint);
- error(node, "Conversion of shifted operand '%s' to '%s' is not allowed", x_str, to_type);
+ gbString type_str = type_to_string(type_hint);
+ error(x->expr, "Shifted operand '%s' cannot convert to non-integer type '%s'", x_str, type_str);
gb_string_free(x_str);
- gb_string_free(to_type);
+ gb_string_free(type_str);
x->mode = Addressing_Invalid;
+ return;
}
- } else if (!is_type_integer(x->type)) {
- gbString x_str = expr_to_string(x->expr);
- error(node, "Non-integer shifted operand '%s' is not allowed", x_str);
- gb_string_free(x_str);
- x->mode = Addressing_Invalid;
+ } else {
+ check_is_expressible(c, x, default_type(t_untyped_integer));
+ }
+ if (x->mode == Addressing_Invalid) {
+ return;
}
- // x->value = x_val;
- return;
}
- }
-
- if (y->mode == Addressing_Constant && big_int_is_neg(&y->value.value_integer)) {
- gbString err_str = expr_to_string(y->expr);
- error(node, "Shift amount cannot be negative: '%s'", err_str);
- gb_string_free(err_str);
- }
- if (!is_type_integer(x->type)) {
- gbString err_str = expr_to_string(x->expr);
- error(node, "Shift operand '%s' must be an integer", err_str);
- gb_string_free(err_str);
- x->mode = Addressing_Invalid;
- return;
- }
-
- if (is_type_untyped(y->type)) {
- convert_to_typed(c, y, t_uint);
+ x->mode = Addressing_Value;
}
-
- x->mode = Addressing_Value;
}
-
-
gb_internal bool check_is_castable_to(CheckerContext *c, Operand *operand, Type *y) {
if (check_is_assignable_to(c, operand, y)) {
return true;
@@ -3319,6 +3392,11 @@ gb_internal bool check_is_castable_to(CheckerContext *c, Operand *operand, Type
return true;
}
+ // []u16 <-> string16 (not cstring16)
+ if (is_type_u16_slice(src) && (is_type_string16(dst) && !is_type_cstring16(dst))) {
+ return true;
+ }
+
// cstring -> string
if (are_types_identical(src, t_cstring) && are_types_identical(dst, t_string)) {
if (operand->mode != Addressing_Constant) {
@@ -3326,6 +3404,14 @@ gb_internal bool check_is_castable_to(CheckerContext *c, Operand *operand, Type
}
return true;
}
+ // cstring16 -> string16
+ if (are_types_identical(src, t_cstring16) && are_types_identical(dst, t_string16)) {
+ if (operand->mode != Addressing_Constant) {
+ add_package_dependency(c, "runtime", "cstring16_to_string16");
+ }
+ return true;
+ }
+
// cstring -> ^u8
if (are_types_identical(src, t_cstring) && is_type_u8_ptr(dst)) {
return !is_constant;
@@ -3351,6 +3437,34 @@ gb_internal bool check_is_castable_to(CheckerContext *c, Operand *operand, Type
if (is_type_rawptr(src) && are_types_identical(dst, t_cstring)) {
return !is_constant;
}
+
+ // cstring -> ^u16
+ if (are_types_identical(src, t_cstring16) && is_type_u16_ptr(dst)) {
+ return !is_constant;
+ }
+ // cstring -> [^]u16
+ if (are_types_identical(src, t_cstring16) && is_type_u16_multi_ptr(dst)) {
+ return !is_constant;
+ }
+ // cstring16 -> rawptr
+ if (are_types_identical(src, t_cstring16) && is_type_rawptr(dst)) {
+ return !is_constant;
+ }
+
+
+ // ^u16 -> cstring16
+ if (is_type_u16_ptr(src) && are_types_identical(dst, t_cstring16)) {
+ return !is_constant;
+ }
+ // [^]u16 -> cstring
+ if (is_type_u16_multi_ptr(src) && are_types_identical(dst, t_cstring16)) {
+ return !is_constant;
+ }
+ // rawptr -> cstring16
+ if (is_type_rawptr(src) && are_types_identical(dst, t_cstring16)) {
+ return !is_constant;
+ }
+
// proc <-> proc
if (is_type_proc(src) && is_type_proc(dst)) {
if (is_type_polymorphic(dst)) {
@@ -3406,6 +3520,7 @@ gb_internal bool check_is_castable_to(CheckerContext *c, Operand *operand, Type
gb_internal bool check_cast_internal(CheckerContext *c, Operand *x, Type *type) {
bool is_const_expr = x->mode == Addressing_Constant;
+
Type *bt = base_type(type);
if (is_const_expr && is_type_constant_type(bt)) {
if (core_type(bt)->kind == Type_Basic) {
@@ -3427,6 +3542,9 @@ gb_internal bool check_cast_internal(CheckerContext *c, Operand *x, Type *type)
} else if (is_type_slice(type) && is_type_string(x->type)) {
x->mode = Addressing_Value;
} else if (is_type_union(type)) {
+ if (is_type_union_constantable(type)) {
+ return true;
+ }
x->mode = Addressing_Value;
}
if (x->mode == Addressing_Value) {
@@ -3666,6 +3784,11 @@ gb_internal bool check_transmute(CheckerContext *c, Ast *node, Operand *o, Type
}
gb_internal bool check_binary_array_expr(CheckerContext *c, Token op, Operand *x, Operand *y) {
+ if (is_type_array_like(x->type) || is_type_array_like(y->type)) {
+ if (op.kind == Token_CmpAnd || op.kind == Token_CmpOr) {
+ error(op, "Array programming is not allowed with the operator '%.*s'", LIT(op.string));
+ }
+ }
if (is_type_array(x->type) && !is_type_array(y->type)) {
if (check_is_assignable_to(c, y, x->type)) {
if (check_binary_op(c, x, op)) {
@@ -3841,6 +3964,59 @@ matrix_error:
}
+gb_internal void check_binary_expr_dependency(CheckerContext *c, Token op, Type *bt, bool REQUIRE) {
+ if (op.kind == Token_Mod || op.kind == Token_ModEq ||
+ op.kind == Token_ModMod || op.kind == Token_ModModEq) {
+ if (bt->kind == Type_Basic) switch (bt->Basic.kind) {
+ case Basic_u128: add_package_dependency(c, "runtime", "umodti3", REQUIRE); break;
+ case Basic_i128: add_package_dependency(c, "runtime", "modti3", REQUIRE); break;
+ }
+ } else if (op.kind == Token_Quo || op.kind == Token_QuoEq) {
+ if (bt->kind == Type_Basic) switch (bt->Basic.kind) {
+ case Basic_complex32: add_package_dependency(c, "runtime", "quo_complex32"); break;
+ case Basic_complex64: add_package_dependency(c, "runtime", "quo_complex64"); break;
+ case Basic_complex128: add_package_dependency(c, "runtime", "quo_complex128"); break;
+ case Basic_quaternion64: add_package_dependency(c, "runtime", "quo_quaternion64"); break;
+ case Basic_quaternion128: add_package_dependency(c, "runtime", "quo_quaternion128"); break;
+ case Basic_quaternion256: add_package_dependency(c, "runtime", "quo_quaternion256"); break;
+
+ case Basic_u128: add_package_dependency(c, "runtime", "udivti3", REQUIRE); break;
+ case Basic_i128: add_package_dependency(c, "runtime", "divti3", REQUIRE); break;
+ }
+ } else if (op.kind == Token_Mul || op.kind == Token_MulEq) {
+ if (bt->kind == Type_Basic) switch (bt->Basic.kind) {
+ case Basic_quaternion64: add_package_dependency(c, "runtime", "mul_quaternion64"); break;
+ case Basic_quaternion128: add_package_dependency(c, "runtime", "mul_quaternion128"); break;
+ case Basic_quaternion256: add_package_dependency(c, "runtime", "mul_quaternion256"); break;
+
+
+ case Basic_u128:
+ case Basic_i128:
+ if (is_arch_wasm()) {
+ add_package_dependency(c, "runtime", "__multi3", REQUIRE);
+ }
+ break;
+ }
+ } else if (op.kind == Token_Shl || op.kind == Token_ShlEq) {
+ if (bt->kind == Type_Basic) switch (bt->Basic.kind) {
+ case Basic_u128:
+ case Basic_i128:
+ if (is_arch_wasm()) {
+ add_package_dependency(c, "runtime", "__ashlti3", REQUIRE);
+ }
+ break;
+ }
+ } else if (op.kind == Token_Shr || op.kind == Token_ShrEq) {
+ if (bt->kind == Type_Basic) switch (bt->Basic.kind) {
+ case Basic_u128:
+ case Basic_i128:
+ if (is_arch_wasm()) {
+ add_package_dependency(c, "runtime", "__lshrti3", REQUIRE);
+ }
+ break;
+ }
+ }
+}
gb_internal void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, Type *type_hint, bool use_lhs_as_type_hint=false) {
GB_ASSERT(node->kind == Ast_BinaryExpr);
@@ -4038,58 +4214,10 @@ gb_internal void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, Typ
}
bool REQUIRE = true;
- Type *bt = base_type(x->type);
- if (op.kind == Token_Mod || op.kind == Token_ModEq ||
- op.kind == Token_ModMod || op.kind == Token_ModModEq) {
- if (bt->kind == Type_Basic) switch (bt->Basic.kind) {
- case Basic_u128: add_package_dependency(c, "runtime", "umodti3", REQUIRE); break;
- case Basic_i128: add_package_dependency(c, "runtime", "modti3", REQUIRE); break;
- }
- } else if (op.kind == Token_Quo || op.kind == Token_QuoEq) {
- if (bt->kind == Type_Basic) switch (bt->Basic.kind) {
- case Basic_complex32: add_package_dependency(c, "runtime", "quo_complex32"); break;
- case Basic_complex64: add_package_dependency(c, "runtime", "quo_complex64"); break;
- case Basic_complex128: add_package_dependency(c, "runtime", "quo_complex128"); break;
- case Basic_quaternion64: add_package_dependency(c, "runtime", "quo_quaternion64"); break;
- case Basic_quaternion128: add_package_dependency(c, "runtime", "quo_quaternion128"); break;
- case Basic_quaternion256: add_package_dependency(c, "runtime", "quo_quaternion256"); break;
-
- case Basic_u128: add_package_dependency(c, "runtime", "udivti3", REQUIRE); break;
- case Basic_i128: add_package_dependency(c, "runtime", "divti3", REQUIRE); break;
- }
- } else if (op.kind == Token_Mul || op.kind == Token_MulEq) {
- if (bt->kind == Type_Basic) switch (bt->Basic.kind) {
- case Basic_quaternion64: add_package_dependency(c, "runtime", "mul_quaternion64"); break;
- case Basic_quaternion128: add_package_dependency(c, "runtime", "mul_quaternion128"); break;
- case Basic_quaternion256: add_package_dependency(c, "runtime", "mul_quaternion256"); break;
-
-
- case Basic_u128:
- case Basic_i128:
- if (is_arch_wasm()) {
- add_package_dependency(c, "runtime", "__multi3", REQUIRE);
- }
- break;
- }
- } else if (op.kind == Token_Shl || op.kind == Token_ShlEq) {
- if (bt->kind == Type_Basic) switch (bt->Basic.kind) {
- case Basic_u128:
- case Basic_i128:
- if (is_arch_wasm()) {
- add_package_dependency(c, "runtime", "__ashlti3", REQUIRE);
- }
- break;
- }
- } else if (op.kind == Token_Shr || op.kind == Token_ShrEq) {
- if (bt->kind == Type_Basic) switch (bt->Basic.kind) {
- case Basic_u128:
- case Basic_i128:
- if (is_arch_wasm()) {
- add_package_dependency(c, "runtime", "__lshrti3", REQUIRE);
- }
- break;
- }
- }
+ Type *btx = base_type(x->type);
+ Type *bty = base_type(y->type);
+ check_binary_expr_dependency(c, op, btx, REQUIRE);
+ check_binary_expr_dependency(c, op, bty, REQUIRE);
if (token_is_shift(op.kind)) {
check_shift(c, x, y, node, type_hint);
@@ -4204,7 +4332,25 @@ gb_internal void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, Typ
}
if (fail) {
- error(y->expr, "Division by zero not allowed");
+ if (is_type_integer(x->type) || (x->mode == Addressing_Constant && x->value.kind == ExactValue_Integer)) {
+ if (check_for_integer_division_by_zero(c, node) != IntegerDivisionByZero_Trap) {
+ // Okay
+ break;
+ }
+ }
+
+ switch (op.kind) {
+ case Token_Mod:
+ case Token_ModMod:
+ case Token_ModEq:
+ case Token_ModModEq:
+ error(y->expr, "Division by zero through '%.*s' not allowed", LIT(token_strings[op.kind]));
+ break;
+ case Token_Quo:
+ case Token_QuoEq:
+ error(y->expr, "Division by zero not allowed");
+ break;
+ }
x->mode = Addressing_Invalid;
return;
}
@@ -4244,7 +4390,59 @@ gb_internal void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, Typ
}
}
- x->value = exact_binary_operator_value(op.kind, a, b);
+ match_exact_values(&a, &b);
+
+
+ IntegerDivisionByZeroKind zero_behaviour = check_for_integer_division_by_zero(c, node);
+ if (zero_behaviour != IntegerDivisionByZero_Trap &&
+ b.kind == ExactValue_Integer && big_int_is_zero(&b.value_integer) &&
+ (op.kind == Token_QuoEq || op.kind == Token_Mod || op.kind == Token_ModMod)) {
+ if (op.kind == Token_QuoEq) {
+ switch (zero_behaviour) {
+ case IntegerDivisionByZero_Zero:
+ // x/0 == 0
+ x->value = b;
+ break;
+ case IntegerDivisionByZero_Self:
+ // x/0 == x
+ x->value = a;
+ break;
+ case IntegerDivisionByZero_AllBits:
+ // x/0 == 0b111...111
+ if (is_type_untyped(x->type)) {
+ x->value = exact_value_i64(-1);
+ } else {
+ x->value = exact_unary_operator_value(Token_Xor, b, cast(i32)(8*type_size_of(x->type)), is_type_unsigned(x->type));
+ }
+ break;
+ }
+ } else {
+ /*
+ NOTE(bill): @integer division by zero rules
+
+ truncated: r = a - b*trunc(a/b)
+ floored: r = a - b*floor(a/b)
+
+ IFF a/0 == 0, then (a%0 == a) or (a%%0 == a)
+ IFF a/0 == a, then (a%0 == 0) or (a%%0 == 0)
+ IFF a/0 == 0b111..., then (a%0 == a) or (a%%0 == a)
+ */
+
+ switch (zero_behaviour) {
+ case IntegerDivisionByZero_Zero:
+ case IntegerDivisionByZero_AllBits:
+ // x%0 == x
+ x->value = a;
+ break;
+ case IntegerDivisionByZero_Self:
+ // x%0 == 0
+ x->value = b;
+ break;
+ }
+ }
+ } else {
+ x->value = exact_binary_operator_value(op.kind, a, b);
+ }
if (is_type_typed(x->type)) {
if (node != nullptr) {
@@ -4462,7 +4660,6 @@ gb_internal ExactValue convert_exact_value_for_type(ExactValue v, Type *type) {
}
gb_internal void convert_to_typed(CheckerContext *c, Operand *operand, Type *target_type) {
- // GB_ASSERT_NOT_NULL(target_type);
if (target_type == nullptr || operand->mode == Addressing_Invalid ||
operand->mode == Addressing_Type ||
is_type_typed(operand->type) ||
@@ -4504,8 +4701,7 @@ gb_internal void convert_to_typed(CheckerContext *c, Operand *operand, Type *tar
} else {
switch (operand->type->Basic.kind) {
case Basic_UntypedBool:
- if (!is_type_boolean(target_type) &&
- !is_type_integer(target_type)) {
+ if (!is_type_boolean(target_type)) {
operand->mode = Addressing_Invalid;
convert_untyped_error(c, operand, target_type);
return;
@@ -4528,6 +4724,8 @@ gb_internal void convert_to_typed(CheckerContext *c, Operand *operand, Type *tar
// target_type = t_untyped_nil;
} else if (is_type_cstring(target_type)) {
// target_type = t_untyped_nil;
+ } else if (is_type_cstring16(target_type)) {
+ // target_type = t_untyped_nil;
} else if (!type_has_nil(target_type)) {
operand->mode = Addressing_Invalid;
convert_untyped_error(c, operand, target_type);
@@ -4555,6 +4753,13 @@ gb_internal void convert_to_typed(CheckerContext *c, Operand *operand, Type *tar
break;
}
}
+ } else if (operand->value.kind == ExactValue_String16) {
+ String16 s = operand->value.value_string16;
+ if (is_type_u16_array(t)) {
+ if (s.len == t->Array.count) {
+ break;
+ }
+ }
}
operand->mode = Addressing_Invalid;
convert_untyped_error(c, operand, target_type);
@@ -4625,7 +4830,7 @@ gb_internal void convert_to_typed(CheckerContext *c, Operand *operand, Type *tar
TEMPORARY_ALLOCATOR_GUARD();
isize count = t->Union.variants.count;
- ValidIndexAndScore *valids = gb_alloc_array(temporary_allocator(), ValidIndexAndScore, count);
+ ValidIndexAndScore *valids = temporary_alloc_array<ValidIndexAndScore>(count);
isize valid_count = 0;
isize first_success_index = -1;
for_array(i, t->Union.variants) {
@@ -4666,7 +4871,10 @@ gb_internal void convert_to_typed(CheckerContext *c, Operand *operand, Type *tar
break;
}
operand->type = new_type;
- operand->mode = Addressing_Value;
+ if (operand->mode != Addressing_Constant ||
+ !elem_type_can_be_constant(operand->type)) {
+ operand->mode = Addressing_Value;
+ }
break;
} else if (valid_count > 1) {
ERROR_BLOCK();
@@ -4884,6 +5092,12 @@ gb_internal ExactValue get_constant_field_single(CheckerContext *c, ExactValue v
if (success_) *success_ = true;
if (finish_) *finish_ = true;
return exact_value_u64(val);
+ } else if (value.kind == ExactValue_String16) {
+ GB_ASSERT(0 <= index && index < value.value_string.len);
+ u16 val = value.value_string16[index];
+ if (success_) *success_ = true;
+ if (finish_) *finish_ = true;
+ return exact_value_u64(val);
}
if (value.kind != ExactValue_Compound) {
if (success_) *success_ = true;
@@ -4902,7 +5116,11 @@ gb_internal ExactValue get_constant_field_single(CheckerContext *c, ExactValue v
}
if (cl->elems[0]->kind == Ast_FieldValue) {
- if (is_type_struct(node->tav.type)) {
+ if (is_type_raw_union(node->tav.type)) {
+ if (success_) *success_ = false;
+ if (finish_) *finish_ = true;
+ return empty_exact_value;
+ } else if (is_type_struct(node->tav.type)) {
bool found = false;
for (Ast *elem : cl->elems) {
if (elem->kind != Ast_FieldValue) {
@@ -4911,7 +5129,6 @@ gb_internal ExactValue get_constant_field_single(CheckerContext *c, ExactValue v
ast_node(fv, FieldValue, elem);
String name = fv->field->Ident.token.string;
Selection sub_sel = lookup_field(node->tav.type, name, false);
- defer (array_free(&sub_sel.index));
if (sub_sel.index.count > 0 &&
sub_sel.index[0] == index) {
value = fv->value->tav.value;
@@ -5172,7 +5389,7 @@ gb_internal Entity *check_entity_from_ident_or_selector(CheckerContext *c, Ast *
}
} else */if (node->kind == Ast_Ident) {
String name = node->Ident.token.string;
- return scope_lookup(c->scope, name);
+ return scope_lookup(c->scope, name, node->Ident.hash);
} else if (!ident_only) if (node->kind == Ast_SelectorExpr) {
ast_node(se, SelectorExpr, node);
if (se->token.kind == Token_ArrowRight) {
@@ -5194,7 +5411,7 @@ gb_internal Entity *check_entity_from_ident_or_selector(CheckerContext *c, Ast *
if (op_expr->kind == Ast_Ident) {
String op_name = op_expr->Ident.token.string;
- Entity *e = scope_lookup(c->scope, op_name);
+ Entity *e = scope_lookup(c->scope, op_name, op_expr->Ident.hash);
if (e == nullptr) {
return nullptr;
}
@@ -5291,7 +5508,7 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod
if (op_expr->kind == Ast_Ident) {
String op_name = op_expr->Ident.token.string;
- Entity *e = scope_lookup(c->scope, op_name);
+ Entity *e = scope_lookup(c->scope, op_name, op_expr->Ident.hash);
add_entity_use(c, op_expr, e);
expr_entity = e;
@@ -5331,16 +5548,6 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod
return nullptr;
}
- check_entity_decl(c, entity, nullptr, nullptr);
- if (entity->kind == Entity_ProcGroup) {
- operand->mode = Addressing_ProcGroup;
- operand->proc_group = entity;
-
- add_type_and_value(c, operand->expr, operand->mode, operand->type, operand->value);
- return entity;
- }
- GB_ASSERT_MSG(entity->type != nullptr, "%.*s (%.*s)", LIT(entity->token.string), LIT(entity_strings[entity->kind]));
-
if (!is_entity_exported(entity, allow_builtin)) {
gbString sel_str = expr_to_string(selector);
error(node, "'%s' is not exported by '%.*s'", sel_str, LIT(import_name));
@@ -5351,36 +5558,15 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod
// return nullptr;
}
+ check_entity_decl(c, entity, nullptr, nullptr);
if (entity->kind == Entity_ProcGroup) {
- Array<Entity *> procs = entity->ProcGroup.entities;
- bool skip = false;
- for (Entity *p : procs) {
- Type *t = base_type(p->type);
- if (t == t_invalid) {
- continue;
- }
-
- Operand x = {};
- x.mode = Addressing_Value;
- x.type = t;
- if (type_hint != nullptr) {
- if (check_is_assignable_to(c, &x, type_hint)) {
- entity = p;
- skip = true;
- break;
- }
- }
- }
+ operand->mode = Addressing_ProcGroup;
+ operand->proc_group = entity;
- if (!skip) {
- GB_ASSERT(entity != nullptr);
- operand->mode = Addressing_ProcGroup;
- operand->type = t_invalid;
- operand->expr = node;
- operand->proc_group = entity;
- return entity;
- }
+ add_type_and_value(c, operand->expr, operand->mode, operand->type, operand->value);
+ return entity;
}
+ GB_ASSERT_MSG(entity->type != nullptr, "%.*s (%.*s)", LIT(entity->token.string), LIT(entity_strings[entity->kind]));
}
}
@@ -5419,8 +5605,18 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod
}
}
+ if (operand->type && is_type_simd_vector(type_deref(operand->type))) {
+ String field_name = selector->Ident.token.string;
+ if (field_name.len == 1) {
+ error(op_expr, "Extracting an element from a #simd array using .%.*s syntax is disallowed, prefer `simd.extract`", LIT(field_name));
+ } else {
+ error(op_expr, "Extracting elements from a #simd array using .%.*s syntax is disallowed, prefer `swizzle`", LIT(field_name));
+ }
+ return nullptr;
+ }
+
if (entity == nullptr && selector->kind == Ast_Ident && operand->type != nullptr &&
- (is_type_array(type_deref(operand->type)) || is_type_simd_vector(type_deref(operand->type)))) {
+ (is_type_array(type_deref(operand->type)))) {
String field_name = selector->Ident.token.string;
if (1 < field_name.len && field_name.len <= 4) {
u8 swizzles_xyzw[4] = {'x', 'y', 'z', 'w'};
@@ -5475,7 +5671,7 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod
Type *original_type = operand->type;
Type *array_type = base_type(type_deref(original_type));
- GB_ASSERT(array_type->kind == Type_Array || array_type->kind == Type_SimdVector);
+ GB_ASSERT(array_type->kind == Type_Array);
i64 array_count = get_array_type_count(array_type);
@@ -5516,10 +5712,6 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod
break;
}
- if (array_type->kind == Type_SimdVector) {
- operand->mode = Addressing_Value;
- }
-
Entity *swizzle_entity = alloc_entity_variable(nullptr, make_token_ident(field_name), operand->type, EntityState_Resolved);
add_type_and_value(c, operand->expr, operand->mode, operand->type, operand->value);
return swizzle_entity;
@@ -5642,7 +5834,7 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod
switch (entity->kind) {
case Entity_Constant:
- operand->value = entity->Constant.value;
+ operand->value = entity->Constant.value;
operand->mode = Addressing_Constant;
if (operand->value.kind == ExactValue_Procedure) {
Entity *proc = strip_entity_wrapping(operand->value.value_procedure);
@@ -5739,7 +5931,7 @@ gb_internal bool check_identifier_exists(Scope *s, Ast *node, bool nested = fals
return true;
}
} else {
- Entity *e = scope_lookup(s, name);
+ Entity *e = scope_lookup(s, name, i->hash);
if (e != nullptr) {
if (out_scope) *out_scope = e->scope;
return true;
@@ -5758,22 +5950,6 @@ gb_internal bool check_identifier_exists(Scope *s, Ast *node, bool nested = fals
return false;
}
-gb_internal bool check_no_copy_assignment(Operand const &o, String const &context) {
- if (o.type && is_type_no_copy(o.type)) {
- Ast *expr = unparen_expr(o.expr);
- if (expr && o.mode != Addressing_Constant && o.mode != Addressing_Type) {
- if (expr->kind == Ast_CallExpr) {
- // Okay
- } else {
- error(o.expr, "Invalid use of #no_copy value in %.*s", LIT(context));
- return true;
- }
- }
- }
- return false;
-}
-
-
gb_internal bool check_assignment_arguments(CheckerContext *ctx, Array<Operand> const &lhs, Array<Operand> *operands, Slice<Ast *> const &rhs) {
bool optional_ok = false;
isize tuple_index = 0;
@@ -5844,7 +6020,6 @@ gb_internal bool check_assignment_arguments(CheckerContext *ctx, Array<Operand>
for (Entity *e : tuple->variables) {
o.type = e->type;
array_add(operands, o);
- check_no_copy_assignment(o, str_lit("assignment"));
}
tuple_index += tuple->variables.count;
@@ -5859,12 +6034,12 @@ typedef u32 UnpackFlags;
enum UnpackFlag : u32 {
UnpackFlag_None = 0,
UnpackFlag_AllowOk = 1<<0,
- UnpackFlag_IsVariadic = 1<<1,
- UnpackFlag_AllowUndef = 1<<2,
+ UnpackFlag_AllowUndef = 1<<1,
};
-gb_internal bool check_unpack_arguments(CheckerContext *ctx, Entity **lhs, isize lhs_count, Array<Operand> *operands, Slice<Ast *> const &rhs_arguments, UnpackFlags flags) {
+gb_internal bool check_unpack_arguments(CheckerContext *ctx, Entity **lhs, isize lhs_count, Array<Operand> *operands, Slice<Ast *> const &rhs_arguments, UnpackFlags flags,
+ isize variadic_index = -1) {
auto const &add_dependencies_from_unpacking = [](CheckerContext *c, Entity **lhs, isize lhs_count, isize tuple_index, isize tuple_count) -> isize {
if (lhs == nullptr || c->decl == nullptr) {
return tuple_count;
@@ -5880,7 +6055,7 @@ gb_internal bool check_unpack_arguments(CheckerContext *ctx, Entity **lhs, isize
}
rw_mutex_shared_lock(&decl->deps_mutex);
rw_mutex_lock(&c->decl->deps_mutex);
- for (Entity *dep : decl->deps) {
+ FOR_PTR_SET(dep, decl->deps) {
ptr_set_add(&c->decl->deps, dep);
}
rw_mutex_unlock(&c->decl->deps_mutex);
@@ -5889,11 +6064,14 @@ gb_internal bool check_unpack_arguments(CheckerContext *ctx, Entity **lhs, isize
return tuple_count;
};
-
bool allow_ok = (flags & UnpackFlag_AllowOk) != 0;
- bool is_variadic = (flags & UnpackFlag_IsVariadic) != 0;
bool allow_undef = (flags & UnpackFlag_AllowUndef) != 0;
+ bool is_variadic = variadic_index > -1;
+ if (!is_variadic) {
+ variadic_index = lhs_count;
+ }
+
bool optional_ok = false;
isize tuple_index = 0;
for (Ast *rhs : rhs_arguments) {
@@ -5909,26 +6087,18 @@ gb_internal bool check_unpack_arguments(CheckerContext *ctx, Entity **lhs, isize
Type *type_hint = nullptr;
-
- if (lhs != nullptr && tuple_index < lhs_count) {
- // NOTE(bill): override DeclInfo for dependency
- Entity *e = lhs[tuple_index];
- if (e != nullptr) {
- type_hint = e->type;
- if (e->flags & EntityFlag_Ellipsis) {
- GB_ASSERT(is_type_slice(e->type));
- GB_ASSERT(e->type->kind == Type_Slice);
- type_hint = e->type->Slice.elem;
+ if (lhs != nullptr) {
+ if (tuple_index < variadic_index) {
+ // NOTE(bill): override DeclInfo for dependency
+ Entity *e = lhs[tuple_index];
+ if (e != nullptr) {
+ type_hint = e->type;
}
- }
- } else if (lhs != nullptr && tuple_index >= lhs_count && is_variadic) {
- // NOTE(bill): override DeclInfo for dependency
- Entity *e = lhs[lhs_count-1];
- if (e != nullptr) {
- type_hint = e->type;
- if (e->flags & EntityFlag_Ellipsis) {
+ } else if (is_variadic) {
+ Entity *e = lhs[variadic_index];
+ if (e != nullptr) {
+ GB_ASSERT(e->flags & EntityFlag_Ellipsis);
GB_ASSERT(is_type_slice(e->type));
- GB_ASSERT(e->type->kind == Type_Slice);
type_hint = e->type->Slice.elem;
}
}
@@ -6075,7 +6245,8 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A
Entity *entity, Type *proc_type,
Array<Operand> positional_operands, Array<Operand> const &named_operands,
CallArgumentErrorMode show_error_mode,
- CallArgumentData *data) {
+ CallArgumentData *data,
+ bool checking_proc_group) {
TEMPORARY_ALLOCATOR_GUARD();
CallArgumentError err = CallArgumentError_None;
@@ -6112,7 +6283,7 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A
}
GB_ASSERT(ce->split_args);
- auto visited = slice_make<bool>(temporary_allocator(), pt->param_count);
+ auto visited = temporary_slice_make<bool>(pt->param_count);
auto ordered_operands = array_make<Operand>(temporary_allocator(), pt->param_count);
defer ({
for (Operand const &o : ordered_operands) {
@@ -6236,29 +6407,46 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A
}
- for (Operand const &o : ordered_operands) {
- if (o.mode != Addressing_Invalid) {
- check_no_copy_assignment(o, str_lit("procedure call expression"));
- }
- }
-
for (isize i = 0; i < pt->param_count; i++) {
if (!visited[i]) {
Entity *e = pt->params->Tuple.variables[i];
+ bool context_allocator_error = false;
if (e->kind == Entity_Variable) {
if (e->Variable.param_value.kind != ParameterValue_Invalid) {
- ordered_operands[i].mode = Addressing_Value;
- ordered_operands[i].type = e->type;
- ordered_operands[i].expr = e->Variable.param_value.original_ast_expr;
+ if (ast_file_vet_explicit_allocators(c->file) && !checking_proc_group) {
+ // NOTE(lucas): check if we are trying to default to context.allocator or context.temp_allocator
+ if (e->Variable.param_value.original_ast_expr->kind == Ast_SelectorExpr) {
+ auto& expr = e->Variable.param_value.original_ast_expr->SelectorExpr.expr;
+ auto& selector = e->Variable.param_value.original_ast_expr->SelectorExpr.selector;
+ if (expr->kind == Ast_Implicit &&
+ expr->Implicit.string == STR_LIT("context") &&
+ selector->kind == Ast_Ident &&
+ (selector->Ident.token.string == STR_LIT("allocator") ||
+ selector->Ident.token.string == STR_LIT("temp_allocator"))) {
+ context_allocator_error = true;
+ }
+ }
+ }
- dummy_argument_count += 1;
- score += assign_score_function(1);
- continue;
+ if (!context_allocator_error) {
+ ordered_operands[i].mode = Addressing_Value;
+ ordered_operands[i].type = e->type;
+ ordered_operands[i].expr = e->Variable.param_value.original_ast_expr;
+
+ dummy_argument_count += 1;
+ score += assign_score_function(1);
+ continue;
+ }
}
}
if (show_error) {
- if (e->kind == Entity_TypeName) {
+ if (context_allocator_error) {
+ gbString str = type_to_string(e->type);
+ error(call, "Parameter '%.*s' of type '%s' must be explicitly provided in procedure call",
+ LIT(e->token.string), str);
+ gb_string_free(str);
+ } else if (e->kind == Entity_TypeName) {
error(call, "Type parameter '%.*s' is missing in procedure call",
LIT(e->token.string));
} else if (e->kind == Entity_Constant && e->Constant.value.kind != ExactValue_Invalid) {
@@ -6310,6 +6498,21 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A
}
}
+ if (e && e->kind == Entity_Constant && is_type_proc(e->type)) {
+ bool ok = false;
+ if (o->mode == Addressing_Constant) {
+ ok = true;
+ } else if (o->value.kind == ExactValue_Procedure) {
+ ok = true;
+ }
+
+ if (!ok) {
+ if (show_error) {
+ error(o->expr, "Expected a constant procedure value for the argument '%.*s'", LIT(e->token.string));
+ }
+ err = CallArgumentError_NoneConstantParameter;
+ }
+ }
if (!err && is_type_any(param_type)) {
add_type_info_type(c, o->type);
@@ -6468,17 +6671,16 @@ gb_internal bool is_call_expr_field_value(AstCallExpr *ce) {
return ce->args[0]->kind == Ast_FieldValue;
}
-gb_internal Entity **populate_proc_parameter_list(CheckerContext *c, Type *proc_type, isize *lhs_count_, bool *is_variadic) {
+gb_internal Entity **populate_proc_parameter_list(CheckerContext *c, Type *proc_type, isize *lhs_count_) {
Entity **lhs = nullptr;
isize lhs_count = -1;
- if (proc_type == nullptr) {
+ if (proc_type == nullptr || proc_type == t_invalid) {
return nullptr;
}
GB_ASSERT(is_type_proc(proc_type));
TypeProc *pt = &base_type(proc_type)->Proc;
- *is_variadic = pt->variadic;
if (!pt->is_polymorphic || pt->is_poly_specialized) {
if (pt->params != nullptr) {
@@ -6534,7 +6736,7 @@ gb_internal bool evaluate_where_clauses(CheckerContext *ctx, Ast *call_expr, Sco
Entity *e = entry.value;
switch (e->kind) {
case Entity_TypeName: {
- if (print_count == 0) error_line("\n\tWith the following definitions:\n");
+ // if (print_count == 0) error_line("\n\tWith the following definitions:\n");
gbString str = type_to_string(e->type);
error_line("\t\t%.*s :: %s;\n", LIT(e->token.string), str);
@@ -6653,7 +6855,8 @@ gb_internal bool check_call_arguments_single(CheckerContext *c, Ast *call, Opera
Entity *e, Type *proc_type,
Array<Operand> const &positional_operands, Array<Operand> const &named_operands,
CallArgumentErrorMode show_error_mode,
- CallArgumentData *data) {
+ CallArgumentData *data,
+ bool checking_proc_group) {
bool return_on_failure = show_error_mode == CallArgumentErrorMode::NoErrors;
@@ -6672,9 +6875,12 @@ gb_internal bool check_call_arguments_single(CheckerContext *c, Ast *call, Opera
GB_ASSERT(proc_type != nullptr);
proc_type = base_type(proc_type);
+ if (proc_type == t_invalid) {
+ return false;
+ }
GB_ASSERT(proc_type->kind == Type_Proc);
- CallArgumentError err = check_call_arguments_internal(c, call, e, proc_type, positional_operands, named_operands, show_error_mode, data);
+ CallArgumentError err = check_call_arguments_internal(c, call, e, proc_type, positional_operands, named_operands, show_error_mode, data, checking_proc_group);
if (return_on_failure && err != CallArgumentError_None) {
return false;
}
@@ -6799,31 +7005,40 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c,
array_unordered_remove(&procs, proc_index);
continue;
}
+ if (!pt->Proc.variadic && max_arg_count != ISIZE_MAX && param_count < max_arg_count) {
+ array_unordered_remove(&procs, proc_index);
+ continue;
+ }
proc_index++;
}
}
Entity **lhs = nullptr;
isize lhs_count = -1;
- bool is_variadic = false;
+ i32 variadic_index = -1;
- auto positional_operands = array_make<Operand>(heap_allocator(), 0, 0);
- auto named_operands = array_make<Operand>(heap_allocator(), 0, 0);
- defer (array_free(&positional_operands));
- defer (array_free(&named_operands));
+ TEMPORARY_ALLOCATOR_GUARD();
+
+ auto positional_operands = array_make<Operand>(temporary_allocator(), 0, 0);
+ auto named_operands = array_make<Operand>(temporary_allocator(), 0, 0);
if (procs.count == 1) {
Entity *e = procs[0];
-
- lhs = populate_proc_parameter_list(c, e->type, &lhs_count, &is_variadic);
- check_unpack_arguments(c, lhs, lhs_count, &positional_operands, positional_args, is_variadic ? UnpackFlag_IsVariadic : UnpackFlag_None);
+ Type *pt = base_type(e->type);
+ if (pt != nullptr && is_type_proc(pt)) {
+ lhs = populate_proc_parameter_list(c, pt, &lhs_count);
+ if (pt->Proc.variadic) {
+ variadic_index = pt->Proc.variadic_index;
+ }
+ }
+ check_unpack_arguments(c, lhs, lhs_count, &positional_operands, positional_args, UnpackFlag_None, variadic_index);
if (check_named_arguments(c, e->type, named_args, &named_operands, true)) {
check_call_arguments_single(c, call, operand,
e, e->type,
positional_operands, named_operands,
CallArgumentErrorMode::ShowErrors,
- &data);
+ &data, false);
}
return data;
}
@@ -6846,7 +7061,7 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c,
if (proc_arg_count >= 0) {
lhs_count = proc_arg_count;
if (lhs_count > 0) {
- lhs = gb_alloc_array(heap_allocator(), Entity *, lhs_count);
+ lhs = gb_alloc_array(temporary_allocator(), Entity *, lhs_count);
for (isize param_index = 0; param_index < lhs_count; param_index++) {
Entity *e = nullptr;
for (Entity *p : procs) {
@@ -6876,11 +7091,30 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c,
}
lhs[param_index] = e;
}
+
+ for (Entity *p : procs) {
+ Type *pt = base_type(p->type);
+ if (!(pt != nullptr && is_type_proc(pt))) {
+ continue;
+ }
+
+ if (pt->Proc.is_polymorphic) {
+ if (variadic_index == -1) {
+ variadic_index = pt->Proc.variadic_index;
+ } else if (variadic_index != pt->Proc.variadic_index) {
+ variadic_index = -1;
+ break;
+ }
+ } else {
+ variadic_index = -1;
+ break;
+ }
+ }
}
}
}
- check_unpack_arguments(c, lhs, lhs_count, &positional_operands, positional_args, is_variadic ? UnpackFlag_IsVariadic : UnpackFlag_None);
+ check_unpack_arguments(c, lhs, lhs_count, &positional_operands, positional_args, UnpackFlag_None, variadic_index);
for_array(i, named_args) {
Ast *arg = named_args[i];
@@ -6913,13 +7147,9 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c,
array_add(&named_operands, o);
}
- gb_free(heap_allocator(), lhs);
-
- auto valids = array_make<ValidIndexAndScore>(heap_allocator(), 0, procs.count);
- defer (array_free(&valids));
+ auto valids = array_make<ValidIndexAndScore>(temporary_allocator(), 0, procs.count);
- auto proc_entities = array_make<Entity *>(heap_allocator(), 0, procs.count*2 + 1);
- defer (array_free(&proc_entities));
+ auto proc_entities = array_make<Entity *>(temporary_allocator(), 0, procs.count*2 + 1);
for (Entity *proc : procs) {
array_add(&proc_entities, proc);
}
@@ -6929,6 +7159,7 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c,
gbString expr_name = expr_to_string(operand->expr);
defer (gb_string_free(expr_name));
+ c->in_proc_group = true;
for_array(i, procs) {
Entity *p = procs[i];
if (p->flags & EntityFlag_Disabled) {
@@ -6948,7 +7179,7 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c,
p, pt,
positional_operands, named_operands,
CallArgumentErrorMode::NoErrors,
- &data);
+ &data, true);
if (!is_a_candidate) {
continue;
}
@@ -6971,6 +7202,7 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c,
array_add(&valids, item);
}
}
+ c->in_proc_group = false;
if (max_matched_features > 0) {
for_array(i, valids) {
@@ -7048,7 +7280,7 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c,
}
// Try to reduce the list further for `$T: typeid` like parameters
- bool *possibly_ignore = gb_alloc_array(temporary_allocator(), bool, procs.count);
+ bool *possibly_ignore = temporary_alloc_array<bool>(procs.count);
isize possibly_ignore_set = 0;
if (true) {
@@ -7136,7 +7368,7 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c,
}
isize max_spaces = gb_max(max_name_length, max_type_length);
- char *spaces = gb_alloc_array(temporary_allocator(), char, max_spaces+1);
+ char *spaces = temporary_alloc_array<char>(max_spaces+1);
for (isize i = 0; i < max_spaces; i++) {
spaces[i] = ' ';
}
@@ -7257,7 +7489,7 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c,
e, e->type,
positional_operands, named_operands,
CallArgumentErrorMode::ShowErrors,
- &data);
+ &data, false);
return data;
}
@@ -7311,20 +7543,22 @@ gb_internal CallArgumentData check_call_arguments(CheckerContext *c, Operand *op
return check_call_arguments_proc_group(c, operand, call);
}
- auto positional_operands = array_make<Operand>(heap_allocator(), 0, positional_args.count);
- auto named_operands = array_make<Operand>(heap_allocator(), 0, 0);
+ TEMPORARY_ALLOCATOR_GUARD();
- defer (array_free(&positional_operands));
- defer (array_free(&named_operands));
+ auto positional_operands = array_make<Operand>(temporary_allocator(), 0, positional_args.count);
+ auto named_operands = array_make<Operand>(temporary_allocator(), 0, 0);
if (positional_args.count > 0) {
- isize lhs_count = -1;
- bool is_variadic = false;
Entity **lhs = nullptr;
+ isize lhs_count = -1;
+ i32 variadic_index = -1;
if (pt != nullptr) {
- lhs = populate_proc_parameter_list(c, proc_type, &lhs_count, &is_variadic);
+ lhs = populate_proc_parameter_list(c, proc_type, &lhs_count);
+ if (pt->variadic) {
+ variadic_index = pt->variadic_index;
+ }
}
- check_unpack_arguments(c, lhs, lhs_count, &positional_operands, positional_args, is_variadic ? UnpackFlag_IsVariadic : UnpackFlag_None);
+ check_unpack_arguments(c, lhs, lhs_count, &positional_operands, positional_args, UnpackFlag_None, variadic_index);
}
if (named_args.count > 0) {
@@ -7367,7 +7601,7 @@ gb_internal CallArgumentData check_call_arguments(CheckerContext *c, Operand *op
nullptr, proc_type,
positional_operands, named_operands,
CallArgumentErrorMode::ShowErrors,
- &data);
+ &data, false);
} else if (pt) {
data.result_type = pt->results;
}
@@ -7399,6 +7633,8 @@ gb_internal isize lookup_polymorphic_record_parameter(Type *t, String parameter_
gb_internal CallArgumentError check_polymorphic_record_type(CheckerContext *c, Operand *operand, Ast *call) {
+ TEMPORARY_ALLOCATOR_GUARD();
+
ast_node(ce, CallExpr, call);
Type *original_type = operand->type;
@@ -7407,7 +7643,6 @@ gb_internal CallArgumentError check_polymorphic_record_type(CheckerContext *c, O
bool show_error = true;
Array<Operand> operands = {};
- defer (array_free(&operands));
CallArgumentError err = CallArgumentError_None;
@@ -7415,15 +7650,14 @@ gb_internal CallArgumentError check_polymorphic_record_type(CheckerContext *c, O
{
// NOTE(bill, 2019-10-26): Allow a cycle in the parameters but not in the fields themselves
auto prev_type_path = c->type_path;
- c->type_path = new_checker_type_path();
- defer ({
- destroy_checker_type_path(c->type_path);
- c->type_path = prev_type_path;
- });
+ TEMPORARY_ALLOCATOR_GUARD();
+
+ c->type_path = new_checker_type_path(temporary_allocator());
+ defer (c->type_path = prev_type_path);
if (is_call_expr_field_value(ce)) {
named_fields = true;
- operands = array_make<Operand>(heap_allocator(), ce->args.count);
+ operands = array_make<Operand>(temporary_allocator(), ce->args.count);
for_array(i, ce->args) {
Ast *arg = ce->args[i];
ast_node(fv, FieldValue, arg);
@@ -7455,7 +7689,7 @@ gb_internal CallArgumentError check_polymorphic_record_type(CheckerContext *c, O
}
} else {
- operands = array_make<Operand>(heap_allocator(), 0, 2*ce->args.count);
+ operands = array_make<Operand>(temporary_allocator(), 0, 2*ce->args.count);
Entity **lhs = nullptr;
isize lhs_count = -1;
@@ -7497,7 +7731,7 @@ gb_internal CallArgumentError check_polymorphic_record_type(CheckerContext *c, O
} else {
TEMPORARY_ALLOCATOR_GUARD();
- bool *visited = gb_alloc_array(temporary_allocator(), bool, param_count);
+ bool *visited = temporary_alloc_array<bool>(param_count);
// LEAK(bill)
ordered_operands = array_make<Operand>(permanent_allocator(), param_count);
@@ -7681,11 +7915,10 @@ gb_internal CallArgumentError check_polymorphic_record_type(CheckerContext *c, O
{
GenTypesData *found_gen_types = ensure_polymorphic_record_entity_has_gen_types(c, original_type);
-
mutex_lock(&found_gen_types->mutex);
defer (mutex_unlock(&found_gen_types->mutex));
- Entity *found_entity = find_polymorphic_record_entity(found_gen_types, param_count, ordered_operands);
+ Entity *found_entity = find_polymorphic_record_entity(found_gen_types, param_count, ordered_operands);
if (found_entity) {
operand->mode = Addressing_Type;
operand->type = found_entity->type;
@@ -7745,7 +7978,7 @@ gb_internal CallArgumentError check_polymorphic_record_type(CheckerContext *c, O
s = gb_string_append_fmt(s, "$%.*s", LIT(name));
if (v->kind == Entity_TypeName) {
- if (v->type->kind != Type_Generic) {
+ if (v->type != nullptr && v->type->kind != Type_Generic) {
s = gb_string_append_fmt(s, "=");
s = write_type_to_string(s, v->type, false);
}
@@ -7813,6 +8046,173 @@ gb_internal bool check_call_parameter_mixture(Slice<Ast *> const &args, char con
return Expr_Stmt; \
}
+gb_internal ExprKind check_call_expr_as_type_cast(CheckerContext *c, Operand *operand, Ast *call, Slice<Ast *> const &args, Type *type_hint) {
+ GB_ASSERT(operand->mode == Addressing_Type);
+ Type *t = operand->type;
+ if (is_type_polymorphic_record(t)) {
+ CHECK_CALL_PARAMETER_MIXTURE_OR_RETURN("polymorphic type construction");
+
+ if (!is_type_named(t)) {
+ gbString s = expr_to_string(operand->expr);
+ error(call, "Illegal use of an unnamed polymorphic record, %s", s);
+ gb_string_free(s);
+ operand->mode = Addressing_Invalid;
+ operand->type = t_invalid;;
+ return Expr_Expr;
+ }
+ auto err = check_polymorphic_record_type(c, operand, call);
+ if (err == 0) {
+ Ast *ident = operand->expr;
+ while (ident->kind == Ast_SelectorExpr) {
+ Ast *s = ident->SelectorExpr.selector;
+ ident = s;
+ }
+ Type *ot = operand->type;
+ GB_ASSERT(ot->kind == Type_Named);
+ Entity *e = ot->Named.type_name;
+ add_entity_use(c, ident, e);
+ add_type_and_value(c, call, Addressing_Type, ot, empty_exact_value);
+ } else {
+ operand->mode = Addressing_Invalid;
+ operand->type = t_invalid;
+ }
+ } else {
+ CHECK_CALL_PARAMETER_MIXTURE_OR_RETURN("type conversion");
+
+ operand->mode = Addressing_Invalid;
+ isize arg_count = args.count;
+ switch (arg_count) {
+ case 0:
+ {
+ gbString str = type_to_string(t);
+ error(call, "Missing argument in conversion to '%s'", str);
+ gb_string_free(str);
+ } break;
+ default:
+ {
+ gbString str = type_to_string(t);
+ if (t->kind == Type_Basic) {
+ ERROR_BLOCK();
+ switch (t->Basic.kind) {
+ case Basic_complex32:
+ case Basic_complex64:
+ case Basic_complex128:
+ error(call, "Too many arguments in conversion to '%s'", str);
+ error_line("\tSuggestion: %s(1+2i) or construct with 'complex'\n", str);
+ break;
+ case Basic_quaternion64:
+ case Basic_quaternion128:
+ case Basic_quaternion256:
+ error(call, "Too many arguments in conversion to '%s'", str);
+ error_line("\tSuggestion: %s(1+2i+3j+4k) or construct with 'quaternion'\n", str);
+ break;
+ default:
+ error(call, "Too many arguments in conversion to '%s'", str);
+ }
+ } else {
+ error(call, "Too many arguments in conversion to '%s'", str);
+ }
+ gb_string_free(str);
+ } break;
+ case 1: {
+ Ast *arg = args[0];
+ if (arg->kind == Ast_FieldValue) {
+ error(call, "'field = value' cannot be used in a type conversion");
+ arg = arg->FieldValue.value;
+ // NOTE(bill): Carry on the cast regardless
+ }
+ check_expr_with_type_hint(c, operand, arg, t);
+ if (operand->mode != Addressing_Invalid) {
+ if (is_type_polymorphic(t)) {
+ error(call, "A polymorphic type cannot be used in a type conversion");
+ } else {
+ // NOTE(bill): Otherwise the compiler can override the polymorphic type
+ // as it assumes it is determining the type
+ check_cast(c, operand, t);
+ }
+ }
+ operand->type = t;
+ operand->expr = call;
+
+
+ if (operand->mode != Addressing_Invalid) {
+ update_untyped_expr_type(c, arg, t, false);
+ }
+ break;
+ }
+ }
+ }
+ return Expr_Expr;
+}
+
+
+void add_objc_proc_type(CheckerContext *c, Ast *call, Type *return_type, Slice<Type *> param_types);
+
+gb_internal void check_objc_call_expr(CheckerContext *c, Operand *operand, Ast *call, Entity *proc_entity, Type *proc_type) {
+ auto &proc = proc_type->Proc;
+ Slice<Entity *> params = proc.params ? proc.params->Tuple.variables : Slice<Entity *>{};
+
+ Type *self_type = nullptr;
+ isize params_start = 1;
+
+ ast_node(ce, CallExpr, call);
+
+ Type *return_type = proc.result_count == 0 ? nullptr : proc.results->Tuple.variables[0]->type;
+ bool is_return_instancetype = return_type != nullptr && return_type == t_objc_instancetype;
+
+ if (params.count == 0 || !is_type_objc_ptr_to_object(params[0]->type)) {
+ if (!proc_entity->Procedure.is_objc_class_method) {
+ // Not a class method, invalid call
+ error(call, "Invalid Objective-C call: The Objective-C method is not a class method but this first parameter is not an Objective-C object pointer.");
+ return;
+ }
+
+ if (is_return_instancetype) {
+ if (ce->proc->kind == Ast_SelectorExpr) {
+ ast_node(se, SelectorExpr, ce->proc);
+
+ // NOTE(harold): These should have already been checked, right?
+ GB_ASSERT(se->expr->tav.mode == Addressing_Type && se->expr->tav.type->kind == Type_Named);
+
+ return_type = alloc_type_pointer(se->expr->tav.type);
+ } else {
+ return_type = proc_entity->Procedure.objc_class->type;
+ }
+ }
+
+ self_type = t_objc_Class;
+ params_start = 0;
+ } else if (ce->args.count > 0) {
+ GB_ASSERT(is_type_objc_ptr_to_object(params[0]->type));
+
+ if (ce->args[0]->tav.objc_super_target) {
+ self_type = t_objc_super_ptr;
+ } else {
+ self_type = ce->args[0]->tav.type;
+ }
+
+ if (is_return_instancetype) {
+ // NOTE(harold): These should have already been checked, right?
+ GB_ASSERT(ce->args[0]->tav.type && ce->args[0]->tav.type->kind == Type_Pointer && ce->args[0]->tav.type->Pointer.elem->kind == Type_Named);
+
+ return_type = ce->args[0]->tav.type;
+ }
+ }
+
+ auto param_types = slice_make<Type *>(permanent_allocator(), proc.param_count + 2 - params_start);
+ param_types[0] = self_type;
+ param_types[1] = t_objc_SEL;
+
+ for (isize i = params_start; i < params.count; i++) {
+ param_types[i+2-params_start] = params[i]->type;
+ }
+
+ if (is_return_instancetype) {
+ operand->type = return_type;
+ }
+
+ add_objc_proc_type(c, call, return_type, param_types);
+}
gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Ast *proc, Slice<Ast *> const &args, ProcInlining inlining, Type *type_hint) {
if (proc != nullptr &&
@@ -7869,79 +8269,7 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c
}
if (operand->mode == Addressing_Type) {
- Type *t = operand->type;
- if (is_type_polymorphic_record(t)) {
- CHECK_CALL_PARAMETER_MIXTURE_OR_RETURN("polymorphic type construction");
-
- if (!is_type_named(t)) {
- gbString s = expr_to_string(operand->expr);
- error(call, "Illegal use of an unnamed polymorphic record, %s", s);
- gb_string_free(s);
- operand->mode = Addressing_Invalid;
- operand->type = t_invalid;;
- return Expr_Expr;
- }
- auto err = check_polymorphic_record_type(c, operand, call);
- if (err == 0) {
- Ast *ident = operand->expr;
- while (ident->kind == Ast_SelectorExpr) {
- Ast *s = ident->SelectorExpr.selector;
- ident = s;
- }
- Type *ot = operand->type;
- GB_ASSERT(ot->kind == Type_Named);
- Entity *e = ot->Named.type_name;
- add_entity_use(c, ident, e);
- add_type_and_value(c, call, Addressing_Type, ot, empty_exact_value);
- } else {
- operand->mode = Addressing_Invalid;
- operand->type = t_invalid;
- }
- } else {
- CHECK_CALL_PARAMETER_MIXTURE_OR_RETURN("type conversion");
-
- operand->mode = Addressing_Invalid;
- isize arg_count = args.count;
- switch (arg_count) {
- case 0:
- {
- gbString str = type_to_string(t);
- error(call, "Missing argument in conversion to '%s'", str);
- gb_string_free(str);
- } break;
- default:
- {
- gbString str = type_to_string(t);
- error(call, "Too many arguments in conversion to '%s'", str);
- gb_string_free(str);
- } break;
- case 1: {
- Ast *arg = args[0];
- if (arg->kind == Ast_FieldValue) {
- error(call, "'field = value' cannot be used in a type conversion");
- arg = arg->FieldValue.value;
- // NOTE(bill): Carry on the cast regardless
- }
- check_expr_with_type_hint(c, operand, arg, t);
- if (operand->mode != Addressing_Invalid) {
- if (is_type_polymorphic(t)) {
- error(call, "A polymorphic type cannot be used in a type conversion");
- } else {
- // NOTE(bill): Otherwise the compiler can override the polymorphic type
- // as it assumes it is determining the type
- check_cast(c, operand, t);
- }
- }
- operand->type = t;
- operand->expr = call;
- if (operand->mode != Addressing_Invalid) {
- update_untyped_expr_type(c, arg, t, false);
- }
- break;
- }
- }
- }
- return Expr_Expr;
+ return check_call_expr_as_type_cast(c, operand, call, args, type_hint);
}
if (operand->mode == Addressing_Builtin) {
@@ -8028,7 +8356,13 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c
if (pt->kind == Type_Proc && pt->Proc.calling_convention == ProcCC_Odin) {
if ((c->scope->flags & ScopeFlag_ContextDefined) == 0) {
- error(call, "'context' has not been defined within this scope, but is required for this procedure call");
+ ERROR_BLOCK();
+ if (c->scope->flags & ScopeFlag_File) {
+ error(call, "Procedures requiring a 'context' cannot be called at the global scope");
+ } else {
+ error(call, "'context' has not been defined within this scope, but is required for this procedure call");
+ error_line("\tSuggestion: 'context = runtime.default_context()'");
+ }
}
}
@@ -8107,9 +8441,10 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c
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;
+ Entity *e = c->curr_proc_decl->entity.load();
+ GB_ASSERT(e);
+ GB_ASSERT(e->type->kind == Type_Proc);
+ String scope_features = e->type->Proc.enable_target_feature;
if (!check_target_feature_is_superset_of(scope_features, pt->Proc.enable_target_feature, &invalid)) {
ERROR_BLOCK();
error(call, "Inlined procedure enables target feature '%.*s', this requires the calling procedure to at least enable the same feature", LIT(invalid));
@@ -8132,7 +8467,7 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c
type = pt;
}
type = base_type(type);
- if (type->kind == Type_Proc && type->Proc.optional_ok) {
+ if (type->kind == Type_Proc && type->Proc.optional_ok && type->Proc.result_count > 0) {
operand->mode = Addressing_OptionalOk;
operand->type = type->Proc.results->Tuple.variables[0]->type;
if (operand->expr != nullptr && operand->expr->kind == Ast_CallExpr) {
@@ -8141,6 +8476,12 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c
}
}
+ Entity *proc_entity = entity_from_expr(call->CallExpr.proc);
+ bool is_objc_call = proc_entity && proc_entity->kind == Entity_Procedure && proc_entity->Procedure.is_objc_impl_or_import;
+ if (is_objc_call) {
+ check_objc_call_expr(c, operand, call, proc_entity, pt);
+ }
+
return Expr_Expr;
}
@@ -8175,6 +8516,7 @@ gb_internal bool check_set_index_data(Operand *o, Type *t, bool indirection, i64
case Type_Basic:
if (t->Basic.kind == Basic_string) {
if (o->mode == Addressing_Constant) {
+ GB_ASSERT(o->value.kind == ExactValue_String);
*max_count = o->value.value_string.len;
}
if (o->mode != Addressing_Constant) {
@@ -8182,6 +8524,16 @@ gb_internal bool check_set_index_data(Operand *o, Type *t, bool indirection, i64
}
o->type = t_u8;
return true;
+ } else if (t->Basic.kind == Basic_string16) {
+ if (o->mode == Addressing_Constant) {
+ GB_ASSERT(o->value.kind == ExactValue_String16);
+ *max_count = o->value.value_string16.len;
+ }
+ if (o->mode != Addressing_Constant) {
+ o->mode = Addressing_Value;
+ }
+ o->type = t_u16;
+ return true;
} else if (t->Basic.kind == Basic_UntypedString) {
if (o->mode == Addressing_Constant) {
*max_count = o->value.value_string.len;
@@ -8391,7 +8743,7 @@ gb_internal bool check_range(CheckerContext *c, Ast *node, bool is_for_loop, Ope
return true;
}
-gb_internal bool check_is_operand_compound_lit_constant(CheckerContext *c, Operand *o) {
+gb_internal bool check_is_operand_compound_lit_constant(CheckerContext *c, Operand *o, Type *field_type) {
if (is_operand_nil(*o)) {
return true;
}
@@ -8406,6 +8758,13 @@ gb_internal bool check_is_operand_compound_lit_constant(CheckerContext *c, Opera
return true;
}
}
+ if (field_type != nullptr && is_type_typeid(field_type) && o->mode == Addressing_Type) {
+ add_type_info_type(c, o->type);
+ return true;
+ }
+ if (is_type_any(field_type)) {
+ return false;
+ }
return o->mode == Addressing_Constant;
}
@@ -8635,7 +8994,7 @@ gb_internal void add_constant_switch_case(CheckerContext *ctx, SeenMap *seen, Op
isize count = multi_map_count(seen, key);
if (count) {
TEMPORARY_ALLOCATOR_GUARD();
- TypeAndToken *taps = gb_alloc_array(temporary_allocator(), TypeAndToken, count);
+ TypeAndToken *taps = temporary_alloc_array<TypeAndToken>(count);
multi_map_get_all(seen, key, taps);
for (isize i = 0; i < count; i++) {
@@ -8709,23 +9068,52 @@ gb_internal ExprKind check_basic_directive_expr(CheckerContext *c, Operand *o, A
String name = bd->name.string;
if (name == "file") {
String file = get_file_path_string(bd->token.pos.file_id);
- if (build_context.obfuscate_source_code_locations) {
+ switch (build_context.source_code_location_info) {
+ case SourceCodeLocationInfo_Normal:
+ break;
+ case SourceCodeLocationInfo_Obfuscated:
file = obfuscate_string(file, "F");
+ break;
+ case SourceCodeLocationInfo_Filename:
+ file = last_path_element(file);
+ break;
+ case SourceCodeLocationInfo_None:
+ file = str_lit("");
+ break;
}
o->type = t_untyped_string;
o->value = exact_value_string(file);
} else if (name == "directory") {
String file = get_file_path_string(bd->token.pos.file_id);
String path = dir_from_path(file);
- if (build_context.obfuscate_source_code_locations) {
+ switch (build_context.source_code_location_info) {
+ case SourceCodeLocationInfo_Normal:
+ break;
+ case SourceCodeLocationInfo_Obfuscated:
path = obfuscate_string(path, "D");
+ break;
+ case SourceCodeLocationInfo_Filename:
+ path = last_path_element(path);
+ break;
+ case SourceCodeLocationInfo_None:
+ path = str_lit("");
+ break;
}
o->type = t_untyped_string;
o->value = exact_value_string(path);
} else if (name == "line") {
i32 line = bd->token.pos.line;
- if (build_context.obfuscate_source_code_locations) {
+ switch (build_context.source_code_location_info) {
+ case SourceCodeLocationInfo_Normal:
+ break;
+ case SourceCodeLocationInfo_Obfuscated:
line = obfuscate_i32(line);
+ break;
+ case SourceCodeLocationInfo_Filename:
+ break;
+ case SourceCodeLocationInfo_None:
+ line = 0;
+ break;
}
o->type = t_untyped_integer;
o->value = exact_value_i64(line);
@@ -8736,8 +9124,17 @@ gb_internal ExprKind check_basic_directive_expr(CheckerContext *c, Operand *o, A
o->value = exact_value_string(str_lit(""));
} else {
String p = c->proc_name;
- if (build_context.obfuscate_source_code_locations) {
+ switch (build_context.source_code_location_info) {
+ case SourceCodeLocationInfo_Normal:
+ break;
+ case SourceCodeLocationInfo_Obfuscated:
p = obfuscate_string(p, "P");
+ break;
+ case SourceCodeLocationInfo_Filename:
+ break;
+ case SourceCodeLocationInfo_None:
+ p = str_lit("");
+ break;
}
o->type = t_untyped_string;
o->value = exact_value_string(p);
@@ -8969,8 +9366,14 @@ gb_internal ExprKind check_or_else_expr(CheckerContext *c, Operand *o, Ast *node
o->expr = node;
return Expr_Expr;
}
+
+ Type *left_type = nullptr;
+ Type *right_type = nullptr;
+ check_or_else_split_types(c, &x, name, &left_type, &right_type);
+ add_type_and_value(c, arg, x.mode, x.type, x.value);
+
bool y_is_diverging = false;
- check_expr_base(c, &y, default_value, x.type);
+ check_expr_base(c, &y, default_value, left_type);
switch (y.mode) {
case Addressing_NoValue:
if (is_diverging_expr(y.expr)) {
@@ -8995,14 +9398,21 @@ gb_internal ExprKind check_or_else_expr(CheckerContext *c, Operand *o, Ast *node
return Expr_Expr;
}
- Type *left_type = nullptr;
- Type *right_type = nullptr;
- check_or_else_split_types(c, &x, name, &left_type, &right_type);
- add_type_and_value(c, arg, x.mode, x.type, x.value);
-
if (left_type != nullptr) {
if (!y_is_diverging) {
- check_assignment(c, &y, left_type, name);
+ if (is_type_tuple(left_type)) {
+ if (!is_type_tuple(y.type)) {
+ error(y.expr, "Found a single value where a %td-valued expression was expected", left_type->Tuple.variables.count);
+ } else if (!are_types_identical(left_type, y.type)) {
+ gbString xt = type_to_string(left_type);
+ gbString yt = type_to_string(y.type);
+ error(y.expr, "Mismatched types, expected (%s), got (%s)", xt, yt);
+ gb_string_free(yt);
+ gb_string_free(xt);
+ }
+ } else {
+ check_assignment(c, &y, left_type, name);
+ }
}
} else {
check_or_else_expr_no_value_error(c, name, x, type_hint);
@@ -9305,8 +9715,7 @@ gb_internal void check_compound_literal_field_values(CheckerContext *c, Slice<As
break;
}
}
- if (is_constant &&
- (is_type_any(ft) || is_type_union(ft) || is_type_raw_union(ft) || is_type_typeid(ft))) {
+ if (is_constant && elem_cannot_be_constant(ft)) {
is_constant = false;
}
}
@@ -9341,11 +9750,11 @@ gb_internal void check_compound_literal_field_values(CheckerContext *c, Slice<As
Operand o = {};
check_expr_or_type(c, &o, fv->value, field->type);
- if (is_type_any(field->type) || is_type_union(field->type) || is_type_raw_union(field->type) || is_type_typeid(field->type)) {
+ if (elem_cannot_be_constant(field->type)) {
is_constant = false;
}
if (is_constant) {
- is_constant = check_is_operand_compound_lit_constant(c, &o);
+ is_constant = check_is_operand_compound_lit_constant(c, &o, field->type);
}
u8 prev_bit_field_bit_size = c->bit_field_bit_size;
@@ -9378,7 +9787,10 @@ gb_internal bool is_expr_inferred_fixed_array(Ast *type_expr) {
}
gb_internal bool check_for_dynamic_literals(CheckerContext *c, Ast *node, AstCompoundLit *cl) {
- if (cl->elems.count > 0 && (check_feature_flags(c, node) & OptInFeatureFlag_DynamicLiterals) == 0) {
+ if (cl->elems.count == 0) {
+ return false;
+ }
+ if ((check_feature_flags(c, node) & OptInFeatureFlag_DynamicLiterals) == 0 && !build_context.dynamic_literals) {
ERROR_BLOCK();
error(node, "Compound literals of dynamic types are disabled by default");
error_line("\tSuggestion: If you want to enable them for this specific file, add '#+feature dynamic-literals' at the top of the file\n");
@@ -9389,9 +9801,31 @@ gb_internal bool check_for_dynamic_literals(CheckerContext *c, Ast *node, AstCom
error_line("\tWarning: As '-default-to-panic-allocator' has been set, the dynamic compound literal may not be initialized as expected\n");
}
return false;
+ } else if (c->curr_proc_decl != nullptr && c->curr_proc_calling_convention != ProcCC_Odin) {
+ if (c->scope != nullptr && (c->scope->flags & ScopeFlag_ContextDefined) == 0) {
+ error(node, "Compound literals of dynamic types require a 'context' to defined");
+ }
}
- return cl->elems.count > 0;
+ return true;
+}
+
+gb_internal IntegerDivisionByZeroKind check_for_integer_division_by_zero(CheckerContext *c, Ast *node) {
+ // TODO(bill): per file `#+feature` flags
+ u64 flags = check_feature_flags(c, node);
+ if ((flags & OptInFeatureFlag_IntegerDivisionByZero_Trap) != 0) {
+ return IntegerDivisionByZero_Trap;
+ }
+ if ((flags & OptInFeatureFlag_IntegerDivisionByZero_Zero) != 0) {
+ return IntegerDivisionByZero_Zero;
+ }
+ if ((flags & OptInFeatureFlag_IntegerDivisionByZero_Self) != 0) {
+ return IntegerDivisionByZero_Self;
+ }
+ if ((flags & OptInFeatureFlag_IntegerDivisionByZero_AllBits) != 0) {
+ return IntegerDivisionByZero_AllBits;
+ }
+ return build_context.integer_division_by_zero_behaviour;
}
gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) {
@@ -9404,6 +9838,7 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
}
bool is_to_be_determined_array_count = false;
bool is_constant = true;
+ bool is_soa = false;
Ast *type_expr = cl->type;
@@ -9436,8 +9871,14 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
GB_ASSERT(tag->kind == Ast_BasicDirective);
String name = tag->BasicDirective.name.string;
if (name == "soa") {
- error(node, "#soa arrays are not supported for compound literals");
- return kind;
+ is_soa = true;
+ if (count == nullptr) {
+ error(node, "#soa slices are not supported for compound literals");
+ return kind;
+ } else if (count->kind == Ast_UnaryExpr &&
+ count->UnaryExpr.op.kind == Token_Question) {
+ error(node, "#soa fixed length arrays must specify their length and cannot use ?");
+ }
}
}
}
@@ -9447,7 +9888,8 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
GB_ASSERT(tag->kind == Ast_BasicDirective);
String name = tag->BasicDirective.name.string;
if (name == "soa") {
- error(node, "#soa arrays are not supported for compound literals");
+ is_soa = true;
+ error(node, "#soa dynamic arrays are not supported for compound literals");
return kind;
}
}
@@ -9476,101 +9918,101 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
switch (t->kind) {
- case Type_Struct: {
+ case Type_Struct:
if (cl->elems.count == 0) {
break; // NOTE(bill): No need to init
}
- if (t->Struct.soa_kind != StructSoa_None) {
- error(node, "#soa arrays are not supported for compound literals");
- break;
- }
+ if (t->Struct.soa_kind == StructSoa_None) {
+ if (t->Struct.is_raw_union) {
+ if (cl->elems.count > 0) {
+ // NOTE: unions cannot be constant
+ is_constant = elem_type_can_be_constant(t);
- if (t->Struct.is_raw_union) {
- if (cl->elems.count > 0) {
- // NOTE: unions cannot be constant
- is_constant = false;
-
- if (cl->elems[0]->kind != Ast_FieldValue) {
- gbString type_str = type_to_string(type);
- error(node, "%s ('struct #raw_union') compound literals are only allowed to contain 'field = value' elements", type_str);
- gb_string_free(type_str);
- } else {
- if (cl->elems.count != 1) {
+ if (cl->elems[0]->kind != Ast_FieldValue) {
gbString type_str = type_to_string(type);
- error(node, "%s ('struct #raw_union') compound literals are only allowed to contain up to 1 'field = value' element, got %td", type_str, cl->elems.count);
+ error(node, "%s ('struct #raw_union') compound literals are only allowed to contain 'field = value' elements", type_str);
gb_string_free(type_str);
} else {
- check_compound_literal_field_values(c, cl->elems, o, type, is_constant);
+ if (cl->elems.count != 1) {
+ gbString type_str = type_to_string(type);
+ error(node, "%s ('struct #raw_union') compound literals are only allowed to contain up to 1 'field = value' element, got %td", type_str, cl->elems.count);
+ gb_string_free(type_str);
+ } else {
+ check_compound_literal_field_values(c, cl->elems, o, type, is_constant);
+ }
}
}
- }
- break;
- }
-
- wait_signal_until_available(&t->Struct.fields_wait_signal);
- isize field_count = t->Struct.fields.count;
- isize min_field_count = t->Struct.fields.count;
- for (isize i = min_field_count-1; i >= 0; i--) {
- Entity *e = t->Struct.fields[i];
- GB_ASSERT(e->kind == Entity_Variable);
- if (e->Variable.param_value.kind != ParameterValue_Invalid) {
- min_field_count--;
- } else {
break;
}
- }
-
- if (cl->elems[0]->kind == Ast_FieldValue) {
- check_compound_literal_field_values(c, cl->elems, o, type, is_constant);
- } else {
- bool seen_field_value = false;
- for_array(index, cl->elems) {
- Entity *field = nullptr;
- Ast *elem = cl->elems[index];
- if (elem->kind == Ast_FieldValue) {
- seen_field_value = true;
- error(elem, "Mixture of 'field = value' and value elements in a literal is not allowed");
- continue;
- } else if (seen_field_value) {
- error(elem, "Value elements cannot be used after a 'field = value'");
- continue;
- }
- if (index >= field_count) {
- error(elem, "Too many values in structure literal, expected %td, got %td", field_count, cl->elems.count);
+ wait_signal_until_available(&t->Struct.fields_wait_signal);
+ isize field_count = t->Struct.fields.count;
+ isize min_field_count = t->Struct.fields.count;
+ for (isize i = min_field_count-1; i >= 0; i--) {
+ Entity *e = t->Struct.fields[i];
+ GB_ASSERT(e->kind == Entity_Variable);
+ if (e->Variable.param_value.kind != ParameterValue_Invalid) {
+ min_field_count--;
+ } else {
break;
}
+ }
- if (field == nullptr) {
- field = t->Struct.fields[index];
- }
+ if (cl->elems[0]->kind == Ast_FieldValue) {
+ check_compound_literal_field_values(c, cl->elems, o, type, is_constant);
+ } else {
+ bool seen_field_value = false;
- Operand o = {};
- check_expr_or_type(c, &o, elem, field->type);
+ for_array(index, cl->elems) {
+ Entity *field = nullptr;
+ Ast *elem = cl->elems[index];
+ if (elem->kind == Ast_FieldValue) {
+ seen_field_value = true;
+ error(elem, "Mixture of 'field = value' and value elements in a literal is not allowed");
+ continue;
+ } else if (seen_field_value) {
+ error(elem, "Value elements cannot be used after a 'field = value'");
+ continue;
+ }
+ if (index >= field_count) {
+ error(elem, "Too many values in structure literal, expected %td, got %td", field_count, cl->elems.count);
+ break;
+ }
- if (is_type_any(field->type) || is_type_union(field->type) || is_type_raw_union(field->type) || is_type_typeid(field->type)) {
- is_constant = false;
- }
- if (is_constant) {
- is_constant = check_is_operand_compound_lit_constant(c, &o);
- }
+ if (field == nullptr) {
+ field = t->Struct.fields[index];
+ }
- check_assignment(c, &o, field->type, str_lit("structure literal"));
- }
- if (cl->elems.count < field_count) {
- if (min_field_count < field_count) {
- if (cl->elems.count < min_field_count) {
- error(cl->close, "Too few values in structure literal, expected at least %td, got %td", min_field_count, cl->elems.count);
- }
- } else {
- error(cl->close, "Too few values in structure literal, expected %td, got %td", field_count, cl->elems.count);
+ Operand o = {};
+ check_expr_or_type(c, &o, elem, field->type);
+
+ if (elem_cannot_be_constant(field->type)) {
+ is_constant = false;
+ }
+ if (is_constant) {
+ is_constant = check_is_operand_compound_lit_constant(c, &o, field->type);
+ }
+
+ check_assignment(c, &o, field->type, str_lit("structure literal"));
+ }
+ if (cl->elems.count < field_count) {
+ if (min_field_count < field_count) {
+ if (cl->elems.count < min_field_count) {
+ error(cl->close, "Too few values in structure literal, expected at least %td, got %td", min_field_count, cl->elems.count);
+ }
+ } else {
+ error(cl->close, "Too few values in structure literal, expected %td, got %td", field_count, cl->elems.count);
+ }
}
}
- }
- break;
- }
+ break;
+ } else if (t->Struct.soa_kind != StructSoa_Fixed) {
+ error(node, "#soa slices and dynamic arrays are not supported for compound literals");
+ break;
+ }
+ /*fallthrough*/
case Type_Slice:
case Type_Array:
@@ -9581,7 +10023,14 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
Type *elem_type = nullptr;
String context_name = {};
i64 max_type_count = -1;
- if (t->kind == Type_Slice) {
+ if (t->kind == Type_Struct) {
+ GB_ASSERT(t->Struct.soa_kind == StructSoa_Fixed);
+ elem_type = t->Struct.soa_elem;
+ context_name = str_lit("#soa array literal");
+ if (!is_to_be_determined_array_count) {
+ max_type_count = t->Struct.soa_count;
+ }
+ } else if (t->kind == Type_Slice) {
elem_type = t->Slice.elem;
context_name = str_lit("slice literal");
} else if (t->kind == Type_Array) {
@@ -9590,6 +10039,12 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
if (!is_to_be_determined_array_count) {
max_type_count = t->Array.count;
}
+ } else if (t->kind == Type_Array) {
+ elem_type = t->Array.elem;
+ context_name = str_lit("array literal");
+ if (!is_to_be_determined_array_count) {
+ max_type_count = t->Array.count;
+ }
} else if (t->kind == Type_DynamicArray) {
elem_type = t->DynamicArray.elem;
context_name = str_lit("dynamic array literal");
@@ -9681,7 +10136,9 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
check_expr_with_type_hint(c, &operand, fv->value, elem_type);
check_assignment(c, &operand, elem_type, context_name);
- is_constant = is_constant && operand.mode == Addressing_Constant;
+ if (is_constant) {
+ is_constant = check_is_operand_compound_lit_constant(c, &operand, elem_type);
+ }
} else {
Operand op_index = {};
check_expr(c, &op_index, fv->field);
@@ -9713,7 +10170,9 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
check_expr_with_type_hint(c, &operand, fv->value, elem_type);
check_assignment(c, &operand, elem_type, context_name);
- is_constant = is_constant && operand.mode == Addressing_Constant;
+ if (is_constant) {
+ is_constant = check_is_operand_compound_lit_constant(c, &operand, elem_type);
+ }
}
}
@@ -9740,7 +10199,9 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
check_expr_with_type_hint(c, &operand, e, elem_type);
check_assignment(c, &operand, elem_type, context_name);
- is_constant = is_constant && operand.mode == Addressing_Constant;
+ if (is_constant) {
+ is_constant = check_is_operand_compound_lit_constant(c, &operand, elem_type);
+ }
}
if (max < index) {
@@ -9757,6 +10218,15 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
error(node, "Expected %lld values for this array literal, got %lld", cast(long long)t->Array.count, cast(long long)max);
}
}
+ } else if (t->kind == Type_Struct) {
+ GB_ASSERT(t->Struct.soa_kind == StructSoa_Fixed);
+ if (is_to_be_determined_array_count) {
+ t->Struct.soa_count = cast(i32)max;
+ } else if (cl->elems.count > 0 && cl->elems[0]->kind != Ast_FieldValue) {
+ if (0 < max && max < t->Struct.soa_count) {
+ error(node, "Expected %lld values for this #soa array literal, got %lld", cast(long long)t->Struct.soa_count, cast(long long)max);
+ }
+ }
}
@@ -9905,7 +10375,9 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
check_expr_with_type_hint(c, &operand, fv->value, elem_type);
check_assignment(c, &operand, elem_type, context_name);
- is_constant = is_constant && operand.mode == Addressing_Constant;
+ if (is_constant) {
+ is_constant = check_is_operand_compound_lit_constant(c, &operand, elem_type);
+ }
TokenKind upper_op = Token_LtEq;
if (op.kind == Token_RangeHalf) {
@@ -9946,7 +10418,9 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
check_expr_with_type_hint(c, &operand, fv->value, elem_type);
check_assignment(c, &operand, elem_type, context_name);
- is_constant = is_constant && operand.mode == Addressing_Constant;
+ if (is_constant) {
+ is_constant = check_is_operand_compound_lit_constant(c, &operand, elem_type);
+ }
add_to_seen_map(c, &seen, op_index);
}
@@ -9976,7 +10450,9 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
check_expr_with_type_hint(c, &operand, e, elem_type);
check_assignment(c, &operand, elem_type, context_name);
- is_constant = is_constant && operand.mode == Addressing_Constant;
+ if (is_constant) {
+ is_constant = check_is_operand_compound_lit_constant(c, &operand, elem_type);
+ }
}
if (max < index) {
@@ -10180,51 +10656,47 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
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;
- } else {
- for (Ast *elem : cl->elems) {
- if (elem->kind == Ast_FieldValue) {
- error(elem, "'field = value' in a bit_set a literal is not allowed");
- continue;
- }
+ for (Ast *elem : cl->elems) {
+ if (elem->kind == Ast_FieldValue) {
+ error(elem, "'field = value' in a bit_set literal is not allowed");
+ is_constant = false;
+ continue;
+ }
- check_expr_with_type_hint(c, o, elem, et);
+ check_expr_with_type_hint(c, o, elem, et);
- if (is_constant) {
- is_constant = o->mode == Addressing_Constant;
- }
-
- if (elem->kind == Ast_BinaryExpr) {
- switch (elem->BinaryExpr.op.kind) {
- case Token_Or:
- {
- gbString x = expr_to_string(elem->BinaryExpr.left);
- gbString y = expr_to_string(elem->BinaryExpr.right);
- gbString e = expr_to_string(elem);
- error(elem, "Was the following intended? '%s, %s'; if not, surround the expression with parentheses '(%s)'", x, y, e);
- gb_string_free(e);
- gb_string_free(y);
- gb_string_free(x);
- }
- break;
+ if (is_constant) {
+ is_constant = o->mode == Addressing_Constant;
+ }
+
+ if (elem->kind == Ast_BinaryExpr) {
+ switch (elem->BinaryExpr.op.kind) {
+ case Token_Or:
+ {
+ gbString x = expr_to_string(elem->BinaryExpr.left);
+ gbString y = expr_to_string(elem->BinaryExpr.right);
+ gbString e = expr_to_string(elem);
+ error(elem, "Was the following intended? '%s, %s'; if not, surround the expression with parentheses '(%s)'", x, y, e);
+ gb_string_free(e);
+ gb_string_free(y);
+ gb_string_free(x);
}
+ break;
}
+ }
- check_assignment(c, o, t->BitSet.elem, str_lit("bit_set literal"));
- if (o->mode == Addressing_Constant) {
- i64 lower = t->BitSet.lower;
- i64 upper = t->BitSet.upper;
- i64 v = exact_value_to_i64(o->value);
- if (lower <= v && v <= upper) {
- // okay
- } else {
- gbString s = expr_to_string(o->expr);
- error(elem, "Bit field value out of bounds, %s (%lld) not in the range %lld .. %lld", s, v, lower, upper);
- gb_string_free(s);
- continue;
- }
+ check_assignment(c, o, t->BitSet.elem, str_lit("bit_set literal"));
+ if (o->mode == Addressing_Constant) {
+ i64 lower = t->BitSet.lower;
+ i64 upper = t->BitSet.upper;
+ i64 v = exact_value_to_i64(o->value);
+ if (lower <= v && v <= upper) {
+ // okay
+ } else {
+ gbString s = expr_to_string(o->expr);
+ error(elem, "Bit field value out of bounds, %s (%lld) not in the range %lld .. %lld", s, v, lower, upper);
+ gb_string_free(s);
+ continue;
}
}
}
@@ -10586,7 +11058,7 @@ gb_internal ExprKind check_selector_call_expr(CheckerContext *c, Operand *o, Ast
}
}
- auto modified_args = slice_make<Ast *>(heap_allocator(), ce->args.count+1);
+ auto modified_args = permanent_slice_make<Ast *>(ce->args.count+1);
modified_args[0] = first_arg;
slice_copy(&modified_args, ce->args, 1);
ce->args = modified_args;
@@ -10751,9 +11223,17 @@ gb_internal ExprKind check_slice_expr(CheckerContext *c, Operand *o, Ast *node,
if (t->Basic.kind == Basic_string || t->Basic.kind == Basic_UntypedString) {
valid = true;
if (o->mode == Addressing_Constant) {
+ GB_ASSERT(o->value.kind == ExactValue_String);
max_count = o->value.value_string.len;
}
o->type = type_deref(o->type);
+ } else if (t->Basic.kind == Basic_string16) {
+ valid = true;
+ if (o->mode == Addressing_Constant) {
+ GB_ASSERT(o->value.kind == ExactValue_String16);
+ max_count = o->value.value_string16.len;
+ }
+ o->type = type_deref(o->type);
}
break;
@@ -10908,15 +11388,21 @@ gb_internal ExprKind check_slice_expr(CheckerContext *c, Operand *o, Ast *node,
o->expr = node;
return kind;
}
-
- String s = {};
- if (o->value.kind == ExactValue_String) {
- s = o->value.value_string;
- }
-
o->mode = Addressing_Constant;
o->type = t;
- o->value = exact_value_string(substring(s, cast(isize)indices[0], cast(isize)indices[1]));
+
+ if (o->value.kind == ExactValue_String16) {
+ String16 s = o->value.value_string16;
+
+ o->value = exact_value_string16(substring(s, cast(isize)indices[0], cast(isize)indices[1]));
+ } else {
+ String s = {};
+ if (o->value.kind == ExactValue_String) {
+ s = o->value.value_string;
+ }
+
+ o->value = exact_value_string(substring(s, cast(isize)indices[0], cast(isize)indices[1]));
+ }
}
return kind;
}
@@ -10961,7 +11447,7 @@ gb_internal ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast
return kind;
case_end;
- case_ast_node(i, Implicit, node)
+ case_ast_node(i, Implicit, node);
switch (i->kind) {
case Token_context:
{
@@ -10997,7 +11483,7 @@ gb_internal ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast
case_ast_node(u, Uninit, node);
o->mode = Addressing_Value;
o->type = t_untyped_uninit;
- error(node, "Use of --- outside of variable declaration");
+ error(node, "Global variables will always be zeroed if left unassigned, --- is disallowed");
case_end;
@@ -11005,6 +11491,7 @@ gb_internal ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast
Type *t = t_invalid;
switch (node->tav.value.kind) {
case ExactValue_String: t = t_untyped_string; break;
+ case ExactValue_String16: t = t_string16; break; // TODO(bill): determine this correctly
case ExactValue_Float: t = t_untyped_float; break;
case ExactValue_Complex: t = t_untyped_complex; break;
case ExactValue_Quaternion: t = t_untyped_quaternion; break;
@@ -11074,6 +11561,7 @@ gb_internal ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast
o->mode = Addressing_Value;
o->type = type;
+ o->value = exact_value_procedure(node);
case_end;
case_ast_node(te, TernaryIfExpr, node);
@@ -11232,6 +11720,13 @@ gb_internal ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast
o->mode = Addressing_Invalid;
o->expr = node;
return kind;
+ } else if (o->mode == Addressing_Type) {
+ gbString str = expr_to_string(o->expr);
+ error(o->expr, "Cannot dereference '%s' because it is a type", str);
+
+ o->mode = Addressing_Invalid;
+ o->expr = node;
+ return kind;
} else {
Type *t = base_type(o->type);
if (t->kind == Type_Pointer && !is_type_empty_union(t->Pointer.elem)) {
@@ -11434,6 +11929,8 @@ gb_internal bool is_exact_value_zero(ExactValue const &v) {
return !v.value_bool;
case ExactValue_String:
return v.value_string.len == 0;
+ case ExactValue_String16:
+ return v.value_string16.len == 0;
case ExactValue_Integer:
return big_int_is_zero(&v.value_integer);
case ExactValue_Float:
diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp
index e81996566..5e7a8e323 100644
--- a/src/check_stmt.cpp
+++ b/src/check_stmt.cpp
@@ -418,7 +418,7 @@ gb_internal bool check_is_terminating(Ast *node, String const &label) {
-gb_internal Type *check_assignment_variable(CheckerContext *ctx, Operand *lhs, Operand *rhs) {
+gb_internal Type *check_assignment_variable(CheckerContext *ctx, Operand *lhs, Operand *rhs, String context_name) {
if (rhs->mode == Addressing_Invalid) {
return nullptr;
}
@@ -430,8 +430,6 @@ gb_internal Type *check_assignment_variable(CheckerContext *ctx, Operand *lhs, O
Ast *node = unparen_expr(lhs->expr);
- check_no_copy_assignment(*rhs, str_lit("assignment"));
-
// NOTE(bill): Ignore assignments to '_'
if (is_blank_ident(node)) {
check_assignment(ctx, rhs, nullptr, str_lit("assignment to '_' identifier"));
@@ -486,7 +484,7 @@ gb_internal Type *check_assignment_variable(CheckerContext *ctx, Operand *lhs, O
}
if (ident_node != nullptr) {
ast_node(i, Ident, ident_node);
- e = scope_lookup(ctx->scope, i->token.string);
+ e = scope_lookup(ctx->scope, i->token.string, i->hash);
if (e != nullptr && e->kind == Entity_Variable) {
used = (e->flags & EntityFlag_Used) != 0; // NOTE(bill): Make backup just in case
}
@@ -630,7 +628,7 @@ gb_internal Type *check_assignment_variable(CheckerContext *ctx, Operand *lhs, O
ctx->bit_field_bit_size = lhs_e->Variable.bit_field_bit_size;
}
- check_assignment(ctx, rhs, assignment_type, str_lit("assignment"));
+ check_assignment(ctx, rhs, assignment_type, context_name);
ctx->bit_field_bit_size = prev_bit_field_bit_size;
@@ -976,7 +974,14 @@ gb_internal void check_unroll_range_stmt(CheckerContext *ctx, Ast *node, u32 mod
Type *t = base_type(operand.type);
switch (t->kind) {
case Type_Basic:
- if (is_type_string(t) && t->Basic.kind != Basic_cstring) {
+ if (is_type_string16(t) && t->Basic.kind != Basic_cstring) {
+ val0 = t_rune;
+ val1 = t_int;
+ inline_for_depth = exact_value_i64(operand.value.value_string.len);
+ if (unroll_count > 0) {
+ error(node, "#unroll(%lld) does not support strings", cast(long long)unroll_count);
+ }
+ } else if (is_type_string(t) && t->Basic.kind != Basic_cstring) {
val0 = t_rune;
val1 = t_int;
inline_for_depth = exact_value_i64(operand.value.value_string.len);
@@ -1238,7 +1243,11 @@ gb_internal void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags
add_to_seen_map(ctx, &seen, upper_op, x, lhs, rhs);
- if (is_type_string(x.type)) {
+ if (is_type_string16(x.type)) {
+ // NOTE(bill): Force dependency for strings here
+ add_package_dependency(ctx, "runtime", "string16_le");
+ add_package_dependency(ctx, "runtime", "string16_lt");
+ } else if (is_type_string(x.type)) {
// NOTE(bill): Force dependency for strings here
add_package_dependency(ctx, "runtime", "string_le");
add_package_dependency(ctx, "runtime", "string_lt");
@@ -1593,6 +1602,20 @@ gb_internal void check_type_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_
error_line("\tSuggestion: Was '#partial switch' wanted?\n");
}
}
+
+ if (build_context.strict_style) {
+ Token stok = ss->token;
+ for_array(i, bs->stmts) {
+ Ast *stmt = bs->stmts[i];
+ if (stmt->kind != Ast_CaseClause) {
+ continue;
+ }
+ Token ctok = stmt->CaseClause.token;
+ if (ctok.pos.column > stok.pos.column) {
+ error(ctok, "With '-strict-style', 'case' statements must share the same column as the 'switch' token");
+ }
+ }
+ }
}
gb_internal void check_block_stmt_for_errors(CheckerContext *ctx, Ast *body) {
@@ -1758,7 +1781,16 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags)
switch (t->kind) {
case Type_Basic:
- if (t->Basic.kind == Basic_string || t->Basic.kind == Basic_UntypedString) {
+ if (t->Basic.kind == Basic_string16) {
+ is_possibly_addressable = false;
+ array_add(&vals, t_rune);
+ array_add(&vals, t_int);
+ if (is_reverse) {
+ add_package_dependency(ctx, "runtime", "string16_decode_last_rune");
+ } else {
+ add_package_dependency(ctx, "runtime", "string16_decode_rune");
+ }
+ } else if (t->Basic.kind == Basic_string || t->Basic.kind == Basic_UntypedString) {
is_possibly_addressable = false;
array_add(&vals, t_rune);
array_add(&vals, t_int);
@@ -1780,8 +1812,9 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags)
error(node, "Iteration over a bit_set of an enum is not allowed runtime type information (RTTI) has been disallowed");
}
if (rs->vals.count == 1 && rs->vals[0] && rs->vals[0]->kind == Ast_Ident) {
- String name = rs->vals[0]->Ident.token.string;
- Entity *found = scope_lookup(ctx->scope, name);
+ AstIdent *ident = &rs->vals[0]->Ident;
+ String name = ident->token.string;
+ Entity *found = scope_lookup(ctx->scope, name, ident->hash);
if (found && are_types_identical(found->type, t->BitSet.elem)) {
ERROR_BLOCK();
gbString s = expr_to_string(expr);
@@ -1825,8 +1858,9 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags)
error(node, "#reverse for is not supported for map types, as maps are unordered");
}
if (rs->vals.count == 1 && rs->vals[0] && rs->vals[0]->kind == Ast_Ident) {
- String name = rs->vals[0]->Ident.token.string;
- Entity *found = scope_lookup(ctx->scope, name);
+ AstIdent *ident = &rs->vals[0]->Ident;
+ String name = ident->token.string;
+ Entity *found = scope_lookup(ctx->scope, name, ident->hash);
if (found && are_types_identical(found->type, t->Map.key)) {
ERROR_BLOCK();
gbString s = expr_to_string(expr);
@@ -1931,7 +1965,7 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags)
}
auto rhs = slice_from_array(vals);
- auto lhs = slice_make<Ast *>(temporary_allocator(), rhs.count);
+ auto lhs = temporary_slice_make<Ast *>(rhs.count);
slice_copy(&lhs, rs->vals);
isize addressable_index = cast(isize)is_map;
@@ -2108,10 +2142,12 @@ gb_internal void check_value_decl_stmt(CheckerContext *ctx, Ast *node, u32 mod_f
if (init_type == nullptr) {
init_type = t_invalid;
} else if (is_type_polymorphic(base_type(init_type))) {
+ /* DISABLED: This error seems too aggressive for instantiated generic types.
gbString str = type_to_string(init_type);
error(vd->type, "Invalid use of a polymorphic type '%s' in variable declaration", str);
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];
@@ -2402,7 +2438,7 @@ gb_internal void check_assign_stmt(CheckerContext *ctx, Ast *node) {
isize lhs_count = as->lhs.count;
if (lhs_count == 0) {
- error(as->op, "Missing lhs in assignment statement");
+ error(as->op, "Missing LHS in assignment statement");
return;
}
@@ -2435,7 +2471,7 @@ gb_internal void check_assign_stmt(CheckerContext *ctx, Ast *node) {
if (lhs_to_ignore[i]) {
continue;
}
- check_assignment_variable(ctx, &lhs_operands[i], &rhs_operands[i]);
+ check_assignment_variable(ctx, &lhs_operands[i], &rhs_operands[i], str_lit("assignment"));
}
if (lhs_count != rhs_count) {
error(as->lhs[0], "Assignment count mismatch '%td' = '%td'", lhs_count, rhs_count);
@@ -2445,11 +2481,11 @@ gb_internal void check_assign_stmt(CheckerContext *ctx, Ast *node) {
// a += 1; // Single-sided
Token op = as->op;
if (as->lhs.count != 1 || as->rhs.count != 1) {
- error(op, "Assignment operation '%.*s' requires single-valued expressions", LIT(op.string));
+ error(op, "Assignment operator '%.*s' requires single-valued operands", LIT(op.string));
return;
}
if (!gb_is_between(op.kind, Token__AssignOpBegin+1, Token__AssignOpEnd-1)) {
- error(op, "Unknown Assignment operation '%.*s'", LIT(op.string));
+ error(op, "Unknown assignment operator '%.*s'", LIT(op.string));
return;
}
Operand lhs = {Addressing_Invalid};
@@ -2458,15 +2494,16 @@ gb_internal void check_assign_stmt(CheckerContext *ctx, Ast *node) {
ast_node(be, BinaryExpr, binary_expr);
be->op = op;
be->op.kind = cast(TokenKind)(cast(i32)be->op.kind - (Token_AddEq - Token_Add));
- // NOTE(bill): Only use the first one will be used
+ // NOTE(bill): Only use the first one will be used
be->left = as->lhs[0];
be->right = as->rhs[0];
check_expr(ctx, &lhs, as->lhs[0]);
check_binary_expr(ctx, &rhs, binary_expr, nullptr, true);
if (rhs.mode != Addressing_Invalid) {
- // NOTE(bill): Only use the first one will be used
- check_assignment_variable(ctx, &lhs, &rhs);
+ be->op.string = substring(be->op.string, 0, be->op.string.len - 1);
+ rhs.expr = binary_expr;
+ check_assignment_variable(ctx, &lhs, &rhs, str_lit("assignment operation"));
}
}
}
@@ -2504,6 +2541,78 @@ gb_internal void check_if_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
check_close_scope(ctx);
}
+// NOTE(bill): This is very basic escape analysis
+// This needs to be improved tremendously, and a lot of it done during the
+// middle-end (or LLVM side) to improve checks and error messages
+void check_unsafe_return(Operand const &o, Type *type, Ast *expr) {
+ auto const unsafe_return_error = [](Operand const &o, char const *msg, Type *extra_type=nullptr) {
+ gbString s = expr_to_string(o.expr);
+ if (extra_type) {
+ gbString t = type_to_string(extra_type);
+ error(o.expr, "It is unsafe to return %s ('%s') of type ('%s') from a procedure, as it uses the current stack frame's memory", msg, s, t);
+ gb_string_free(t);
+ } else {
+ error(o.expr, "It is unsafe to return %s ('%s') from a procedure, as it uses the current stack frame's memory", msg, s);
+ }
+ gb_string_free(s);
+ };
+
+ if (type == nullptr || expr == nullptr) {
+ return;
+ }
+
+ if (expr->kind == Ast_CompoundLit && is_type_slice(type)) {
+ ast_node(cl, CompoundLit, expr);
+ if (cl->elems.count == 0) {
+ return;
+ }
+ unsafe_return_error(o, "a compound literal of a slice");
+ } else if (expr->kind == Ast_UnaryExpr && expr->UnaryExpr.op.kind == Token_And) {
+ Ast *x = unparen_expr(expr->UnaryExpr.expr);
+ 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) {
+ unsafe_return_error(o, "the address of a compound literal");
+ } else if (x->kind == Ast_IndexExpr) {
+ Entity *f = entity_of_node(x->IndexExpr.expr);
+ if (f && (is_type_array_like(f->type) || is_type_matrix(f->type))) {
+ if (is_entity_local_variable(f)) {
+ unsafe_return_error(o, "the address of an indexed variable", f->type);
+ }
+ }
+ } else if (x->kind == Ast_MatrixIndexExpr) {
+ Entity *f = entity_of_node(x->MatrixIndexExpr.expr);
+ if (f && (is_type_matrix(f->type) && is_entity_local_variable(f))) {
+ 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(type)) {
+ ERROR_BLOCK();
+ unsafe_return_error(o, "a compound literal of a slice");
+ error_line("\tNote: A constant slice value will use the memory of the current stack frame\n");
+ } else if (expr->kind == Ast_CompoundLit) {
+ ast_node(cl, CompoundLit, expr);
+ for (Ast *elem : cl->elems) {
+ if (elem->kind == Ast_FieldValue) {
+ ast_node(fv, FieldValue, elem);
+ Entity *e = entity_of_node(fv->field);
+ if (e != nullptr) {
+ check_unsafe_return(o, e->type, fv->value);
+ }
+ }
+ }
+ }
+}
+
gb_internal void check_return_stmt(CheckerContext *ctx, Ast *node) {
ast_node(rs, ReturnStmt, node);
@@ -2532,8 +2641,9 @@ gb_internal void check_return_stmt(CheckerContext *ctx, Ast *node) {
result_count = proc_type->Proc.results->Tuple.variables.count;
}
- auto operands = array_make<Operand>(heap_allocator(), 0, 2*rs->results.count);
- defer (array_free(&operands));
+ TEMPORARY_ALLOCATOR_GUARD();
+
+ auto operands = array_make<Operand>(temporary_allocator(), 0, 2*rs->results.count);
check_unpack_arguments(ctx, result_entities, result_count, &operands, rs->results, UnpackFlag_AllowOk);
@@ -2579,61 +2689,7 @@ gb_internal void check_return_stmt(CheckerContext *ctx, Ast *node) {
expr = unparen_expr(arg);
}
- auto unsafe_return_error = [](Operand const &o, char const *msg, Type *extra_type=nullptr) {
- gbString s = expr_to_string(o.expr);
- if (extra_type) {
- gbString t = type_to_string(extra_type);
- error(o.expr, "It is unsafe to return %s ('%s') of type ('%s') from a procedure, as it uses the current stack frame's memory", msg, s, t);
- gb_string_free(t);
- } else {
- error(o.expr, "It is unsafe to return %s ('%s') from a procedure, as it uses the current stack frame's memory", msg, s);
- }
- gb_string_free(s);
- };
-
-
- // NOTE(bill): This is very basic escape analysis
- // This needs to be improved tremendously, and a lot of it done during the
- // middle-end (or LLVM side) to improve checks and error messages
- if (expr->kind == Ast_CompoundLit && is_type_slice(o.type)) {
- ast_node(cl, CompoundLit, expr);
- if (cl->elems.count == 0) {
- continue;
- }
- unsafe_return_error(o, "a compound literal of a slice");
- } else if (expr->kind == Ast_UnaryExpr && expr->UnaryExpr.op.kind == Token_And) {
- Ast *x = unparen_expr(expr->UnaryExpr.expr);
- 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) {
- unsafe_return_error(o, "the address of a compound literal");
- } else if (x->kind == Ast_IndexExpr) {
- Entity *f = entity_of_node(x->IndexExpr.expr);
- if (f && (is_type_array_like(f->type) || is_type_matrix(f->type))) {
- if (is_entity_local_variable(f)) {
- unsafe_return_error(o, "the address of an indexed variable", f->type);
- }
- }
- } else if (x->kind == Ast_MatrixIndexExpr) {
- Entity *f = entity_of_node(x->MatrixIndexExpr.expr);
- if (f && (is_type_matrix(f->type) && is_entity_local_variable(f))) {
- 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");
- error_line("\tNote: A constant slice value will use the memory of the current stack frame\n");
- }
+ check_unsafe_return(o, o.type, expr);
}
}
@@ -2755,6 +2811,51 @@ gb_internal void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags)
if (ctx->decl) {
ctx->decl->defer_used += 1;
}
+
+ // NOTE(bill): Handling errors/warnings
+
+ Ast *stmt = ds->stmt;
+ Ast *original_stmt = stmt;
+
+ if (stmt->kind == Ast_BlockStmt && stmt->BlockStmt.stmts.count == 0) {
+ break; // empty defer statement
+ }
+
+ bool is_singular = true;
+ while (is_singular && stmt->kind == Ast_BlockStmt) {
+ Ast *inner_stmt = nullptr;
+ for (Ast *s : stmt->BlockStmt.stmts) {
+ if (s->kind == Ast_EmptyStmt) {
+ continue;
+ }
+ if (inner_stmt != nullptr) {
+ is_singular = false;
+ break;
+ }
+ inner_stmt = s;
+ }
+
+ if (inner_stmt != nullptr) {
+ stmt = inner_stmt;
+ }
+ }
+ if (!is_singular) {
+ stmt = original_stmt;
+ }
+
+ switch (stmt->kind) {
+ case_ast_node(as, AssignStmt, stmt);
+ if (as->op.kind != Token_Eq) {
+ break;
+ }
+ for (Ast *lhs : as->lhs) {
+ Entity *e = entity_of_node(lhs);
+ if (e && e->flags & EntityFlag_Result) {
+ error(lhs, "Assignments to named return values within 'defer' will not affect the value that is returned");
+ }
+ }
+ case_end;
+ }
}
case_end;
@@ -2812,6 +2913,7 @@ gb_internal void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags)
case Ast_BlockStmt:
case Ast_IfStmt:
case Ast_SwitchStmt:
+ case Ast_TypeSwitchStmt:
if (token.kind != Token_break) {
error(bs->label, "Label '%.*s' can only be used with 'break'", LIT(e->token.string));
}
diff --git a/src/check_type.cpp b/src/check_type.cpp
index 9d4defbb2..0819de217 100644
--- a/src/check_type.cpp
+++ b/src/check_type.cpp
@@ -267,28 +267,39 @@ gb_internal bool check_custom_align(CheckerContext *ctx, Ast *node, i64 *align_,
gb_internal GenTypesData *ensure_polymorphic_record_entity_has_gen_types(CheckerContext *ctx, Type *original_type) {
- mutex_lock(&ctx->info->gen_types_mutex); // @@global
-
GenTypesData *found_gen_types = nullptr;
- auto *found_gen_types_ptr = map_get(&ctx->info->gen_types, original_type);
- if (found_gen_types_ptr == nullptr) {
+
+ GB_ASSERT(original_type->kind == Type_Named);
+ mutex_lock(&original_type->Named.gen_types_data_mutex);
+ if (original_type->Named.gen_types_data == nullptr) {
GenTypesData *gen_types = gb_alloc_item(permanent_allocator(), GenTypesData);
gen_types->types = array_make<Entity *>(heap_allocator());
- map_set(&ctx->info->gen_types, original_type, gen_types);
- found_gen_types_ptr = map_get(&ctx->info->gen_types, original_type);
+ original_type->Named.gen_types_data = gen_types;
}
- found_gen_types = *found_gen_types_ptr;
- GB_ASSERT(found_gen_types != nullptr);
- mutex_unlock(&ctx->info->gen_types_mutex); // @@global
+ found_gen_types = original_type->Named.gen_types_data;
+
+ mutex_unlock(&original_type->Named.gen_types_data_mutex);
+
return found_gen_types;
}
gb_internal void add_polymorphic_record_entity(CheckerContext *ctx, Ast *node, Type *named_type, Type *original_type) {
GB_ASSERT(is_type_named(named_type));
+ GB_ASSERT(original_type->kind == Type_Named);
gbAllocator a = heap_allocator();
Scope *s = ctx->scope->parent;
+ AstPackage *pkg = nullptr;
+ if (original_type->Named.type_name && original_type->Named.type_name->pkg) {
+ pkg = original_type->Named.type_name->pkg;
+ }
+
+ if (pkg == nullptr) {
+ // NOTE(bill): if the `pkg` cannot be determined, default to the current context's pkg instead
+ pkg = ctx->pkg;
+ }
+
Entity *e = nullptr;
{
Token token = ast_token(node);
@@ -300,12 +311,12 @@ gb_internal void add_polymorphic_record_entity(CheckerContext *ctx, Ast *node, T
e = alloc_entity_type_name(s, token, named_type);
e->state = EntityState_Resolved;
e->file = ctx->file;
- e->pkg = ctx->pkg;
+ e->pkg = pkg;
+ e->TypeName.original_type_for_parapoly = original_type;
add_entity_use(ctx, node, e);
}
named_type->Named.type_name = e;
- GB_ASSERT(original_type->kind == Type_Named);
e->TypeName.objc_class_name = original_type->Named.type_name->TypeName.objc_class_name;
// TODO(bill): Is this even correct? Or should the metadata be copied?
e->TypeName.objc_metadata = original_type->Named.type_name->TypeName.objc_metadata;
@@ -646,7 +657,6 @@ gb_internal void check_struct_type(CheckerContext *ctx, Type *struct_type, Ast *
struct_type->Struct.node = node;
struct_type->Struct.scope = ctx->scope;
struct_type->Struct.is_packed = st->is_packed;
- struct_type->Struct.is_no_copy = st->is_no_copy;
struct_type->Struct.polymorphic_params = check_record_polymorphic_params(
ctx, st->polymorphic_params,
&struct_type->Struct.is_polymorphic,
@@ -1097,7 +1107,7 @@ gb_internal void check_bit_field_type(CheckerContext *ctx, Type *bit_field_type,
GB_ASSERT(fields.count <= bf->fields.count);
- auto bit_offsets = slice_make<i64>(permanent_allocator(), fields.count);
+ auto bit_offsets = permanent_slice_make<i64>(fields.count);
i64 curr_offset = 0;
for_array(i, bit_sizes) {
bit_offsets[i] = curr_offset;
@@ -1155,8 +1165,7 @@ gb_internal void check_bit_field_type(CheckerContext *ctx, Type *bit_field_type,
}
}
-
-
+ #if 0 // Reconsider at a later date
if (bit_sizes.count > 0 && is_type_integer(backing_type)) {
bool all_booleans = is_type_boolean(fields[0]->type);
bool all_ones = bit_sizes[0] == 1;
@@ -1182,7 +1191,7 @@ gb_internal void check_bit_field_type(CheckerContext *ctx, Type *bit_field_type,
}
}
}
-
+ #endif
bit_field_type->BitField.fields = slice_from_array(fields);
bit_field_type->BitField.bit_sizes = slice_from_array(bit_sizes);
@@ -1911,9 +1920,18 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para
case ParameterValue_Location:
case ParameterValue_Expression:
case ParameterValue_Value:
+ // Special case for polymorphic procedures as default values
+ if (param_value.ast_value != nullptr) {
+ Entity *e = entity_from_expr(param_value.ast_value);
+ if (e != nullptr && e->kind == Entity_Procedure && is_type_polymorphic(e->type)) {
+ // Allow polymorphic procedures as default parameter values
+ // The type will be correctly determined at call site
+ break;
+ }
+ }
gbString str = type_to_string(type);
error(params[i], "A default value for a parameter must not be a polymorphic constant type, got %s", str);
- gb_string_free(str);
+ gb_string_free(str);
break;
}
}
@@ -2068,7 +2086,9 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para
if (op.mode == Addressing_Constant) {
poly_const = op.value;
} else {
- error(op.expr, "Expected a constant value for this polymorphic name parameter, got %s", expr_to_string(op.expr));
+ if (!ctx->in_proc_group) {
+ error(op.expr, "Expected a constant value for this polymorphic name parameter, got %s", expr_to_string(op.expr));
+ }
success = false;
}
}
@@ -2082,7 +2102,9 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para
if (type != t_invalid && !check_is_assignable_to(ctx, &op, type, allow_array_programming)) {
bool ok = true;
if (p->flags&FieldFlag_any_int) {
- if ((!is_type_integer(op.type) && !is_type_enum(op.type)) || (!is_type_integer(type) && !is_type_enum(type))) {
+ if (op.type == nullptr) {
+ ok = false;
+ } else if ((!is_type_integer(op.type) && !is_type_enum(op.type)) || (!is_type_integer(type) && !is_type_enum(type))) {
ok = false;
} else if (!check_is_castable_to(ctx, &op, type)) {
ok = false;
@@ -2686,7 +2708,7 @@ gb_internal Type *get_map_cell_type(Type *type) {
// Padding exists
Type *s = alloc_type_struct();
Scope *scope = create_scope(nullptr, nullptr);
- s->Struct.fields = slice_make<Entity *>(permanent_allocator(), 2);
+ s->Struct.fields = permanent_slice_make<Entity *>(2);
s->Struct.fields[0] = alloc_entity_field(scope, make_token_ident("v"), alloc_type_array(type, len), false, 0, EntityState_Resolved);
s->Struct.fields[1] = alloc_entity_field(scope, make_token_ident("_"), alloc_type_array(t_u8, padding), false, 1, EntityState_Resolved);
s->Struct.scope = scope;
@@ -2711,7 +2733,7 @@ gb_internal void init_map_internal_debug_types(Type *type) {
Type *metadata_type = alloc_type_struct();
Scope *metadata_scope = create_scope(nullptr, nullptr);
- metadata_type->Struct.fields = slice_make<Entity *>(permanent_allocator(), 5);
+ metadata_type->Struct.fields = permanent_slice_make<Entity *>(5);
metadata_type->Struct.fields[0] = alloc_entity_field(metadata_scope, make_token_ident("key"), key, false, 0, EntityState_Resolved);
metadata_type->Struct.fields[1] = alloc_entity_field(metadata_scope, make_token_ident("value"), value, false, 1, EntityState_Resolved);
metadata_type->Struct.fields[2] = alloc_entity_field(metadata_scope, make_token_ident("hash"), t_uintptr, false, 2, EntityState_Resolved);
@@ -2729,7 +2751,7 @@ gb_internal void init_map_internal_debug_types(Type *type) {
Scope *scope = create_scope(nullptr, nullptr);
Type *debug_type = alloc_type_struct();
- debug_type->Struct.fields = slice_make<Entity *>(permanent_allocator(), 3);
+ debug_type->Struct.fields = permanent_slice_make<Entity *>(3);
debug_type->Struct.fields[0] = alloc_entity_field(scope, make_token_ident("data"), metadata_type, false, 0, EntityState_Resolved);
debug_type->Struct.fields[1] = alloc_entity_field(scope, make_token_ident("len"), t_int, false, 1, EntityState_Resolved);
debug_type->Struct.fields[2] = alloc_entity_field(scope, make_token_ident("allocator"), t_allocator, false, 2, EntityState_Resolved);
@@ -2773,6 +2795,21 @@ gb_internal void add_map_key_type_dependencies(CheckerContext *ctx, Type *key) {
return;
}
+ if (key->kind == Type_Basic) {
+ if (key->Basic.flags & BasicFlag_Quaternion) {
+ add_package_dependency(ctx, "runtime", "default_hasher_f64");
+ add_package_dependency(ctx, "runtime", "default_hasher_quaternion256");
+ return;
+ } else if (key->Basic.flags & BasicFlag_Complex) {
+ add_package_dependency(ctx, "runtime", "default_hasher_f64");
+ add_package_dependency(ctx, "runtime", "default_hasher_complex128");
+ return;
+ } else if (key->Basic.flags & BasicFlag_Float) {
+ add_package_dependency(ctx, "runtime", "default_hasher_f64");
+ return;
+ }
+ }
+
if (key->kind == Type_Struct) {
add_package_dependency(ctx, "runtime", "default_hasher");
for_array(i, key->Struct.fields) {
@@ -2947,13 +2984,13 @@ gb_internal bool complete_soa_type(Checker *checker, Type *t, bool wait_to_finis
if (wait_to_finish) {
wait_signal_until_available(&old_struct->Struct.fields_wait_signal);
} else {
- GB_ASSERT(old_struct->Struct.fields_wait_signal.futex.load());
+ GB_ASSERT(old_struct->Struct.fields_wait_signal.futex.load() != 0);
}
field_count = old_struct->Struct.fields.count;
- t->Struct.fields = slice_make<Entity *>(permanent_allocator(), field_count+extra_field_count);
- t->Struct.tags = gb_alloc_array(permanent_allocator(), String, field_count+extra_field_count);
+ t->Struct.fields = permanent_slice_make<Entity *>(field_count+extra_field_count);
+ t->Struct.tags = permanent_alloc_array<String>(field_count+extra_field_count);
auto const &add_entity = [](Scope *scope, Entity *entity) {
@@ -3071,7 +3108,7 @@ gb_internal Type *make_soa_struct_internal(CheckerContext *ctx, Ast *array_typ_e
if (is_polymorphic) {
field_count = 0;
- soa_struct->Struct.fields = slice_make<Entity *>(permanent_allocator(), field_count+extra_field_count);
+ soa_struct->Struct.fields = permanent_slice_make<Entity *>(field_count+extra_field_count);
soa_struct->Struct.tags = gb_alloc_array(permanent_allocator(), String, field_count+extra_field_count);
soa_struct->Struct.soa_count = 0;
@@ -3081,7 +3118,7 @@ gb_internal Type *make_soa_struct_internal(CheckerContext *ctx, Ast *array_typ_e
Type *old_array = base_type(elem);
field_count = cast(isize)old_array->Array.count;
- soa_struct->Struct.fields = slice_make<Entity *>(permanent_allocator(), field_count+extra_field_count);
+ soa_struct->Struct.fields = permanent_slice_make<Entity *>(field_count+extra_field_count);
soa_struct->Struct.tags = gb_alloc_array(permanent_allocator(), String, field_count+extra_field_count);
string_map_init(&scope->elements, 8);
@@ -3123,8 +3160,8 @@ gb_internal Type *make_soa_struct_internal(CheckerContext *ctx, Ast *array_typ_e
if (old_struct->Struct.fields_wait_signal.futex.load()) {
field_count = old_struct->Struct.fields.count;
- soa_struct->Struct.fields = slice_make<Entity *>(permanent_allocator(), field_count+extra_field_count);
- soa_struct->Struct.tags = gb_alloc_array(permanent_allocator(), String, field_count+extra_field_count);
+ soa_struct->Struct.fields = permanent_slice_make<Entity *>(field_count+extra_field_count);
+ soa_struct->Struct.tags = permanent_alloc_array<String>(field_count+extra_field_count);
for_array(i, old_struct->Struct.fields) {
Entity *old_field = old_struct->Struct.fields[i];
@@ -3259,7 +3296,7 @@ gb_internal void check_array_type_internal(CheckerContext *ctx, Ast *e, Type **t
}
if (count < 0) {
- error(at->count, "? can only be used in conjuction with compound literals");
+ error(at->count, "? can only be used in conjunction with compound literals");
count = 0;
}
@@ -3281,8 +3318,11 @@ gb_internal void check_array_type_internal(CheckerContext *ctx, Ast *e, Type **t
if (generic_type != nullptr) {
// Ignore
} else if (count < 1 || !is_power_of_two(count)) {
- error(at->count, "Invalid length for #simd, expected a power of two length, got '%lld'", cast(long long)count);
*type = alloc_type_array(elem, count, generic_type);
+ if (ctx->disallow_polymorphic_return_types && count == 0) {
+ return;
+ }
+ error(at->count, "Invalid length for #simd, expected a power of two length, got '%lld'", cast(long long)count);
return;
}
@@ -3316,7 +3356,7 @@ gb_internal void check_array_type_internal(CheckerContext *ctx, Ast *e, Type **t
}
}
gb_internal bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, Type *named_type) {
- GB_ASSERT_NOT_NULL(type);
+ GB_ASSERT(type != nullptr);
if (e == nullptr) {
*type = t_invalid;
return true;
@@ -3473,8 +3513,9 @@ gb_internal bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, T
case_ast_node(pt, PointerType, e);
CheckerContext c = *ctx;
- c.type_path = new_checker_type_path();
- defer (destroy_checker_type_path(c.type_path));
+
+ TEMPORARY_ALLOCATOR_GUARD();
+ c.type_path = new_checker_type_path(temporary_allocator());
Type *elem = t_invalid;
Operand o = {};
@@ -3502,6 +3543,17 @@ gb_internal bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, T
elem = o.type;
}
+ if (!ctx->in_polymorphic_specialization && ctx->disallow_polymorphic_return_types) {
+ Type *t = base_type(elem);
+ if (t != nullptr &&
+ unparen_expr(pt->type)->kind == Ast_Ident &&
+ is_type_polymorphic_record_unspecialized(t)) {
+ gbString err_str = expr_to_string(e);
+ error(e, "Invalid use of a non-specialized polymorphic type '%s'", err_str);
+ gb_string_free(err_str);
+ }
+ }
+
if (pt->tag != nullptr) {
GB_ASSERT(pt->tag->kind == Ast_BasicDirective);
@@ -3697,8 +3749,8 @@ gb_internal bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, T
gb_internal Type *check_type(CheckerContext *ctx, Ast *e) {
CheckerContext c = *ctx;
- c.type_path = new_checker_type_path();
- defer (destroy_checker_type_path(c.type_path));
+ TEMPORARY_ALLOCATOR_GUARD();
+ c.type_path = new_checker_type_path(temporary_allocator());
return check_type_expr(&c, e, nullptr);
}
@@ -3766,7 +3818,11 @@ gb_internal Type *check_type_expr(CheckerContext *ctx, Ast *e, Type *named_type)
#if 0
error(e, "Invalid type definition of '%.*s'", LIT(type->Named.name));
#endif
- type->Named.base = t_invalid;
+ if (type->Named.type_name->TypeName.is_type_alias) {
+ // NOTE(laytan): keep it null, type declaration is a mini "cycle" to be filled later.
+ } else {
+ type->Named.base = t_invalid;
+ }
}
if (is_type_polymorphic(type)) {
@@ -3784,7 +3840,7 @@ gb_internal Type *check_type_expr(CheckerContext *ctx, Ast *e, Type *named_type)
}
#endif
- if (is_type_typed(type)) {
+ if (type->kind == Type_Named && type->Named.base == nullptr || is_type_typed(type)) {
add_type_and_value(ctx, e, Addressing_Type, type, empty_exact_value);
} else {
gbString name = type_to_string(type);
diff --git a/src/checker.cpp b/src/checker.cpp
index bee3f1efe..0a8590570 100644
--- a/src/checker.cpp
+++ b/src/checker.cpp
@@ -380,16 +380,25 @@ gb_internal Entity *scope_lookup_current(Scope *s, String const &name) {
}
-gb_internal void scope_lookup_parent(Scope *scope, String const &name, Scope **scope_, Entity **entity_) {
+gb_global std::atomic<bool> in_single_threaded_checker_stage;
+
+gb_internal void scope_lookup_parent(Scope *scope, String const &name, Scope **scope_, Entity **entity_, u32 hash) {
+ bool is_single_threaded = in_single_threaded_checker_stage.load(std::memory_order_relaxed);
if (scope != nullptr) {
bool gone_thru_proc = false;
bool gone_thru_package = false;
- StringHashKey key = string_hash_string(name);
+ StringHashKey key = {};
+ if (hash) {
+ key.hash = hash;
+ key.string = name;
+ } else {
+ key = string_hash_string(name);
+ }
for (Scope *s = scope; s != nullptr; s = s->parent) {
Entity **found = nullptr;
- rw_mutex_shared_lock(&s->mutex);
+ if (!is_single_threaded) rw_mutex_shared_lock(&s->mutex);
found = string_map_get(&s->elements, key);
- rw_mutex_shared_unlock(&s->mutex);
+ if (!is_single_threaded) rw_mutex_shared_unlock(&s->mutex);
if (found) {
Entity *e = *found;
if (gone_thru_proc) {
@@ -424,9 +433,9 @@ gb_internal void scope_lookup_parent(Scope *scope, String const &name, Scope **s
if (scope_) *scope_ = nullptr;
}
-gb_internal Entity *scope_lookup(Scope *s, String const &name) {
+gb_internal Entity *scope_lookup(Scope *s, String const &name, u32 hash) {
Entity *entity = nullptr;
- scope_lookup_parent(s, name, nullptr, &entity);
+ scope_lookup_parent(s, name, nullptr, &entity, hash);
return entity;
}
@@ -507,11 +516,9 @@ 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;
- if (in_single_threaded_checker_stage) {
+ if (in_single_threaded_checker_stage.load(std::memory_order_relaxed)) {
return scope_insert_with_name_no_mutex(s, name, entity);
} else {
return scope_insert_with_name(s, name, entity);
@@ -565,6 +572,26 @@ gb_internal u64 check_feature_flags(CheckerContext *c, Ast *node) {
return 0;
}
+gb_internal u64 check_feature_flags(Entity *e) {
+ if (e == nullptr) {
+ return 0;
+ }
+ AstFile *file = nullptr;
+ if (e->file == nullptr) {
+ file = e->file;
+ }
+ if (file == nullptr) {
+ if (e->decl_info && e->decl_info->decl_node) {
+ file = e->decl_info->decl_node->file();
+ }
+ }
+ if (file != nullptr && file->feature_flags_set) {
+ return file->feature_flags;
+ }
+ return 0;
+}
+
+
enum VettedEntityKind {
VettedEntity_Invalid,
@@ -702,9 +729,10 @@ gb_internal bool check_vet_unused(Checker *c, Entity *e, VettedEntity *ve) {
gb_internal void check_scope_usage_internal(Checker *c, Scope *scope, u64 vet_flags, bool per_entity) {
u64 original_vet_flags = vet_flags;
+
+ TEMPORARY_ALLOCATOR_GUARD();
Array<VettedEntity> vetted_entities = {};
- array_init(&vetted_entities, heap_allocator());
- defer (array_free(&vetted_entities));
+ array_init(&vetted_entities, temporary_allocator());
rw_mutex_shared_lock(&scope->mutex);
for (auto const &entry : scope->elements) {
@@ -728,12 +756,17 @@ gb_internal void check_scope_usage_internal(Checker *c, Scope *scope, u64 vet_fl
bool is_unused = false;
if (vet_unused && check_vet_unused(c, e, &ve_unused)) {
is_unused = true;
- } else if (vet_unused_procedures &&
- e->kind == Entity_Procedure) {
+ } else if (vet_unused_procedures && e->kind == Entity_Procedure) {
if (e->flags&EntityFlag_Used) {
is_unused = false;
} else if (e->flags & EntityFlag_Require) {
is_unused = false;
+ } else if (e->flags & EntityFlag_Init) {
+ is_unused = false;
+ } else if (e->flags & EntityFlag_Fini) {
+ is_unused = false;
+ } else if (e->Procedure.is_export) {
+ is_unused = false;
} else if (e->pkg && e->pkg->kind == Package_Init && e->token.string == "main") {
is_unused = false;
} else {
@@ -751,7 +784,7 @@ gb_internal void check_scope_usage_internal(Checker *c, Scope *scope, u64 vet_fl
array_add(&vetted_entities, ve_unused);
} else if (is_shadowed) {
array_add(&vetted_entities, ve_shadowed);
- } else if (e->kind == Entity_Variable && (e->flags & (EntityFlag_Param|EntityFlag_Using|EntityFlag_Static)) == 0 && !e->Variable.is_global) {
+ } else if (e->kind == Entity_Variable && (e->flags & (EntityFlag_Param|EntityFlag_Using|EntityFlag_Static|EntityFlag_Field)) == 0 && !e->Variable.is_global) {
i64 sz = type_size_of(e->type);
// TODO(bill): When is a good size warn?
// Is >256 KiB good enough?
@@ -827,9 +860,13 @@ gb_internal void check_scope_usage(Checker *c, Scope *scope, u64 vet_flags) {
gb_internal void add_dependency(CheckerInfo *info, DeclInfo *d, Entity *e) {
- rw_mutex_lock(&d->deps_mutex);
- ptr_set_add(&d->deps, e);
- rw_mutex_unlock(&d->deps_mutex);
+ if (in_single_threaded_checker_stage.load(std::memory_order_relaxed)) {
+ ptr_set_add(&d->deps, e);
+ } else {
+ rw_mutex_lock(&d->deps_mutex);
+ ptr_set_add(&d->deps, e);
+ rw_mutex_unlock(&d->deps_mutex);
+ }
}
gb_internal void add_type_info_dependency(CheckerInfo *info, DeclInfo *d, Type *type) {
if (d == nullptr || type == nullptr) {
@@ -1073,11 +1110,30 @@ gb_internal void init_universal(void) {
add_global_bool_constant("true", true);
add_global_bool_constant("false", false);
- add_global_string_constant("ODIN_VENDOR", bc->ODIN_VENDOR);
- add_global_string_constant("ODIN_VERSION", bc->ODIN_VERSION);
- add_global_string_constant("ODIN_ROOT", bc->ODIN_ROOT);
- add_global_string_constant("ODIN_BUILD_PROJECT_NAME", bc->ODIN_BUILD_PROJECT_NAME);
- add_global_string_constant("ODIN_WINDOWS_SUBSYSTEM", bc->ODIN_WINDOWS_SUBSYSTEM);
+ add_global_string_constant("ODIN_VENDOR", bc->ODIN_VENDOR);
+ add_global_string_constant("ODIN_VERSION", bc->ODIN_VERSION);
+ add_global_string_constant("ODIN_ROOT", bc->ODIN_ROOT);
+ add_global_string_constant("ODIN_BUILD_PROJECT_NAME", bc->ODIN_BUILD_PROJECT_NAME);
+
+ {
+ GlobalEnumValue values[Windows_Subsystem_COUNT] = {
+ {"Unknown", Windows_Subsystem_UNKNOWN},
+ {"Boot_Application", Windows_Subsystem_BOOT_APPLICATION},
+ {"Console", Windows_Subsystem_CONSOLE},
+ {"EFI_Application", Windows_Subsystem_EFI_APPLICATION},
+ {"EFI_Boot_Service_Driver", Windows_Subsystem_EFI_BOOT_SERVICE_DRIVER},
+ {"EFI_Rom", Windows_Subsystem_EFI_ROM},
+ {"EFI_Runtime_Driver", Windows_Subsystem_EFI_RUNTIME_DRIVER},
+ {"Native", Windows_Subsystem_NATIVE},
+ {"Posix", Windows_Subsystem_POSIX},
+ {"Windows", Windows_Subsystem_WINDOWS},
+ {"Windows_CE", Windows_Subsystem_WINDOWSCE},
+ };
+
+ auto fields = add_global_enum_type(str_lit("Odin_Windows_Subsystem_Type"), values, gb_count_of(values));
+ add_global_enum_constant(fields, "ODIN_WINDOWS_SUBSYSTEM", bc->ODIN_WINDOWS_SUBSYSTEM);
+ add_global_string_constant("ODIN_WINDOWS_SUBSYSTEM_STRING", windows_subsystem_names[bc->ODIN_WINDOWS_SUBSYSTEM]);
+ }
{
GlobalEnumValue values[TargetOs_COUNT] = {
@@ -1097,7 +1153,7 @@ gb_internal void init_universal(void) {
};
auto fields = add_global_enum_type(str_lit("Odin_OS_Type"), values, gb_count_of(values));
- add_global_enum_constant(fields, "ODIN_OS", bc->metrics.os);
+ add_global_enum_constant(fields, "ODIN_OS", bc->metrics.os);
add_global_string_constant("ODIN_OS_STRING", target_os_names[bc->metrics.os]);
}
@@ -1147,8 +1203,10 @@ gb_internal void init_universal(void) {
{
GlobalEnumValue values[Subtarget_COUNT] = {
- {"Default", Subtarget_Default},
- {"iOS", Subtarget_iOS},
+ {"Default", Subtarget_Default},
+ {"iPhone", Subtarget_iPhone},
+ {"iPhoneSimulator", Subtarget_iPhoneSimulator},
+ {"Android", Subtarget_Android},
};
auto fields = add_global_enum_type(str_lit("Odin_Platform_Subtarget_Type"), values, gb_count_of(values));
@@ -1337,23 +1395,29 @@ gb_internal void init_universal(void) {
}
- t_u8_ptr = alloc_type_pointer(t_u8);
- t_u8_multi_ptr = alloc_type_multi_pointer(t_u8);
- t_int_ptr = alloc_type_pointer(t_int);
- t_i64_ptr = alloc_type_pointer(t_i64);
- t_f64_ptr = alloc_type_pointer(t_f64);
- t_u8_slice = alloc_type_slice(t_u8);
- t_string_slice = alloc_type_slice(t_string);
+ t_u8_ptr = alloc_type_pointer(t_u8);
+ t_u8_multi_ptr = alloc_type_multi_pointer(t_u8);
+ t_u16_ptr = alloc_type_pointer(t_u16);
+ t_u16_multi_ptr = alloc_type_multi_pointer(t_u16);
+ t_int_ptr = alloc_type_pointer(t_int);
+ t_i64_ptr = alloc_type_pointer(t_i64);
+ t_f64_ptr = alloc_type_pointer(t_f64);
+ t_u8_slice = alloc_type_slice(t_u8);
+ t_string_slice = alloc_type_slice(t_string);
// intrinsics types for objective-c stuff
{
t_objc_object = add_global_type_name(intrinsics_pkg->scope, str_lit("objc_object"), alloc_type_struct_complete());
t_objc_selector = add_global_type_name(intrinsics_pkg->scope, str_lit("objc_selector"), alloc_type_struct_complete());
t_objc_class = add_global_type_name(intrinsics_pkg->scope, str_lit("objc_class"), alloc_type_struct_complete());
+ t_objc_ivar = add_global_type_name(intrinsics_pkg->scope, str_lit("objc_ivar"), alloc_type_struct_complete());
t_objc_id = alloc_type_pointer(t_objc_object);
t_objc_SEL = alloc_type_pointer(t_objc_selector);
t_objc_Class = alloc_type_pointer(t_objc_class);
+ t_objc_Ivar = alloc_type_pointer(t_objc_ivar);
+
+ t_objc_instancetype = add_global_type_name(intrinsics_pkg->scope, str_lit("objc_instancetype"), t_objc_id);
}
}
@@ -1369,13 +1433,10 @@ gb_internal void init_checker_info(CheckerInfo *i) {
array_init(&i->entities, a);
map_init(&i->global_untyped);
string_map_init(&i->foreigns);
- // map_init(&i->gen_procs);
- map_init(&i->gen_types);
type_set_init(&i->min_dep_type_info_set);
map_init(&i->min_dep_type_info_index_map);
- // map_init(&i->type_info_map);
string_map_init(&i->files);
string_map_init(&i->packages);
array_init(&i->variable_init_order, a);
@@ -1386,8 +1447,13 @@ gb_internal void init_checker_info(CheckerInfo *i) {
array_init(&i->defineables, a);
map_init(&i->objc_msgSend_types);
+ mpsc_init(&i->objc_class_implementations, a);
+ string_set_init(&i->obcj_class_name_set, 0);
+ map_init(&i->objc_method_implementations);
+
string_map_init(&i->load_file_cache);
- array_init(&i->all_procedures, heap_allocator());
+ array_init(&i->all_procedures, a);
+ mpsc_init(&i->all_procedures_queue, a);
mpsc_init(&i->entity_queue, a); // 1<<20);
mpsc_init(&i->definition_queue, a); //); // 1<<20);
@@ -1397,6 +1463,9 @@ gb_internal void init_checker_info(CheckerInfo *i) {
mpsc_init(&i->foreign_decls_to_check, a); // 1<<10);
mpsc_init(&i->intrinsics_entry_point_usage, a); // 1<<10); // just waste some memory here, even if it probably never used
+ mpsc_init(&i->raddbg_type_views_queue, a);
+ array_init(&i->raddbg_type_views, a);
+
string_map_init(&i->load_directory_cache);
map_init(&i->load_directory_map);
}
@@ -1406,8 +1475,6 @@ gb_internal void destroy_checker_info(CheckerInfo *i) {
array_free(&i->entities);
map_destroy(&i->global_untyped);
string_map_destroy(&i->foreigns);
- // map_destroy(&i->gen_procs);
- map_destroy(&i->gen_types);
type_set_destroy(&i->min_dep_type_info_set);
map_destroy(&i->min_dep_type_info_index_map);
@@ -1418,6 +1485,10 @@ gb_internal void destroy_checker_info(CheckerInfo *i) {
array_free(&i->required_foreign_imports_through_force);
array_free(&i->defineables);
+ array_free(&i->all_procedures);
+
+ mpsc_destroy(&i->all_procedures_queue);
+
mpsc_destroy(&i->entity_queue);
mpsc_destroy(&i->definition_queue);
mpsc_destroy(&i->required_global_variable_queue);
@@ -1425,7 +1496,14 @@ gb_internal void destroy_checker_info(CheckerInfo *i) {
mpsc_destroy(&i->foreign_imports_to_check_fullpaths);
mpsc_destroy(&i->foreign_decls_to_check);
+ mpsc_destroy(&i->raddbg_type_views_queue);
+ array_free(&i->raddbg_type_views);
+
map_destroy(&i->objc_msgSend_types);
+ string_set_destroy(&i->obcj_class_name_set);
+ mpsc_destroy(&i->objc_class_implementations);
+ map_destroy(&i->objc_method_implementations);
+
string_map_destroy(&i->load_file_cache);
string_map_destroy(&i->load_directory_cache);
map_destroy(&i->load_directory_map);
@@ -1438,12 +1516,12 @@ gb_internal CheckerContext make_checker_context(Checker *c) {
ctx.scope = builtin_pkg->scope;
ctx.pkg = builtin_pkg;
- ctx.type_path = new_checker_type_path();
+ ctx.type_path = new_checker_type_path(heap_allocator());
ctx.type_level = 0;
return ctx;
}
gb_internal void destroy_checker_context(CheckerContext *ctx) {
- destroy_checker_type_path(ctx->type_path);
+ destroy_checker_type_path(ctx->type_path, heap_allocator());
}
gb_internal bool add_curr_ast_file(CheckerContext *ctx, AstFile *file) {
@@ -1496,6 +1574,8 @@ gb_internal void init_checker(Checker *c) {
TIME_SECTION("init proc queues");
mpsc_init(&c->procs_with_deferred_to_check, a); //, 1<<10);
+ mpsc_init(&c->procs_with_objc_context_provider_to_check, a);
+
// NOTE(bill): 1 Mi elements should be enough on average
array_init(&c->procs_to_check, heap_allocator(), 0, 1<<20);
@@ -1646,7 +1726,7 @@ gb_internal void check_remove_expr_info(CheckerContext *c, Ast *e) {
}
gb_internal isize type_info_index(CheckerInfo *info, TypeInfoPair pair, bool error_on_failure) {
- mutex_lock(&info->minimum_dependency_type_info_mutex);
+ rw_mutex_shared_lock(&info->minimum_dependency_type_info_mutex);
isize entry_index = -1;
u64 hash = pair.hash;
@@ -1654,7 +1734,7 @@ gb_internal isize type_info_index(CheckerInfo *info, TypeInfoPair pair, bool err
if (found_entry_index) {
entry_index = *found_entry_index;
}
- mutex_unlock(&info->minimum_dependency_type_info_mutex);
+ rw_mutex_shared_unlock(&info->minimum_dependency_type_info_mutex);
if (error_on_failure && entry_index < 0) {
compiler_error("Type_Info for '%s' could not be found", type_to_string(pair.type));
@@ -1692,7 +1772,7 @@ gb_internal void add_untyped(CheckerContext *c, Ast *expr, AddressingMode mode,
check_set_expr_info(c, expr, mode, type, value);
}
-gb_internal void add_type_and_value(CheckerContext *ctx, Ast *expr, AddressingMode mode, Type *type, ExactValue const &value) {
+gb_internal void add_type_and_value(CheckerContext *ctx, Ast *expr, AddressingMode mode, Type *type, ExactValue const &value, bool use_mutex) {
if (expr == nullptr) {
return;
}
@@ -1710,7 +1790,7 @@ gb_internal void add_type_and_value(CheckerContext *ctx, Ast *expr, AddressingMo
mutex = &ctx->pkg->type_and_value_mutex;
}
- mutex_lock(mutex);
+ if (use_mutex) mutex_lock(mutex);
Ast *prev_expr = nullptr;
while (prev_expr != expr) {
prev_expr = expr;
@@ -1731,8 +1811,11 @@ gb_internal void add_type_and_value(CheckerContext *ctx, Ast *expr, AddressingMo
}
expr = unparen_expr(expr);
+ if (expr == nullptr) {
+ break;
+ };
}
- mutex_unlock(mutex);
+ if (use_mutex) mutex_unlock(mutex);
}
gb_internal void add_entity_definition(CheckerInfo *i, Ast *identifier, Entity *entity) {
@@ -1974,8 +2057,8 @@ gb_internal void add_entity_and_decl_info(CheckerContext *c, Ast *identifier, En
add_entity_definition(info, identifier, e);
GB_ASSERT(e->decl_info == nullptr);
e->decl_info = d;
- d->entity = e;
e->pkg = c->pkg;
+ d->entity.store(e);
isize queue_count = -1;
bool is_lazy = false;
@@ -2021,7 +2104,7 @@ gb_internal void add_type_info_type(CheckerContext *c, Type *t) {
}
gb_internal void add_type_info_type_internal(CheckerContext *c, Type *t) {
- if (t == nullptr) {
+ if (t == nullptr || c == nullptr) {
return;
}
@@ -2276,11 +2359,9 @@ gb_internal void check_procedure_later(Checker *c, ProcInfo *info) {
}
if (DEBUG_CHECK_ALL_PROCEDURES) {
- MUTEX_GUARD_BLOCK(&c->info.all_procedures_mutex) {
- GB_ASSERT(info != nullptr);
- GB_ASSERT(info->decl != nullptr);
- array_add(&c->info.all_procedures, info);
- }
+ GB_ASSERT(info != nullptr);
+ GB_ASSERT(info->decl != nullptr);
+ mpsc_enqueue(&c->info.all_procedures_queue, info);
}
}
@@ -2308,7 +2389,7 @@ gb_internal void add_min_dep_type_info(Checker *c, Type *t) {
return;
}
- if (type_set_update(&c->info.min_dep_type_info_set, t)) {
+ if (type_set_update_with_mutex(&c->info.min_dep_type_info_set, t, &c->info.min_dep_type_info_set_mutex)) {
return;
}
@@ -2483,15 +2564,15 @@ gb_internal void add_min_dep_type_info(Checker *c, Type *t) {
}
}
+gb_internal void add_dependency_to_set_threaded(Checker *c, Entity *entity);
+
+gb_global std::atomic<Checker *> global_checker_ptr;
gb_internal void add_dependency_to_set(Checker *c, Entity *entity) {
if (entity == nullptr) {
return;
}
- CheckerInfo *info = &c->info;
- auto *set = &info->minimum_dependency_set;
-
if (entity->type != nullptr &&
is_type_polymorphic(entity->type)) {
DeclInfo *decl = decl_info_of_entity(entity);
@@ -2500,7 +2581,7 @@ gb_internal void add_dependency_to_set(Checker *c, Entity *entity) {
}
}
- if (ptr_set_update(set, entity)) {
+ if (entity->min_dep_count.fetch_add(1, std::memory_order_relaxed) > 0) {
return;
}
@@ -2511,29 +2592,108 @@ gb_internal void add_dependency_to_set(Checker *c, Entity *entity) {
for (TypeInfoPair const tt : decl->type_info_deps) {
add_min_dep_type_info(c, tt.type);
}
+ FOR_PTR_SET(e, decl->deps) {
+ switch (e->kind) {
+ case Entity_Procedure:
+ if (e->Procedure.is_foreign) {
+ Entity *fl = e->Procedure.foreign_library;
+ if (fl != nullptr) {
+ GB_ASSERT_MSG(fl->kind == Entity_LibraryName &&
+ (fl->flags&EntityFlag_Used),
+ "%.*s", LIT(entity->token.string));
+ add_dependency_to_set(c, fl);
+ }
+ }
+ break;
+ case Entity_Variable:
+ if (e->Variable.is_foreign) {
+ Entity *fl = e->Variable.foreign_library;
+ if (fl != nullptr) {
+ GB_ASSERT_MSG(fl->kind == Entity_LibraryName &&
+ (fl->flags&EntityFlag_Used),
+ "%.*s", LIT(entity->token.string));
+ add_dependency_to_set(c, fl);
+ }
+ }
+ break;
+ }
+ }
- for (Entity *e : decl->deps) {
+ FOR_PTR_SET(e, decl->deps) {
add_dependency_to_set(c, e);
- if (e->kind == Entity_Procedure && e->Procedure.is_foreign) {
- Entity *fl = e->Procedure.foreign_library;
- if (fl != nullptr) {
- GB_ASSERT_MSG(fl->kind == Entity_LibraryName &&
- (fl->flags&EntityFlag_Used),
- "%.*s", LIT(entity->token.string));
- add_dependency_to_set(c, fl);
+ }
+
+}
+gb_internal WORKER_TASK_PROC(add_dependency_to_set_worker) {
+ Checker *c = global_checker_ptr.load(std::memory_order_relaxed);
+ Entity *entity = cast(Entity *)data;
+ if (entity == nullptr) {
+ return 0;
+ }
+
+ if (entity->type != nullptr &&
+ is_type_polymorphic(entity->type)) {
+ DeclInfo *decl = decl_info_of_entity(entity);
+ if (decl != nullptr && decl->gen_proc_type == nullptr) {
+ return 0;
+ }
+ }
+
+ if (entity->min_dep_count.fetch_add(1, std::memory_order_relaxed) > 0) {
+ return 0;
+ }
+
+ DeclInfo *decl = decl_info_of_entity(entity);
+ if (decl == nullptr) {
+ return 0;
+ }
+ for (TypeInfoPair const tt : decl->type_info_deps) {
+ add_min_dep_type_info(c, tt.type);
+ }
+
+ FOR_PTR_SET(e, decl->deps) {
+ switch (e->kind) {
+ case Entity_Procedure:
+ if (e->Procedure.is_foreign) {
+ Entity *fl = e->Procedure.foreign_library;
+ if (fl != nullptr) {
+ GB_ASSERT_MSG(fl->kind == Entity_LibraryName &&
+ (fl->flags&EntityFlag_Used),
+ "%.*s", LIT(entity->token.string));
+ add_dependency_to_set_threaded(c, fl);
+ }
}
- } else if (e->kind == Entity_Variable && e->Variable.is_foreign) {
- Entity *fl = e->Variable.foreign_library;
- if (fl != nullptr) {
- GB_ASSERT_MSG(fl->kind == Entity_LibraryName &&
- (fl->flags&EntityFlag_Used),
- "%.*s", LIT(entity->token.string));
- add_dependency_to_set(c, fl);
+ break;
+ case Entity_Variable:
+ if (e->Variable.is_foreign) {
+ Entity *fl = e->Variable.foreign_library;
+ if (fl != nullptr) {
+ GB_ASSERT_MSG(fl->kind == Entity_LibraryName &&
+ (fl->flags&EntityFlag_Used),
+ "%.*s", LIT(entity->token.string));
+ add_dependency_to_set_threaded(c, fl);
+ }
}
+ break;
}
}
+
+ FOR_PTR_SET(e, decl->deps) {
+ add_dependency_to_set_threaded(c, e);
+ }
+
+ return 0;
+}
+
+
+gb_internal void add_dependency_to_set_threaded(Checker *c, Entity *entity) {
+ if (entity == nullptr) {
+ return;
+ }
+ thread_pool_add_task(add_dependency_to_set_worker, entity);
}
+
gb_internal void force_add_dependency_entity(Checker *c, Scope *scope, String const &name) {
Entity *e = scope_lookup(scope, name);
if (e == nullptr) {
@@ -2583,27 +2743,35 @@ gb_internal void collect_testing_procedures_of_package(Checker *c, AstPackage *p
}
gb_internal void generate_minimum_dependency_set_internal(Checker *c, Entity *start) {
+ // auto const &add_to_set = add_dependency_to_set;
+ auto const &add_to_set = add_dependency_to_set_threaded;
+
+ Scope *builtin_scope = builtin_pkg->scope;
for_array(i, c->info.definitions) {
Entity *e = c->info.definitions[i];
- if (e->scope == builtin_pkg->scope) {
+ if (e->scope == builtin_scope) {
if (e->type == nullptr) {
- add_dependency_to_set(c, e);
+ add_to_set(c, e);
+ }
+ } else if (e->kind == Entity_Procedure) {
+ if (e->Procedure.is_export) {
+ add_to_set(c, e);
+ }
+ } else if (e->kind == Entity_Variable) {
+ if (e->Variable.is_export) {
+ add_to_set(c, e);
}
- } else if (e->kind == Entity_Procedure && e->Procedure.is_export) {
- add_dependency_to_set(c, e);
- } else if (e->kind == Entity_Variable && e->Variable.is_export) {
- add_dependency_to_set(c, e);
}
}
for (Entity *e; mpsc_dequeue(&c->info.required_foreign_imports_through_force_queue, &e); /**/) {
array_add(&c->info.required_foreign_imports_through_force, e);
- add_dependency_to_set(c, e);
+ add_to_set(c, e);
}
for (Entity *e; mpsc_dequeue(&c->info.required_global_variable_queue, &e); /**/) {
e->flags |= EntityFlag_Used;
- add_dependency_to_set(c, e);
+ add_to_set(c, e);
}
for_array(i, c->info.entities) {
@@ -2611,16 +2779,16 @@ gb_internal void generate_minimum_dependency_set_internal(Checker *c, Entity *st
switch (e->kind) {
case Entity_Variable:
if (e->Variable.is_export) {
- add_dependency_to_set(c, e);
+ add_to_set(c, e);
} else if (e->flags & EntityFlag_Require) {
- add_dependency_to_set(c, e);
+ add_to_set(c, e);
}
break;
case Entity_Procedure:
if (e->Procedure.is_export) {
- add_dependency_to_set(c, e);
+ add_to_set(c, e);
} else if (e->flags & EntityFlag_Require) {
- add_dependency_to_set(c, e);
+ add_to_set(c, e);
}
if (e->flags & EntityFlag_Init) {
Type *t = base_type(e->type);
@@ -2635,6 +2803,15 @@ gb_internal void generate_minimum_dependency_set_internal(Checker *c, Entity *st
is_init = false;
}
+ u64 feature_flags = check_feature_flags(e);
+ if ((feature_flags & OptInFeatureFlag_GlobalContext) == 0) {
+ if (t->Proc.calling_convention != ProcCC_Contextless) {
+ ERROR_BLOCK();
+ error(e->token, "@(init) procedures must be declared as \"contextless\"");
+ error_line("\tSuggestion: this can be bypassed, for the time being, with '#+feature global-context'");
+ }
+ }
+
if ((e->scope->flags & (ScopeFlag_File|ScopeFlag_Pkg)) == 0) {
error(e->token, "@(init) procedures must be declared at the file scope");
is_init = false;
@@ -2645,8 +2822,13 @@ gb_internal void generate_minimum_dependency_set_internal(Checker *c, Entity *st
is_init = false;
}
+ if (is_blank_ident(e->token)) {
+ error(e->token, "An @(init) procedure must not use a blank identifier as its name");
+ }
+
+
if (is_init) {
- add_dependency_to_set(c, e);
+ add_to_set(c, e);
array_add(&c->info.init_procedures, e);
}
} else if (e->flags & EntityFlag_Fini) {
@@ -2662,13 +2844,26 @@ gb_internal void generate_minimum_dependency_set_internal(Checker *c, Entity *st
is_fini = false;
}
+ u64 feature_flags = check_feature_flags(e);
+ if ((feature_flags & OptInFeatureFlag_GlobalContext) == 0) {
+ if (t->Proc.calling_convention != ProcCC_Contextless) {
+ ERROR_BLOCK();
+ error(e->token, "@(fini) procedures must be declared as \"contextless\"");
+ error_line("\tSuggestion: this can be bypassed, for the time being, with '#+feature global-context'");
+ }
+ }
+
if ((e->scope->flags & (ScopeFlag_File|ScopeFlag_Pkg)) == 0) {
error(e->token, "@(fini) procedures must be declared at the file scope");
is_fini = false;
}
+ if (is_blank_ident(e->token)) {
+ error(e->token, "An @(fini) procedure must not use a blank identifier as its name");
+ }
+
if (is_fini) {
- add_dependency_to_set(c, e);
+ add_to_set(c, e);
array_add(&c->info.fini_procedures, e);
}
}
@@ -2685,7 +2880,7 @@ gb_internal void generate_minimum_dependency_set_internal(Checker *c, Entity *st
Entity *e = entry.value;
if (e != nullptr) {
e->flags |= EntityFlag_Used;
- add_dependency_to_set(c, e);
+ add_to_set(c, e);
}
}
@@ -2700,16 +2895,11 @@ gb_internal void generate_minimum_dependency_set_internal(Checker *c, Entity *st
}
} else if (start != nullptr) {
start->flags |= EntityFlag_Used;
- add_dependency_to_set(c, start);
+ add_to_set(c, start);
}
}
gb_internal void generate_minimum_dependency_set(Checker *c, Entity *start) {
- isize entity_count = c->info.entities.count;
- isize min_dep_set_cap = next_pow2_isize(entity_count*4); // empirically determined factor
-
- ptr_set_init(&c->info.minimum_dependency_set, min_dep_set_cap);
-
#define FORCE_ADD_RUNTIME_ENTITIES(condition, ...) do { \
if (condition) { \
String entities[] = {__VA_ARGS__}; \
@@ -2808,12 +2998,13 @@ gb_internal void generate_minimum_dependency_set(Checker *c, Entity *start) {
generate_minimum_dependency_set_internal(c, start);
+ thread_pool_wait();
+
#undef FORCE_ADD_RUNTIME_ENTITIES
}
-gb_internal bool is_entity_a_dependency(Entity *e) {
- if (e == nullptr) return false;
+gb_internal gb_inline bool is_entity_a_dependency(Entity *e) {
switch (e->kind) {
case Entity_Procedure:
return true;
@@ -2826,83 +3017,112 @@ gb_internal bool is_entity_a_dependency(Entity *e) {
return false;
}
-gb_internal Array<EntityGraphNode *> generate_entity_dependency_graph(CheckerInfo *info, gbAllocator allocator) {
- PtrMap<Entity *, EntityGraphNode *> M = {};
- map_init(&M, info->entities.count);
- defer (map_destroy(&M));
+gb_internal Array<EntityGraphNode *> generate_entity_dependency_graph(CheckerInfo *info, Arena *arena) {
+ PtrMap<Entity *, EntityGraphNode *> M_procs = {};
+ PtrMap<Entity *, EntityGraphNode *> M_vars = {};
+ PtrMap<Entity *, EntityGraphNode *> M_other = {};
+
+ map_init(&M_procs, info->entities.count);
+ defer (map_destroy(&M_procs));
+
+ map_init(&M_vars, info->entities.count);
+ defer (map_destroy(&M_vars));
+
+ map_init(&M_other, info->entities.count);
+ defer (map_destroy(&M_other));
+
for_array(i, info->entities) {
Entity *e = info->entities[i];
- if (is_entity_a_dependency(e)) {
- EntityGraphNode *n = gb_alloc_item(allocator, EntityGraphNode);
- n->entity = e;
- map_set(&M, e, n);
+ if (e == nullptr || !is_entity_a_dependency(e)) {
+ continue;
+ }
+ EntityGraphNode *n = arena_alloc_item<EntityGraphNode>(arena);
+ n->entity = e;
+ switch (e->kind) {
+ case Entity_Procedure: map_set(&M_procs, e, n); break;
+ case Entity_Variable: map_set(&M_vars, e, n); break;
+ default: map_set(&M_other, e, n); break;
}
}
TIME_SECTION("generate_entity_dependency_graph: Calculate edges for graph M - Part 1");
// Calculate edges for graph M
- for (auto const &entry : M) {
+ for (auto const &entry : M_procs) {
EntityGraphNode *n = entry.value;
Entity *e = n->entity;
DeclInfo *decl = decl_info_of_entity(e);
GB_ASSERT(decl != nullptr);
- for (Entity *dep : decl->deps) {
+ FOR_PTR_SET(dep, decl->deps) {
+ GB_ASSERT(dep != nullptr);
if (dep->flags & EntityFlag_Field) {
continue;
}
- GB_ASSERT(dep != nullptr);
- if (is_entity_a_dependency(dep)) {
- EntityGraphNode *m = map_must_get(&M, dep);
- entity_graph_node_set_add(&n->succ, m);
- entity_graph_node_set_add(&m->pred, n);
+ if (!is_entity_a_dependency(dep)) {
+ continue;
+ }
+ EntityGraphNode *m = nullptr;
+
+ switch (dep->kind) {
+ case Entity_Procedure: m = map_must_get(&M_procs, dep); break;
+ case Entity_Variable: m = map_must_get(&M_vars, dep); break;
+ default: m = map_must_get(&M_other, dep); break;
}
+ entity_graph_node_set_add(&n->succ, m);
+ entity_graph_node_set_add(&m->pred, n);
}
}
- TIME_SECTION("generate_entity_dependency_graph: Calculate edges for graph M - Part 2");
- auto G = array_make<EntityGraphNode *>(allocator, 0, M.count);
+ TIME_SECTION("generate_entity_dependency_graph: Calculate edges for graph M - Part 2a (init)");
- for (auto const &m_entry : M) {
- auto *e = m_entry.key;
+ auto G = array_make<EntityGraphNode *>(arena_allocator(arena), 0, M_procs.count + M_vars.count + M_other.count);
+
+ TIME_SECTION("generate_entity_dependency_graph: Calculate edges for graph M - Part 2b (procs)");
+
+ for (auto const &m_entry : M_procs) {
EntityGraphNode *n = m_entry.value;
- if (e->kind == Entity_Procedure) {
- // Connect each pred 'p' of 'n' with each succ 's' and from
- // the procedure node
- for (EntityGraphNode *p : n->pred) {
+ // Connect each pred 'p' of 'n' with each succ 's' and from
+ // the procedure node
+ FOR_PTR_SET(p, n->pred) {
+ // Ignore self-cycles
+ if (p == n) {
+ continue;
+ }
+ // Each succ 's' of 'n' becomes a succ of 'p', and
+ // each pred 'p' of 'n' becomes a pred of 's'
+ FOR_PTR_SET(s, n->succ) {
// Ignore self-cycles
- if (p != n) {
- // Each succ 's' of 'n' becomes a succ of 'p', and
- // each pred 'p' of 'n' becomes a pred of 's'
- for (EntityGraphNode *s : n->succ) {
- // Ignore self-cycles
- if (s != n) {
- if (p->entity->kind == Entity_Procedure &&
- s->entity->kind == Entity_Procedure) {
- // NOTE(bill, 2020-11-15): Only care about variable initialization ordering
- // TODO(bill): This is probably wrong!!!!
- continue;
- }
- // IMPORTANT NOTE/TODO(bill, 2020-11-15): These three calls take the majority of the
- // the time to process
- entity_graph_node_set_add(&p->succ, s);
- entity_graph_node_set_add(&s->pred, p);
- // Remove edge to 'n'
- entity_graph_node_set_remove(&s->pred, n);
- }
- }
-
- // Remove edge to 'n'
- entity_graph_node_set_remove(&p->succ, n);
+ if (s == n) {
+ continue;
+ }
+ if (p->entity->kind == Entity_Procedure &&
+ s->entity->kind == Entity_Procedure) {
+ // NOTE(bill, 2020-11-15): Only care about variable initialization ordering
+ // TODO(bill): This is probably wrong!!!!
+ continue;
}
+ // IMPORTANT NOTE/TODO(bill, 2020-11-15): These three calls take the majority of the
+ // the time to process
+ entity_graph_node_set_add(&p->succ, s);
+ entity_graph_node_set_add(&s->pred, p);
+ // Remove edge to 'n'
+ entity_graph_node_set_remove(&s->pred, n);
}
- } else if (e->kind == Entity_Variable) {
- array_add(&G, n);
+
+ // Remove edge to 'n'
+ entity_graph_node_set_remove(&p->succ, n);
}
}
+ TIME_SECTION("generate_entity_dependency_graph: Calculate edges for graph M - Part 2c (vars)");
+
+ for (auto const &m_entry : M_vars) {
+ EntityGraphNode *n = m_entry.value;
+ array_add(&G, n);
+ }
+
TIME_SECTION("generate_entity_dependency_graph: Dependency Count Checker");
for_array(i, G) {
EntityGraphNode *n = G[i];
@@ -2986,19 +3206,17 @@ gb_internal Type *find_type_in_pkg(CheckerInfo *info, String const &pkg, String
return e->type;
}
-gb_internal CheckerTypePath *new_checker_type_path() {
- gbAllocator a = heap_allocator();
- auto *tp = gb_alloc_item(a, CheckerTypePath);
- array_init(tp, a, 0, 16);
+gb_internal CheckerTypePath *new_checker_type_path(gbAllocator allocator) {
+ auto *tp = gb_alloc_item(allocator, CheckerTypePath);
+ array_init(tp, allocator, 0, 16);
return tp;
}
-gb_internal void destroy_checker_type_path(CheckerTypePath *tp) {
+gb_internal void destroy_checker_type_path(CheckerTypePath *tp, gbAllocator allocator) {
array_free(tp);
- gb_free(heap_allocator(), tp);
+ gb_free(allocator, tp);
}
-
gb_internal void check_type_path_push(CheckerContext *c, Entity *e) {
GB_ASSERT(c->type_path != nullptr);
GB_ASSERT(e != nullptr);
@@ -3057,6 +3275,9 @@ gb_internal void init_core_type_info(Checker *c) {
GB_ASSERT(tis->fields.count == 5);
+ Entity *type_info_string_encoding_kind = find_core_entity(c, str_lit("Type_Info_String_Encoding_Kind"));
+ t_type_info_string_encoding_kind = type_info_string_encoding_kind->type;
+
Entity *type_info_variant = tis->fields[4];
Type *tiv_type = type_info_variant->type;
GB_ASSERT(is_type_union(tiv_type));
@@ -3167,12 +3388,20 @@ gb_internal void init_core_map_type(Checker *c) {
t_raw_map_ptr = alloc_type_pointer(t_raw_map);
}
+gb_internal void init_core_objc_c(Checker *c) {
+ if (build_context.metrics.os == TargetOs_darwin) {
+ t_objc_super = find_core_type(c, str_lit("objc_super"));
+ t_objc_super_ptr = alloc_type_pointer(t_objc_super);
+ }
+}
+
gb_internal void init_preload(Checker *c) {
init_core_type_info(c);
init_mem_allocator(c);
init_core_context(c);
init_core_source_code_location(c);
init_core_map_type(c);
+ init_core_objc_c(c);
}
gb_internal ExactValue check_decl_attribute_value(CheckerContext *c, Ast *value) {
@@ -3653,6 +3882,33 @@ gb_internal DECL_ATTRIBUTE_PROC(proc_decl_attribute) {
}
}
return true;
+ } else if (name == "objc_implement") {
+ ExactValue ev = check_decl_attribute_value(c, value);
+ if (ev.kind == ExactValue_Bool) {
+ ac->objc_is_implementation = ev.value_bool;
+
+ if (!ac->objc_is_implementation) {
+ ac->objc_is_disabled_implement = true;
+ }
+ } else if (ev.kind == ExactValue_Invalid) {
+ ac->objc_is_implementation = true;
+ } else {
+ error(elem, "Expected a boolean value, or no value, for '%.*s'", LIT(name));
+ }
+
+ return true;
+ } else if (name == "objc_selector") {
+ ExactValue ev = check_decl_attribute_value(c, value);
+ if (ev.kind == ExactValue_String) {
+ if (string_is_valid_identifier(ev.value_string)) {
+ ac->objc_selector = ev.value_string;
+ } else {
+ error(elem, "Invalid identifier for '%.*s', got '%.*s'", LIT(name), LIT(ev.value_string));
+ }
+ } else {
+ error(elem, "Expected a string value for '%.*s'", LIT(name));
+ }
+ return true;
} else if (name == "require_target_feature") {
ExactValue ev = check_decl_attribute_value(c, value);
if (ev.kind == ExactValue_String) {
@@ -3702,6 +3958,18 @@ gb_internal DECL_ATTRIBUTE_PROC(proc_decl_attribute) {
}
ac->instrumentation_exit = true;
return true;
+ } else if (name == "no_sanitize_address") {
+ if (value != nullptr) {
+ error(value, "'%.*s' expects no parameter", LIT(name));
+ }
+ ac->no_sanitize_address = true;
+ return true;
+ } else if (name == "no_sanitize_memory") {
+ if (value != nullptr) {
+ error(value, "'%.*s' expects no parameter", LIT(name));
+ }
+ ac->no_sanitize_memory = true;
+ return true;
}
return false;
}
@@ -3892,6 +4160,66 @@ gb_internal DECL_ATTRIBUTE_PROC(type_decl_attribute) {
ac->objc_class = ev.value_string;
}
return true;
+ } else if (name == "objc_implement") {
+ ExactValue ev = check_decl_attribute_value(c, value);
+ if (ev.kind == ExactValue_Bool) {
+ ac->objc_is_implementation = ev.value_bool;
+ } else if (ev.kind == ExactValue_Invalid) {
+ ac->objc_is_implementation = true;
+ } else {
+ error(elem, "Expected a boolean value, or no value, for '%.*s'", LIT(name));
+ }
+ return true;
+ } else if (name == "objc_superclass") {
+ Type *objc_superclass = check_type(c, value);
+
+ if (objc_superclass != nullptr) {
+ ac->objc_superclass = objc_superclass;
+ } else {
+ error(value, "'%.*s' expected a named type", LIT(name));
+ }
+ return true;
+ } else if (name == "objc_ivar") {
+ Type *objc_ivar = check_type(c, value);
+
+ if (objc_ivar != nullptr && objc_ivar->kind == Type_Named) {
+ ac->objc_ivar = objc_ivar;
+ } else {
+ error(value, "'%.*s' expected a named type", LIT(name));
+ }
+ return true;
+ } else if (name == "objc_context_provider") {
+ Operand o = {};
+ check_expr(c, &o, value);
+ Entity *e = entity_of_node(o.expr);
+
+ if (e != nullptr) {
+ if (ac->objc_context_provider != nullptr) {
+ error(elem, "Previous usage of a 'objc_context_provider' attribute");
+ }
+ if (e->kind != Entity_Procedure) {
+ error(elem, "'objc_context_provider' must refer to a procedure");
+ } else {
+ ac->objc_context_provider = e;
+ }
+
+ return true;
+ }
+ } else if (name == "raddbg_type_view") {
+ ExactValue ev = check_decl_attribute_value(c, value);
+ if (ev.kind == ExactValue_Invalid) {
+ ac->raddbg_type_view = true;
+ } else if (ev.kind == ExactValue_String) {
+ ac->raddbg_type_view = true;
+ ac->raddbg_type_view_string = ev.value_string;
+
+ if (ev.value_string.len == 0) {
+ error(elem, "Expected a non-empty string for '%.*s'", LIT(name));
+ }
+ } else {
+ error(elem, "Expected a string or no value for '%.*s'", LIT(name));
+ }
+ return true;
}
return false;
}
@@ -4651,7 +4979,7 @@ 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;
+ in_single_threaded_checker_stage.store(true, std::memory_order_relaxed);
// NOTE(bill): This must be single threaded
// Don't bother trying
@@ -4673,7 +5001,7 @@ gb_internal void check_all_global_entities(Checker *c) {
}
}
- in_single_threaded_checker_stage = false;
+ in_single_threaded_checker_stage.store(false, std::memory_order_relaxed);
}
@@ -4811,14 +5139,14 @@ gb_internal void add_import_dependency_node(Checker *c, Ast *decl, PtrMap<AstPac
}
}
-gb_internal Array<ImportGraphNode *> generate_import_dependency_graph(Checker *c) {
+gb_internal Array<ImportGraphNode *> generate_import_dependency_graph(Checker *c, gbAllocator allocator) {
PtrMap<AstPackage *, ImportGraphNode *> M = {};
map_init(&M, 2*c->parser->packages.count);
defer (map_destroy(&M));
for_array(i, c->parser->packages) {
AstPackage *pkg = c->parser->packages[i];
- ImportGraphNode *n = import_graph_node_create(heap_allocator(), pkg);
+ ImportGraphNode *n = import_graph_node_create(allocator, pkg);
map_set(&M, pkg, n);
}
@@ -4835,7 +5163,7 @@ gb_internal Array<ImportGraphNode *> generate_import_dependency_graph(Checker *c
}
Array<ImportGraphNode *> G = {};
- array_init(&G, heap_allocator(), 0, M.count);
+ array_init(&G, allocator, 0, M.count);
isize i = 0;
for (auto const &entry : M) {
@@ -4854,7 +5182,7 @@ struct ImportPathItem {
Ast * decl;
};
-gb_internal Array<ImportPathItem> find_import_path(Checker *c, AstPackage *start, AstPackage *end, PtrSet<AstPackage *> *visited) {
+gb_internal Array<ImportPathItem> find_import_path(Checker *c, AstPackage *start, AstPackage *end, PtrSet<AstPackage *> *visited, gbAllocator allocator) {
Array<ImportPathItem> empty_path = {};
if (ptr_set_update(visited, start)) {
@@ -4888,11 +5216,11 @@ gb_internal Array<ImportPathItem> find_import_path(Checker *c, AstPackage *start
ImportPathItem item = {pkg, decl};
if (pkg == end) {
- auto path = array_make<ImportPathItem>(heap_allocator());
+ auto path = array_make<ImportPathItem>(allocator);
array_add(&path, item);
return path;
}
- auto next_path = find_import_path(c, pkg, end, visited);
+ auto next_path = find_import_path(c, pkg, end, visited, allocator);
if (next_path.count > 0) {
array_add(&next_path, item);
return next_path;
@@ -4972,9 +5300,10 @@ gb_internal void check_add_import_decl(CheckerContext *ctx, Ast *decl) {
GB_ASSERT(scope->flags&ScopeFlag_Pkg);
- if (ptr_set_update(&parent_scope->imported, scope)) {
- // error(token, "Multiple import of the same file within this scope");
- }
+ ptr_set_add(&parent_scope->imported, scope);
+ // if (ptr_set_update(&parent_scope->imported, scope)) {
+ // // error(token, "Multiple import of the same file within this scope");
+ // }
String import_name = path_to_entity_name(id->import_name.string, id->fullpath, false);
if (is_blank_ident(import_name)) {
@@ -5496,14 +5825,9 @@ gb_internal void check_export_entities(Checker *c) {
}
gb_internal void check_import_entities(Checker *c) {
- Array<ImportGraphNode *> dep_graph = generate_import_dependency_graph(c);
- defer ({
- for_array(i, dep_graph) {
- import_graph_node_destroy(dep_graph[i], heap_allocator());
- }
- array_free(&dep_graph);
- });
+ TEMPORARY_ALLOCATOR_GUARD();
+ Array<ImportGraphNode *> dep_graph = generate_import_dependency_graph(c, temporary_allocator());
TIME_SECTION("check_import_entities - sort packages");
// NOTE(bill): Priority queue
@@ -5513,8 +5837,7 @@ gb_internal void check_import_entities(Checker *c) {
defer (ptr_set_destroy(&emitted));
Array<ImportGraphNode *> package_order = {};
- array_init(&package_order, heap_allocator(), 0, c->parser->packages.count);
- defer (array_free(&package_order));
+ array_init(&package_order, temporary_allocator(), 0, c->parser->packages.count);
while (pq.queue.count > 0) {
ImportGraphNode *n = priority_queue_pop(&pq);
@@ -5522,11 +5845,12 @@ gb_internal void check_import_entities(Checker *c) {
AstPackage *pkg = n->pkg;
if (n->dep_count > 0) {
+ TEMPORARY_ALLOCATOR_GUARD();
+
PtrSet<AstPackage *> visited = {};
defer (ptr_set_destroy(&visited));
- auto path = find_import_path(c, pkg, pkg, &visited);
- defer (array_free(&path));
+ auto path = find_import_path(c, pkg, pkg, &visited, temporary_allocator());
if (path.count > 1) {
ImportPathItem item = path[path.count-1];
@@ -5541,7 +5865,7 @@ gb_internal void check_import_entities(Checker *c) {
}
}
- for (ImportGraphNode *p : n->pred) {
+ FOR_PTR_SET(p, n->pred) {
p->dep_count = gb_max(p->dep_count-1, 0);
priority_queue_fix(&pq, p->index);
}
@@ -5644,9 +5968,9 @@ gb_internal void check_import_entities(Checker *c) {
}
-gb_internal Array<Entity *> find_entity_path(Entity *start, Entity *end, PtrSet<Entity *> *visited = nullptr);
+gb_internal Array<Entity *> find_entity_path(Entity *start, Entity *end, gbAllocator allocator, PtrSet<Entity *> *visited = nullptr);
-gb_internal bool find_entity_path_tuple(Type *tuple, Entity *end, PtrSet<Entity *> *visited, Array<Entity *> *path_) {
+gb_internal bool find_entity_path_tuple(Type *tuple, Entity *end, gbAllocator allocator, PtrSet<Entity *> *visited, Array<Entity *> *path_) {
GB_ASSERT(path_ != nullptr);
if (tuple == nullptr) {
return false;
@@ -5658,14 +5982,15 @@ gb_internal bool find_entity_path_tuple(Type *tuple, Entity *end, PtrSet<Entity
if (var_decl == nullptr) {
continue;
}
- for (Entity *dep : var_decl->deps) {
+
+ FOR_PTR_SET(dep, var_decl->deps) {
if (dep == end) {
- auto path = array_make<Entity *>(heap_allocator());
+ auto path = array_make<Entity *>(allocator);
array_add(&path, dep);
*path_ = path;
return true;
}
- auto next_path = find_entity_path(dep, end, visited);
+ auto next_path = find_entity_path(dep, end, allocator, visited);
if (next_path.count > 0) {
array_add(&next_path, dep);
*path_ = next_path;
@@ -5677,7 +6002,7 @@ gb_internal bool find_entity_path_tuple(Type *tuple, Entity *end, PtrSet<Entity
return false;
}
-gb_internal Array<Entity *> find_entity_path(Entity *start, Entity *end, PtrSet<Entity *> *visited) {
+gb_internal Array<Entity *> find_entity_path(Entity *start, Entity *end, gbAllocator allocator, PtrSet<Entity *> *visited) {
PtrSet<Entity *> visited_ = {};
bool made_visited = false;
if (visited == nullptr) {
@@ -5701,20 +6026,20 @@ gb_internal Array<Entity *> find_entity_path(Entity *start, Entity *end, PtrSet<
GB_ASSERT(t->kind == Type_Proc);
Array<Entity *> path = {};
- if (find_entity_path_tuple(t->Proc.params, end, visited, &path)) {
+ if (find_entity_path_tuple(t->Proc.params, end, allocator, visited, &path)) {
return path;
}
- if (find_entity_path_tuple(t->Proc.results, end, visited, &path)) {
+ if (find_entity_path_tuple(t->Proc.results, end, allocator, visited, &path)) {
return path;
}
} else {
- for (Entity *dep : decl->deps) {
+ FOR_PTR_SET(dep, decl->deps) {
if (dep == end) {
- auto path = array_make<Entity *>(heap_allocator());
+ auto path = array_make<Entity *>(allocator);
array_add(&path, dep);
return path;
}
- auto next_path = find_entity_path(dep, end, visited);
+ auto next_path = find_entity_path(dep, end, allocator, visited);
if (next_path.count > 0) {
array_add(&next_path, dep);
return next_path;
@@ -5730,13 +6055,10 @@ gb_internal void calculate_global_init_order(Checker *c) {
CheckerInfo *info = &c->info;
TIME_SECTION("calculate_global_init_order: generate entity dependency graph");
- Array<EntityGraphNode *> dep_graph = generate_entity_dependency_graph(info, heap_allocator());
- defer ({
- for_array(i, dep_graph) {
- entity_graph_node_destroy(dep_graph[i], heap_allocator());
- }
- array_free(&dep_graph);
- });
+ Arena *temporary_arena = get_arena(ThreadArena_Temporary);
+ ArenaTempGuard temporary_arena_guard(temporary_arena);
+
+ Array<EntityGraphNode *> dep_graph = generate_entity_dependency_graph(info, temporary_arena);
TIME_SECTION("calculate_global_init_order: priority queue create");
// NOTE(bill): Priority queue
@@ -5751,8 +6073,8 @@ gb_internal void calculate_global_init_order(Checker *c) {
Entity *e = n->entity;
if (n->dep_count > 0) {
- auto path = find_entity_path(e, e);
- defer (array_free(&path));
+ TEMPORARY_ALLOCATOR_GUARD();
+ auto path = find_entity_path(e, e, temporary_allocator());
if (path.count > 0) {
Entity *e = path[0];
@@ -5765,7 +6087,7 @@ gb_internal void calculate_global_init_order(Checker *c) {
}
}
- for (EntityGraphNode *p : n->pred) {
+ FOR_PTR_SET(p, n->pred) {
p->dep_count -= 1;
p->dep_count = gb_max(p->dep_count, 0);
priority_queue_fix(&pq, p->index);
@@ -5869,7 +6191,7 @@ gb_internal bool check_proc_info(Checker *c, ProcInfo *pi, UntypedExprInfoMap *u
switch (pi->decl->proc_checked_state.load()) {
case ProcCheckedState_InProgress:
if (e) {
- GB_ASSERT(global_procedure_body_in_worker_queue.load());
+ GB_ASSERT(global_procedure_body_in_worker_queue.load() != 0);
}
return false;
case ProcCheckedState_Checked:
@@ -5958,7 +6280,7 @@ gb_internal bool check_proc_info(Checker *c, ProcInfo *pi, UntypedExprInfoMap *u
add_untyped_expressions(&c->info, ctx.untyped);
rw_mutex_shared_lock(&ctx.decl->deps_mutex);
- for (Entity *dep : ctx.decl->deps) {
+ FOR_PTR_SET(dep, ctx.decl->deps) {
if (dep && dep->kind == Entity_Procedure &&
(dep->flags & EntityFlag_ProcBodyChecked) == 0) {
check_procedure_later_from_entity(c, dep, NULL);
@@ -5987,8 +6309,10 @@ gb_internal void check_unchecked_bodies(Checker *c) {
// use the `procs_to_check` array
global_procedure_body_in_worker_queue = false;
- for (Entity *e : c->info.minimum_dependency_set) {
- check_procedure_later_from_entity(c, e, "check_unchecked_bodies");
+ for (Entity *e : c->info.entities) {
+ if (e->min_dep_count.load(std::memory_order_relaxed) > 0) {
+ check_procedure_later_from_entity(c, e, "check_unchecked_bodies");
+ }
}
if (!global_procedure_body_in_worker_queue) {
@@ -6011,8 +6335,9 @@ gb_internal void check_safety_all_procedures_for_unchecked(Checker *c) {
defer (map_destroy(&untyped));
- for_array(i, c->info.all_procedures) {
- ProcInfo *pi = c->info.all_procedures[i];
+ array_reserve(&c->info.all_procedures, c->info.all_procedures_queue.count.load());
+
+ for (ProcInfo *pi = nullptr; mpsc_dequeue(&c->info.all_procedures_queue, &pi); /**/) {
GB_ASSERT(pi != nullptr);
GB_ASSERT(pi->decl != nullptr);
Entity *e = pi->decl->entity;
@@ -6027,6 +6352,8 @@ gb_internal void check_safety_all_procedures_for_unchecked(Checker *c) {
consume_proc_info(c, pi, &untyped);
}
}
+
+ array_add(&c->info.all_procedures, pi);
}
}
@@ -6121,7 +6448,7 @@ gb_internal WORKER_TASK_PROC(check_proc_info_worker_proc) {
gb_internal void check_init_worker_data(Checker *c) {
u32 thread_count = cast(u32)global_thread_pool.threads.count;
- check_procedure_bodies_worker_data = gb_alloc_array(permanent_allocator(), CheckProcedureBodyWorkerData, thread_count);
+ check_procedure_bodies_worker_data = permanent_alloc_array<CheckProcedureBodyWorkerData>(thread_count);
for (isize i = 0; i < thread_count; i++) {
check_procedure_bodies_worker_data[i].c = c;
@@ -6183,7 +6510,7 @@ gb_internal Type *tuple_to_pointers(Type *ot) {
Type *t = alloc_type_tuple();
- t->Tuple.variables = slice_make<Entity *>(heap_allocator(), ot->Tuple.variables.count);
+ t->Tuple.variables = permanent_slice_make<Entity *>(ot->Tuple.variables.count);
Scope *scope = nullptr;
for_array(i, t->Tuple.variables) {
@@ -6225,6 +6552,12 @@ gb_internal void check_deferred_procedures(Checker *c) {
continue;
}
+ if (dst->flags & EntityFlag_Disabled) {
+ // Prevent procedures that have been disabled from acting as deferrals.
+ src->Procedure.deferred_procedure = {};
+ continue;
+ }
+
GB_ASSERT(is_type_proc(src->type));
GB_ASSERT(is_type_proc(dst->type));
Type *src_params = base_type(src->type)->Proc.params;
@@ -6380,6 +6713,309 @@ gb_internal void check_deferred_procedures(Checker *c) {
}
+gb_internal void handle_raddbg_type_view(Checker *c, RaddbgTypeView const &type_view) {
+ auto const struct_tag_lookup = [](String tag, char const *key_c, String *value_) -> bool {
+ String t = tag;
+ String key = make_string_c(key_c);
+ while (t.len != 0) {
+ isize i = 0;
+ while (i < t.len && t[i] == ' ') { // Skip whitespace
+ i += 1;
+ }
+ t.text += i;
+ t.len -= i;
+ if (t.len == 0) {
+ break;
+ }
+
+ i = 0;
+
+ while (i < t.len) {
+ u8 c = t[i];
+ if (c == ':' || c == '"') {
+ break;
+ } else if ((0 <= c && c < ' ') || (0x7f <= c && c <= 0x9f)) {
+ // break if control character is found
+ break;
+ }
+ i += 1;
+ }
+
+ if (i == 0) {
+ break;
+ }
+ if (i+1 >= t.len) {
+ break;
+ }
+ if (t[i] != ':' || t[i+1] != '"') {
+ break;
+ }
+ String name = {t.text, i};
+ t = {t.text+i+1, t.len-(i+1)};
+
+ i = 1;
+ while (i < t.len && t[i] != '"') { // find closing quote
+ if (t[i] == '\\') {
+ i += 1; // Skip escaped characters
+ }
+ i += 1;
+ }
+ if (i >= t.len) {
+ break;
+ }
+
+ String value = {t.text, i+1};
+ t = {t.text+i+1, t.len-(i+1)};
+
+ if (key == name) {
+ value = {value.text+1, i-1};
+ value = string_trim_whitespace(value);
+ if (value_) *value_ = value;
+ return true;
+ }
+ }
+ return false;
+ };
+
+ auto const parse_int = [](String s, isize *offset_, u64 *result_) -> bool {
+ isize offset = *offset_;
+ isize new_offset = *offset_;
+
+ u64 result = 0;
+
+ while (new_offset < s.len) {
+ u8 c = s[new_offset];
+ if (!('0' <= c && c <= '9')) {
+ break;
+ }
+
+ new_offset += 1;
+ result *= 10;
+ result += u64(c)-'0';
+ }
+
+ *offset_ = new_offset;
+ *result_ = result;
+ return new_offset > offset;
+ };
+
+ Type *type = type_view.type;
+ if (type == nullptr || type == t_invalid) {
+ return;
+ }
+ String view = type_view.view;
+ if (view.len != 0) {
+ array_add(&c->info.raddbg_type_views, RaddbgTypeView{type, view});
+ return;
+ }
+
+ // NOTE(bill): Generate one automatically from the struct field tags if they exist
+ // If it cannot be generated, it'll be ignored/err
+
+ Type *bt = base_type(type);
+ if (is_type_struct(type)) {
+ GB_ASSERT(bt->kind == Type_Struct);
+ if (bt->Struct.tags != nullptr) {
+ bool found_any = false;
+
+ for (isize i = 0; i < bt->Struct.fields.count; i++) {
+ String tag = bt->Struct.tags[i];
+ String value = {};
+ if (struct_tag_lookup(tag, "raddbg", &value)) {
+ found_any = true;
+ } else if (struct_tag_lookup(tag, "fmt", &value)) {
+ found_any = true;
+ }
+ }
+
+ if (!found_any) {
+ return;
+ }
+
+ gbString s = gb_string_make(heap_allocator(), "");
+
+ s = gb_string_appendc(s, "rows($");
+
+ for (isize i = 0; i < bt->Struct.fields.count; i++) {
+ Entity *field = bt->Struct.fields[i];
+ GB_ASSERT(field != nullptr);
+ String name = field->token.string;
+ String tag = bt->Struct.tags[i];
+ String value = {};
+ bool custom_rule = false;
+
+ bool raddbg_seen = false;
+ if (struct_tag_lookup(tag, "raddbg", &value)) {
+ raddbg_seen = true;
+ if (value == "-") {
+ // Ignore this field entirely;
+ continue;
+ }
+ }
+
+ s = gb_string_appendc(s, ", ");
+
+ if (raddbg_seen) {
+ if (value == "") {
+ // ignore
+ } else {
+ s = gb_string_append_length(s, value.text, value.len);
+ custom_rule = true;
+ }
+ } else if (struct_tag_lookup(tag, "fmt", &value)) {
+ if (value == "" || value == "-") {
+ // ignore
+ } else {
+ auto p = string_partition(value, make_string_c(","));
+ String head = p.head;
+ String tail = p.tail;
+
+ isize i = 0;
+
+ for (bool ok = true; ok && i < head.len; i += 1) {
+ switch (head[i]) {
+ case '+':
+ case '-':
+ case ' ':
+ case '#':
+ case '0':
+ break;
+ default:
+ i -= 1;
+ ok = false;
+ break;
+ }
+ }
+
+ u64 prec = 0;
+ u64 width = 0;
+ bool width_ok = parse_int(head, &i, &width);
+ bool prec_ok = false;
+ if (i < head.len && head[i] == '.') {
+ i += 1;
+ prec_ok = parse_int(head, &i, &prec);
+ }
+
+
+ Rune verb = 0;
+ if (i >= head.len || head[i] == ' ') {
+ verb = 'v';
+ } else {
+ utf8_decode(head.text+i, head.len-i, &verb);
+ }
+
+ isize paren_count = 0;
+
+
+ if (width_ok) {
+ s = gb_string_appendc(s, "digits(");
+ custom_rule = true;
+ }
+
+ switch (verb) {
+ case 'b':
+ s = gb_string_appendc(s, "bin(");
+ paren_count += 1;
+ break;
+ case 'd':
+ s = gb_string_appendc(s, "dec(");
+ paren_count += 1;
+ break;
+ case 'x':
+ case 'X':
+ s = gb_string_appendc(s, "hex(");
+ paren_count += 1;
+ break;
+ case 'o':
+ s = gb_string_appendc(s, "oct(");
+ paren_count += 1;
+ break;
+ }
+
+
+ if (tail.len != 0 && tail != "0") {
+ s = gb_string_appendc(s, "array(");
+ s = gb_string_append_length(s, name.text, name.len);
+ if (is_type_slice(field->type) || is_type_dynamic_array(field->type)) {
+ s = gb_string_appendc(s, ".data");
+ }
+ s = gb_string_appendc(s, ", ");
+ s = gb_string_append_length(s, tail.text, tail.len);
+ s = gb_string_appendc(s, ")");
+ custom_rule = true;
+ } else {
+ s = gb_string_append_length(s, name.text, name.len);
+ custom_rule = true;
+ }
+
+
+ for (isize j = 0; j < paren_count; j++) {
+ s = gb_string_appendc(s, ")");
+ custom_rule = true;
+ }
+ if (width_ok) {
+ s = gb_string_append_fmt(s, ", %llu)", cast(unsigned long long)width);
+ }
+ }
+ }
+
+ if (!custom_rule) {
+ s = gb_string_append_length(s, name.text, name.len);
+ }
+ }
+
+ s = gb_string_appendc(s, ")");
+
+ view = make_string((u8 const *)s, gb_string_length(s));
+ }
+ }
+
+ if (view.len == 0) {
+ // Ignore the type, it didn't anything custom
+ return;
+ }
+
+ array_add(&c->info.raddbg_type_views, RaddbgTypeView{type, view});
+}
+
+gb_internal void check_objc_context_provider_procedures(Checker *c) {
+ for (Entity *e = nullptr; mpsc_dequeue(&c->procs_with_objc_context_provider_to_check, &e); /**/) {
+ GB_ASSERT(e->kind == Entity_TypeName);
+
+ Entity *proc_entity = e->TypeName.objc_context_provider;
+ GB_ASSERT(proc_entity->kind == Entity_Procedure);
+
+ auto &proc = proc_entity->type->Proc;
+
+ Type *return_type = proc.result_count != 1 ? t_untyped_nil : base_named_type(proc.results->Tuple.variables[0]->type);
+ if (return_type != t_context) {
+ error(proc_entity->token, "The @(objc_context_provider) procedure must only return a context.");
+ }
+
+ const char *self_param_err = "The @(objc_context_provider) procedure must take as a parameter a single pointer to the @(objc_type) value.";
+ if (proc.param_count != 1) {
+ error(proc_entity->token, self_param_err);
+ }
+
+ Type *self_param = base_type(proc.params->Tuple.variables[0]->type);
+ if (self_param->kind != Type_Pointer) {
+ error(proc_entity->token, self_param_err);
+ }
+
+ Type *self_type = base_named_type(self_param->Pointer.elem);
+ if (!internal_check_is_assignable_to(self_type, e->type) &&
+ !(e->TypeName.objc_ivar && internal_check_is_assignable_to(self_type, e->TypeName.objc_ivar))) {
+ error(proc_entity->token, self_param_err);
+ }
+ if (proc.calling_convention != ProcCC_CDecl && proc.calling_convention != ProcCC_Contextless) {
+ error(e->token, self_param_err);
+ }
+ if (proc.is_polymorphic) {
+ error(e->token, self_param_err);
+ }
+ }
+}
+
gb_internal void check_unique_package_names(Checker *c) {
ERROR_BLOCK();
@@ -6414,6 +7050,19 @@ gb_internal void check_unique_package_names(Checker *c) {
"\tThere is no relation between a package name and the directory that contains it, so they can be completely different\n"
"\tA package name is required for link name prefixing to have a consistent ABI\n");
error_line("%s found at previous location\n", token_pos_to_string(ast_token(prev).pos));
+
+ // NOTE(Jeroen): Check if the conflicting imports are the same case-folded directory
+ // See https://github.com/odin-lang/Odin/issues/5080
+ #if defined(GB_SYSTEM_WINDOWS)
+ String dir_a = pkg->files[0]->directory;
+ String dir_b = (*found)->files[0]->directory;
+
+ if (str_eq_ignore_case(dir_a, dir_b)) {
+ error_line("\tRemember that Windows case-folds paths, and so %.*s and %.*s are the same directory.\n", LIT(dir_a), LIT(dir_b));
+ // Could also perform a FS lookup to check which of the two is the actual directory and suggest it, but this should be enough.
+ }
+ #endif
+
end_error_block();
}
}
@@ -6440,6 +7089,7 @@ gb_internal void check_merge_queues_into_arrays(Checker *c) {
}
check_add_entities_from_queues(c);
check_add_definitions_from_queues(c);
+ thread_pool_wait();
}
gb_internal GB_COMPARE_PROC(init_procedures_cmp) {
@@ -6494,17 +7144,23 @@ gb_internal void check_sort_init_and_fini_procedures(Checker *c) {
}
gb_internal void add_type_info_for_type_definitions(Checker *c) {
- for_array(i, c->info.definitions) {
- Entity *e = c->info.definitions[i];
- if (e->kind == Entity_TypeName && e->type != nullptr) {
+ for (Entity *e : c->info.definitions) {
+ if (e->kind == Entity_TypeName && e->type != nullptr && is_type_typed(e->type)) {
+ #if 0
i64 align = type_align_of(e->type);
- if (align > 0 && ptr_set_exists(&c->info.minimum_dependency_set, e)) {
+ if (align > 0 && e->min_dep_count.load(std::memory_order_relaxed) > 0) {
add_type_info_type(&c->builtin_ctx, e->type);
}
+ #else
+ if (e->min_dep_count.load(std::memory_order_relaxed) > 0) {
+ add_type_info_type(&c->builtin_ctx, e->type);
+ }
+ #endif
}
}
}
+#if 0
gb_internal void check_walk_all_dependencies(DeclInfo *decl) {
if (decl == nullptr) {
return;
@@ -6526,8 +7182,116 @@ gb_internal void check_update_dependency_tree_for_procedures(Checker *c) {
check_walk_all_dependencies(decl);
}
}
+#else
+gb_internal void check_walk_all_dependencies(DeclInfo *decl);
+
+gb_internal WORKER_TASK_PROC(check_walk_all_dependencies_worker_proc) {
+ if (data == nullptr) {
+ return 0;
+ }
+ DeclInfo *decl = cast(DeclInfo *)data;
+
+ for (DeclInfo *child = decl->next_child; child != nullptr; child = child->next_sibling) {
+ thread_pool_add_task(check_walk_all_dependencies_worker_proc, child);
+ check_walk_all_dependencies(child);
+ }
+
+ add_deps_from_child_to_parent(decl);
+ return 0;
+}
+
+gb_internal void check_walk_all_dependencies(DeclInfo *decl) {
+ if (decl != nullptr) {
+ thread_pool_add_task(check_walk_all_dependencies_worker_proc, decl);
+ }
+}
+
+gb_internal void check_update_dependency_tree_for_procedures(Checker *c) {
+ mutex_lock(&c->nested_proc_lits_mutex);
+ for (DeclInfo *decl : c->nested_proc_lits) {
+ check_walk_all_dependencies(decl);
+ }
+ mutex_unlock(&c->nested_proc_lits_mutex);
+ for (Entity *e : c->info.entities) {
+ DeclInfo *decl = e->decl_info;
+ check_walk_all_dependencies(decl);
+ }
+
+ thread_pool_wait();
+}
+#endif
+
+gb_internal WORKER_TASK_PROC(check_scope_usage_file_worker) {
+ Checker *c = global_checker_ptr.load(std::memory_order_relaxed);
+ AstFile *f = cast(AstFile *)data;
+ u64 vet_flags = ast_file_vet_flags(f);
+ check_scope_usage(c, f->scope, vet_flags);
+ return 0;
+}
+
+gb_internal WORKER_TASK_PROC(check_scope_usage_pkg_worker) {
+ Checker *c = global_checker_ptr.load(std::memory_order_relaxed);
+ AstPackage *pkg = cast(AstPackage *)data;
+ check_scope_usage_internal(c, pkg->scope, 0, true);
+ return 0;
+}
+
+
+
+gb_internal void check_all_scope_usages(Checker *c) {
+ for (auto const &entry : c->info.files) {
+ AstFile *f = entry.value;
+ thread_pool_add_task(check_scope_usage_file_worker, f);
+ }
+ for (auto const &entry : c->info.packages) {
+ AstPackage *pkg = entry.value;
+ thread_pool_add_task(check_scope_usage_pkg_worker, pkg);
+ }
+
+ thread_pool_wait();
+}
+
+
+gb_internal void check_for_type_cycles(Checker *c) {
+ // NOTE(bill): Check for illegal cyclic type declarations
+ for_array(i, c->info.definitions) {
+ Entity *e = c->info.definitions[i];
+ if (e->kind != Entity_TypeName) {
+ continue;
+ }
+ if (e->type != nullptr && is_type_typed(e->type)) {
+ if (e->TypeName.is_type_alias) {
+ // Ignore for the time being
+ } else {
+ (void)type_align_of(e->type);
+ }
+ }
+ }
+}
+
+gb_internal void check_for_inline_cycles(Checker *c) {
+ for_array(i, c->info.definitions) {
+ Entity *e = c->info.definitions[i];
+ if (e->kind != Entity_Procedure) {
+ continue;
+ }
+ DeclInfo *decl = e->decl_info;
+ ast_node(pl, ProcLit, decl->proc_lit);
+ if (pl->inlining == ProcInlining_inline) {
+ FOR_PTR_SET(dep, decl->deps) {
+ if (dep == e) {
+ error(e->token, "Cannot inline recursive procedure '%.*s'", LIT(e->token.string));
+ break;
+ }
+ }
+ }
+ }
+}
+
gb_internal void check_parsed_files(Checker *c) {
+ global_checker_ptr.store(c, std::memory_order_relaxed);
+
TIME_SECTION("map full filepaths to scope");
add_type_info_type(&c->builtin_ctx, t_invalid);
@@ -6591,16 +7355,9 @@ gb_internal void check_parsed_files(Checker *c) {
TIME_SECTION("add entities from procedure bodies");
check_merge_queues_into_arrays(c);
- TIME_SECTION("check scope usage");
- for (auto const &entry : c->info.files) {
- AstFile *f = entry.value;
- u64 vet_flags = ast_file_vet_flags(f);
- check_scope_usage(c, f->scope, vet_flags);
- }
- for (auto const &entry : c->info.packages) {
- AstPackage *pkg = entry.value;
- check_scope_usage_internal(c, pkg->scope, 0, true);
- }
+ TIME_SECTION("check all scope usages");
+ check_all_scope_usages(c);
+
TIME_SECTION("add basic type information");
// Add "Basic" type information
@@ -6613,29 +7370,18 @@ gb_internal void check_parsed_files(Checker *c) {
}
check_merge_queues_into_arrays(c);
- TIME_SECTION("check for type cycles and inline cycles");
- // NOTE(bill): Check for illegal cyclic type declarations
- for_array(i, c->info.definitions) {
- Entity *e = c->info.definitions[i];
- if (e->kind == Entity_TypeName && e->type != nullptr) {
- (void)type_align_of(e->type);
- } else if (e->kind == Entity_Procedure) {
- DeclInfo *decl = e->decl_info;
- ast_node(pl, ProcLit, decl->proc_lit);
- if (pl->inlining == ProcInlining_inline) {
- for (Entity *dep : decl->deps) {
- if (dep == e) {
- error(e->token, "Cannot inline recursive procedure '%.*s'", LIT(e->token.string));
- break;
- }
- }
- }
- }
- }
+ TIME_SECTION("check for type cycles");
+ check_for_type_cycles(c);
+
+ TIME_SECTION("check for inline cycles");
+ check_for_inline_cycles(c);
TIME_SECTION("check deferred procedures");
check_deferred_procedures(c);
+ TIME_SECTION("check objc context provider procedures");
+ check_objc_context_provider_procedures(c);
+
TIME_SECTION("calculate global init order");
calculate_global_init_order(c);
@@ -6653,11 +7399,9 @@ gb_internal void check_parsed_files(Checker *c) {
check_unchecked_bodies(c);
TIME_SECTION("check #soa types");
-
check_merge_queues_into_arrays(c);
- thread_pool_wait();
- TIME_SECTION("update minimum dependency set");
+ TIME_SECTION("update minimum dependency set again");
generate_minimum_dependency_set_internal(c, c->info.entry_point);
// NOTE(laytan): has to be ran after generate_minimum_dependency_set,
@@ -6722,7 +7466,6 @@ gb_internal void check_parsed_files(Checker *c) {
TIME_SECTION("add untyped expression values");
- // Add untyped expression values
for (UntypedExprInfo u = {}; mpsc_dequeue(&c->global_untyped_queue, &u); /**/) {
GB_ASSERT(u.expr != nullptr && u.info != nullptr);
if (is_type_typed(u.info->type)) {
@@ -6733,9 +7476,10 @@ gb_internal void check_parsed_files(Checker *c) {
TIME_SECTION("initialize and check for collisions in type info array");
{
+ TEMPORARY_ALLOCATOR_GUARD();
+
Array<TypeInfoPair> type_info_types; // sorted after filled
- array_init(&type_info_types, heap_allocator());
- defer (array_free(&type_info_types));
+ array_init(&type_info_types, temporary_allocator());
for (auto const &tt : c->info.min_dep_type_info_set) {
array_add(&type_info_types, tt);
@@ -6765,7 +7509,7 @@ gb_internal void check_parsed_files(Checker *c) {
if (entry.key != tt.hash) {
continue;
}
- auto const &other = type_info_types[entry.value];
+ auto const &other = c->info.type_info_types_hash_map[entry.value];
if (are_types_identical_unique_tuples(tt.type, other.type)) {
continue;
}
@@ -6798,6 +7542,11 @@ gb_internal void check_parsed_files(Checker *c) {
}
}
+ TIME_SECTION("collate type info stuff");
+ for (RaddbgTypeView type_view; mpsc_dequeue(&c->info.raddbg_type_views_queue, &type_view); /**/) {
+ handle_raddbg_type_view(c, type_view);
+ }
+
TIME_SECTION("type check finish");
}
diff --git a/src/checker.hpp b/src/checker.hpp
index d3b2d7d89..bda7b2746 100644
--- a/src/checker.hpp
+++ b/src/checker.hpp
@@ -139,6 +139,8 @@ struct AttributeContext {
bool entry_point_only : 1;
bool instrumentation_enter : 1;
bool instrumentation_exit : 1;
+ bool no_sanitize_address : 1;
+ bool no_sanitize_memory : 1;
bool rodata : 1;
bool ignore_duplicates : 1;
u32 optimization_mode; // ProcedureOptimizationMode
@@ -148,11 +150,20 @@ struct AttributeContext {
String objc_class;
String objc_name;
- bool objc_is_class_method;
+ String objc_selector;
Type * objc_type;
+ Type * objc_superclass;
+ Type * objc_ivar;
+ Entity *objc_context_provider;
+ bool objc_is_class_method;
+ bool objc_is_implementation; // This struct or proc provides a class/method implementation, not a binding to an existing type.
+ bool objc_is_disabled_implement; // This means the method explicitly set @objc_implement to false so it won't be inferred from the class' attribute.
String require_target_feature; // required by the target micro-architecture
String enable_target_feature; // will be enabled for the procedure only
+
+ bool raddbg_type_view;
+ String raddbg_type_view_string;
};
gb_internal gb_inline AttributeContext make_attribute_context(String link_prefix, String link_suffix) {
@@ -198,7 +209,7 @@ struct DeclInfo {
Scope * scope;
- Entity *entity;
+ std::atomic<Entity *> entity;
Ast * decl_node;
Ast * type_expr;
@@ -207,6 +218,8 @@ struct DeclInfo {
Ast * proc_lit; // Ast_ProcLit
Type * gen_proc_type; // Precalculated
+ Entity * para_poly_original;
+
bool is_using;
bool where_clauses_evaluated;
bool foreign_require_results;
@@ -298,11 +311,12 @@ struct EntityGraphNode;
typedef PtrSet<EntityGraphNode *> EntityGraphNodeSet;
struct EntityGraphNode {
- Entity * entity; // Procedure, Variable, Constant
+ Entity *entity; // Procedure, Variable, Constant
+
EntityGraphNodeSet pred;
EntityGraphNodeSet succ;
- isize index; // Index in array/queue
- isize dep_count;
+ isize index; // Index in array/queue
+ isize dep_count;
};
@@ -365,6 +379,11 @@ struct ObjcMsgData {
Type *proc_type;
};
+struct ObjcMethodData {
+ AttributeContext ac;
+ Entity *proc_entity;
+};
+
enum LoadFileTier {
LoadFileTier_Invalid,
LoadFileTier_Exists,
@@ -414,6 +433,11 @@ struct Defineable {
String pos_str;
};
+struct RaddbgTypeView {
+ Type * type;
+ String view;
+};
+
// CheckerInfo stores all the symbol information for a type-checked program
struct CheckerInfo {
Checker *checker;
@@ -427,9 +451,11 @@ struct CheckerInfo {
AstPackage * init_package;
Scope * init_scope;
Entity * entry_point;
- PtrSet<Entity *> minimum_dependency_set;
- BlockingMutex minimum_dependency_type_info_mutex;
+
+ RwMutex minimum_dependency_type_info_mutex;
PtrMap</*type info hash*/u64, /*min dep index*/isize> min_dep_type_info_index_map;
+
+ RWSpinLock min_dep_type_info_set_mutex;
TypeSet min_dep_type_info_set;
Array<TypeInfoPair> type_info_types_hash_map; // 2 * type_info_types.count
@@ -456,8 +482,6 @@ struct CheckerInfo {
RecursiveMutex lazy_mutex; // Mutex required for lazy type checking of specific files
- BlockingMutex gen_types_mutex;
- PtrMap<Type *, GenTypesData *> gen_types;
// BlockingMutex type_info_mutex; // NOT recursive
// Array<TypeInfoPair> type_info_types;
@@ -474,15 +498,26 @@ struct CheckerInfo {
MPSCQueue<Entity *> foreign_imports_to_check_fullpaths;
MPSCQueue<Entity *> foreign_decls_to_check;
+ MPSCQueue<RaddbgTypeView> raddbg_type_views_queue;
+ Array<RaddbgTypeView> raddbg_type_views;
+
MPSCQueue<Ast *> intrinsics_entry_point_usage;
- BlockingMutex objc_types_mutex;
+ BlockingMutex objc_objc_msgSend_mutex;
PtrMap<Ast *, ObjcMsgData> objc_msgSend_types;
+ BlockingMutex objc_class_name_mutex;
+ StringSet obcj_class_name_set;
+ MPSCQueue<Entity *> objc_class_implementations;
+
+ BlockingMutex objc_method_mutex;
+ PtrMap<Type *, Array<ObjcMethodData>> objc_method_implementations;
+
+
BlockingMutex load_file_mutex;
StringMap<LoadFileCache *> load_file_cache;
- BlockingMutex all_procedures_mutex;
+ MPSCQueue<ProcInfo *> all_procedures_queue;
Array<ProcInfo *> all_procedures;
BlockingMutex instrumentation_mutex;
@@ -531,6 +566,7 @@ struct CheckerContext {
u32 stmt_flags;
bool in_enum_type;
+ bool in_proc_group;
bool collect_delayed_decls;
bool allow_polymorphic_types;
bool disallow_polymorphic_return_types; // NOTE(zen3ger): no poly type decl in return types
@@ -555,6 +591,7 @@ struct Checker {
CheckerContext builtin_ctx;
MPSCQueue<Entity *> procs_with_deferred_to_check;
+ MPSCQueue<Entity *> procs_with_objc_context_provider_to_check;
Array<ProcInfo *> procs_to_check;
BlockingMutex nested_proc_lits_mutex;
@@ -588,12 +625,12 @@ gb_internal Entity *entity_of_node(Ast *expr);
gb_internal Entity *scope_lookup_current(Scope *s, String const &name);
-gb_internal Entity *scope_lookup (Scope *s, String const &name);
-gb_internal void scope_lookup_parent (Scope *s, String const &name, Scope **scope_, Entity **entity_);
+gb_internal Entity *scope_lookup (Scope *s, String const &name, u32 hash=0);
+gb_internal void scope_lookup_parent (Scope *s, String const &name, Scope **scope_, Entity **entity_, u32 hash=0);
gb_internal Entity *scope_insert (Scope *s, Entity *entity);
-gb_internal void add_type_and_value (CheckerContext *c, Ast *expression, AddressingMode mode, Type *type, ExactValue const &value);
+gb_internal void add_type_and_value (CheckerContext *c, Ast *expression, AddressingMode mode, Type *type, ExactValue const &value, bool use_mutex=true);
gb_internal ExprInfo *check_get_expr_info (CheckerContext *c, Ast *expr);
gb_internal void add_untyped (CheckerContext *c, Ast *expression, AddressingMode mode, Type *basic_type, ExactValue const &value);
gb_internal void add_entity_use (CheckerContext *c, Ast *identifier, Entity *entity);
@@ -614,8 +651,8 @@ gb_internal void check_collect_entities(CheckerContext *c, Slice<Ast *> const &n
gb_internal void check_collect_entities_from_when_stmt(CheckerContext *c, AstWhenStmt *ws);
gb_internal void check_delayed_file_import_entity(CheckerContext *c, Ast *decl);
-gb_internal CheckerTypePath *new_checker_type_path();
-gb_internal void destroy_checker_type_path(CheckerTypePath *tp);
+gb_internal CheckerTypePath *new_checker_type_path(gbAllocator allocator);
+gb_internal void destroy_checker_type_path(CheckerTypePath *tp, gbAllocator allocator);
gb_internal void check_type_path_push(CheckerContext *c, Entity *e);
gb_internal Entity *check_type_path_pop (CheckerContext *c);
@@ -629,4 +666,4 @@ gb_internal void add_untyped_expressions(CheckerInfo *cinfo, UntypedExprInfoMap
gb_internal GenTypesData *ensure_polymorphic_record_entity_has_gen_types(CheckerContext *ctx, Type *original_type);
-gb_internal void init_map_internal_types(Type *type); \ No newline at end of file
+gb_internal void init_map_internal_types(Type *type);
diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp
index 40dde8240..c2255a6ba 100644
--- a/src/checker_builtin_procs.hpp
+++ b/src/checker_builtin_procs.hpp
@@ -26,6 +26,7 @@ enum BuiltinProcId {
BuiltinProc_conj,
BuiltinProc_expand_values,
+ BuiltinProc_compress_values,
BuiltinProc_min,
BuiltinProc_max,
@@ -60,6 +61,7 @@ enum BuiltinProcId {
BuiltinProc_trap,
BuiltinProc_debug_trap,
BuiltinProc_read_cycle_counter,
+ BuiltinProc_read_cycle_counter_frequency,
BuiltinProc_count_ones,
BuiltinProc_count_zeros,
@@ -170,8 +172,12 @@ BuiltinProc__simd_begin,
BuiltinProc_simd_extract,
BuiltinProc_simd_replace,
+ BuiltinProc_simd_reduce_add_bisect,
+ BuiltinProc_simd_reduce_mul_bisect,
BuiltinProc_simd_reduce_add_ordered,
BuiltinProc_simd_reduce_mul_ordered,
+ BuiltinProc_simd_reduce_add_pairs,
+ BuiltinProc_simd_reduce_mul_pairs,
BuiltinProc_simd_reduce_min,
BuiltinProc_simd_reduce_max,
BuiltinProc_simd_reduce_and,
@@ -186,6 +192,7 @@ BuiltinProc__simd_begin,
BuiltinProc_simd_shuffle,
BuiltinProc_simd_select,
+ BuiltinProc_simd_runtime_swizzle,
BuiltinProc_simd_ceil,
BuiltinProc_simd_floor,
@@ -205,6 +212,9 @@ BuiltinProc__simd_begin,
BuiltinProc_simd_masked_expand_load,
BuiltinProc_simd_masked_compress_store,
+ BuiltinProc_simd_indices,
+
+
// Platform specific SIMD intrinsics
BuiltinProc_simd_x86__MM_SHUFFLE,
BuiltinProc__simd_end,
@@ -216,6 +226,7 @@ BuiltinProc__simd_end,
BuiltinProc_x86_cpuid,
BuiltinProc_x86_xgetbv,
+
// Constant type tests
BuiltinProc__type_begin,
@@ -227,14 +238,19 @@ BuiltinProc__type_begin,
BuiltinProc_type_convert_variants_to_pointers,
BuiltinProc_type_merge,
+ BuiltinProc_type_integer_to_unsigned,
+ BuiltinProc_type_integer_to_signed,
+
BuiltinProc__type_simple_boolean_begin,
BuiltinProc_type_is_boolean,
+ BuiltinProc_type_is_bit_field,
BuiltinProc_type_is_integer,
BuiltinProc_type_is_rune,
BuiltinProc_type_is_float,
BuiltinProc_type_is_complex,
BuiltinProc_type_is_quaternion,
BuiltinProc_type_is_string,
+ BuiltinProc_type_is_string16,
BuiltinProc_type_is_typeid,
BuiltinProc_type_is_any,
@@ -249,6 +265,7 @@ BuiltinProc__type_simple_boolean_begin,
BuiltinProc_type_is_sliceable,
BuiltinProc_type_is_comparable,
BuiltinProc_type_is_simple_compare, // easily compared using memcmp
+ BuiltinProc_type_is_nearly_simple_compare, // easily compared using memcmp (including floats)
BuiltinProc_type_is_dereferenceable,
BuiltinProc_type_is_valid_map_key,
BuiltinProc_type_is_valid_matrix_elements,
@@ -313,6 +330,8 @@ BuiltinProc__type_simple_boolean_end,
BuiltinProc_type_bit_set_backing_type,
+ BuiltinProc_type_enum_is_contiguous,
+
BuiltinProc_type_equal_proc,
BuiltinProc_type_hasher_proc,
BuiltinProc_type_map_info,
@@ -320,6 +339,8 @@ BuiltinProc__type_simple_boolean_end,
BuiltinProc_type_has_shared_fields,
+ BuiltinProc_type_canonical_name,
+
BuiltinProc__type_end,
BuiltinProc_procedure_of,
@@ -331,6 +352,9 @@ BuiltinProc__type_end,
BuiltinProc_objc_find_class,
BuiltinProc_objc_register_selector,
BuiltinProc_objc_register_class,
+ BuiltinProc_objc_ivar_get,
+ BuiltinProc_objc_block,
+ BuiltinProc_objc_super,
BuiltinProc_constant_utf16_cstring,
@@ -368,6 +392,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT("conj"), 1, false, Expr_Expr, BuiltinProcPkg_builtin},
{STR_LIT("expand_values"), 1, false, Expr_Expr, BuiltinProcPkg_builtin},
+ {STR_LIT("compress_values"), 1, true, Expr_Expr, BuiltinProcPkg_builtin},
{STR_LIT("min"), 1, true, Expr_Expr, BuiltinProcPkg_builtin},
{STR_LIT("max"), 1, true, Expr_Expr, BuiltinProcPkg_builtin},
@@ -404,6 +429,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT("trap"), 0, false, Expr_Expr, BuiltinProcPkg_intrinsics, /*diverging*/true},
{STR_LIT("debug_trap"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics, /*diverging*/false},
{STR_LIT("read_cycle_counter"), 0, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("read_cycle_counter_frequency"), 0, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("count_ones"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("count_zeros"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
@@ -515,8 +541,12 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT("simd_extract"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_replace"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("simd_reduce_add_bisect"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("simd_reduce_mul_bisect"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_reduce_add_ordered"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_reduce_mul_ordered"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("simd_reduce_add_pairs"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("simd_reduce_mul_pairs"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_reduce_min"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_reduce_max"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_reduce_and"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
@@ -532,6 +562,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT("simd_shuffle"), 2, true, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_select"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("simd_runtime_swizzle"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_ceil") , 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_floor"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
@@ -551,6 +582,8 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT("simd_masked_expand_load"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_masked_compress_store"), 3, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
+ {STR_LIT("simd_indices"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+
{STR_LIT("simd_x86__MM_SHUFFLE"), 4, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
@@ -569,6 +602,9 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT("type_convert_variants_to_pointers"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_merge"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("type_integer_to_unsigned"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("type_integer_to_signed"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+
{STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
{STR_LIT("type_is_boolean"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_is_integer"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
@@ -577,6 +613,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT("type_is_complex"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_is_quaternion"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_is_string"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("type_is_string16"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_is_typeid"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_is_any"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
@@ -591,6 +628,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT("type_is_sliceable"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_is_comparable"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_is_simple_compare"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("type_is_nearly_simple_compare"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_is_dereferenceable"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_is_valid_map_key"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_is_valid_matrix_elements"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
@@ -608,6 +646,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT("type_is_enum"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_is_proc"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_is_bit_set"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("type_is_bit_field"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_is_simd_vector"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_is_matrix"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
@@ -654,12 +693,15 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT("type_bit_set_backing_type"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("type_enum_is_contiguous"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics },
+
{STR_LIT("type_equal_proc"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_hasher_proc"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_map_info"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_map_cell_info"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_has_shared_fields"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("type_canonical_name"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
@@ -673,6 +715,9 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT("objc_find_class"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("objc_register_selector"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
{STR_LIT("objc_register_class"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
+ {STR_LIT("objc_ivar_get"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
+ {STR_LIT("objc_block"), 1, true, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("objc_super"), 1, true, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("constant_utf16_cstring"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
diff --git a/src/common.cpp b/src/common.cpp
index ad1e5a851..5b007bf2c 100644
--- a/src/common.cpp
+++ b/src/common.cpp
@@ -80,6 +80,13 @@ gb_internal gb_inline bool is_power_of_two(i64 x) {
return !(x & (x-1));
}
+gb_internal gb_inline bool is_power_of_two_u64(u64 x) {
+ if (x == 0) {
+ return false;
+ }
+ return !(x & (x-1));
+}
+
gb_internal int isize_cmp(isize x, isize y) {
if (x < y) {
return -1;
@@ -350,6 +357,7 @@ gb_global bool global_module_path_set = false;
#include "ptr_map.cpp"
#include "ptr_set.cpp"
#include "string_map.cpp"
+#include "string16_map.cpp"
#include "string_set.cpp"
#include "priority_queue.cpp"
#include "thread_pool.cpp"
@@ -669,7 +677,7 @@ gb_internal gb_inline f64 gb_sqrt(f64 x) {
gb_internal wchar_t **command_line_to_wargv(wchar_t *cmd_line, int *_argc) {
u32 i, j;
- u32 len = cast(u32)string16_len(cmd_line);
+ u32 len = cast(u32)string16_len(cast(u16 *)cmd_line);
i = ((len+2)/2)*gb_size_of(void *) + gb_size_of(void *);
wchar_t **argv = cast(wchar_t **)GlobalAlloc(GMEM_FIXED, i + (len+2)*gb_size_of(wchar_t));
diff --git a/src/common_memory.cpp b/src/common_memory.cpp
index 47b2796a9..addd43687 100644
--- a/src/common_memory.cpp
+++ b/src/common_memory.cpp
@@ -113,6 +113,13 @@ gb_internal void *arena_alloc(Arena *arena, isize min_size, isize alignment) {
return ptr;
}
+
+template <typename T>
+gb_internal T *arena_alloc_item(Arena *arena) {
+ return cast(T *)arena_alloc(arena, gb_size_of(T), gb_align_of(T));
+}
+
+
gb_internal void arena_free_all(Arena *arena) {
while (arena->curr_block != nullptr) {
MemoryBlock *free_block = arena->curr_block;
@@ -346,7 +353,7 @@ gb_internal gbAllocator arena_allocator(Arena *arena) {
gb_internal GB_ALLOCATOR_PROC(arena_allocator_proc) {
void *ptr = nullptr;
Arena *arena = cast(Arena *)allocator_data;
- GB_ASSERT_NOT_NULL(arena);
+ GB_ASSERT(arena != nullptr);
switch (type) {
case gbAllocation_Alloc:
@@ -394,6 +401,48 @@ gb_internal Arena *get_arena(ThreadArenaKind kind) {
}
+template <typename T>
+gb_internal T *permanent_alloc_item() {
+ Arena *arena = get_arena(ThreadArena_Permanent);
+ return arena_alloc_item<T>(arena);
+}
+
+template <typename T>
+gb_internal T *permanent_alloc_array(isize count) {
+ Arena *arena = get_arena(ThreadArena_Permanent);
+ return cast(T *)arena_alloc(arena, gb_size_of(T)*count, gb_align_of(T));
+}
+
+template <typename T>
+gb_internal Slice<T> permanent_slice_make(isize count) {
+ Arena *arena = get_arena(ThreadArena_Permanent);
+ T *data = cast(T *)arena_alloc(arena, gb_size_of(T)*count, gb_align_of(T));
+ return {data, count};
+}
+
+template <typename T>
+gb_internal T *temporary_alloc_item() {
+ Arena *arena = get_arena(ThreadArena_Temporary);
+ return arena_alloc_item<T>(arena);
+}
+
+template <typename T>
+gb_internal T *temporary_alloc_array(isize count) {
+ Arena *arena = get_arena(ThreadArena_Temporary);
+ return cast(T *)arena_alloc(arena, gb_size_of(T)*count, gb_align_of(T));
+}
+
+template <typename T>
+gb_internal Slice<T> temporary_slice_make(isize count) {
+ Arena *arena = get_arena(ThreadArena_Temporary);
+ T *data = cast(T *)arena_alloc(arena, gb_size_of(T)*count, gb_align_of(T));
+ return {data, count};
+}
+
+
+
+
+
gb_internal GB_ALLOCATOR_PROC(thread_arena_allocator_proc) {
void *ptr = nullptr;
@@ -432,15 +481,16 @@ gb_internal gbAllocator permanent_allocator() {
}
gb_internal gbAllocator temporary_allocator() {
- return {thread_arena_allocator_proc, cast(void *)cast(uintptr)ThreadArena_Permanent};
+ // return {thread_arena_allocator_proc, cast(void *)cast(uintptr)ThreadArena_Temporary};
+ return permanent_allocator();
}
#define TEMP_ARENA_GUARD(arena) ArenaTempGuard GB_DEFER_3(_arena_guard_){arena}
-// #define TEMPORARY_ALLOCATOR_GUARD()
-#define TEMPORARY_ALLOCATOR_GUARD() TEMP_ARENA_GUARD(get_arena(ThreadArena_Temporary))
+// #define TEMPORARY_ALLOCATOR_GUARD() TEMP_ARENA_GUARD(get_arena(ThreadArena_Temporary))
+#define TEMPORARY_ALLOCATOR_GUARD()
#define PERMANENT_ALLOCATOR_GUARD()
diff --git a/src/docs_writer.cpp b/src/docs_writer.cpp
index a92ffc7ad..1f2325980 100644
--- a/src/docs_writer.cpp
+++ b/src/docs_writer.cpp
@@ -43,7 +43,7 @@ struct OdinDocWriter {
};
gb_internal OdinDocEntityIndex odin_doc_add_entity(OdinDocWriter *w, Entity *e);
-gb_internal OdinDocTypeIndex odin_doc_type(OdinDocWriter *w, Type *type);
+gb_internal OdinDocTypeIndex odin_doc_type(OdinDocWriter *w, Type *type, bool cache);
template <typename T>
gb_internal void odin_doc_writer_item_tracker_init(OdinDocWriterItemTracker<T> *t, isize size) {
@@ -467,8 +467,8 @@ gb_internal OdinDocArray<OdinDocString> odin_doc_where_clauses(OdinDocWriter *w,
return odin_write_slice(w, clauses.data, clauses.count);
}
-gb_internal OdinDocArray<OdinDocTypeIndex> odin_doc_type_as_slice(OdinDocWriter *w, Type *type) {
- OdinDocTypeIndex index = odin_doc_type(w, type);
+gb_internal OdinDocArray<OdinDocTypeIndex> odin_doc_type_as_slice(OdinDocWriter *w, Type *type, bool cache=true) {
+ OdinDocTypeIndex index = odin_doc_type(w, type, cache);
return odin_write_item_as_slice(w, index);
}
@@ -479,7 +479,7 @@ gb_internal OdinDocArray<OdinDocEntityIndex> odin_doc_add_entity_as_slice(OdinDo
-gb_internal OdinDocTypeIndex odin_doc_type(OdinDocWriter *w, Type *type) {
+gb_internal OdinDocTypeIndex odin_doc_type(OdinDocWriter *w, Type *type, bool cache=true) {
if (type == nullptr) {
return 0;
}
@@ -491,10 +491,13 @@ gb_internal OdinDocTypeIndex odin_doc_type(OdinDocWriter *w, Type *type) {
}
}
- u64 type_hash = type_hash_canonical_type(type);
- OdinDocTypeIndex *found = map_get(&w->type_cache, type_hash);
- if (found) {
- return *found;
+ u64 type_hash = {0};
+ if (cache) {
+ type_hash = type_hash_canonical_type(type);
+ OdinDocTypeIndex *found = map_get(&w->type_cache, type_hash);
+ if (found) {
+ return *found;
+ }
}
@@ -502,7 +505,9 @@ gb_internal OdinDocTypeIndex odin_doc_type(OdinDocWriter *w, Type *type) {
OdinDocType doc_type = {};
OdinDocTypeIndex type_index = 0;
type_index = odin_doc_write_item(w, &w->types, &doc_type, &dst);
- map_set(&w->type_cache, type_hash, type_index);
+ if (cache) {
+ map_set(&w->type_cache, type_hash, type_index);
+ }
switch (type->kind) {
case Type_Basic:
@@ -527,7 +532,10 @@ gb_internal OdinDocTypeIndex odin_doc_type(OdinDocWriter *w, Type *type) {
doc_type.kind = OdinDocType_Generic;
doc_type.name = odin_doc_write_string(w, name);
if (type->Generic.specialized) {
- doc_type.types = odin_doc_type_as_slice(w, type->Generic.specialized);
+ // NOTE(laytan): do not look at the cache for the specialization, it would resolve
+ // to the same entry as the type itself because `default_type` resolves to the
+ // specialization of a generic type.
+ doc_type.types = odin_doc_type_as_slice(w, type->Generic.specialized, cache=false);
}
}
break;
@@ -1163,4 +1171,4 @@ gb_internal void odin_doc_write(CheckerInfo *info, char const *filename) {
gb_internal bool is_in_doc_writer(void) {
return g_in_doc_writer.load();
-} \ No newline at end of file
+}
diff --git a/src/entity.cpp b/src/entity.cpp
index b2148aa7b..2b21fdcac 100644
--- a/src/entity.cpp
+++ b/src/entity.cpp
@@ -164,6 +164,7 @@ struct Entity {
u64 id;
std::atomic<u64> flags;
std::atomic<EntityState> state;
+ std::atomic<i32> min_dep_count;
Token token;
Scope * scope;
Type * type;
@@ -233,8 +234,13 @@ struct Entity {
} Variable;
struct {
Type * type_parameter_specialization;
+ Type * original_type_for_parapoly;
String ir_mangled_name;
bool is_type_alias;
+ bool objc_is_implementation;
+ Type* objc_superclass;
+ Type* objc_ivar;
+ Entity*objc_context_provider;
String objc_class_name;
TypeNameObjCMetadata *objc_metadata;
} TypeName;
@@ -245,6 +251,8 @@ struct Entity {
String link_name;
String link_prefix;
String link_suffix;
+ String objc_selector_name;
+ Entity *objc_class;
DeferredProcedure deferred_procedure;
struct GenProcsData *gen_procs;
@@ -258,6 +266,10 @@ struct Entity {
bool is_memcpy_like : 1;
bool uses_branch_location : 1;
bool is_anonymous : 1;
+ bool no_sanitize_address : 1;
+ bool no_sanitize_memory : 1;
+ bool is_objc_impl_or_import : 1;
+ bool is_objc_class_method : 1;
} Procedure;
struct {
Array<Entity *> entities;
@@ -341,7 +353,7 @@ gb_internal Entity *alloc_entity(EntityKind kind, Scope *scope, Token token, Typ
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);
+ entity->file = thread_unsafe_get_ast_file_from_id(token.pos.file_id);
}
return entity;
}
diff --git a/src/error.cpp b/src/error.cpp
index 1492b00c7..53bc01654 100644
--- a/src/error.cpp
+++ b/src/error.cpp
@@ -86,7 +86,7 @@ gb_internal char *token_pos_to_string(TokenPos const &pos);
gb_internal bool set_file_path_string(i32 index, String const &path) {
bool ok = false;
GB_ASSERT(index >= 0);
- mutex_lock(&global_error_collector.path_mutex);
+ // mutex_lock(&global_error_collector.path_mutex);
mutex_lock(&global_files_mutex);
if (index >= global_file_path_strings.count) {
@@ -99,14 +99,14 @@ gb_internal bool set_file_path_string(i32 index, String const &path) {
}
mutex_unlock(&global_files_mutex);
- mutex_unlock(&global_error_collector.path_mutex);
+ // mutex_unlock(&global_error_collector.path_mutex);
return ok;
}
gb_internal bool thread_safe_set_ast_file_from_id(i32 index, AstFile *file) {
bool ok = false;
GB_ASSERT(index >= 0);
- mutex_lock(&global_error_collector.path_mutex);
+ // mutex_lock(&global_error_collector.path_mutex);
mutex_lock(&global_files_mutex);
if (index >= global_files.count) {
@@ -118,13 +118,13 @@ gb_internal bool thread_safe_set_ast_file_from_id(i32 index, AstFile *file) {
ok = true;
}
mutex_unlock(&global_files_mutex);
- mutex_unlock(&global_error_collector.path_mutex);
+ // mutex_unlock(&global_error_collector.path_mutex);
return ok;
}
gb_internal String get_file_path_string(i32 index) {
GB_ASSERT(index >= 0);
- mutex_lock(&global_error_collector.path_mutex);
+ // mutex_lock(&global_error_collector.path_mutex);
mutex_lock(&global_files_mutex);
String path = {};
@@ -133,13 +133,13 @@ gb_internal String get_file_path_string(i32 index) {
}
mutex_unlock(&global_files_mutex);
- mutex_unlock(&global_error_collector.path_mutex);
+ // mutex_unlock(&global_error_collector.path_mutex);
return path;
}
gb_internal AstFile *thread_safe_get_ast_file_from_id(i32 index) {
GB_ASSERT(index >= 0);
- mutex_lock(&global_error_collector.path_mutex);
+ // mutex_lock(&global_error_collector.path_mutex);
mutex_lock(&global_files_mutex);
AstFile *file = nullptr;
@@ -148,7 +148,18 @@ gb_internal AstFile *thread_safe_get_ast_file_from_id(i32 index) {
}
mutex_unlock(&global_files_mutex);
- mutex_unlock(&global_error_collector.path_mutex);
+ // mutex_unlock(&global_error_collector.path_mutex);
+ return file;
+}
+
+
+// use AFTER PARSER
+gb_internal AstFile *thread_unsafe_get_ast_file_from_id(i32 index) {
+ GB_ASSERT(index >= 0);
+ AstFile *file = nullptr;
+ if (index < global_files.count) {
+ file = global_files[index];
+ }
return file;
}
@@ -823,7 +834,7 @@ gb_internal int error_value_cmp(void const *a, void const *b) {
gb_global String error_article_table[][2] = {
{str_lit("a "), str_lit("bit_set literal")},
{str_lit("a "), str_lit("constant declaration")},
- {str_lit("a "), str_lit("dynamiic array literal")},
+ {str_lit("a "), str_lit("dynamic array literal")},
{str_lit("a "), str_lit("map index")},
{str_lit("a "), str_lit("map literal")},
{str_lit("a "), str_lit("matrix literal")},
diff --git a/src/exact_value.cpp b/src/exact_value.cpp
index ceaed84c1..f2aed84c2 100644
--- a/src/exact_value.cpp
+++ b/src/exact_value.cpp
@@ -29,6 +29,7 @@ enum ExactValueKind {
ExactValue_Compound = 8,
ExactValue_Procedure = 9,
ExactValue_Typeid = 10,
+ ExactValue_String16 = 11,
ExactValue_Count,
};
@@ -46,6 +47,7 @@ struct ExactValue {
Ast * value_compound;
Ast * value_procedure;
Type * value_typeid;
+ String16 value_string16;
};
};
@@ -66,6 +68,9 @@ gb_internal uintptr hash_exact_value(ExactValue v) {
case ExactValue_String:
res = gb_fnv32a(v.value_string.text, v.value_string.len);
break;
+ case ExactValue_String16:
+ res = gb_fnv32a(v.value_string.text, v.value_string.len*gb_size_of(u16));
+ break;
case ExactValue_Integer:
{
u32 key = gb_fnv32a(v.value_integer.dp, gb_size_of(*v.value_integer.dp) * v.value_integer.used);
@@ -118,6 +123,11 @@ gb_internal ExactValue exact_value_string(String string) {
result.value_string = string;
return result;
}
+gb_internal ExactValue exact_value_string16(String16 string) {
+ ExactValue result = {ExactValue_String16};
+ result.value_string16 = string;
+ return result;
+}
gb_internal ExactValue exact_value_i64(i64 i) {
ExactValue result = {ExactValue_Integer};
@@ -656,6 +666,7 @@ gb_internal i32 exact_value_order(ExactValue const &v) {
return 0;
case ExactValue_Bool:
case ExactValue_String:
+ case ExactValue_String16:
return 1;
case ExactValue_Integer:
return 2;
@@ -689,6 +700,7 @@ gb_internal void match_exact_values(ExactValue *x, ExactValue *y) {
case ExactValue_Bool:
case ExactValue_String:
+ case ExactValue_String16:
case ExactValue_Quaternion:
case ExactValue_Pointer:
case ExactValue_Compound:
@@ -891,7 +903,18 @@ gb_internal ExactValue exact_binary_operator_value(TokenKind op, ExactValue x, E
gb_memmove(data, sx.text, sx.len);
gb_memmove(data+sx.len, sy.text, sy.len);
return exact_value_string(make_string(data, len));
- break;
+ }
+ case ExactValue_String16: {
+ if (op != Token_Add) goto error;
+
+ // NOTE(bill): How do you minimize this over allocation?
+ String sx = x.value_string;
+ String sy = y.value_string;
+ isize len = sx.len+sy.len;
+ u16 *data = gb_alloc_array(permanent_allocator(), u16, len);
+ gb_memmove(data, sx.text, sx.len*gb_size_of(u16));
+ gb_memmove(data+sx.len, sy.text, sy.len*gb_size_of(u16));
+ return exact_value_string16(make_string16(data, len));
}
}
@@ -954,6 +977,10 @@ gb_internal bool compare_exact_values(TokenKind op, ExactValue x, ExactValue y)
case ExactValue_Float: {
f64 a = x.value_float;
f64 b = y.value_float;
+ if (isnan(a) || isnan(b)) {
+ return op == Token_NotEq;
+ }
+
switch (op) {
case Token_CmpEq: return cmp_f64(a, b) == 0;
case Token_NotEq: return cmp_f64(a, b) != 0;
@@ -990,6 +1017,19 @@ gb_internal bool compare_exact_values(TokenKind op, ExactValue x, ExactValue y)
}
break;
}
+ case ExactValue_String16: {
+ String16 a = x.value_string16;
+ String16 b = y.value_string16;
+ switch (op) {
+ case Token_CmpEq: return a == b;
+ case Token_NotEq: return a != b;
+ case Token_Lt: return a < b;
+ case Token_LtEq: return a <= b;
+ case Token_Gt: return a > b;
+ case Token_GtEq: return a >= b;
+ }
+ break;
+ }
case ExactValue_Pointer: {
switch (op) {
@@ -1046,6 +1086,20 @@ gb_internal gbString write_exact_value_to_string(gbString str, ExactValue const
gb_free(heap_allocator(), s.text);
return str;
}
+ case ExactValue_String16: {
+ String s = quote_to_ascii(heap_allocator(), v.value_string16);
+ string_limit = gb_max(string_limit, 36);
+ if (s.len <= string_limit) {
+ str = gb_string_append_length(str, s.text, s.len);
+ } else {
+ isize n = string_limit/5;
+ str = gb_string_append_length(str, s.text, n);
+ str = gb_string_append_fmt(str, "\"..%lld chars..\"", s.len-(2*n));
+ str = gb_string_append_length(str, s.text+s.len-n, n);
+ }
+ gb_free(heap_allocator(), s.text);
+ return str;
+ }
case ExactValue_Integer: {
String s = big_int_to_string(heap_allocator(), &v.value_integer);
str = gb_string_append_length(str, s.text, s.len);
diff --git a/src/gb/gb.h b/src/gb/gb.h
index 98c362e93..c52f63cec 100644
--- a/src/gb/gb.h
+++ b/src/gb/gb.h
@@ -497,7 +497,11 @@ typedef i32 b32; // NOTE(bill): Prefer this!!!
#if !defined(gb_no_asan)
#if defined(_MSC_VER)
- #define gb_no_asan __declspec(no_sanitize_address)
+ #if _MSC_VER >= 1930
+ #define gb_no_asan __declspec(no_sanitize_address)
+ #else
+ #define gb_no_asan
+ #endif
#else
#define gb_no_asan __attribute__((disable_sanitizer_instrumentation))
#endif
@@ -710,13 +714,15 @@ extern "C++" {
} while (0)
#endif
+
+#if defined(DISABLE_ASSERT)
+#define GB_ASSERT(cond) gb_unused(cond)
+#endif
+
#ifndef GB_ASSERT
#define GB_ASSERT(cond) GB_ASSERT_MSG(cond, NULL)
#endif
-#ifndef GB_ASSERT_NOT_NULL
-#define GB_ASSERT_NOT_NULL(ptr) GB_ASSERT_MSG((ptr) != NULL, #ptr " must not be NULL")
-#endif
// NOTE(bill): Things that shouldn't happen with a message!
#ifndef GB_PANIC
@@ -3715,7 +3721,7 @@ gb_inline i32 gb_strcmp(char const *s1, char const *s2) {
}
gb_inline char *gb_strcpy(char *dest, char const *source) {
- GB_ASSERT_NOT_NULL(dest);
+ GB_ASSERT(dest != NULL);
if (source) {
char *str = dest;
while (*source) *str++ = *source++;
@@ -3725,7 +3731,7 @@ gb_inline char *gb_strcpy(char *dest, char const *source) {
gb_inline char *gb_strncpy(char *dest, char const *source, isize len) {
- GB_ASSERT_NOT_NULL(dest);
+ GB_ASSERT(dest != NULL);
if (source) {
char *str = dest;
while (len > 0 && *source) {
@@ -3742,7 +3748,7 @@ gb_inline char *gb_strncpy(char *dest, char const *source, isize len) {
gb_inline isize gb_strlcpy(char *dest, char const *source, isize len) {
isize result = 0;
- GB_ASSERT_NOT_NULL(dest);
+ GB_ASSERT(dest != NULL);
if (source) {
char const *source_start = source;
char *str = dest;
@@ -4865,8 +4871,8 @@ u64 gb_murmur64_seed(void const *data_, isize len, u64 seed) {
u64 h = seed ^ (len * m);
u64 const *data = cast(u64 const *)data_;
- u8 const *data2 = cast(u8 const *)data_;
u64 const* end = data + (len / 8);
+ u8 const *data2 = cast(u8 const *)end;
while (data != end) {
u64 k = *data++;
@@ -5632,7 +5638,7 @@ gbFileContents gb_file_read_contents(gbAllocator a, b32 zero_terminate, char con
void gb_file_free_contents(gbFileContents *fc) {
if (fc == NULL || fc->size == 0) return;
- GB_ASSERT_NOT_NULL(fc->data);
+ GB_ASSERT(fc->data != NULL);
gb_free(fc->allocator, fc->data);
fc->data = NULL;
fc->size = 0;
@@ -5644,7 +5650,7 @@ void gb_file_free_contents(gbFileContents *fc) {
gb_inline b32 gb_path_is_absolute(char const *path) {
b32 result = false;
- GB_ASSERT_NOT_NULL(path);
+ GB_ASSERT(path != NULL);
#if defined(GB_SYSTEM_WINDOWS)
result == (gb_strlen(path) > 2) &&
gb_char_is_alpha(path[0]) &&
@@ -5659,7 +5665,7 @@ gb_inline b32 gb_path_is_relative(char const *path) { return !gb_path_is_absolut
gb_inline b32 gb_path_is_root(char const *path) {
b32 result = false;
- GB_ASSERT_NOT_NULL(path);
+ GB_ASSERT(path != NULL);
#if defined(GB_SYSTEM_WINDOWS)
result = gb_path_is_absolute(path) && (gb_strlen(path) == 3);
#else
@@ -5670,14 +5676,14 @@ gb_inline b32 gb_path_is_root(char const *path) {
gb_inline char const *gb_path_base_name(char const *path) {
char const *ls;
- GB_ASSERT_NOT_NULL(path);
+ GB_ASSERT(path != NULL);
ls = gb_char_last_occurence(path, '/');
return (ls == NULL) ? path : ls+1;
}
gb_inline char const *gb_path_extension(char const *path) {
char const *ld;
- GB_ASSERT_NOT_NULL(path);
+ GB_ASSERT(path != NULL);
ld = gb_char_last_occurence(path, '.');
return (ld == NULL) ? NULL : ld+1;
}
@@ -5838,18 +5844,25 @@ gb_inline isize gb_printf_err_va(char const *fmt, va_list va) {
gb_inline isize gb_fprintf_va(struct gbFile *f, char const *fmt, va_list va) {
char buf[4096];
- isize len = gb_snprintf_va(buf, gb_size_of(buf), fmt, va);
+ va_list va_save;
+ va_copy(va_save, va);
+ isize len = gb_snprintf_va(buf, gb_size_of(buf), fmt, va_save);
+ va_end(va_save);
char *new_buf = NULL;
isize n = gb_size_of(buf);
while (len < 0) {
+ va_copy(va_save, va);
+ defer (va_end(va_save));
n <<= 1;
gb_free(gb_heap_allocator(), new_buf);
new_buf = gb_alloc_array(gb_heap_allocator(), char, n);;
- len = gb_snprintf_va(new_buf, n, fmt, va);
+ len = gb_snprintf_va(new_buf, n, fmt, va_save);
}
- gb_file_write(f, buf, len-1); // NOTE(bill): prevent extra whitespace
if (new_buf != NULL) {
+ gb_file_write(f, new_buf, len-1); // NOTE(bill): prevent extra whitespace
gb_free(gb_heap_allocator(), new_buf);
+ } else {
+ gb_file_write(f, buf, len-1); // NOTE(bill): prevent extra whitespace
}
return len;
}
@@ -5912,7 +5925,7 @@ gb_internal isize gb__print_string(char *text, isize max_len, gbprivFmtInfo *inf
len = info->precision < len ? info->precision : len;
}
- res += gb_strlcpy(text, str, len);
+ res += gb_strlcpy(text, str, gb_min(len, remaining));
if (info->width > res) {
isize padding = info->width - len;
@@ -5930,7 +5943,7 @@ gb_internal isize gb__print_string(char *text, isize max_len, gbprivFmtInfo *inf
}
}
- res += gb_strlcpy(text, str, len);
+ res += gb_strlcpy(text, str, gb_min(len, remaining));
}
@@ -6066,15 +6079,16 @@ gb_internal isize gb__print_f64(char *text, isize max_len, gbprivFmtInfo *info,
gb_no_inline isize gb_snprintf_va(char *text, isize max_len, char const *fmt, va_list va) {
char const *text_begin = text;
- isize remaining = max_len, res;
+ isize remaining = max_len - 1, res;
- while (*fmt) {
+ while (*fmt && remaining > 0) {
gbprivFmtInfo info = {0};
isize len = 0;
info.precision = -1;
- while (*fmt && *fmt != '%' && remaining) {
+ while (remaining > 0 && *fmt && *fmt != '%') {
*text++ = *fmt++;
+ remaining--;
}
if (*fmt == '%') {
@@ -6240,7 +6254,7 @@ gb_no_inline isize gb_snprintf_va(char *text, isize max_len, char const *fmt, va
text += len;
if (len >= remaining) {
- remaining = gb_min(remaining, 1);
+ remaining = 0;
} else {
remaining -= len;
}
diff --git a/src/linker.cpp b/src/linker.cpp
index cf2ef638d..f1e0335d5 100644
--- a/src/linker.cpp
+++ b/src/linker.cpp
@@ -7,19 +7,15 @@ struct LinkerData {
Array<String> output_temp_paths;
String output_base;
String output_name;
-#if defined(GB_SYSTEM_OSX)
- b8 needs_system_library_linked;
-#endif
+ bool needs_system_library_linked;
};
gb_internal i32 system_exec_command_line_app(char const *name, char const *fmt, ...);
gb_internal bool system_exec_command_line_app_output(char const *command, gbString *output);
-#if defined(GB_SYSTEM_OSX)
gb_internal void linker_enable_system_library_linking(LinkerData *ld) {
- ld->needs_system_library_linked = 1;
+ ld->needs_system_library_linked = true;
}
-#endif
gb_internal void linker_data_init(LinkerData *ld, CheckerInfo *info, String const &init_fullpath) {
gbAllocator ha = heap_allocator();
@@ -28,9 +24,7 @@ gb_internal void linker_data_init(LinkerData *ld, CheckerInfo *info, String cons
array_init(&ld->foreign_libraries, ha, 0, 1024);
ptr_set_init(&ld->foreign_libraries_set, 1024);
-#if defined(GB_SYSTEM_OSX)
- ld->needs_system_library_linked = 0;
-#endif
+ ld->needs_system_library_linked = false;
if (build_context.out_filepath.len == 0) {
ld->output_name = remove_directory_from_path(init_fullpath);
@@ -136,6 +130,9 @@ gb_internal i32 linker_stage(LinkerData *gen) {
return result;
}
+ bool is_cross_linking = false;
+ bool is_android = false;
+
if (build_context.cross_compiling && selected_target_metrics->metrics == &target_essence_amd64) {
#if defined(GB_SYSTEM_UNIX)
result = system_exec_command_line_app("linker", "x86_64-essence-gcc \"%.*s.o\" -o \"%.*s\" %.*s %.*s",
@@ -146,32 +143,50 @@ gb_internal i32 linker_stage(LinkerData *gen) {
LIT(target_arch_names[build_context.metrics.arch])
);
#endif
- } else if (build_context.cross_compiling && build_context.different_os) {
- gb_printf_err("Linking for cross compilation for this platform is not yet supported (%.*s %.*s)\n",
- LIT(target_os_names[build_context.metrics.os]),
- LIT(target_arch_names[build_context.metrics.arch])
- );
- build_context.keep_object_files = true;
+ } else if (build_context.cross_compiling && (build_context.different_os || selected_subtarget != Subtarget_Default)) {
+ switch (selected_subtarget) {
+ case Subtarget_Android:
+ is_cross_linking = true;
+ is_android = true;
+ goto try_cross_linking;
+ default:
+ gb_printf_err("Linking for cross compilation for this platform is not yet supported (%.*s %.*s)\n",
+ LIT(target_os_names[build_context.metrics.os]),
+ LIT(target_arch_names[build_context.metrics.arch])
+ );
+ build_context.keep_object_files = true;
+ break;
+ }
} else {
+try_cross_linking:;
+
#if defined(GB_SYSTEM_WINDOWS)
- bool is_windows = true;
+ String section_name = str_lit("msvc-link");
+ bool is_windows = build_context.metrics.os == TargetOs_windows;
#else
+ String section_name = str_lit("lld-link");
bool is_windows = false;
#endif
- #if defined(GB_SYSTEM_OSX)
- bool is_osx = true;
- #else
- bool is_osx = false;
+
+ bool is_osx = build_context.metrics.os == TargetOs_darwin;
+
+
+ switch (build_context.linker_choice) {
+ case Linker_Default: break;
+ case Linker_lld: section_name = str_lit("lld-link"); break;
+ #if defined(GB_SYSTEM_LINUX)
+ case Linker_mold: section_name = str_lit("mold-link"); break;
#endif
+ #if defined(GB_SYSTEM_WINDOWS)
+ case Linker_radlink: section_name = str_lit("rad-link"); break;
+ #endif
+ default:
+ gb_printf_err("'%.*s' linker is not support for this platform\n", LIT(linker_choices[build_context.linker_choice]));
+ return 1;
+ }
if (is_windows) {
- String section_name = str_lit("msvc-link");
- switch (build_context.linker_choice) {
- case Linker_Default: break;
- case Linker_lld: section_name = str_lit("lld-link"); break;
- case Linker_radlink: section_name = str_lit("rad-link"); break;
- }
timings_start_section(timings, section_name);
gbString lib_str = gb_string_make(heap_allocator(), "");
@@ -273,13 +288,20 @@ gb_internal i32 linker_stage(LinkerData *gen) {
if (build_context.build_mode == BuildMode_DynamicLibrary) {
link_settings = gb_string_append_fmt(link_settings, " /DLL");
+ if (build_context.no_entry_point) {
+ link_settings = gb_string_append_fmt(link_settings, " /NOENTRY");
+ }
} else {
- link_settings = gb_string_append_fmt(link_settings, " /ENTRY:mainCRTStartup");
+ // For i386 with CRT, libcmt provides the entry point
+ // For other cases or no_crt, we need to specify the entry point
+ if (!(build_context.metrics.arch == TargetArch_i386 && !build_context.no_crt)) {
+ link_settings = gb_string_append_fmt(link_settings, " /ENTRY:mainCRTStartup");
+ }
}
- if (build_context.pdb_filepath != "") {
- String pdb_path = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_PDB]);
- link_settings = gb_string_append_fmt(link_settings, " /PDB:\"%.*s\"", LIT(pdb_path));
+ if (build_context.build_paths[BuildPath_Symbols].name != "") {
+ String symbol_path = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_Symbols]);
+ link_settings = gb_string_append_fmt(link_settings, " /PDB:\"%.*s\"", LIT(symbol_path));
}
if (build_context.build_mode != BuildMode_StaticLibrary) {
@@ -317,7 +339,7 @@ gb_internal i32 linker_stage(LinkerData *gen) {
"",
LIT(build_context.ODIN_ROOT), object_files, LIT(output_filename),
link_settings,
- LIT(build_context.ODIN_WINDOWS_SUBSYSTEM),
+ LIT(windows_subsystem_names[build_context.ODIN_WINDOWS_SUBSYSTEM]),
LIT(build_context.link_flags),
LIT(build_context.extra_linker_flags),
lib_str
@@ -337,7 +359,7 @@ gb_internal i32 linker_stage(LinkerData *gen) {
"",
LIT(build_context.ODIN_ROOT), object_files, LIT(output_filename),
link_settings,
- LIT(build_context.ODIN_WINDOWS_SUBSYSTEM),
+ LIT(windows_subsystem_names[build_context.ODIN_WINDOWS_SUBSYSTEM]),
LIT(build_context.link_flags),
LIT(build_context.extra_linker_flags),
lib_str
@@ -400,7 +422,7 @@ gb_internal i32 linker_stage(LinkerData *gen) {
"",
LIT(vs_exe_path), LIT(linker_name), object_files, LIT(res_path), LIT(output_filename),
link_settings,
- LIT(build_context.ODIN_WINDOWS_SUBSYSTEM),
+ LIT(windows_subsystem_names[build_context.ODIN_WINDOWS_SUBSYSTEM]),
LIT(build_context.link_flags),
LIT(build_context.extra_linker_flags),
lib_str
@@ -412,25 +434,32 @@ gb_internal i32 linker_stage(LinkerData *gen) {
}
}
} else {
- timings_start_section(timings, str_lit("ld-link"));
+
+ timings_start_section(timings, section_name);
+
+ int const ODIN_ANDROID_API_LEVEL = build_context.ODIN_ANDROID_API_LEVEL;
+
+ String ODIN_ANDROID_NDK = build_context.ODIN_ANDROID_NDK;
+ String ODIN_ANDROID_NDK_TOOLCHAIN = build_context.ODIN_ANDROID_NDK_TOOLCHAIN;
+ String ODIN_ANDROID_NDK_TOOLCHAIN_LIB = build_context.ODIN_ANDROID_NDK_TOOLCHAIN_LIB;
+ String ODIN_ANDROID_NDK_TOOLCHAIN_LIB_LEVEL = build_context.ODIN_ANDROID_NDK_TOOLCHAIN_LIB_LEVEL;
+ String ODIN_ANDROID_NDK_TOOLCHAIN_SYSROOT = build_context.ODIN_ANDROID_NDK_TOOLCHAIN_SYSROOT;
// Link using `clang`, unless overridden by `ODIN_CLANG_PATH` environment variable.
const char* clang_path = gb_get_env("ODIN_CLANG_PATH", permanent_allocator());
+ bool has_odin_clang_path_env = true;
if (clang_path == NULL) {
clang_path = "clang";
+ has_odin_clang_path_env = false;
}
- // NOTE(vassvik): get cwd, for used for local shared libs linking, since those have to be relative to the exe
- char cwd[256];
- #if !defined(GB_SYSTEM_WINDOWS)
- getcwd(&cwd[0], 256);
- #endif
- //printf("%s\n", cwd);
-
// NOTE(vassvik): needs to add the root to the library search paths, so that the full filenames of the library
// files can be passed with -l:
- gbString lib_str = gb_string_make(heap_allocator(), "-L/");
+ gbString lib_str = gb_string_make(heap_allocator(), "");
defer (gb_string_free(lib_str));
+ #if !defined(GB_SYSTEM_WINDOWS)
+ lib_str = gb_string_appendc(lib_str, "-L/ ");
+ #endif
StringSet asm_files = {};
string_set_init(&asm_files, 64);
@@ -496,19 +525,20 @@ gb_internal i32 linker_stage(LinkerData *gen) {
}
String obj_format;
- #if defined(GB_ARCH_64_BIT)
- if (is_osx) {
- obj_format = str_lit("macho64");
- } else {
- obj_format = str_lit("elf64");
- }
- #elif defined(GB_ARCH_32_BIT)
- if (is_osx) {
- obj_format = str_lit("macho32");
+ if (build_context.metrics.ptr_size == 8) {
+ if (is_osx) {
+ obj_format = str_lit("macho64");
+ } else {
+ obj_format = str_lit("elf64");
+ }
} else {
- obj_format = str_lit("elf32");
+ GB_ASSERT(build_context.metrics.ptr_size == 4);
+ if (is_osx) {
+ obj_format = str_lit("macho32");
+ } else {
+ obj_format = str_lit("elf32");
+ }
}
- #endif // GB_ARCH_*_BIT
if (build_context.metrics.arch == TargetArch_riscv64) {
result = system_exec_command_line_app("clang",
@@ -579,7 +609,7 @@ gb_internal i32 linker_stage(LinkerData *gen) {
// Do not add libc again, this is added later already, and omitted with
// the `-no-crt` flag, not skipping here would cause duplicate library
// warnings when linking on darwin and might link libc silently even with `-no-crt`.
- if (lib == str_lit("System.framework") || lib == str_lit("c")) {
+ if (lib == str_lit("System.framework") || lib == str_lit("System") || lib == str_lit("c")) {
continue;
}
@@ -594,7 +624,7 @@ gb_internal i32 linker_stage(LinkerData *gen) {
// object
// dynamic lib
// static libs, absolute full path relative to the file in which the lib was imported from
- lib_str = gb_string_append_fmt(lib_str, " %.*s ", LIT(lib));
+ lib_str = gb_string_append_fmt(lib_str, " \"%.*s\" ", LIT(lib));
} else {
// dynamic or static system lib, just link regularly searching system library paths
lib_str = gb_string_append_fmt(lib_str, " -l%.*s ", LIT(lib));
@@ -618,8 +648,86 @@ gb_internal i32 linker_stage(LinkerData *gen) {
gbString object_files = gb_string_make(heap_allocator(), "");
defer (gb_string_free(object_files));
+
+
+ if (is_android) { // NOTE(bill): glue code needed for Android
+ TIME_SECTION("Android Native App Glue Compile");
+
+ String android_glue_object = {};
+ String android_glue_static_lib = {};
+
+ char hash_buf[64] = {};
+ gb_snprintf(hash_buf, gb_size_of(hash_buf), "%p", &hash_buf);
+ String hash = make_string_c(hash_buf);
+
+ String temp_dir = normalize_path(temporary_allocator(), temporary_directory(temporary_allocator()), NIX_SEPARATOR_STRING);
+ android_glue_object = concatenate4_strings(temporary_allocator(), temp_dir, str_lit("android_native_app_glue-"), hash, str_lit(".o"));
+ android_glue_static_lib = concatenate4_strings(permanent_allocator(), temp_dir, str_lit("libandroid_native_app_glue-"), hash, str_lit(".a"));
+
+ gbString glue = gb_string_make_length(heap_allocator(), ODIN_ANDROID_NDK_TOOLCHAIN.text, ODIN_ANDROID_NDK_TOOLCHAIN.len);
+ defer (gb_string_free(glue));
+
+ glue = gb_string_append_fmt(glue, "bin/clang");
+ glue = gb_string_append_fmt(glue, " --target=aarch64-linux-android%d ", ODIN_ANDROID_API_LEVEL);
+ glue = gb_string_appendc(glue, "-c \"");
+ glue = gb_string_append_length(glue, ODIN_ANDROID_NDK.text, ODIN_ANDROID_NDK.len);
+ glue = gb_string_appendc(glue, "sources/android/native_app_glue/android_native_app_glue.c");
+ glue = gb_string_appendc(glue, "\" ");
+ glue = gb_string_appendc(glue, "-o \"");
+ glue = gb_string_append_length(glue, android_glue_object.text, android_glue_object.len);
+ glue = gb_string_appendc(glue, "\" ");
+
+ glue = gb_string_appendc(glue, "--sysroot \"");
+ glue = gb_string_append_length(glue, ODIN_ANDROID_NDK_TOOLCHAIN.text, ODIN_ANDROID_NDK_TOOLCHAIN.len);
+ glue = gb_string_appendc(glue, "sysroot");
+ glue = gb_string_appendc(glue, "\" ");
+
+ glue = gb_string_appendc(glue, "\"-I");
+ glue = gb_string_append_length(glue, ODIN_ANDROID_NDK_TOOLCHAIN.text, ODIN_ANDROID_NDK_TOOLCHAIN.len);
+ glue = gb_string_appendc(glue, "sysroot/usr/include/");
+ glue = gb_string_appendc(glue, "\" ");
+
+ glue = gb_string_appendc(glue, "\"-I");
+ glue = gb_string_append_length(glue, ODIN_ANDROID_NDK_TOOLCHAIN.text, ODIN_ANDROID_NDK_TOOLCHAIN.len);
+ glue = gb_string_appendc(glue, "sysroot/usr/include/aarch64-linux-android/");
+ glue = gb_string_appendc(glue, "\" ");
+
+
+ glue = gb_string_appendc(glue, "-Wno-macro-redefined ");
+
+ result = system_exec_command_line_app("android-native-app-glue-compile", glue);
+ if (result) {
+ return result;
+ }
+
+ TIME_SECTION("Android Native App Glue ar");
+
+ gbString ar = gb_string_make_length(heap_allocator(), ODIN_ANDROID_NDK_TOOLCHAIN.text, ODIN_ANDROID_NDK_TOOLCHAIN.len);
+ defer (gb_string_free(ar));
+
+ ar = gb_string_appendc(ar, "bin/llvm-ar");
+
+ ar = gb_string_appendc(ar, " rcs ");
+
+ ar = gb_string_appendc(ar, "\"");
+ ar = gb_string_append_length(ar, android_glue_static_lib.text, android_glue_static_lib.len);
+ ar = gb_string_appendc(ar, "\" ");
+
+ ar = gb_string_appendc(ar, "\"");
+ ar = gb_string_append_length(ar, android_glue_object.text, android_glue_object.len);
+ ar = gb_string_appendc(ar, "\" ");
+
+ result = system_exec_command_line_app("android-native-app-glue-ar", ar);
+ if (result) {
+ return result;
+ }
+
+ object_files = gb_string_append_fmt(object_files, "\'%.*s\' ", LIT(android_glue_static_lib));
+ }
+
+
for (String object_path : gen->output_object_paths) {
- object_files = gb_string_append_fmt(object_files, "\"%.*s\" ", LIT(object_path));
+ object_files = gb_string_append_fmt(object_files, "\'%.*s\' ", LIT(object_path));
}
gbString link_settings = gb_string_make_reserve(heap_allocator(), 32);
@@ -661,7 +769,9 @@ gb_internal i32 linker_stage(LinkerData *gen) {
link_settings = gb_string_appendc(link_settings, "-Wl,-init,'_odin_entry_point' ");
link_settings = gb_string_appendc(link_settings, "-Wl,-fini,'_odin_exit_point' ");
}
-
+ } else if (is_android) {
+ // Always shared even in android!
+ link_settings = gb_string_appendc(link_settings, "-shared ");
}
if (build_context.build_mode == BuildMode_Executable && build_context.reloc_mode == RelocMode_PIC) {
@@ -670,6 +780,7 @@ gb_internal i32 linker_stage(LinkerData *gen) {
if (build_context.metrics.os != TargetOs_openbsd
&& build_context.metrics.os != TargetOs_haiku
&& build_context.metrics.arch != TargetArch_riscv64
+ && !is_android
) {
// OpenBSD and Haiku default to PIE executable. do not pass -no-pie for it.
link_settings = gb_string_appendc(link_settings, "-no-pie ");
@@ -679,7 +790,65 @@ gb_internal i32 linker_stage(LinkerData *gen) {
gbString platform_lib_str = gb_string_make(heap_allocator(), "");
defer (gb_string_free(platform_lib_str));
if (build_context.metrics.os == TargetOs_darwin) {
- platform_lib_str = gb_string_appendc(platform_lib_str, "-Wl,-syslibroot /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk -L/usr/local/lib ");
+ // Get the SDK path.
+ gbString darwin_sdk_path = gb_string_make(temporary_allocator(), "");
+
+ char const* darwin_platform_name = "MacOSX";
+ char const* darwin_xcrun_sdk_name = "macosx";
+ char const* darwin_min_version_id = "macosx";
+
+ const char* original_clang_path = clang_path;
+
+ // NOTE(harold): We set the clang_path to run through xcrun because otherwise it complaints about the the sysroot
+ // being set to 'MacOSX' even though we've set the sysroot to the correct SDK (-Wincompatible-sysroot).
+ // This is because it is likely not using the SDK's toolchain Apple Clang but another one installed in the system.
+ switch (selected_subtarget) {
+ case Subtarget_iPhone:
+ darwin_platform_name = "iPhoneOS";
+ darwin_xcrun_sdk_name = "iphoneos";
+ darwin_min_version_id = "ios";
+ if (!has_odin_clang_path_env) {
+ clang_path = "xcrun --sdk iphoneos clang";
+ }
+ break;
+ case Subtarget_iPhoneSimulator:
+ darwin_platform_name = "iPhoneSimulator";
+ darwin_xcrun_sdk_name = "iphonesimulator";
+ darwin_min_version_id = "ios-simulator";
+ if (!has_odin_clang_path_env) {
+ clang_path = "xcrun --sdk iphonesimulator clang";
+ }
+ break;
+ }
+
+ gbString darwin_find_sdk_cmd = gb_string_make(temporary_allocator(), "");
+ darwin_find_sdk_cmd = gb_string_append_fmt(darwin_find_sdk_cmd, "xcrun --sdk %s --show-sdk-path", darwin_xcrun_sdk_name);
+
+ if (!system_exec_command_line_app_output(darwin_find_sdk_cmd, &darwin_sdk_path)) {
+
+ // Fallback to default clang, since `xcrun --sdk` did not work.
+ clang_path = original_clang_path;
+
+ // Best-effort fallback to known locations
+ gbString darwin_sdk_path = gb_string_make(temporary_allocator(), "");
+ darwin_sdk_path = gb_string_append_fmt(darwin_sdk_path, "/Library/Developer/CommandLineTools/SDKs/%s.sdk", darwin_platform_name);
+
+ if (!path_is_directory(make_string_c(darwin_sdk_path))) {
+ gb_string_clear(darwin_sdk_path);
+ darwin_sdk_path = gb_string_append_fmt(darwin_sdk_path, "/Applications/Xcode.app/Contents/Developer/Platforms/%s.platform/Developer/SDKs/%s.sdk", darwin_platform_name);
+
+ if (!path_is_directory(make_string_c(darwin_sdk_path))) {
+ gb_printf_err("Failed to find %s SDK\n", darwin_platform_name);
+ return -1;
+ }
+ }
+ } else {
+ // Trim the trailing newline.
+ darwin_sdk_path = gb_string_trim_space(darwin_sdk_path);
+ }
+ platform_lib_str = gb_string_append_fmt(platform_lib_str, "--sysroot %s ", darwin_sdk_path);
+
+ platform_lib_str = gb_string_appendc(platform_lib_str, "-L/usr/local/lib ");
// Homebrew's default library path, checking if it exists to avoid linking warnings.
if (gb_file_exists("/opt/homebrew/lib")) {
@@ -693,14 +862,57 @@ gb_internal i32 linker_stage(LinkerData *gen) {
// Only specify this flag if the user has given a minimum version to target.
// This will cause warnings to show up for mismatched libraries.
- if (build_context.minimum_os_version_string_given) {
- link_settings = gb_string_append_fmt(link_settings, "-mmacosx-version-min=%.*s ", LIT(build_context.minimum_os_version_string));
+ // NOTE(harold): For device subtargets we have to explicitly set the default version to
+ // avoid the same warning since we configure our own minimum version when compiling for devices.
+ if (build_context.minimum_os_version_string_given || selected_subtarget != Subtarget_Default) {
+ link_settings = gb_string_append_fmt(link_settings, "-m%s-version-min=%.*s ", darwin_min_version_id, LIT(build_context.minimum_os_version_string));
}
if (build_context.build_mode != BuildMode_DynamicLibrary) {
// This points the linker to where the entry point is
link_settings = gb_string_appendc(link_settings, "-e _main ");
}
+ } else if (build_context.metrics.os == TargetOs_freebsd) {
+ if (build_context.sanitizer_flags & (SanitizerFlag_Address | SanitizerFlag_Memory)) {
+ // It's imperative that `pthread` is linked before `libc`,
+ // otherwise ASan/MSan will be unable to call `pthread_key_create`
+ // because FreeBSD's `libthr` implementation of `pthread`
+ // needs to replace the relevant stubs first.
+ //
+ // (Presumably TSan implements its own `pthread` interface,
+ // which is why it isn't required.)
+ //
+ // See: https://reviews.llvm.org/D39254
+ platform_lib_str = gb_string_appendc(platform_lib_str, "-lpthread ");
+ }
+ // FreeBSD pkg installs third-party shared libraries in /usr/local/lib.
+ platform_lib_str = gb_string_appendc(platform_lib_str, "-Wl,-L/usr/local/lib ");
+ } else if (build_context.metrics.os == TargetOs_openbsd) {
+ // OpenBSD ports install shared libraries in /usr/local/lib. Also, we must explicitly link libpthread.
+ platform_lib_str = gb_string_appendc(platform_lib_str, "-lpthread -Wl,-L/usr/local/lib ");
+ // Until the LLVM back-end can be adapted to emit endbr64 instructions on amd64, we
+ // need to pass -z nobtcfi in order to allow the resulting program to run under
+ // OpenBSD 7.4 and newer. Once support is added at compile time, this can be dropped.
+ platform_lib_str = gb_string_appendc(platform_lib_str, "-Wl,-z,nobtcfi ");
+ }
+
+ if (is_android) {
+ GB_ASSERT(ODIN_ANDROID_NDK_TOOLCHAIN_LIB.len != 0);
+ GB_ASSERT(ODIN_ANDROID_NDK_TOOLCHAIN_LIB_LEVEL.len != 0);
+ GB_ASSERT(ODIN_ANDROID_NDK_TOOLCHAIN_SYSROOT.len != 0);
+
+ platform_lib_str = gb_string_appendc(platform_lib_str, "\"-L");
+ platform_lib_str = gb_string_append_length(platform_lib_str, ODIN_ANDROID_NDK_TOOLCHAIN_LIB_LEVEL.text, ODIN_ANDROID_NDK_TOOLCHAIN_LIB_LEVEL.len);
+ platform_lib_str = gb_string_appendc(platform_lib_str, "\" ");
+
+ platform_lib_str = gb_string_appendc(platform_lib_str, "-landroid ");
+ platform_lib_str = gb_string_appendc(platform_lib_str, "-llog ");
+
+ platform_lib_str = gb_string_appendc(platform_lib_str, "\"--sysroot=");
+ platform_lib_str = gb_string_append_length(platform_lib_str, ODIN_ANDROID_NDK_TOOLCHAIN_SYSROOT.text, ODIN_ANDROID_NDK_TOOLCHAIN_SYSROOT.len);
+ platform_lib_str = gb_string_appendc(platform_lib_str, "\" ");
+
+ link_settings = gb_string_appendc(link_settings, "-u ANativeActivity_onCreate ");
}
if (!build_context.no_rpath) {
@@ -709,24 +921,36 @@ gb_internal i32 linker_stage(LinkerData *gen) {
if (build_context.metrics.os == TargetOs_darwin) {
link_settings = gb_string_appendc(link_settings, "-Wl,-rpath,@loader_path ");
} else {
- link_settings = gb_string_appendc(link_settings, "-Wl,-rpath,\\$ORIGIN ");
+ if (is_android) {
+ // ignore
+ } else {
+ link_settings = gb_string_appendc(link_settings, "-Wl,-rpath,\\$ORIGIN ");
+ }
}
}
if (!build_context.no_crt) {
- platform_lib_str = gb_string_appendc(platform_lib_str, "-lm ");
+ lib_str = gb_string_appendc(lib_str, "-lm ");
if (build_context.metrics.os == TargetOs_darwin) {
// NOTE: adding this causes a warning about duplicate libraries, I think it is
// automatically assumed/added by clang when you don't do `-nostdlib`.
- // platform_lib_str = gb_string_appendc(platform_lib_str, "-lSystem ");
+ // lib_str = gb_string_appendc(lib_str, "-lSystem ");
} else {
- platform_lib_str = gb_string_appendc(platform_lib_str, "-lc ");
+ lib_str = gb_string_appendc(lib_str, "-lc ");
}
}
- gbString link_command_line = gb_string_make(heap_allocator(), clang_path);
+ gbString link_command_line = gb_string_make(heap_allocator(), "");
defer (gb_string_free(link_command_line));
+ if (is_android) {
+ gbString ndk_bin_directory = gb_string_make_length(temporary_allocator(), ODIN_ANDROID_NDK_TOOLCHAIN.text, ODIN_ANDROID_NDK_TOOLCHAIN.len);
+ link_command_line = gb_string_appendc(link_command_line, ndk_bin_directory);
+ link_command_line = gb_string_appendc(link_command_line, "bin/clang");
+ link_command_line = gb_string_append_fmt(link_command_line, " --target=aarch64-linux-android%d ", ODIN_ANDROID_API_LEVEL);
+ } else {
+ link_command_line = gb_string_appendc(link_command_line, clang_path);
+ }
link_command_line = gb_string_appendc(link_command_line, " -Wno-unused-command-line-argument ");
link_command_line = gb_string_appendc(link_command_line, object_files);
link_command_line = gb_string_append_fmt(link_command_line, " -o \"%.*s\" ", LIT(output_filename));
@@ -736,9 +960,17 @@ gb_internal i32 linker_stage(LinkerData *gen) {
link_command_line = gb_string_append_fmt(link_command_line, " %.*s ", LIT(build_context.extra_linker_flags));
link_command_line = gb_string_append_fmt(link_command_line, " %s ", link_settings);
+
+ if (is_android) {
+ TIME_SECTION("Linking");
+ }
+
if (build_context.linker_choice == Linker_lld) {
link_command_line = gb_string_append_fmt(link_command_line, " -fuse-ld=lld");
result = system_exec_command_line_app("lld-link", link_command_line);
+ } else if (build_context.linker_choice == Linker_mold) {
+ link_command_line = gb_string_append_fmt(link_command_line, " -fuse-ld=mold");
+ result = system_exec_command_line_app("mold-link", link_command_line);
} else {
result = system_exec_command_line_app("ld-link", link_command_line);
}
diff --git a/src/llvm-c/Analysis.h b/src/llvm-c/Analysis.h
index 6b93b5c3d..270b145a4 100644
--- a/src/llvm-c/Analysis.h
+++ b/src/llvm-c/Analysis.h
@@ -19,8 +19,8 @@
#ifndef LLVM_C_ANALYSIS_H
#define LLVM_C_ANALYSIS_H
-#include "ExternC.h"
-#include "Types.h"
+#include "llvm-c/ExternC.h"
+#include "llvm-c/Types.h"
LLVM_C_EXTERN_C_BEGIN
diff --git a/src/llvm-c/BitReader.h b/src/llvm-c/BitReader.h
index 725f3fa84..088107468 100644
--- a/src/llvm-c/BitReader.h
+++ b/src/llvm-c/BitReader.h
@@ -19,8 +19,8 @@
#ifndef LLVM_C_BITREADER_H
#define LLVM_C_BITREADER_H
-#include "ExternC.h"
-#include "Types.h"
+#include "llvm-c/ExternC.h"
+#include "llvm-c/Types.h"
LLVM_C_EXTERN_C_BEGIN
diff --git a/src/llvm-c/BitWriter.h b/src/llvm-c/BitWriter.h
index ba4a61afc..ea84b6593 100644
--- a/src/llvm-c/BitWriter.h
+++ b/src/llvm-c/BitWriter.h
@@ -19,8 +19,8 @@
#ifndef LLVM_C_BITWRITER_H
#define LLVM_C_BITWRITER_H
-#include "ExternC.h"
-#include "Types.h"
+#include "llvm-c/ExternC.h"
+#include "llvm-c/Types.h"
LLVM_C_EXTERN_C_BEGIN
diff --git a/src/llvm-c/Comdat.h b/src/llvm-c/Comdat.h
index 30df20799..8002bc058 100644
--- a/src/llvm-c/Comdat.h
+++ b/src/llvm-c/Comdat.h
@@ -14,8 +14,8 @@
#ifndef LLVM_C_COMDAT_H
#define LLVM_C_COMDAT_H
-#include "ExternC.h"
-#include "Types.h"
+#include "llvm-c/ExternC.h"
+#include "llvm-c/Types.h"
LLVM_C_EXTERN_C_BEGIN
diff --git a/src/llvm-c/Config/llvm-config.h b/src/llvm-c/Config/llvm-config.h
index e4edb83c5..8b77597c9 100644
--- a/src/llvm-c/Config/llvm-config.h
+++ b/src/llvm-c/Config/llvm-config.h
@@ -142,16 +142,16 @@
#define LLVM_USE_PERF 0
/* Major version of the LLVM API */
-#define LLVM_VERSION_MAJOR 18
+#define LLVM_VERSION_MAJOR 20
/* Minor version of the LLVM API */
#define LLVM_VERSION_MINOR 1
/* Patch version of the LLVM API */
-#define LLVM_VERSION_PATCH 8
+#define LLVM_VERSION_PATCH 0
/* LLVM version string */
-#define LLVM_VERSION_STRING "18.1.8"
+#define LLVM_VERSION_STRING "20.1.0"
/* Whether LLVM records statistics for use with GetStatistics(),
* PrintStatistics() or PrintStatisticsJSON()
diff --git a/src/llvm-c/Core.h b/src/llvm-c/Core.h
index 25b8248fd..dc8ecf4fb 100644
--- a/src/llvm-c/Core.h
+++ b/src/llvm-c/Core.h
@@ -15,11 +15,11 @@
#ifndef LLVM_C_CORE_H
#define LLVM_C_CORE_H
-#include "Deprecated.h"
-#include "ErrorHandling.h"
-#include "ExternC.h"
+#include "llvm-c/Deprecated.h"
+#include "llvm-c/ErrorHandling.h"
+#include "llvm-c/ExternC.h"
-#include "Types.h"
+#include "llvm-c/Types.h"
LLVM_C_EXTERN_C_BEGIN
@@ -146,27 +146,27 @@ typedef enum {
} LLVMOpcode;
typedef enum {
- LLVMVoidTypeKind, /**< type with no size */
- LLVMHalfTypeKind, /**< 16 bit floating point type */
- LLVMFloatTypeKind, /**< 32 bit floating point type */
- LLVMDoubleTypeKind, /**< 64 bit floating point type */
- LLVMX86_FP80TypeKind, /**< 80 bit floating point type (X87) */
- LLVMFP128TypeKind, /**< 128 bit floating point type (112-bit mantissa)*/
- LLVMPPC_FP128TypeKind, /**< 128 bit floating point type (two 64-bits) */
- LLVMLabelTypeKind, /**< Labels */
- LLVMIntegerTypeKind, /**< Arbitrary bit width integers */
- LLVMFunctionTypeKind, /**< Functions */
- LLVMStructTypeKind, /**< Structures */
- LLVMArrayTypeKind, /**< Arrays */
- LLVMPointerTypeKind, /**< Pointers */
- LLVMVectorTypeKind, /**< Fixed width SIMD vector type */
- LLVMMetadataTypeKind, /**< Metadata */
- LLVMX86_MMXTypeKind, /**< X86 MMX */
- LLVMTokenTypeKind, /**< Tokens */
- LLVMScalableVectorTypeKind, /**< Scalable SIMD vector type */
- LLVMBFloatTypeKind, /**< 16 bit brain floating point type */
- LLVMX86_AMXTypeKind, /**< X86 AMX */
- LLVMTargetExtTypeKind, /**< Target extension type */
+ LLVMVoidTypeKind = 0, /**< type with no size */
+ LLVMHalfTypeKind = 1, /**< 16 bit floating point type */
+ LLVMFloatTypeKind = 2, /**< 32 bit floating point type */
+ LLVMDoubleTypeKind = 3, /**< 64 bit floating point type */
+ LLVMX86_FP80TypeKind = 4, /**< 80 bit floating point type (X87) */
+ LLVMFP128TypeKind = 5, /**< 128 bit floating point type (112-bit mantissa)*/
+ LLVMPPC_FP128TypeKind = 6, /**< 128 bit floating point type (two 64-bits) */
+ LLVMLabelTypeKind = 7, /**< Labels */
+ LLVMIntegerTypeKind = 8, /**< Arbitrary bit width integers */
+ LLVMFunctionTypeKind = 9, /**< Functions */
+ LLVMStructTypeKind = 10, /**< Structures */
+ LLVMArrayTypeKind = 11, /**< Arrays */
+ LLVMPointerTypeKind = 12, /**< Pointers */
+ LLVMVectorTypeKind = 13, /**< Fixed width SIMD vector type */
+ LLVMMetadataTypeKind = 14, /**< Metadata */
+ /* 15 previously used by LLVMX86_MMXTypeKind */
+ LLVMTokenTypeKind = 16, /**< Tokens */
+ LLVMScalableVectorTypeKind = 17, /**< Scalable SIMD vector type */
+ LLVMBFloatTypeKind = 18, /**< 16 bit brain floating point type */
+ LLVMX86_AMXTypeKind = 19, /**< X86 AMX */
+ LLVMTargetExtTypeKind = 20, /**< Target extension type */
} LLVMTypeKind;
typedef enum {
@@ -286,6 +286,7 @@ typedef enum {
LLVMInstructionValueKind,
LLVMPoisonValueValueKind,
LLVMConstantTargetNoneValueKind,
+ LLVMConstantPtrAuthValueKind,
} LLVMValueKind;
typedef enum {
@@ -361,35 +362,42 @@ typedef enum {
} LLVMAtomicOrdering;
typedef enum {
- LLVMAtomicRMWBinOpXchg, /**< Set the new value and return the one old */
- LLVMAtomicRMWBinOpAdd, /**< Add a value and return the old one */
- LLVMAtomicRMWBinOpSub, /**< Subtract a value and return the old one */
- LLVMAtomicRMWBinOpAnd, /**< And a value and return the old one */
- LLVMAtomicRMWBinOpNand, /**< Not-And a value and return the old one */
- LLVMAtomicRMWBinOpOr, /**< OR a value and return the old one */
- LLVMAtomicRMWBinOpXor, /**< Xor a value and return the old one */
- LLVMAtomicRMWBinOpMax, /**< Sets the value if it's greater than the
- original using a signed comparison and return
- the old one */
- LLVMAtomicRMWBinOpMin, /**< Sets the value if it's Smaller than the
- original using a signed comparison and return
- the old one */
- LLVMAtomicRMWBinOpUMax, /**< Sets the value if it's greater than the
- original using an unsigned comparison and return
- the old one */
- LLVMAtomicRMWBinOpUMin, /**< Sets the value if it's greater than the
- original using an unsigned comparison and return
- the old one */
- LLVMAtomicRMWBinOpFAdd, /**< Add a floating point value and return the
- old one */
- LLVMAtomicRMWBinOpFSub, /**< Subtract a floating point value and return the
+ LLVMAtomicRMWBinOpXchg, /**< Set the new value and return the one old */
+ LLVMAtomicRMWBinOpAdd, /**< Add a value and return the old one */
+ LLVMAtomicRMWBinOpSub, /**< Subtract a value and return the old one */
+ LLVMAtomicRMWBinOpAnd, /**< And a value and return the old one */
+ LLVMAtomicRMWBinOpNand, /**< Not-And a value and return the old one */
+ LLVMAtomicRMWBinOpOr, /**< OR a value and return the old one */
+ LLVMAtomicRMWBinOpXor, /**< Xor a value and return the old one */
+ LLVMAtomicRMWBinOpMax, /**< Sets the value if it's greater than the
+ original using a signed comparison and return
+ the old one */
+ LLVMAtomicRMWBinOpMin, /**< Sets the value if it's Smaller than the
+ original using a signed comparison and return
+ the old one */
+ LLVMAtomicRMWBinOpUMax, /**< Sets the value if it's greater than the
+ original using an unsigned comparison and return
+ the old one */
+ LLVMAtomicRMWBinOpUMin, /**< Sets the value if it's greater than the
+ original using an unsigned comparison and return
+ the old one */
+ LLVMAtomicRMWBinOpFAdd, /**< Add a floating point value and return the
old one */
- LLVMAtomicRMWBinOpFMax, /**< Sets the value if it's greater than the
- original using an floating point comparison and
- return the old one */
- LLVMAtomicRMWBinOpFMin, /**< Sets the value if it's smaller than the
- original using an floating point comparison and
- return the old one */
+ LLVMAtomicRMWBinOpFSub, /**< Subtract a floating point value and return the
+ old one */
+ LLVMAtomicRMWBinOpFMax, /**< Sets the value if it's greater than the
+ original using an floating point comparison and
+ return the old one */
+ LLVMAtomicRMWBinOpFMin, /**< Sets the value if it's smaller than the
+ original using an floating point comparison and
+ return the old one */
+ LLVMAtomicRMWBinOpUIncWrap, /**< Increments the value, wrapping back to zero
+ when incremented above input value */
+ LLVMAtomicRMWBinOpUDecWrap, /**< Decrements the value, wrapping back to
+ the input value when decremented below zero */
+ LLVMAtomicRMWBinOpUSubCond, /**<Subtracts the value only if no unsigned
+ overflow */
+ LLVMAtomicRMWBinOpUSubSat, /**<Subtracts the value, clamping to zero */
} LLVMAtomicRMWBinOp;
typedef enum {
@@ -467,6 +475,8 @@ enum {
LLVMAttributeFunctionIndex = -1,
};
+typedef unsigned LLVMAttributeIndex;
+
/**
* Tail call kind for LLVMSetTailCallKind and LLVMGetTailCallKind.
*
@@ -481,8 +491,6 @@ typedef enum {
LLVMTailCallKindNoTail = 3,
} LLVMTailCallKind;
-typedef unsigned LLVMAttributeIndex;
-
enum {
LLVMFastMathAllowReassoc = (1 << 0),
LLVMFastMathNoNaNs = (1 << 1),
@@ -506,6 +514,20 @@ enum {
*/
typedef unsigned LLVMFastMathFlags;
+enum {
+ LLVMGEPFlagInBounds = (1 << 0),
+ LLVMGEPFlagNUSW = (1 << 1),
+ LLVMGEPFlagNUW = (1 << 2),
+};
+
+/**
+ * Flags that constrain the allowed wrap semantics of a getelementptr
+ * instruction.
+ *
+ * See https://llvm.org/docs/LangRef.html#getelementptr-instruction
+ */
+typedef unsigned LLVMGEPNoWrapFlags;
+
/**
* @}
*/
@@ -628,6 +650,11 @@ unsigned LLVMGetMDKindIDInContext(LLVMContextRef C, const char *Name,
unsigned LLVMGetMDKindID(const char *Name, unsigned SLen);
/**
+ * Maps a synchronization scope name to a ID unique within this context.
+ */
+unsigned LLVMGetSyncScopeID(LLVMContextRef C, const char *Name, size_t SLen);
+
+/**
* Return an unique id given the name of a enum attribute,
* or 0 if no attribute by that name exists.
*
@@ -670,6 +697,18 @@ LLVMAttributeRef LLVMCreateTypeAttribute(LLVMContextRef C, unsigned KindID,
LLVMTypeRef LLVMGetTypeAttributeValue(LLVMAttributeRef A);
/**
+ * Create a ConstantRange attribute.
+ *
+ * LowerWords and UpperWords need to be NumBits divided by 64 rounded up
+ * elements long.
+ */
+LLVMAttributeRef LLVMCreateConstantRangeAttribute(LLVMContextRef C,
+ unsigned KindID,
+ unsigned NumBits,
+ const uint64_t LowerWords[],
+ const uint64_t UpperWords[]);
+
+/**
* Create a string attribute.
*/
LLVMAttributeRef LLVMCreateStringAttribute(LLVMContextRef C,
@@ -745,6 +784,24 @@ LLVMModuleRef LLVMCloneModule(LLVMModuleRef M);
void LLVMDisposeModule(LLVMModuleRef M);
/**
+ * Soon to be deprecated.
+ * See https://llvm.org/docs/RemoveDIsDebugInfo.html#c-api-changes
+ *
+ * Returns true if the module is in the new debug info mode which uses
+ * non-instruction debug records instead of debug intrinsics for variable
+ * location tracking.
+ */
+LLVMBool LLVMIsNewDbgInfoFormat(LLVMModuleRef M);
+
+/**
+ * Soon to be deprecated.
+ * See https://llvm.org/docs/RemoveDIsDebugInfo.html#c-api-changes
+ *
+ * Convert module into desired debug info format.
+ */
+void LLVMSetIsNewDbgInfoFormat(LLVMModuleRef M, LLVMBool UseNewFormat);
+
+/**
* Obtain the identifier of a module.
*
* @param M Module to obtain identifier of
@@ -1131,6 +1188,16 @@ LLVMValueRef LLVMAddFunction(LLVMModuleRef M, const char *Name,
LLVMValueRef LLVMGetNamedFunction(LLVMModuleRef M, const char *Name);
/**
+ * Obtain a Function value from a Module by its name.
+ *
+ * The returned value corresponds to a llvm::Function value.
+ *
+ * @see llvm::Module::getFunction()
+ */
+LLVMValueRef LLVMGetNamedFunctionWithLength(LLVMModuleRef M, const char *Name,
+ size_t Length);
+
+/**
* Obtain an iterator to the first Function in a Module.
*
* @see llvm::Module::begin()
@@ -1619,6 +1686,35 @@ LLVMTypeRef LLVMScalableVectorType(LLVMTypeRef ElementType,
unsigned LLVMGetVectorSize(LLVMTypeRef VectorTy);
/**
+ * Get the pointer value for the associated ConstantPtrAuth constant.
+ *
+ * @see llvm::ConstantPtrAuth::getPointer
+ */
+LLVMValueRef LLVMGetConstantPtrAuthPointer(LLVMValueRef PtrAuth);
+
+/**
+ * Get the key value for the associated ConstantPtrAuth constant.
+ *
+ * @see llvm::ConstantPtrAuth::getKey
+ */
+LLVMValueRef LLVMGetConstantPtrAuthKey(LLVMValueRef PtrAuth);
+
+/**
+ * Get the discriminator value for the associated ConstantPtrAuth constant.
+ *
+ * @see llvm::ConstantPtrAuth::getDiscriminator
+ */
+LLVMValueRef LLVMGetConstantPtrAuthDiscriminator(LLVMValueRef PtrAuth);
+
+/**
+ * Get the address discriminator value for the associated ConstantPtrAuth
+ * constant.
+ *
+ * @see llvm::ConstantPtrAuth::getAddrDiscriminator
+ */
+LLVMValueRef LLVMGetConstantPtrAuthAddrDiscriminator(LLVMValueRef PtrAuth);
+
+/**
* @}
*/
@@ -1639,11 +1735,6 @@ LLVMTypeRef LLVMVoidTypeInContext(LLVMContextRef C);
LLVMTypeRef LLVMLabelTypeInContext(LLVMContextRef C);
/**
- * Create a X86 MMX type in a context.
- */
-LLVMTypeRef LLVMX86MMXTypeInContext(LLVMContextRef C);
-
-/**
* Create a X86 AMX type in a context.
*/
LLVMTypeRef LLVMX86AMXTypeInContext(LLVMContextRef C);
@@ -1664,7 +1755,6 @@ LLVMTypeRef LLVMMetadataTypeInContext(LLVMContextRef C);
*/
LLVMTypeRef LLVMVoidType(void);
LLVMTypeRef LLVMLabelType(void);
-LLVMTypeRef LLVMX86MMXType(void);
LLVMTypeRef LLVMX86AMXType(void);
/**
@@ -1677,6 +1767,42 @@ LLVMTypeRef LLVMTargetExtTypeInContext(LLVMContextRef C, const char *Name,
unsigned IntParamCount);
/**
+ * Obtain the name for this target extension type.
+ *
+ * @see llvm::TargetExtType::getName()
+ */
+const char *LLVMGetTargetExtTypeName(LLVMTypeRef TargetExtTy);
+
+/**
+ * Obtain the number of type parameters for this target extension type.
+ *
+ * @see llvm::TargetExtType::getNumTypeParameters()
+ */
+unsigned LLVMGetTargetExtTypeNumTypeParams(LLVMTypeRef TargetExtTy);
+
+/**
+ * Get the type parameter at the given index for the target extension type.
+ *
+ * @see llvm::TargetExtType::getTypeParameter()
+ */
+LLVMTypeRef LLVMGetTargetExtTypeTypeParam(LLVMTypeRef TargetExtTy,
+ unsigned Idx);
+
+/**
+ * Obtain the number of int parameters for this target extension type.
+ *
+ * @see llvm::TargetExtType::getNumIntParameters()
+ */
+unsigned LLVMGetTargetExtTypeNumIntParams(LLVMTypeRef TargetExtTy);
+
+/**
+ * Get the int parameter at the given index for the target extension type.
+ *
+ * @see llvm::TargetExtType::getIntParameter()
+ */
+unsigned LLVMGetTargetExtTypeIntParam(LLVMTypeRef TargetExtTy, unsigned Idx);
+
+/**
* @}
*/
@@ -1705,6 +1831,10 @@ LLVMTypeRef LLVMTargetExtTypeInContext(LLVMContextRef C, const char *Name,
* @{
*/
+// Currently, clang-format tries to format the LLVM_FOR_EACH_VALUE_SUBCLASS
+// macro in a progressively-indented fashion, which is not desired
+// clang-format off
+
#define LLVM_FOR_EACH_VALUE_SUBCLASS(macro) \
macro(Argument) \
macro(BasicBlock) \
@@ -1724,6 +1854,7 @@ LLVMTypeRef LLVMTargetExtTypeInContext(LLVMContextRef C, const char *Name,
macro(ConstantStruct) \
macro(ConstantTokenNone) \
macro(ConstantVector) \
+ macro(ConstantPtrAuth) \
macro(GlobalValue) \
macro(GlobalAlias) \
macro(GlobalObject) \
@@ -1795,6 +1926,8 @@ LLVMTypeRef LLVMTargetExtTypeInContext(LLVMContextRef C, const char *Name,
macro(AtomicRMWInst) \
macro(FenceInst)
+// clang-format on
+
/**
* @defgroup LLVMCCoreValueGeneral General APIs
*
@@ -1849,6 +1982,21 @@ void LLVMDumpValue(LLVMValueRef Val);
char *LLVMPrintValueToString(LLVMValueRef Val);
/**
+ * Obtain the context to which this value is associated.
+ *
+ * @see llvm::Value::getContext()
+ */
+LLVMContextRef LLVMGetValueContext(LLVMValueRef Val);
+
+/**
+ * Return a string representation of the DbgRecord. Use
+ * LLVMDisposeMessage to free the string.
+ *
+ * @see llvm::DbgRecord::print()
+ */
+char *LLVMPrintDbgRecordToString(LLVMDbgRecordRef Record);
+
+/**
* Replace all uses of a value with another one.
*
* @see llvm::Value::replaceAllUsesWith()
@@ -2165,12 +2313,23 @@ double LLVMConstRealGetDouble(LLVMValueRef ConstantVal, LLVMBool *losesInfo);
/**
* Create a ConstantDataSequential and initialize it with a string.
*
+ * @deprecated LLVMConstStringInContext is deprecated in favor of the API
+ * accurate LLVMConstStringInContext2
* @see llvm::ConstantDataArray::getString()
*/
LLVMValueRef LLVMConstStringInContext(LLVMContextRef C, const char *Str,
unsigned Length, LLVMBool DontNullTerminate);
/**
+ * Create a ConstantDataSequential and initialize it with a string.
+ *
+ * @see llvm::ConstantDataArray::getString()
+ */
+LLVMValueRef LLVMConstStringInContext2(LLVMContextRef C, const char *Str,
+ size_t Length,
+ LLVMBool DontNullTerminate);
+
+/**
* Create a ConstantDataSequential with string content in the global context.
*
* This is the same as LLVMConstStringInContext except it operates on the
@@ -2270,6 +2429,14 @@ LLVM_ATTRIBUTE_C_DEPRECATED(
LLVMValueRef LLVMConstVector(LLVMValueRef *ScalarConstantVals, unsigned Size);
/**
+ * Create a ConstantPtrAuth constant with the given values.
+ *
+ * @see llvm::ConstantPtrAuth::get()
+ */
+LLVMValueRef LLVMConstantPtrAuth(LLVMValueRef Ptr, LLVMValueRef Key,
+ LLVMValueRef Disc, LLVMValueRef AddrDisc);
+
+/**
* @}
*/
@@ -2287,7 +2454,9 @@ LLVMValueRef LLVMAlignOf(LLVMTypeRef Ty);
LLVMValueRef LLVMSizeOf(LLVMTypeRef Ty);
LLVMValueRef LLVMConstNeg(LLVMValueRef ConstantVal);
LLVMValueRef LLVMConstNSWNeg(LLVMValueRef ConstantVal);
-LLVMValueRef LLVMConstNUWNeg(LLVMValueRef ConstantVal);
+LLVM_ATTRIBUTE_C_DEPRECATED(
+ LLVMValueRef LLVMConstNUWNeg(LLVMValueRef ConstantVal),
+ "Use LLVMConstNull instead.");
LLVMValueRef LLVMConstNot(LLVMValueRef ConstantVal);
LLVMValueRef LLVMConstAdd(LLVMValueRef LHSConstant, LLVMValueRef RHSConstant);
LLVMValueRef LLVMConstNSWAdd(LLVMValueRef LHSConstant, LLVMValueRef RHSConstant);
@@ -2299,16 +2468,22 @@ LLVMValueRef LLVMConstMul(LLVMValueRef LHSConstant, LLVMValueRef RHSConstant);
LLVMValueRef LLVMConstNSWMul(LLVMValueRef LHSConstant, LLVMValueRef RHSConstant);
LLVMValueRef LLVMConstNUWMul(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 LLVMConstGEP2(LLVMTypeRef Ty, LLVMValueRef ConstantVal,
LLVMValueRef *ConstantIndices, unsigned NumIndices);
LLVMValueRef LLVMConstInBoundsGEP2(LLVMTypeRef Ty, LLVMValueRef ConstantVal,
LLVMValueRef *ConstantIndices,
unsigned NumIndices);
+/**
+ * Creates a constant GetElementPtr expression. Similar to LLVMConstGEP2, but
+ * allows specifying the no-wrap flags.
+ *
+ * @see llvm::ConstantExpr::getGetElementPtr()
+ */
+LLVMValueRef LLVMConstGEPWithNoWrapFlags(LLVMTypeRef Ty,
+ LLVMValueRef ConstantVal,
+ LLVMValueRef *ConstantIndices,
+ unsigned NumIndices,
+ LLVMGEPNoWrapFlags NoWrapFlags);
LLVMValueRef LLVMConstTrunc(LLVMValueRef ConstantVal, LLVMTypeRef ToType);
LLVMValueRef LLVMConstPtrToInt(LLVMValueRef ConstantVal, LLVMTypeRef ToType);
LLVMValueRef LLVMConstIntToPtr(LLVMValueRef ConstantVal, LLVMTypeRef ToType);
@@ -2328,6 +2503,16 @@ LLVMValueRef LLVMConstShuffleVector(LLVMValueRef VectorAConstant,
LLVMValueRef MaskConstant);
LLVMValueRef LLVMBlockAddress(LLVMValueRef F, LLVMBasicBlockRef BB);
+/**
+ * Gets the function associated with a given BlockAddress constant value.
+ */
+LLVMValueRef LLVMGetBlockAddressFunction(LLVMValueRef BlockAddr);
+
+/**
+ * Gets the basic block associated with a given BlockAddress constant value.
+ */
+LLVMBasicBlockRef LLVMGetBlockAddressBasicBlock(LLVMValueRef BlockAddr);
+
/** Deprecated: Use LLVMGetInlineAsm instead. */
LLVMValueRef LLVMConstInlineAsm(LLVMTypeRef Ty,
const char *AsmString, const char *Constraints,
@@ -2473,6 +2658,8 @@ LLVMValueRef LLVMAddGlobalInAddressSpace(LLVMModuleRef M, LLVMTypeRef Ty,
const char *Name,
unsigned AddressSpace);
LLVMValueRef LLVMGetNamedGlobal(LLVMModuleRef M, const char *Name);
+LLVMValueRef LLVMGetNamedGlobalWithLength(LLVMModuleRef M, const char *Name,
+ size_t Length);
LLVMValueRef LLVMGetFirstGlobal(LLVMModuleRef M);
LLVMValueRef LLVMGetLastGlobal(LLVMModuleRef M);
LLVMValueRef LLVMGetNextGlobal(LLVMValueRef GlobalVar);
@@ -2608,7 +2795,7 @@ void LLVMSetPersonalityFn(LLVMValueRef Fn, LLVMValueRef PersonalityFn);
/**
* Obtain the intrinsic ID number which matches the given function name.
*
- * @see llvm::Function::lookupIntrinsicID()
+ * @see llvm::Intrinsic::lookupIntrinsicID()
*/
unsigned LLVMLookupIntrinsicID(const char *Name, size_t NameLen);
@@ -2620,10 +2807,10 @@ unsigned LLVMLookupIntrinsicID(const char *Name, size_t NameLen);
unsigned LLVMGetIntrinsicID(LLVMValueRef Fn);
/**
- * Create or insert the declaration of an intrinsic. For overloaded intrinsics,
+ * Get or insert the declaration of an intrinsic. For overloaded intrinsics,
* parameter types must be provided to uniquely identify an overload.
*
- * @see llvm::Intrinsic::getDeclaration()
+ * @see llvm::Intrinsic::getOrInsertDeclaration()
*/
LLVMValueRef LLVMGetIntrinsicDeclaration(LLVMModuleRef Mod,
unsigned ID,
@@ -2647,10 +2834,8 @@ LLVMTypeRef LLVMIntrinsicGetType(LLVMContextRef Ctx, unsigned ID,
const char *LLVMIntrinsicGetName(unsigned ID, size_t *NameLength);
/** Deprecated: Use LLVMIntrinsicCopyOverloadedName2 instead. */
-const char *LLVMIntrinsicCopyOverloadedName(unsigned ID,
- LLVMTypeRef *ParamTypes,
- size_t ParamCount,
- size_t *NameLength);
+char *LLVMIntrinsicCopyOverloadedName(unsigned ID, LLVMTypeRef *ParamTypes,
+ size_t ParamCount, size_t *NameLength);
/**
* Copies the name of an overloaded intrinsic identified by a given list of
@@ -2663,10 +2848,9 @@ const char *LLVMIntrinsicCopyOverloadedName(unsigned ID,
*
* @see llvm::Intrinsic::getName()
*/
-const char *LLVMIntrinsicCopyOverloadedName2(LLVMModuleRef Mod, unsigned ID,
- LLVMTypeRef *ParamTypes,
- size_t ParamCount,
- size_t *NameLength);
+char *LLVMIntrinsicCopyOverloadedName2(LLVMModuleRef Mod, unsigned ID,
+ LLVMTypeRef *ParamTypes,
+ size_t ParamCount, size_t *NameLength);
/**
* Obtain if the intrinsic identified by the given ID is overloaded.
@@ -2710,6 +2894,44 @@ const char *LLVMGetGC(LLVMValueRef Fn);
void LLVMSetGC(LLVMValueRef Fn, const char *Name);
/**
+ * Gets the prefix data associated with a function. Only valid on functions, and
+ * only if LLVMHasPrefixData returns true.
+ * See https://llvm.org/docs/LangRef.html#prefix-data
+ */
+LLVMValueRef LLVMGetPrefixData(LLVMValueRef Fn);
+
+/**
+ * Check if a given function has prefix data. Only valid on functions.
+ * See https://llvm.org/docs/LangRef.html#prefix-data
+ */
+LLVMBool LLVMHasPrefixData(LLVMValueRef Fn);
+
+/**
+ * Sets the prefix data for the function. Only valid on functions.
+ * See https://llvm.org/docs/LangRef.html#prefix-data
+ */
+void LLVMSetPrefixData(LLVMValueRef Fn, LLVMValueRef prefixData);
+
+/**
+ * Gets the prologue data associated with a function. Only valid on functions,
+ * and only if LLVMHasPrologueData returns true.
+ * See https://llvm.org/docs/LangRef.html#prologue-data
+ */
+LLVMValueRef LLVMGetPrologueData(LLVMValueRef Fn);
+
+/**
+ * Check if a given function has prologue data. Only valid on functions.
+ * See https://llvm.org/docs/LangRef.html#prologue-data
+ */
+LLVMBool LLVMHasPrologueData(LLVMValueRef Fn);
+
+/**
+ * Sets the prologue data for the function. Only valid on functions.
+ * See https://llvm.org/docs/LangRef.html#prologue-data
+ */
+void LLVMSetPrologueData(LLVMValueRef Fn, LLVMValueRef prologueData);
+
+/**
* Add an attribute to a function.
*
* @see llvm::Function::addAttribute()
@@ -3426,8 +3648,7 @@ LLVMOpcode LLVMGetInstructionOpcode(LLVMValueRef Inst);
/**
* Obtain the predicate of an instruction.
*
- * This is only valid for instructions that correspond to llvm::ICmpInst
- * or llvm::ConstantExpr whose opcode is llvm::Instruction::ICmp.
+ * This is only valid for instructions that correspond to llvm::ICmpInst.
*
* @see llvm::ICmpInst::getPredicate()
*/
@@ -3436,8 +3657,7 @@ LLVMIntPredicate LLVMGetICmpPredicate(LLVMValueRef Inst);
/**
* Obtain the float predicate of an instruction.
*
- * This is only valid for instructions that correspond to llvm::FCmpInst
- * or llvm::ConstantExpr whose opcode is llvm::Instruction::FCmp.
+ * This is only valid for instructions that correspond to llvm::FCmpInst.
*
* @see llvm::FCmpInst::getPredicate()
*/
@@ -3463,6 +3683,41 @@ LLVMValueRef LLVMInstructionClone(LLVMValueRef Inst);
LLVMValueRef LLVMIsATerminatorInst(LLVMValueRef Inst);
/**
+ * Obtain the first debug record attached to an instruction.
+ *
+ * Use LLVMGetNextDbgRecord() and LLVMGetPreviousDbgRecord() to traverse the
+ * sequence of DbgRecords.
+ *
+ * Return the first DbgRecord attached to Inst or NULL if there are none.
+ *
+ * @see llvm::Instruction::getDbgRecordRange()
+ */
+LLVMDbgRecordRef LLVMGetFirstDbgRecord(LLVMValueRef Inst);
+
+/**
+ * Obtain the last debug record attached to an instruction.
+ *
+ * Return the last DbgRecord attached to Inst or NULL if there are none.
+ *
+ * @see llvm::Instruction::getDbgRecordRange()
+ */
+LLVMDbgRecordRef LLVMGetLastDbgRecord(LLVMValueRef Inst);
+
+/**
+ * Obtain the next DbgRecord in the sequence or NULL if there are no more.
+ *
+ * @see llvm::Instruction::getDbgRecordRange()
+ */
+LLVMDbgRecordRef LLVMGetNextDbgRecord(LLVMDbgRecordRef DbgRecord);
+
+/**
+ * Obtain the previous DbgRecord in the sequence or NULL if there are no more.
+ *
+ * @see llvm::Instruction::getDbgRecordRange()
+ */
+LLVMDbgRecordRef LLVMGetPreviousDbgRecord(LLVMDbgRecordRef DbgRecord);
+
+/**
* @defgroup LLVMCCoreValueInstructionCall Call Sites and Invocations
*
* Functions in this group apply to instructions that refer to call
@@ -3635,6 +3890,28 @@ void LLVMSetNormalDest(LLVMValueRef InvokeInst, LLVMBasicBlockRef B);
void LLVMSetUnwindDest(LLVMValueRef InvokeInst, LLVMBasicBlockRef B);
/**
+ * Get the default destination of a CallBr instruction.
+ *
+ * @see llvm::CallBrInst::getDefaultDest()
+ */
+LLVMBasicBlockRef LLVMGetCallBrDefaultDest(LLVMValueRef CallBr);
+
+/**
+ * Get the number of indirect destinations of a CallBr instruction.
+ *
+ * @see llvm::CallBrInst::getNumIndirectDests()
+
+ */
+unsigned LLVMGetCallBrNumIndirectDests(LLVMValueRef CallBr);
+
+/**
+ * Get the indirect destination of a CallBr instruction at the given index.
+ *
+ * @see llvm::CallBrInst::getIndirectDest()
+ */
+LLVMBasicBlockRef LLVMGetCallBrIndirectDest(LLVMValueRef CallBr, unsigned Idx);
+
+/**
* @}
*/
@@ -3751,6 +4028,20 @@ void LLVMSetIsInBounds(LLVMValueRef GEP, LLVMBool InBounds);
LLVMTypeRef LLVMGetGEPSourceElementType(LLVMValueRef GEP);
/**
+ * Get the no-wrap related flags for the given GEP instruction.
+ *
+ * @see llvm::GetElementPtrInst::getNoWrapFlags
+ */
+LLVMGEPNoWrapFlags LLVMGEPGetNoWrapFlags(LLVMValueRef GEP);
+
+/**
+ * Set the no-wrap related flags for the given GEP instruction.
+ *
+ * @see llvm::GetElementPtrInst::setNoWrapFlags
+ */
+void LLVMGEPSetNoWrapFlags(LLVMValueRef GEP, LLVMGEPNoWrapFlags NoWrapFlags);
+
+/**
* @}
*/
@@ -3832,9 +4123,28 @@ const unsigned *LLVMGetIndices(LLVMValueRef Inst);
LLVMBuilderRef LLVMCreateBuilderInContext(LLVMContextRef C);
LLVMBuilderRef LLVMCreateBuilder(void);
+/**
+ * Set the builder position before Instr but after any attached debug records,
+ * or if Instr is null set the position to the end of Block.
+ */
void LLVMPositionBuilder(LLVMBuilderRef Builder, LLVMBasicBlockRef Block,
LLVMValueRef Instr);
+/**
+ * Set the builder position before Instr and any attached debug records,
+ * or if Instr is null set the position to the end of Block.
+ */
+void LLVMPositionBuilderBeforeDbgRecords(LLVMBuilderRef Builder,
+ LLVMBasicBlockRef Block,
+ LLVMValueRef Inst);
+/**
+ * Set the builder position before Instr but after any attached debug records.
+ */
void LLVMPositionBuilderBefore(LLVMBuilderRef Builder, LLVMValueRef Instr);
+/**
+ * Set the builder position before Instr and any attached debug records.
+ */
+void LLVMPositionBuilderBeforeInstrAndDbgRecords(LLVMBuilderRef Builder,
+ LLVMValueRef Instr);
void LLVMPositionBuilderAtEnd(LLVMBuilderRef Builder, LLVMBasicBlockRef Block);
LLVMBasicBlockRef LLVMGetInsertBlock(LLVMBuilderRef Builder);
void LLVMClearInsertionPosition(LLVMBuilderRef Builder);
@@ -3898,6 +4208,13 @@ void LLVMBuilderSetDefaultFPMathTag(LLVMBuilderRef Builder,
LLVMMetadataRef FPMathTag);
/**
+ * Obtain the context to which this builder is associated.
+ *
+ * @see llvm::IRBuilder::getContext()
+ */
+LLVMContextRef LLVMGetBuilderContext(LLVMBuilderRef Builder);
+
+/**
* Deprecated: Passing the NULL location will crash.
* Use LLVMGetCurrentDebugLocation2 instead.
*/
@@ -3920,6 +4237,12 @@ LLVMValueRef LLVMBuildSwitch(LLVMBuilderRef, LLVMValueRef V,
LLVMBasicBlockRef Else, unsigned NumCases);
LLVMValueRef LLVMBuildIndirectBr(LLVMBuilderRef B, LLVMValueRef Addr,
unsigned NumDests);
+LLVMValueRef LLVMBuildCallBr(LLVMBuilderRef B, LLVMTypeRef Ty, LLVMValueRef Fn,
+ LLVMBasicBlockRef DefaultDest,
+ LLVMBasicBlockRef *IndirectDests,
+ unsigned NumIndirectDests, LLVMValueRef *Args,
+ unsigned NumArgs, LLVMOperandBundleRef *Bundles,
+ unsigned NumBundles, const char *Name);
LLVMValueRef LLVMBuildInvoke2(LLVMBuilderRef, LLVMTypeRef Ty, LLVMValueRef Fn,
LLVMValueRef *Args, unsigned NumArgs,
LLVMBasicBlockRef Then, LLVMBasicBlockRef Catch,
@@ -4075,8 +4398,10 @@ LLVMValueRef LLVMBuildBinOp(LLVMBuilderRef B, LLVMOpcode Op,
LLVMValueRef LLVMBuildNeg(LLVMBuilderRef, LLVMValueRef V, const char *Name);
LLVMValueRef LLVMBuildNSWNeg(LLVMBuilderRef B, LLVMValueRef V,
const char *Name);
-LLVMValueRef LLVMBuildNUWNeg(LLVMBuilderRef B, LLVMValueRef V,
- const char *Name);
+LLVM_ATTRIBUTE_C_DEPRECATED(LLVMValueRef LLVMBuildNUWNeg(LLVMBuilderRef B,
+ LLVMValueRef V,
+ const char *Name),
+ "Use LLVMBuildNeg + LLVMSetNUW instead.");
LLVMValueRef LLVMBuildFNeg(LLVMBuilderRef, LLVMValueRef V, const char *Name);
LLVMValueRef LLVMBuildNot(LLVMBuilderRef, LLVMValueRef V, const char *Name);
@@ -4182,11 +4507,25 @@ LLVMValueRef LLVMBuildGEP2(LLVMBuilderRef B, LLVMTypeRef Ty,
LLVMValueRef LLVMBuildInBoundsGEP2(LLVMBuilderRef B, LLVMTypeRef Ty,
LLVMValueRef Pointer, LLVMValueRef *Indices,
unsigned NumIndices, const char *Name);
+/**
+ * Creates a GetElementPtr instruction. Similar to LLVMBuildGEP2, but allows
+ * specifying the no-wrap flags.
+ *
+ * @see llvm::IRBuilder::CreateGEP()
+ */
+LLVMValueRef LLVMBuildGEPWithNoWrapFlags(LLVMBuilderRef B, LLVMTypeRef Ty,
+ LLVMValueRef Pointer,
+ LLVMValueRef *Indices,
+ unsigned NumIndices, const char *Name,
+ LLVMGEPNoWrapFlags NoWrapFlags);
LLVMValueRef LLVMBuildStructGEP2(LLVMBuilderRef B, LLVMTypeRef Ty,
LLVMValueRef Pointer, unsigned Idx,
const char *Name);
LLVMValueRef LLVMBuildGlobalString(LLVMBuilderRef B, const char *Str,
const char *Name);
+/**
+ * Deprecated: Use LLVMBuildGlobalString instead, which has identical behavior.
+ */
LLVMValueRef LLVMBuildGlobalStringPtr(LLVMBuilderRef B, const char *Str,
const char *Name);
LLVMBool LLVMGetVolatile(LLVMValueRef MemoryAccessInst);
@@ -4296,15 +4635,28 @@ LLVMValueRef LLVMBuildPtrDiff2(LLVMBuilderRef, LLVMTypeRef ElemTy,
const char *Name);
LLVMValueRef LLVMBuildFence(LLVMBuilderRef B, LLVMAtomicOrdering ordering,
LLVMBool singleThread, const char *Name);
+LLVMValueRef LLVMBuildFenceSyncScope(LLVMBuilderRef B,
+ LLVMAtomicOrdering ordering, unsigned SSID,
+ const char *Name);
LLVMValueRef LLVMBuildAtomicRMW(LLVMBuilderRef B, LLVMAtomicRMWBinOp op,
LLVMValueRef PTR, LLVMValueRef Val,
LLVMAtomicOrdering ordering,
LLVMBool singleThread);
+LLVMValueRef LLVMBuildAtomicRMWSyncScope(LLVMBuilderRef B,
+ LLVMAtomicRMWBinOp op,
+ LLVMValueRef PTR, LLVMValueRef Val,
+ LLVMAtomicOrdering ordering,
+ unsigned SSID);
LLVMValueRef LLVMBuildAtomicCmpXchg(LLVMBuilderRef B, LLVMValueRef Ptr,
LLVMValueRef Cmp, LLVMValueRef New,
LLVMAtomicOrdering SuccessOrdering,
LLVMAtomicOrdering FailureOrdering,
LLVMBool SingleThread);
+LLVMValueRef LLVMBuildAtomicCmpXchgSyncScope(LLVMBuilderRef B, LLVMValueRef Ptr,
+ LLVMValueRef Cmp, LLVMValueRef New,
+ LLVMAtomicOrdering SuccessOrdering,
+ LLVMAtomicOrdering FailureOrdering,
+ unsigned SSID);
/**
* Get the number of elements in the mask of a ShuffleVector instruction.
@@ -4329,6 +4681,22 @@ int LLVMGetMaskValue(LLVMValueRef ShuffleVectorInst, unsigned Elt);
LLVMBool LLVMIsAtomicSingleThread(LLVMValueRef AtomicInst);
void LLVMSetAtomicSingleThread(LLVMValueRef AtomicInst, LLVMBool SingleThread);
+/**
+ * Returns whether an instruction is an atomic instruction, e.g., atomicrmw,
+ * cmpxchg, fence, or loads and stores with atomic ordering.
+ */
+LLVMBool LLVMIsAtomic(LLVMValueRef Inst);
+
+/**
+ * Returns the synchronization scope ID of an atomic instruction.
+ */
+unsigned LLVMGetAtomicSyncScopeID(LLVMValueRef AtomicInst);
+
+/**
+ * Sets the synchronization scope ID of an atomic instruction.
+ */
+void LLVMSetAtomicSyncScopeID(LLVMValueRef AtomicInst, unsigned SSID);
+
LLVMAtomicOrdering LLVMGetCmpXchgSuccessOrdering(LLVMValueRef CmpXchgInst);
void LLVMSetCmpXchgSuccessOrdering(LLVMValueRef CmpXchgInst,
LLVMAtomicOrdering Ordering);
diff --git a/src/llvm-c/DebugInfo.h b/src/llvm-c/DebugInfo.h
index 93bd9e2ad..ac7ee5a7c 100644
--- a/src/llvm-c/DebugInfo.h
+++ b/src/llvm-c/DebugInfo.h
@@ -16,8 +16,8 @@
#ifndef LLVM_C_DEBUGINFO_H
#define LLVM_C_DEBUGINFO_H
-#include "ExternC.h"
-#include "Types.h"
+#include "llvm-c/ExternC.h"
+#include "llvm-c/Types.h"
LLVM_C_EXTERN_C_BEGIN
@@ -125,7 +125,21 @@ typedef enum {
LLVMDWARFSourceLanguageFortran18,
LLVMDWARFSourceLanguageAda2005,
LLVMDWARFSourceLanguageAda2012,
+ LLVMDWARFSourceLanguageHIP,
+ LLVMDWARFSourceLanguageAssembly,
+ LLVMDWARFSourceLanguageC_sharp,
LLVMDWARFSourceLanguageMojo,
+ LLVMDWARFSourceLanguageGLSL,
+ LLVMDWARFSourceLanguageGLSL_ES,
+ LLVMDWARFSourceLanguageHLSL,
+ LLVMDWARFSourceLanguageOpenCL_CPP,
+ LLVMDWARFSourceLanguageCPP_for_OpenCL,
+ LLVMDWARFSourceLanguageSYCL,
+ LLVMDWARFSourceLanguageRuby,
+ LLVMDWARFSourceLanguageMove,
+ LLVMDWARFSourceLanguageHylo,
+ LLVMDWARFSourceLanguageMetal,
+
// Vendor extensions:
LLVMDWARFSourceLanguageMips_Assembler,
LLVMDWARFSourceLanguageGOOGLE_RenderScript,
@@ -856,13 +870,16 @@ LLVMDIBuilderCreateObjCProperty(LLVMDIBuilderRef Builder,
LLVMMetadataRef Ty);
/**
- * Create a uniqued DIType* clone with FlagObjectPointer and FlagArtificial set.
+ * Create a uniqued DIType* clone with FlagObjectPointer. If \c Implicit
+ * is true, then also set FlagArtificial.
* \param Builder The DIBuilder.
* \param Type The underlying type to which this pointer points.
+ * \param Implicit Indicates whether this pointer was implicitly generated
+ * (i.e., not spelled out in source).
*/
-LLVMMetadataRef
-LLVMDIBuilderCreateObjectPointerType(LLVMDIBuilderRef Builder,
- LLVMMetadataRef Type);
+LLVMMetadataRef LLVMDIBuilderCreateObjectPointerType(LLVMDIBuilderRef Builder,
+ LLVMMetadataRef Type,
+ LLVMBool Implicit);
/**
* Create debugging information entry for a qualified
@@ -1249,66 +1266,84 @@ LLVMMetadataRef LLVMDIBuilderCreateTempGlobalVariableFwdDecl(
LLVMMetadataRef Decl, uint32_t AlignInBits);
/**
- * Insert a new llvm.dbg.declare intrinsic call before the given instruction.
+ * Only use in "new debug format" (LLVMIsNewDbgInfoFormat() is true).
+ * See https://llvm.org/docs/RemoveDIsDebugInfo.html#c-api-changes
+ *
+ * The debug format can be switched later after inserting the records using
+ * LLVMSetIsNewDbgInfoFormat, if needed for legacy or transitionary reasons.
+ *
+ * Insert a Declare DbgRecord before the given instruction.
* \param Builder The DIBuilder.
* \param Storage The storage of the variable to declare.
* \param VarInfo The variable's debug info descriptor.
* \param Expr A complex location expression for the variable.
* \param DebugLoc Debug info location.
- * \param Instr Instruction acting as a location for the new intrinsic.
+ * \param Instr Instruction acting as a location for the new record.
*/
-LLVMValueRef LLVMDIBuilderInsertDeclareBefore(
- LLVMDIBuilderRef Builder, LLVMValueRef Storage, LLVMMetadataRef VarInfo,
- LLVMMetadataRef Expr, LLVMMetadataRef DebugLoc, LLVMValueRef Instr);
+LLVMDbgRecordRef LLVMDIBuilderInsertDeclareRecordBefore(
+ LLVMDIBuilderRef Builder, LLVMValueRef Storage, LLVMMetadataRef VarInfo,
+ LLVMMetadataRef Expr, LLVMMetadataRef DebugLoc, LLVMValueRef Instr);
/**
- * Insert a new llvm.dbg.declare intrinsic call at the end of the given basic
- * block. If the basic block has a terminator instruction, the intrinsic is
- * inserted before that terminator instruction.
+ * Only use in "new debug format" (LLVMIsNewDbgInfoFormat() is true).
+ * See https://llvm.org/docs/RemoveDIsDebugInfo.html#c-api-changes
+ *
+ * The debug format can be switched later after inserting the records using
+ * LLVMSetIsNewDbgInfoFormat, if needed for legacy or transitionary reasons.
+ *
+ * Insert a Declare DbgRecord at the end of the given basic block. If the basic
+ * block has a terminator instruction, the record is inserted before that
+ * terminator instruction.
* \param Builder The DIBuilder.
* \param Storage The storage of the variable to declare.
* \param VarInfo The variable's debug info descriptor.
* \param Expr A complex location expression for the variable.
* \param DebugLoc Debug info location.
- * \param Block Basic block acting as a location for the new intrinsic.
+ * \param Block Basic block acting as a location for the new record.
*/
-LLVMValueRef LLVMDIBuilderInsertDeclareAtEnd(
+LLVMDbgRecordRef LLVMDIBuilderInsertDeclareRecordAtEnd(
LLVMDIBuilderRef Builder, LLVMValueRef Storage, LLVMMetadataRef VarInfo,
LLVMMetadataRef Expr, LLVMMetadataRef DebugLoc, LLVMBasicBlockRef Block);
/**
- * Insert a new llvm.dbg.value intrinsic call before the given instruction.
+ * Only use in "new debug format" (LLVMIsNewDbgInfoFormat() is true).
+ * See https://llvm.org/docs/RemoveDIsDebugInfo.html#c-api-changes
+ *
+ * The debug format can be switched later after inserting the records using
+ * LLVMSetIsNewDbgInfoFormat, if needed for legacy or transitionary reasons.
+ *
+ * Insert a new debug record before the given instruction.
* \param Builder The DIBuilder.
* \param Val The value of the variable.
* \param VarInfo The variable's debug info descriptor.
* \param Expr A complex location expression for the variable.
* \param DebugLoc Debug info location.
- * \param Instr Instruction acting as a location for the new intrinsic.
+ * \param Instr Instruction acting as a location for the new record.
*/
-LLVMValueRef LLVMDIBuilderInsertDbgValueBefore(LLVMDIBuilderRef Builder,
- LLVMValueRef Val,
- LLVMMetadataRef VarInfo,
- LLVMMetadataRef Expr,
- LLVMMetadataRef DebugLoc,
- LLVMValueRef Instr);
+LLVMDbgRecordRef LLVMDIBuilderInsertDbgValueRecordBefore(
+ LLVMDIBuilderRef Builder, LLVMValueRef Val, LLVMMetadataRef VarInfo,
+ LLVMMetadataRef Expr, LLVMMetadataRef DebugLoc, LLVMValueRef Instr);
/**
- * Insert a new llvm.dbg.value intrinsic call at the end of the given basic
- * block. If the basic block has a terminator instruction, the intrinsic is
- * inserted before that terminator instruction.
+ * Only use in "new debug format" (LLVMIsNewDbgInfoFormat() is true).
+ * See https://llvm.org/docs/RemoveDIsDebugInfo.html#c-api-changes
+ *
+ * The debug format can be switched later after inserting the records using
+ * LLVMSetIsNewDbgInfoFormat, if needed for legacy or transitionary reasons.
+ *
+ * Insert a new debug record at the end of the given basic block. If the
+ * basic block has a terminator instruction, the record is inserted before
+ * that terminator instruction.
* \param Builder The DIBuilder.
* \param Val The value of the variable.
* \param VarInfo The variable's debug info descriptor.
* \param Expr A complex location expression for the variable.
* \param DebugLoc Debug info location.
- * \param Block Basic block acting as a location for the new intrinsic.
+ * \param Block Basic block acting as a location for the new record.
*/
-LLVMValueRef LLVMDIBuilderInsertDbgValueAtEnd(LLVMDIBuilderRef Builder,
- LLVMValueRef Val,
- LLVMMetadataRef VarInfo,
- LLVMMetadataRef Expr,
- LLVMMetadataRef DebugLoc,
- LLVMBasicBlockRef Block);
+LLVMDbgRecordRef LLVMDIBuilderInsertDbgValueRecordAtEnd(
+ LLVMDIBuilderRef Builder, LLVMValueRef Val, LLVMMetadataRef VarInfo,
+ LLVMMetadataRef Expr, LLVMMetadataRef DebugLoc, LLVMBasicBlockRef Block);
/**
* Create a new descriptor for a local auto variable.
@@ -1385,6 +1420,52 @@ LLVMMetadataRef LLVMInstructionGetDebugLoc(LLVMValueRef Inst);
void LLVMInstructionSetDebugLoc(LLVMValueRef Inst, LLVMMetadataRef Loc);
/**
+ * Create a new descriptor for a label
+ *
+ * \param Builder The DIBuilder.
+ * \param Scope The scope to create the label in.
+ * \param Name Variable name.
+ * \param NameLen Length of variable name.
+ * \param File The file to create the label in.
+ * \param LineNo Line Number.
+ * \param AlwaysPreserve Preserve the label regardless of optimization.
+ *
+ * @see llvm::DIBuilder::createLabel()
+ */
+LLVMMetadataRef LLVMDIBuilderCreateLabel(
+ LLVMDIBuilderRef Builder,
+ LLVMMetadataRef Context, const char *Name, size_t NameLen,
+ LLVMMetadataRef File, unsigned LineNo, LLVMBool AlwaysPreserve);
+
+/**
+ * Insert a new llvm.dbg.label intrinsic call
+ *
+ * \param Builder The DIBuilder.
+ * \param LabelInfo The Label's debug info descriptor
+ * \param Location The debug info location
+ * \param InsertBefore Location for the new intrinsic.
+ *
+ * @see llvm::DIBuilder::insertLabel()
+ */
+LLVMDbgRecordRef LLVMDIBuilderInsertLabelBefore(
+ LLVMDIBuilderRef Builder, LLVMMetadataRef LabelInfo,
+ LLVMMetadataRef Location, LLVMValueRef InsertBefore);
+
+/**
+ * Insert a new llvm.dbg.label intrinsic call
+ *
+ * \param Builder The DIBuilder.
+ * \param LabelInfo The Label's debug info descriptor
+ * \param Location The debug info location
+ * \param InsertAtEnd Location for the new intrinsic.
+ *
+ * @see llvm::DIBuilder::insertLabel()
+ */
+LLVMDbgRecordRef LLVMDIBuilderInsertLabelAtEnd(
+ LLVMDIBuilderRef Builder, LLVMMetadataRef LabelInfo,
+ LLVMMetadataRef Location, LLVMBasicBlockRef InsertAtEnd);
+
+/**
* Obtain the enumerated type of a Metadata instance.
*
* @see llvm::Metadata::getMetadataID()
diff --git a/src/llvm-c/Disassembler.h b/src/llvm-c/Disassembler.h
index e6642f4ed..4bc6b04dd 100644
--- a/src/llvm-c/Disassembler.h
+++ b/src/llvm-c/Disassembler.h
@@ -15,8 +15,8 @@
#ifndef LLVM_C_DISASSEMBLER_H
#define LLVM_C_DISASSEMBLER_H
-#include "DisassemblerTypes.h"
-#include "ExternC.h"
+#include "llvm-c/DisassemblerTypes.h"
+#include "llvm-c/ExternC.h"
/**
* @defgroup LLVMCDisassembler Disassembler
@@ -79,8 +79,10 @@ int LLVMSetDisasmOptions(LLVMDisasmContextRef DC, uint64_t Options);
#define LLVMDisassembler_Option_AsmPrinterVariant 4
/* The option to set comment on instructions */
#define LLVMDisassembler_Option_SetInstrComments 8
- /* The option to print latency information alongside instructions */
+/* The option to print latency information alongside instructions */
#define LLVMDisassembler_Option_PrintLatency 16
+/* The option to print in color */
+#define LLVMDisassembler_Option_Color 32
/**
* Dispose of a disassembler context.
diff --git a/src/llvm-c/DisassemblerTypes.h b/src/llvm-c/DisassemblerTypes.h
index 6b7ad6104..6999a350e 100644
--- a/src/llvm-c/DisassemblerTypes.h
+++ b/src/llvm-c/DisassemblerTypes.h
@@ -10,7 +10,7 @@
#ifndef LLVM_C_DISASSEMBLERTYPES_H
#define LLVM_C_DISASSEMBLERTYPES_H
-#include "DataTypes.h"
+#include "llvm-c/DataTypes.h"
#ifdef __cplusplus
#include <cstddef>
#else
diff --git a/src/llvm-c/Error.h b/src/llvm-c/Error.h
index 00746c701..874bbcfe8 100644
--- a/src/llvm-c/Error.h
+++ b/src/llvm-c/Error.h
@@ -14,7 +14,7 @@
#ifndef LLVM_C_ERROR_H
#define LLVM_C_ERROR_H
-#include "ExternC.h"
+#include "llvm-c/ExternC.h"
LLVM_C_EXTERN_C_BEGIN
@@ -52,6 +52,14 @@ LLVMErrorTypeId LLVMGetErrorTypeId(LLVMErrorRef Err);
void LLVMConsumeError(LLVMErrorRef Err);
/**
+ * Report a fatal error if Err is a failure value.
+ *
+ * This function can be used to wrap calls to fallible functions ONLY when it is
+ * known that the Error will always be a success value.
+ */
+void LLVMCantFail(LLVMErrorRef Err);
+
+/**
* Returns the given string's error message. This operation consumes the error,
* and the given LLVMErrorRef value is not usable once this call returns.
* The caller is responsible for disposing of the string by calling
diff --git a/src/llvm-c/ErrorHandling.h b/src/llvm-c/ErrorHandling.h
index 7f9b50a95..d9b9f2275 100644
--- a/src/llvm-c/ErrorHandling.h
+++ b/src/llvm-c/ErrorHandling.h
@@ -14,7 +14,7 @@
#ifndef LLVM_C_ERRORHANDLING_H
#define LLVM_C_ERRORHANDLING_H
-#include "ExternC.h"
+#include "llvm-c/ExternC.h"
LLVM_C_EXTERN_C_BEGIN
diff --git a/src/llvm-c/ExecutionEngine.h b/src/llvm-c/ExecutionEngine.h
index 8e72faefd..c5fc9bdb4 100644
--- a/src/llvm-c/ExecutionEngine.h
+++ b/src/llvm-c/ExecutionEngine.h
@@ -19,10 +19,10 @@
#ifndef LLVM_C_EXECUTIONENGINE_H
#define LLVM_C_EXECUTIONENGINE_H
-#include "ExternC.h"
-#include "Target.h"
-#include "TargetMachine.h"
-#include "Types.h"
+#include "llvm-c/ExternC.h"
+#include "llvm-c/Target.h"
+#include "llvm-c/TargetMachine.h"
+#include "llvm-c/Types.h"
LLVM_C_EXTERN_C_BEGIN
diff --git a/src/llvm-c/IRReader.h b/src/llvm-c/IRReader.h
index ec1110c7a..905b84fa5 100644
--- a/src/llvm-c/IRReader.h
+++ b/src/llvm-c/IRReader.h
@@ -14,8 +14,8 @@
#ifndef LLVM_C_IRREADER_H
#define LLVM_C_IRREADER_H
-#include "ExternC.h"
-#include "Types.h"
+#include "llvm-c/ExternC.h"
+#include "llvm-c/Types.h"
LLVM_C_EXTERN_C_BEGIN
diff --git a/src/llvm-c/LLJIT.h b/src/llvm-c/LLJIT.h
index ee207e10e..a58c3b8bb 100644
--- a/src/llvm-c/LLJIT.h
+++ b/src/llvm-c/LLJIT.h
@@ -24,10 +24,10 @@
#ifndef LLVM_C_LLJIT_H
#define LLVM_C_LLJIT_H
-#include "Error.h"
-#include "Orc.h"
-#include "TargetMachine.h"
-#include "Types.h"
+#include "llvm-c/Error.h"
+#include "llvm-c/Orc.h"
+#include "llvm-c/TargetMachine.h"
+#include "llvm-c/Types.h"
LLVM_C_EXTERN_C_BEGIN
diff --git a/src/llvm-c/LLJITUtils.h b/src/llvm-c/LLJITUtils.h
index 57ffedff8..940097432 100644
--- a/src/llvm-c/LLJITUtils.h
+++ b/src/llvm-c/LLJITUtils.h
@@ -26,7 +26,7 @@
#ifndef LLVM_C_LLJITUTILS_H
#define LLVM_C_LLJITUTILS_H
-#include "LLJIT.h"
+#include "llvm-c/LLJIT.h"
LLVM_C_EXTERN_C_BEGIN
diff --git a/src/llvm-c/Linker.h b/src/llvm-c/Linker.h
index 463a2cff9..acff5d5e2 100644
--- a/src/llvm-c/Linker.h
+++ b/src/llvm-c/Linker.h
@@ -14,8 +14,8 @@
#ifndef LLVM_C_LINKER_H
#define LLVM_C_LINKER_H
-#include "ExternC.h"
-#include "Types.h"
+#include "llvm-c/ExternC.h"
+#include "llvm-c/Types.h"
LLVM_C_EXTERN_C_BEGIN
diff --git a/src/llvm-c/Object.h b/src/llvm-c/Object.h
index 1948c3c34..2f39179c1 100644
--- a/src/llvm-c/Object.h
+++ b/src/llvm-c/Object.h
@@ -19,9 +19,9 @@
#ifndef LLVM_C_OBJECT_H
#define LLVM_C_OBJECT_H
-#include "ExternC.h"
-#include "Types.h"
-#include "Config/llvm-config.h"
+#include "llvm-c/ExternC.h"
+#include "llvm-c/Types.h"
+#include "llvm-c/Config/llvm-config.h"
LLVM_C_EXTERN_C_BEGIN
diff --git a/src/llvm-c/Orc.h b/src/llvm-c/Orc.h
index ecd110b4d..8609a8a6d 100644
--- a/src/llvm-c/Orc.h
+++ b/src/llvm-c/Orc.h
@@ -27,9 +27,9 @@
#ifndef LLVM_C_ORC_H
#define LLVM_C_ORC_H
-#include "Error.h"
-#include "TargetMachine.h"
-#include "Types.h"
+#include "llvm-c/Error.h"
+#include "llvm-c/TargetMachine.h"
+#include "llvm-c/Types.h"
LLVM_C_EXTERN_C_BEGIN
@@ -182,6 +182,15 @@ typedef struct {
typedef LLVMOrcCDependenceMapPair *LLVMOrcCDependenceMapPairs;
/**
+ * A set of symbols that share dependencies.
+ */
+typedef struct {
+ LLVMOrcCSymbolsList Symbols;
+ LLVMOrcCDependenceMapPairs Dependencies;
+ size_t NumDependencies;
+} LLVMOrcCSymbolDependenceGroup;
+
+/**
* Lookup kind. This can be used by definition generators when deciding whether
* to produce a definition for a requested symbol.
*
@@ -808,6 +817,19 @@ LLVMErrorRef LLVMOrcMaterializationResponsibilityNotifyResolved(
* that all symbols covered by this MaterializationResponsibility instance
* have been emitted.
*
+ * This function takes ownership of the symbols in the Dependencies struct.
+ * This allows the following pattern...
+ *
+ * LLVMOrcSymbolStringPoolEntryRef Names[] = {...};
+ * LLVMOrcCDependenceMapPair Dependence = {JD, {Names, sizeof(Names)}}
+ * LLVMOrcMaterializationResponsibilityAddDependencies(JD, Name, &Dependence,
+ * 1);
+ *
+ * ... without requiring cleanup of the elements of the Names array afterwards.
+ *
+ * The client is still responsible for deleting the Dependencies.Names arrays,
+ * and the Dependencies array itself.
+ *
* This method will return an error if any symbols being resolved have been
* moved to the error state due to the failure of a dependency. If this
* method returns an error then clients should log it and call
@@ -817,7 +839,8 @@ LLVMErrorRef LLVMOrcMaterializationResponsibilityNotifyResolved(
* LLVMErrorSuccess.
*/
LLVMErrorRef LLVMOrcMaterializationResponsibilityNotifyEmitted(
- LLVMOrcMaterializationResponsibilityRef MR);
+ LLVMOrcMaterializationResponsibilityRef MR,
+ LLVMOrcCSymbolDependenceGroup *SymbolDepGroups, size_t NumSymbolDepGroups);
/**
* Attempt to claim responsibility for new definitions. This method can be
@@ -871,38 +894,6 @@ LLVMErrorRef LLVMOrcMaterializationResponsibilityDelegate(
LLVMOrcMaterializationResponsibilityRef *Result);
/**
- * Adds dependencies to a symbol that the MaterializationResponsibility is
- * responsible for.
- *
- * This function takes ownership of Dependencies struct. The Names
- * array have been retained for this function. This allows the following
- * pattern...
- *
- * LLVMOrcSymbolStringPoolEntryRef Names[] = {...};
- * LLVMOrcCDependenceMapPair Dependence = {JD, {Names, sizeof(Names)}}
- * LLVMOrcMaterializationResponsibilityAddDependencies(JD, Name, &Dependence,
- * 1);
- *
- * ... without requiring cleanup of the elements of the Names array afterwards.
- *
- * The client is still responsible for deleting the Dependencies.Names array
- * itself.
- */
-void LLVMOrcMaterializationResponsibilityAddDependencies(
- LLVMOrcMaterializationResponsibilityRef MR,
- LLVMOrcSymbolStringPoolEntryRef Name,
- LLVMOrcCDependenceMapPairs Dependencies, size_t NumPairs);
-
-/**
- * Adds dependencies to all symbols that the MaterializationResponsibility is
- * responsible for. See LLVMOrcMaterializationResponsibilityAddDependencies for
- * notes about memory responsibility.
- */
-void LLVMOrcMaterializationResponsibilityAddDependenciesForAll(
- LLVMOrcMaterializationResponsibilityRef MR,
- LLVMOrcCDependenceMapPairs Dependencies, size_t NumPairs);
-
-/**
* Create a "bare" JITDylib.
*
* The client is responsible for ensuring that the JITDylib's name is unique,
diff --git a/src/llvm-c/OrcEE.h b/src/llvm-c/OrcEE.h
index aef24c7aa..d451187aa 100644
--- a/src/llvm-c/OrcEE.h
+++ b/src/llvm-c/OrcEE.h
@@ -24,11 +24,11 @@
#ifndef LLVM_C_ORCEE_H
#define LLVM_C_ORCEE_H
-#include "Error.h"
-#include "ExecutionEngine.h"
-#include "Orc.h"
-#include "TargetMachine.h"
-#include "Types.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"
LLVM_C_EXTERN_C_BEGIN
diff --git a/src/llvm-c/Remarks.h b/src/llvm-c/Remarks.h
index 548a4041a..ffe647a65 100644
--- a/src/llvm-c/Remarks.h
+++ b/src/llvm-c/Remarks.h
@@ -15,8 +15,8 @@
#ifndef LLVM_C_REMARKS_H
#define LLVM_C_REMARKS_H
-#include "ExternC.h"
-#include "Types.h"
+#include "llvm-c/ExternC.h"
+#include "llvm-c/Types.h"
#ifdef __cplusplus
#include <cstddef>
#else
diff --git a/src/llvm-c/Support.h b/src/llvm-c/Support.h
index 31a75354c..17657861b 100644
--- a/src/llvm-c/Support.h
+++ b/src/llvm-c/Support.h
@@ -14,9 +14,9 @@
#ifndef LLVM_C_SUPPORT_H
#define LLVM_C_SUPPORT_H
-#include "DataTypes.h"
-#include "ExternC.h"
-#include "Types.h"
+#include "llvm-c/DataTypes.h"
+#include "llvm-c/ExternC.h"
+#include "llvm-c/Types.h"
LLVM_C_EXTERN_C_BEGIN
diff --git a/src/llvm-c/Target.h b/src/llvm-c/Target.h
index 4d03741c4..54367a41b 100644
--- a/src/llvm-c/Target.h
+++ b/src/llvm-c/Target.h
@@ -19,9 +19,9 @@
#ifndef LLVM_C_TARGET_H
#define LLVM_C_TARGET_H
-#include "ExternC.h"
-#include "Types.h"
-#include "Config/llvm-config.h"
+#include "llvm-c/ExternC.h"
+#include "llvm-c/Types.h"
+#include "llvm-c/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 "Config/Targets.def"
+#include "llvm-c/Config/Targets.def"
#undef LLVM_TARGET /* Explicit undef to make SWIG happier */
#define LLVM_TARGET(TargetName) void LLVMInitialize##TargetName##Target(void);
-#include "Config/Targets.def"
+#include "llvm-c/Config/Targets.def"
#undef LLVM_TARGET /* Explicit undef to make SWIG happier */
#define LLVM_TARGET(TargetName) \
void LLVMInitialize##TargetName##TargetMC(void);
-#include "Config/Targets.def"
+#include "llvm-c/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 "Config/AsmPrinters.def"
+#include "llvm-c/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 "Config/AsmParsers.def"
+#include "llvm-c/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 "Config/Disassemblers.def"
+#include "llvm-c/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 "Config/Targets.def"
+#include "llvm-c/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 "Config/Targets.def"
+#include "llvm-c/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 "Config/Targets.def"
+#include "llvm-c/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 "Config/AsmPrinters.def"
+#include "llvm-c/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 "Config/AsmParsers.def"
+#include "llvm-c/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 "Config/Disassemblers.def"
+#include "llvm-c/Config/Disassemblers.def"
#undef LLVM_DISASSEMBLER /* Explicit undef to make SWIG happier */
}
@@ -244,7 +244,7 @@ LLVMTypeRef LLVMIntPtrTypeInContext(LLVMContextRef C, LLVMTargetDataRef TD);
LLVMTypeRef LLVMIntPtrTypeForASInContext(LLVMContextRef C, LLVMTargetDataRef TD,
unsigned AS);
-/** Computes the size of a type in bytes for a target.
+/** Computes the size of a type in bits for a target.
See the method llvm::DataLayout::getTypeSizeInBits. */
unsigned long long LLVMSizeOfTypeInBits(LLVMTargetDataRef TD, LLVMTypeRef Ty);
diff --git a/src/llvm-c/TargetMachine.h b/src/llvm-c/TargetMachine.h
index aa628e216..cbe891380 100644
--- a/src/llvm-c/TargetMachine.h
+++ b/src/llvm-c/TargetMachine.h
@@ -19,9 +19,9 @@
#ifndef LLVM_C_TARGETMACHINE_H
#define LLVM_C_TARGETMACHINE_H
-#include "ExternC.h"
-#include "Target.h"
-#include "Types.h"
+#include "llvm-c/ExternC.h"
+#include "llvm-c/Target.h"
+#include "llvm-c/Types.h"
LLVM_C_EXTERN_C_BEGIN
diff --git a/src/llvm-c/Transforms/PassBuilder.h b/src/llvm-c/Transforms/PassBuilder.h
index 8ad2a9982..d297b57ca 100644
--- a/src/llvm-c/Transforms/PassBuilder.h
+++ b/src/llvm-c/Transforms/PassBuilder.h
@@ -14,9 +14,9 @@
#ifndef LLVM_C_TRANSFORMS_PASSBUILDER_H
#define LLVM_C_TRANSFORMS_PASSBUILDER_H
-#include "../Error.h"
-#include "../TargetMachine.h"
-#include "../Types.h"
+#include "llvm-c/Error.h"
+#include "llvm-c/TargetMachine.h"
+#include "llvm-c/Types.h"
/**
* @defgroup LLVMCCoreNewPM New Pass Manager
@@ -51,6 +51,16 @@ LLVMErrorRef LLVMRunPasses(LLVMModuleRef M, const char *Passes,
LLVMPassBuilderOptionsRef Options);
/**
+ * Construct and run a set of passes over a function.
+ *
+ * This function behaves the same as LLVMRunPasses, but operates on a single
+ * function instead of an entire module.
+ */
+LLVMErrorRef LLVMRunPassesOnFunction(LLVMValueRef F, const char *Passes,
+ LLVMTargetMachineRef TM,
+ LLVMPassBuilderOptionsRef Options);
+
+/**
* Create a new set of options for a PassBuilder
*
* Ownership of the returned instance is given to the client, and they are
@@ -72,6 +82,14 @@ void LLVMPassBuilderOptionsSetVerifyEach(LLVMPassBuilderOptionsRef Options,
void LLVMPassBuilderOptionsSetDebugLogging(LLVMPassBuilderOptionsRef Options,
LLVMBool DebugLogging);
+/**
+ * Specify a custom alias analysis pipeline for the PassBuilder to be used
+ * instead of the default one. The string argument is not copied; the caller
+ * is responsible for ensuring it outlives the PassBuilderOptions instance.
+ */
+void LLVMPassBuilderOptionsSetAAPipeline(LLVMPassBuilderOptionsRef Options,
+ const char *AAPipeline);
+
void LLVMPassBuilderOptionsSetLoopInterleaving(
LLVMPassBuilderOptionsRef Options, LLVMBool LoopInterleaving);
diff --git a/src/llvm-c/Types.h b/src/llvm-c/Types.h
index 77aa7c9b4..4681500ef 100644
--- a/src/llvm-c/Types.h
+++ b/src/llvm-c/Types.h
@@ -14,8 +14,8 @@
#ifndef LLVM_C_TYPES_H
#define LLVM_C_TYPES_H
-#include "DataTypes.h"
-#include "ExternC.h"
+#include "llvm-c/DataTypes.h"
+#include "llvm-c/ExternC.h"
LLVM_C_EXTERN_C_BEGIN
@@ -170,6 +170,11 @@ typedef struct LLVMOpaqueJITEventListener *LLVMJITEventListenerRef;
typedef struct LLVMOpaqueBinary *LLVMBinaryRef;
/**
+ * @see llvm::DbgRecord
+ */
+typedef struct LLVMOpaqueDbgRecord *LLVMDbgRecordRef;
+
+/**
* @}
*/
diff --git a/src/llvm-c/lto.h b/src/llvm-c/lto.h
index 89f76c695..5ceb02224 100644
--- a/src/llvm-c/lto.h
+++ b/src/llvm-c/lto.h
@@ -16,7 +16,7 @@
#ifndef LLVM_C_LTO_H
#define LLVM_C_LTO_H
-#include "ExternC.h"
+#include "llvm-c/ExternC.h"
#ifdef __cplusplus
#include <cstddef>
diff --git a/src/llvm_abi.cpp b/src/llvm_abi.cpp
index 0b2bb7956..9f2faaa08 100644
--- a/src/llvm_abi.cpp
+++ b/src/llvm_abi.cpp
@@ -256,8 +256,10 @@ gb_internal i64 lb_sizeof(LLVMTypeRef type) {
}
break;
+#if LLVM_VERSION_MAJOR < 20
case LLVMX86_MMXTypeKind:
return 8;
+#endif
case LLVMVectorTypeKind:
{
LLVMTypeRef elem = OdinLLVMGetVectorElementType(type);
@@ -310,8 +312,10 @@ gb_internal i64 lb_alignof(LLVMTypeRef type) {
case LLVMArrayTypeKind:
return lb_alignof(OdinLLVMGetArrayElementType(type));
+#if LLVM_VERSION_MAJOR < 20
case LLVMX86_MMXTypeKind:
return 8;
+#endif
case LLVMVectorTypeKind:
{
// TODO(bill): This appears to be correct but LLVM isn't necessarily "great" with regards to documentation
@@ -518,11 +522,30 @@ namespace lbAbiAmd64Win64 {
}
};
+
+gb_internal bool is_llvm_type_slice_like(LLVMTypeRef type) {
+ if (!lb_is_type_kind(type, LLVMStructTypeKind)) {
+ return false;
+ }
+ if (LLVMCountStructElementTypes(type) != 2) {
+ return false;
+ }
+ LLVMTypeRef fields[2] = {};
+ LLVMGetStructElementTypes(type, fields);
+ if (!lb_is_type_kind(fields[0], LLVMPointerTypeKind)) {
+ return false;
+ }
+ return lb_is_type_kind(fields[1], LLVMIntegerTypeKind) && lb_sizeof(fields[1]) == 8;
+
+}
+
// NOTE(bill): I hate `namespace` in C++ but this is just because I don't want to prefix everything
namespace lbAbiAmd64SysV {
enum RegClass {
RegClass_NoClass,
RegClass_Int,
+ RegClass_SSEHs,
+ RegClass_SSEHv,
RegClass_SSEFs,
RegClass_SSEFv,
RegClass_SSEDs,
@@ -541,6 +564,8 @@ namespace lbAbiAmd64SysV {
gb_internal bool is_sse(RegClass reg_class) {
switch (reg_class) {
+ case RegClass_SSEHs:
+ case RegClass_SSEHv:
case RegClass_SSEFs:
case RegClass_SSEFv:
case RegClass_SSEDs:
@@ -569,7 +594,9 @@ namespace lbAbiAmd64SysV {
gb_internal void classify_with(LLVMTypeRef t, Array<RegClass> *cls, i64 ix, i64 off);
gb_internal void fixup(LLVMTypeRef t, Array<RegClass> *cls);
- gb_internal lbArgType amd64_type(LLVMContextRef c, LLVMTypeRef type, Amd64TypeAttributeKind attribute_kind, ProcCallingConvention calling_convention);
+ gb_internal lbArgType amd64_type(LLVMContextRef c, LLVMTypeRef type, Amd64TypeAttributeKind attribute_kind, ProcCallingConvention calling_convention,
+ bool is_arg,
+ i32 *int_regs, i32 *sse_regs);
gb_internal Array<RegClass> classify(LLVMTypeRef t);
gb_internal LLVMTypeRef llreg(LLVMContextRef c, Array<RegClass> const &reg_classes, LLVMTypeRef type);
@@ -579,7 +606,9 @@ namespace lbAbiAmd64SysV {
}
LB_ABI_MODIFY_RETURN_IF_TUPLE_MACRO();
- return amd64_type(c, return_type, Amd64TypeAttribute_StructRect, ft->calling_convention);
+ return amd64_type(c, return_type, Amd64TypeAttribute_StructRect, ft->calling_convention,
+ false,
+ nullptr, nullptr);
}
gb_internal LB_ABI_INFO(abi_info) {
@@ -588,10 +617,16 @@ namespace lbAbiAmd64SysV {
ft->ctx = c;
ft->calling_convention = calling_convention;
+ i32 int_regs = 6; // rdi, rsi, rdx, rcx, r8, r9
+ i32 sse_regs = 8; // xmm0-xmm7
+
ft->args = array_make<lbArgType>(lb_function_type_args_allocator(), arg_count);
for (unsigned i = 0; i < arg_count; i++) {
- ft->args[i] = amd64_type(c, arg_types[i], Amd64TypeAttribute_ByVal, calling_convention);
+ ft->args[i] = amd64_type(c, arg_types[i], Amd64TypeAttribute_ByVal, calling_convention,
+ true,
+ &int_regs, &sse_regs);
}
+
ft->ret = compute_return_type(ft, c, return_type, return_is_defined, return_is_tuple);
return ft;
@@ -634,33 +669,80 @@ namespace lbAbiAmd64SysV {
return false;
}
- gb_internal bool is_llvm_type_slice_like(LLVMTypeRef type) {
- if (!lb_is_type_kind(type, LLVMStructTypeKind)) {
- return false;
- }
- if (LLVMCountStructElementTypes(type) != 2) {
- return false;
+ gb_internal bool is_aggregate(LLVMTypeRef type) {
+ LLVMTypeKind kind = LLVMGetTypeKind(type);
+ switch (kind) {
+ case LLVMStructTypeKind:
+ if (LLVMCountStructElementTypes(type) == 1) {
+ return is_aggregate(LLVMStructGetTypeAtIndex(type, 0));
+ }
+ return true;
+ case LLVMArrayTypeKind:
+ if (LLVMGetArrayLength(type) == 1) {
+ return is_aggregate(LLVMGetElementType(type));
+ }
+ return true;
}
- LLVMTypeRef fields[2] = {};
- LLVMGetStructElementTypes(type, fields);
- if (!lb_is_type_kind(fields[0], LLVMPointerTypeKind)) {
- return false;
+ return false;
+ };
+
+ gb_internal lbArgType amd64_type(LLVMContextRef c, LLVMTypeRef type, Amd64TypeAttributeKind attribute_kind, ProcCallingConvention calling_convention,
+ bool is_arg,
+ i32 *int_regs, i32 *sse_regs) {
+ auto cls = classify(type);
+ i32 needed_int = 0;
+ i32 needed_sse = 0;
+ for (auto c : cls) {
+ switch (c) {
+ case RegClass_Int:
+ needed_int += 1;
+ break;
+ case RegClass_SSEHs:
+ case RegClass_SSEHv:
+ case RegClass_SSEFs:
+ case RegClass_SSEFv:
+ case RegClass_SSEDs:
+ case RegClass_SSEDv:
+ case RegClass_SSEInt8:
+ case RegClass_SSEInt16:
+ case RegClass_SSEInt32:
+ case RegClass_SSEInt64:
+ case RegClass_SSEInt128:
+ case RegClass_SSEUp:
+ needed_sse += 1;
+ break;
+ }
}
- return lb_is_type_kind(fields[1], LLVMIntegerTypeKind) && lb_sizeof(fields[1]) == 8;
- }
+ bool ran_out_of_regs = false;
+ if (int_regs && sse_regs) {
+ *int_regs -= needed_int;
+ *sse_regs -= needed_sse;
+ bool int_ok = *int_regs >= 0;
+ bool sse_ok = *sse_regs >= 0;
+
+ *int_regs = gb_max(*int_regs, 0);
+ *sse_regs = gb_max(*sse_regs, 0);
+
+ if ((!int_ok || !sse_ok) && is_aggregate(type)) {
+ ran_out_of_regs = true;
+ }
+ }
- gb_internal lbArgType amd64_type(LLVMContextRef c, LLVMTypeRef type, Amd64TypeAttributeKind attribute_kind, ProcCallingConvention calling_convention) {
if (is_register(type)) {
LLVMAttributeRef attribute = nullptr;
if (type == LLVMInt1TypeInContext(c)) {
attribute = lb_create_enum_attribute(c, "zeroext");
}
return lb_arg_type_direct(type, nullptr, nullptr, attribute);
- }
-
- auto cls = classify(type);
- if (is_mem_cls(cls, attribute_kind)) {
+ } else if (ran_out_of_regs) {
+ if (is_arg) {
+ return lb_arg_type_indirect_byval(c, type);
+ } else {
+ LLVMAttributeRef attribute = lb_create_enum_attribute_with_type(c, "sret", type);
+ return lb_arg_type_indirect(type, attribute);
+ }
+ } else if (is_mem_cls(cls, attribute_kind)) {
LLVMAttributeRef attribute = nullptr;
if (attribute_kind == Amd64TypeAttribute_ByVal) {
if (is_calling_convention_odin(calling_convention)) {
@@ -728,6 +810,8 @@ namespace lbAbiAmd64SysV {
to_write = RegClass_Memory;
} else if (newv == RegClass_SSEUp) {
switch (oldv) {
+ case RegClass_SSEHv:
+ case RegClass_SSEHs:
case RegClass_SSEFv:
case RegClass_SSEFs:
case RegClass_SSEDv:
@@ -774,14 +858,18 @@ namespace lbAbiAmd64SysV {
} else if (oldv == RegClass_SSEUp) {
oldv = RegClass_SSEDv;
} else if (is_sse(oldv)) {
- i++;
- while (i != e && oldv == RegClass_SSEUp) {
- i++;
+ for (i++; i < e; i++) {
+ RegClass v = (*cls)[cast(isize)i];
+ if (v != RegClass_SSEUp) {
+ break;
+ }
}
} else if (oldv == RegClass_X87) {
- i++;
- while (i != e && oldv == RegClass_X87Up) {
- i++;
+ for (i++; i < e; i++) {
+ RegClass v = (*cls)[cast(isize)i];
+ if (v != RegClass_X87Up) {
+ break;
+ }
}
} else {
i++;
@@ -838,6 +926,7 @@ namespace lbAbiAmd64SysV {
sz -= rs;
break;
}
+ case RegClass_SSEHv:
case RegClass_SSEFv:
case RegClass_SSEDv:
case RegClass_SSEInt8:
@@ -848,6 +937,10 @@ namespace lbAbiAmd64SysV {
unsigned elems_per_word = 0;
LLVMTypeRef elem_type = nullptr;
switch (reg_class) {
+ case RegClass_SSEHv:
+ elems_per_word = 4;
+ elem_type = LLVMHalfTypeInContext(c);
+ break;
case RegClass_SSEFv:
elems_per_word = 2;
elem_type = LLVMFloatTypeInContext(c);
@@ -882,6 +975,10 @@ namespace lbAbiAmd64SysV {
continue;
}
break;
+ case RegClass_SSEHs:
+ array_add(&types, LLVMHalfTypeInContext(c));
+ sz -= 2;
+ break;
case RegClass_SSEFs:
array_add(&types, LLVMFloatTypeInContext(c));
sz -= 4;
@@ -901,7 +998,7 @@ namespace lbAbiAmd64SysV {
return types[0];
}
- return LLVMStructTypeInContext(c, types.data, cast(unsigned)types.count, sz == 0);
+ return LLVMStructTypeInContext(c, types.data, cast(unsigned)types.count, false);
}
gb_internal void classify_with(LLVMTypeRef t, Array<RegClass> *cls, i64 ix, i64 off) {
@@ -928,9 +1025,11 @@ namespace lbAbiAmd64SysV {
break;
}
case LLVMPointerTypeKind:
- case LLVMHalfTypeKind:
unify(cls, ix + off/8, RegClass_Int);
break;
+ case LLVMHalfTypeKind:
+ unify(cls, ix + off/8, (off%8 != 0) ? RegClass_SSEHv : RegClass_SSEHs);
+ break;
case LLVMFloatTypeKind:
unify(cls, ix + off/8, (off%8 == 4) ? RegClass_SSEFv : RegClass_SSEFs);
break;
@@ -970,10 +1069,9 @@ namespace lbAbiAmd64SysV {
i64 elem_sz = lb_sizeof(elem);
LLVMTypeKind elem_kind = LLVMGetTypeKind(elem);
RegClass reg = RegClass_NoClass;
- unsigned elem_width = LLVMGetIntTypeWidth(elem);
switch (elem_kind) {
- case LLVMIntegerTypeKind:
- case LLVMHalfTypeKind:
+ case LLVMIntegerTypeKind: {
+ unsigned elem_width = LLVMGetIntTypeWidth(elem);
switch (elem_width) {
case 8: reg = RegClass_SSEInt8; break;
case 16: reg = RegClass_SSEInt16; break;
@@ -989,6 +1087,10 @@ namespace lbAbiAmd64SysV {
GB_PANIC("Unhandled integer width for vector type %u", elem_width);
}
break;
+ };
+ case LLVMHalfTypeKind:
+ reg = RegClass_SSEHv;
+ break;
case LLVMFloatTypeKind:
reg = RegClass_SSEFv;
break;
@@ -1155,38 +1257,24 @@ namespace lbAbiArm64 {
}
} else {
i64 size = lb_sizeof(return_type);
- if (size <= 16) {
- LLVMTypeRef cast_type = nullptr;
-
- if (size == 0) {
- cast_type = LLVMStructTypeInContext(c, nullptr, 0, false);
- } else if (size <= 8) {
- cast_type = LLVMIntTypeInContext(c, cast(unsigned)(size*8));
- } else {
- unsigned count = cast(unsigned)((size+7)/8);
-
- LLVMTypeRef llvm_i64 = LLVMIntTypeInContext(c, 64);
- LLVMTypeRef *types = gb_alloc_array(temporary_allocator(), LLVMTypeRef, count);
-
- i64 size_copy = size;
- for (unsigned i = 0; i < count; i++) {
- if (size_copy >= 8) {
- types[i] = llvm_i64;
- } else {
- types[i] = LLVMIntTypeInContext(c, 8*cast(unsigned)size_copy);
- }
- size_copy -= 8;
- }
- GB_ASSERT(size_copy <= 0);
- cast_type = LLVMStructTypeInContext(c, types, count, true);
- }
- return lb_arg_type_direct(return_type, cast_type, nullptr, nullptr);
- } else {
+ if (size > 16) {
LB_ABI_MODIFY_RETURN_IF_TUPLE_MACRO();
LLVMAttributeRef attr = lb_create_enum_attribute_with_type(c, "sret", return_type);
return lb_arg_type_indirect(return_type, attr);
}
+
+ GB_ASSERT(size <= 16);
+ LLVMTypeRef cast_type = nullptr;
+ if (size == 0) {
+ cast_type = LLVMStructTypeInContext(c, nullptr, 0, false);
+ } else if (size <= 8) {
+ cast_type = LLVMIntTypeInContext(c, cast(unsigned)(size*8));
+ } else {
+ LLVMTypeRef llvm_i64 = LLVMIntTypeInContext(c, 64);
+ cast_type = llvm_array_type(llvm_i64, 2);
+ }
+ return lb_arg_type_direct(return_type, cast_type, nullptr, nullptr);
}
}
@@ -1251,7 +1339,7 @@ namespace lbAbiWasm {
registers/arguments if possible rather than by pointer.
*/
gb_internal Array<lbArgType> compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count, ProcCallingConvention calling_convention, Type *original_type);
- gb_internal LB_ABI_COMPUTE_RETURN_TYPE(compute_return_type);
+ gb_internal lbArgType compute_return_type(lbFunctionType *ft, LLVMContextRef c, LLVMTypeRef return_type, bool return_is_defined, bool return_is_tuple, Type* original_type);
enum {MAX_DIRECT_STRUCT_SIZE = 32};
@@ -1261,7 +1349,9 @@ namespace lbAbiWasm {
ft->ctx = c;
ft->calling_convention = calling_convention;
ft->args = compute_arg_types(c, arg_types, arg_count, calling_convention, original_type);
- ft->ret = compute_return_type(ft, c, return_type, return_is_defined, return_is_tuple);
+
+ GB_ASSERT(original_type->kind == Type_Proc);
+ ft->ret = compute_return_type(ft, c, return_type, return_is_defined, return_is_tuple, original_type->Proc.results);
return ft;
}
@@ -1297,7 +1387,7 @@ namespace lbAbiWasm {
return false;
}
- gb_internal bool type_can_be_direct(LLVMTypeRef type, ProcCallingConvention calling_convention) {
+ gb_internal bool type_can_be_direct(LLVMTypeRef type, Type *original_type, ProcCallingConvention calling_convention) {
LLVMTypeKind kind = LLVMGetTypeKind(type);
i64 sz = lb_sizeof(type);
if (sz == 0) {
@@ -1310,9 +1400,21 @@ namespace lbAbiWasm {
return false;
} else if (kind == LLVMStructTypeKind) {
unsigned count = LLVMCountStructElementTypes(type);
+
+ // NOTE(laytan): raw unions are always structs with 1 field in LLVM, need to check our own def.
+ Type *bt = base_type(original_type);
+ if (bt->kind == Type_Struct && bt->Struct.is_raw_union) {
+ count = cast(unsigned)bt->Struct.fields.count;
+ }
+
if (count == 1) {
- return type_can_be_direct(LLVMStructGetTypeAtIndex(type, 0), calling_convention);
+ return type_can_be_direct(
+ LLVMStructGetTypeAtIndex(type, 0),
+ type_internal_index(original_type, 0),
+ calling_convention
+ );
}
+
} else if (is_basic_register_type(type)) {
return true;
}
@@ -1336,7 +1438,7 @@ namespace lbAbiWasm {
return false;
}
- gb_internal lbArgType is_struct(LLVMContextRef c, LLVMTypeRef type, ProcCallingConvention calling_convention) {
+ gb_internal lbArgType is_struct(LLVMContextRef c, LLVMTypeRef type, Type *original_type, ProcCallingConvention calling_convention) {
LLVMTypeKind kind = LLVMGetTypeKind(type);
GB_ASSERT(kind == LLVMArrayTypeKind || kind == LLVMStructTypeKind);
@@ -1344,15 +1446,15 @@ namespace lbAbiWasm {
if (sz == 0) {
return lb_arg_type_ignore(type);
}
- if (type_can_be_direct(type, calling_convention)) {
+ if (type_can_be_direct(type, original_type, calling_convention)) {
return lb_arg_type_direct(type);
}
return lb_arg_type_indirect(type, nullptr);
}
- gb_internal lbArgType pseudo_slice(LLVMContextRef c, LLVMTypeRef type, ProcCallingConvention calling_convention) {
+ gb_internal lbArgType pseudo_slice(LLVMContextRef c, LLVMTypeRef type, Type *original_type, ProcCallingConvention calling_convention) {
if (build_context.metrics.ptr_size < build_context.metrics.int_size &&
- type_can_be_direct(type, calling_convention)) {
+ type_can_be_direct(type, original_type, calling_convention)) {
LLVMTypeRef types[2] = {
LLVMStructGetTypeAtIndex(type, 0),
// ignore padding
@@ -1361,7 +1463,7 @@ namespace lbAbiWasm {
LLVMTypeRef new_type = LLVMStructTypeInContext(c, types, gb_count_of(types), false);
return lb_arg_type_direct(type, new_type, nullptr, nullptr);
} else {
- return is_struct(c, type, calling_convention);
+ return is_struct(c, type, original_type, calling_convention);
}
}
@@ -1382,9 +1484,9 @@ namespace lbAbiWasm {
LLVMTypeKind kind = LLVMGetTypeKind(t);
if (kind == LLVMStructTypeKind || kind == LLVMArrayTypeKind) {
if (is_type_slice(ptype) || is_type_string(ptype)) {
- args[i] = pseudo_slice(c, t, calling_convention);
+ args[i] = pseudo_slice(c, t, ptype, calling_convention);
} else {
- args[i] = is_struct(c, t, calling_convention);
+ args[i] = is_struct(c, t, ptype, calling_convention);
}
} else {
args[i] = non_struct(c, t, false);
@@ -1393,11 +1495,11 @@ namespace lbAbiWasm {
return args;
}
- gb_internal LB_ABI_COMPUTE_RETURN_TYPE(compute_return_type) {
+ gb_internal lbArgType compute_return_type(lbFunctionType *ft, LLVMContextRef c, LLVMTypeRef return_type, bool return_is_defined, bool return_is_tuple, Type* original_type) {
if (!return_is_defined) {
return lb_arg_type_direct(LLVMVoidTypeInContext(c));
} else if (lb_is_type_kind(return_type, LLVMStructTypeKind) || lb_is_type_kind(return_type, LLVMArrayTypeKind)) {
- if (type_can_be_direct(return_type, ft->calling_convention)) {
+ if (type_can_be_direct(return_type, original_type, ft->calling_convention)) {
return lb_arg_type_direct(return_type);
} else if (ft->calling_convention != ProcCC_CDecl) {
i64 sz = lb_sizeof(return_type);
@@ -1409,7 +1511,35 @@ namespace lbAbiWasm {
}
}
- LB_ABI_MODIFY_RETURN_IF_TUPLE_MACRO();
+ // Multiple returns.
+ if (return_is_tuple) { \
+ lbArgType return_arg = {};
+ if (lb_is_type_kind(return_type, LLVMStructTypeKind)) {
+ unsigned field_count = LLVMCountStructElementTypes(return_type);
+ if (field_count > 1) {
+ ft->original_arg_count = ft->args.count;
+ ft->multiple_return_original_type = return_type;
+
+ for (unsigned i = 0; i < field_count-1; i++) {
+ LLVMTypeRef field_type = LLVMStructGetTypeAtIndex(return_type, i);
+ LLVMTypeRef field_pointer_type = LLVMPointerType(field_type, 0);
+ lbArgType ret_partial = lb_arg_type_direct(field_pointer_type);
+ array_add(&ft->args, ret_partial);
+ }
+
+ return_arg = compute_return_type(
+ ft,
+ c,
+ LLVMStructGetTypeAtIndex(return_type, field_count-1),
+ true, false,
+ type_internal_index(original_type, field_count-1)
+ );
+ }
+ }
+ if (return_arg.type != nullptr) {
+ return return_arg;
+ }
+ }
LLVMAttributeRef attr = lb_create_enum_attribute_with_type(c, "sret", return_type);
return lb_arg_type_indirect(return_type, attr);
@@ -1814,7 +1944,8 @@ gb_internal LB_ABI_INFO(lb_get_abi_info) {
return_type, return_is_defined,
ALLOW_SPLIT_MULTI_RETURNS && return_is_tuple && is_calling_convention_odin(calling_convention),
calling_convention,
- base_type(original_type));
+ base_type(original_type)
+ );
// NOTE(bill): this is handled here rather than when developing the type in `lb_type_internal_for_procedures_raw`
diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp
index 88e099930..921084250 100644
--- a/src/llvm_backend.cpp
+++ b/src/llvm_backend.cpp
@@ -8,7 +8,11 @@
#endif
#ifndef LLVM_IGNORE_VERIFICATION
-#define LLVM_IGNORE_VERIFICATION 0
+#define LLVM_IGNORE_VERIFICATION build_context.internal_ignore_llvm_verification
+#endif
+
+#ifndef LLVM_WEAK_MONOMORPHIZATION
+#define LLVM_WEAK_MONOMORPHIZATION (USE_SEPARATE_MODULES && build_context.internal_weak_monomorphization)
#endif
@@ -242,26 +246,12 @@ gb_internal String lb_internal_gen_name_from_type(char const *prefix, Type *type
return proc_name;
}
-
-gb_internal lbValue lb_equal_proc_for_type(lbModule *m, Type *type) {
- type = base_type(type);
- GB_ASSERT(is_type_comparable(type));
+gb_internal void lb_equal_proc_generate_body(lbModule *m, lbProcedure *p) {
+ Type *type = p->internal_gen_type;
Type *pt = alloc_type_pointer(type);
LLVMTypeRef ptr_type = lb_type(m, pt);
- String proc_name = lb_internal_gen_name_from_type("__$equal", type);
- lbProcedure **found = string_map_get(&m->gen_procs, proc_name);
- lbProcedure *compare_proc = nullptr;
- if (found) {
- compare_proc = *found;
- GB_ASSERT(compare_proc != nullptr);
- return {compare_proc->value, compare_proc->type};
- }
-
-
- lbProcedure *p = lb_create_dummy_procedure(m, proc_name, t_equal_proc);
- string_map_set(&m->gen_procs, proc_name, p);
lb_begin_procedure_body(p);
LLVMSetLinkage(p->value, LLVMInternalLinkage);
@@ -389,9 +379,29 @@ gb_internal lbValue lb_equal_proc_for_type(lbModule *m, Type *type) {
}
lb_end_procedure_body(p);
+}
+
+gb_internal lbValue lb_equal_proc_for_type(lbModule *m, Type *type) {
+ type = base_type(type);
+ GB_ASSERT(is_type_comparable(type));
+
+ String proc_name = lb_internal_gen_name_from_type("__$equal", type);
+ lbProcedure **found = string_map_get(&m->gen_procs, proc_name);
+ if (found) {
+ lbProcedure *p = *found;
+ GB_ASSERT(p != nullptr);
+ return {p->value, p->type};
+ }
+
+ lbProcedure *p = lb_create_dummy_procedure(m, proc_name, t_equal_proc);
+ string_map_set(&m->gen_procs, proc_name, p);
+ p->internal_gen_type = type;
+ p->generate_body = lb_equal_proc_generate_body;
- compare_proc = p;
- return {compare_proc->value, compare_proc->type};
+ // p->generate_body(m, p);
+ mpsc_enqueue(&m->procedures_to_generate, p);
+
+ return {p->value, p->type};
}
gb_internal lbValue lb_simple_compare_hash(lbProcedure *p, Type *type, lbValue data, lbValue seed) {
@@ -563,6 +573,53 @@ gb_internal lbValue lb_hasher_proc_for_type(lbModule *m, Type *type) {
lbValue res = lb_emit_runtime_call(p, "default_hasher_string", args);
lb_add_callsite_force_inline(p, res);
LLVMBuildRet(p->builder, res.value);
+ } else if (is_type_float(type)) {
+ lbValue ptr = lb_emit_conv(p, data, pt);
+ lbValue v = lb_emit_load(p, ptr);
+ v = lb_emit_conv(p, v, t_f64);
+
+ auto args = array_make<lbValue>(temporary_allocator(), 2);
+ args[0] = v;
+ args[1] = seed;
+ lbValue res = lb_emit_runtime_call(p, "default_hasher_f64", args);
+ lb_add_callsite_force_inline(p, res);
+ LLVMBuildRet(p->builder, res.value);
+ } else if (is_type_complex(type)) {
+ lbValue ptr = lb_emit_conv(p, data, pt);
+ lbValue xp = lb_emit_struct_ep(p, ptr, 0);
+ lbValue yp = lb_emit_struct_ep(p, ptr, 1);
+
+ lbValue x = lb_emit_conv(p, lb_emit_load(p, xp), t_f64);
+ lbValue y = lb_emit_conv(p, lb_emit_load(p, yp), t_f64);
+
+ auto args = array_make<lbValue>(temporary_allocator(), 3);
+ args[0] = x;
+ args[1] = y;
+ args[2] = seed;
+ lbValue res = lb_emit_runtime_call(p, "default_hasher_complex128", args);
+ lb_add_callsite_force_inline(p, res);
+ LLVMBuildRet(p->builder, res.value);
+ } else if (is_type_quaternion(type)) {
+ lbValue ptr = lb_emit_conv(p, data, pt);
+ lbValue xp = lb_emit_struct_ep(p, ptr, 0);
+ lbValue yp = lb_emit_struct_ep(p, ptr, 1);
+ lbValue zp = lb_emit_struct_ep(p, ptr, 2);
+ lbValue wp = lb_emit_struct_ep(p, ptr, 3);
+
+ lbValue x = lb_emit_conv(p, lb_emit_load(p, xp), t_f64);
+ lbValue y = lb_emit_conv(p, lb_emit_load(p, yp), t_f64);
+ lbValue z = lb_emit_conv(p, lb_emit_load(p, zp), t_f64);
+ lbValue w = lb_emit_conv(p, lb_emit_load(p, wp), t_f64);
+
+ auto args = array_make<lbValue>(temporary_allocator(), 5);
+ args[0] = x;
+ args[1] = y;
+ args[2] = z;
+ args[3] = w;
+ args[4] = seed;
+ lbValue res = lb_emit_runtime_call(p, "default_hasher_quaternion256", args);
+ lb_add_callsite_force_inline(p, res);
+ LLVMBuildRet(p->builder, res.value);
} else {
GB_PANIC("Unhandled type for hasher: %s", type_to_string(type));
}
@@ -573,6 +630,7 @@ gb_internal lbValue lb_hasher_proc_for_type(lbModule *m, Type *type) {
#define LLVM_SET_VALUE_NAME(value, name) LLVMSetValueName2((value), (name), gb_count_of((name))-1);
+
gb_internal lbValue lb_map_get_proc_for_type(lbModule *m, Type *type) {
GB_ASSERT(!build_context.dynamic_map_calls);
type = base_type(type);
@@ -587,6 +645,9 @@ gb_internal lbValue lb_map_get_proc_for_type(lbModule *m, Type *type) {
lbProcedure *p = lb_create_dummy_procedure(m, proc_name, t_map_get_proc);
string_map_set(&m->gen_procs, proc_name, p);
+
+ p->internal_gen_type = type;
+
lb_begin_procedure_body(p);
defer (lb_end_procedure_body(p));
@@ -1106,15 +1167,6 @@ gb_internal lbValue lb_dynamic_map_reserve(lbProcedure *p, lbValue const &map_pt
return lb_emit_runtime_call(p, "__dynamic_map_reserve", args);
}
-
-struct lbGlobalVariable {
- lbValue var;
- lbValue init;
- DeclInfo *decl;
- bool is_initialized;
-};
-
-
gb_internal lbProcedure *lb_create_objc_names(lbModule *main_module) {
if (build_context.metrics.os != TargetOs_darwin) {
return nullptr;
@@ -1126,30 +1178,763 @@ gb_internal lbProcedure *lb_create_objc_names(lbModule *main_module) {
return p;
}
-gb_internal void lb_finalize_objc_names(lbProcedure *p) {
+String lb_get_objc_type_encoding(Type *t, isize pointer_depth = 0) {
+ // NOTE(harold): See https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html#//apple_ref/doc/uid/TP40008048-CH100
+
+ // NOTE(harold): Darwin targets are always 64-bit. Should we drop this and assume "q" always?
+ #define INT_SIZE_ENCODING (build_context.metrics.int_size == 4 ? "i" : "q")
+ switch (t->kind) {
+ case Type_Basic: {
+ switch (t->Basic.kind) {
+ case Basic_Invalid:
+ return str_lit("?");
+
+ case Basic_llvm_bool:
+ case Basic_bool:
+ case Basic_b8:
+ return str_lit("B");
+
+ case Basic_b16:
+ return str_lit("C");
+ case Basic_b32:
+ return str_lit("I");
+ case Basic_b64:
+ return str_lit("q");
+ case Basic_i8:
+ return str_lit("c");
+ case Basic_u8:
+ return str_lit("C");
+ case Basic_i16:
+ case Basic_i16le:
+ case Basic_i16be:
+ return str_lit("s");
+ case Basic_u16:
+ case Basic_u16le:
+ case Basic_u16be:
+ return str_lit("S");
+ case Basic_i32:
+ case Basic_i32le:
+ case Basic_i32be:
+ return str_lit("i");
+ case Basic_u32le:
+ case Basic_u32:
+ case Basic_u32be:
+ return str_lit("I");
+ case Basic_i64:
+ case Basic_i64le:
+ case Basic_i64be:
+ return str_lit("q");
+ case Basic_u64:
+ case Basic_u64le:
+ case Basic_u64be:
+ return str_lit("Q");
+ case Basic_i128:
+ case Basic_i128le:
+ case Basic_i128be:
+ return str_lit("t");
+ case Basic_u128:
+ case Basic_u128le:
+ case Basic_u128be:
+ return str_lit("T");
+ case Basic_rune:
+ return str_lit("I");
+ case Basic_f16:
+ case Basic_f16le:
+ case Basic_f16be:
+ return str_lit("s"); // @harold: Closest we've got?
+ case Basic_f32:
+ case Basic_f32le:
+ case Basic_f32be:
+ return str_lit("f");
+ case Basic_f64:
+ case Basic_f64le:
+ case Basic_f64be:
+ return str_lit("d");
+
+ case Basic_complex32: return str_lit("{complex32=ss}"); // No f16 encoding, so fallback to i16, as above in Basic_f16*
+ case Basic_complex64: return str_lit("{complex64=ff}");
+ case Basic_complex128: return str_lit("{complex128=dd}");
+ case Basic_quaternion64: return str_lit("{quaternion64=ssss}");
+ case Basic_quaternion128: return str_lit("{quaternion128=ffff}");
+ case Basic_quaternion256: return str_lit("{quaternion256=dddd}");
+
+ case Basic_int:
+ return str_lit(INT_SIZE_ENCODING);
+ case Basic_uint:
+ return build_context.metrics.int_size == 4 ? str_lit("I") : str_lit("Q");
+ case Basic_uintptr:
+ case Basic_rawptr:
+ return str_lit("^v");
+
+ case Basic_string:
+ return build_context.metrics.int_size == 4 ? str_lit("{string=*i}") : str_lit("{string=*q}");
+
+ case Basic_string16:
+ return build_context.metrics.int_size == 4 ? str_lit("{string16=*i}") : str_lit("{string16=*q}");
+
+ case Basic_cstring: return str_lit("*");
+ case Basic_cstring16: return str_lit("*");
+
+
+ case Basic_any: return str_lit("{any=^v^v}"); // rawptr + ^Type_Info
+
+ case Basic_typeid:
+ GB_ASSERT(t->Basic.size == 8);
+ return str_lit("q");
+
+ // Untyped types
+ case Basic_UntypedBool:
+ case Basic_UntypedInteger:
+ case Basic_UntypedFloat:
+ case Basic_UntypedComplex:
+ case Basic_UntypedQuaternion:
+ case Basic_UntypedString:
+ case Basic_UntypedRune:
+ case Basic_UntypedNil:
+ case Basic_UntypedUninit:
+ GB_PANIC("Untyped types cannot be @encoded()");
+ return str_lit("?");
+ }
+ break;
+ }
+
+ case Type_Named:
+ case Type_Struct:
+ case Type_Union: {
+ Type* base = t;
+ if (base->kind == Type_Named) {
+ base = base_type(base);
+ if(base->kind != Type_Struct && base->kind != Type_Union) {
+ return lb_get_objc_type_encoding(base, pointer_depth);
+ }
+ }
+
+ const bool is_union = base->kind == Type_Union;
+ if (!is_union) {
+ // Treat struct as an Objective-C Class?
+ if (has_type_got_objc_class_attribute(t) && pointer_depth == 0) {
+ return str_lit("#");
+ }
+ }
+
+ if (is_type_objc_object(base)) {
+ return str_lit("@");
+ }
+
+
+ gbString s = gb_string_make_reserve(temporary_allocator(), 16);
+ s = gb_string_append_length(s, is_union ? "(" :"{", 1);
+ if (t->kind == Type_Named) {
+ s = gb_string_append_length(s, t->Named.name.text, t->Named.name.len);
+ }
+
+ // Write fields
+ if (pointer_depth < 2) {
+ s = gb_string_append_length(s, "=", 1);
+
+ if (!is_union) {
+ for( auto& f : base->Struct.fields ) {
+ String field_type = lb_get_objc_type_encoding(f->type, pointer_depth);
+ s = gb_string_append_length(s, field_type.text, field_type.len);
+ }
+ } else {
+ for( auto& v : base->Union.variants ) {
+ String variant_type = lb_get_objc_type_encoding(v, pointer_depth);
+ s = gb_string_append_length(s, variant_type.text, variant_type.len);
+ }
+ }
+ }
+
+ s = gb_string_append_length(s, is_union ? ")" :"}", 1);
+
+ return make_string_c(s);
+ }
+
+ case Type_Generic:
+ GB_PANIC("Generic types cannot be @encoded()");
+ return str_lit("?");
+
+ case Type_Pointer: {
+ // NOTE: These types are pointers, so we must check here for special cases
+ // Check for objc_SEL
+ if (internal_check_is_assignable_to(t, t_objc_SEL)) {
+ return str_lit(":");
+ }
+
+ // Check for objc_Class
+ if (internal_check_is_assignable_to(t, t_objc_Class)) {
+ return str_lit("#");
+ }
+
+ String pointee = lb_get_objc_type_encoding(t->Pointer.elem, pointer_depth +1);
+ // Special case for Objective-C Objects
+ if (pointer_depth == 0 && pointee == "@") {
+ return pointee;
+ }
+
+ return concatenate_strings(temporary_allocator(), str_lit("^"), pointee);
+ }
+
+ case Type_MultiPointer:
+ return concatenate_strings(temporary_allocator(), str_lit("^"), lb_get_objc_type_encoding(t->Pointer.elem, pointer_depth +1));
+
+ case Type_Array: {
+ String type_str = lb_get_objc_type_encoding(t->Array.elem, pointer_depth);
+
+ gbString s = gb_string_make_reserve(temporary_allocator(), type_str.len + 8);
+ s = gb_string_append_fmt(s, "[%lld%.*s]", t->Array.count, LIT(type_str));
+ return make_string_c(s);
+ }
+
+ case Type_EnumeratedArray: {
+ String type_str = lb_get_objc_type_encoding(t->EnumeratedArray.elem, pointer_depth);
+
+ gbString s = gb_string_make_reserve(temporary_allocator(), type_str.len + 8);
+ s = gb_string_append_fmt(s, "[%lld%.*s]", t->EnumeratedArray.count, LIT(type_str));
+ return make_string_c(s);
+ }
+
+ case Type_Slice: {
+ String type_str = lb_get_objc_type_encoding(t->Slice.elem, pointer_depth);
+ gbString s = gb_string_make_reserve(temporary_allocator(), type_str.len + 8);
+ s = gb_string_append_fmt(s, "{slice=^%.*s%s}", LIT(type_str), INT_SIZE_ENCODING);
+ return make_string_c(s);
+ }
+
+ case Type_DynamicArray: {
+ String type_str = lb_get_objc_type_encoding(t->DynamicArray.elem, pointer_depth);
+ gbString s = gb_string_make_reserve(temporary_allocator(), type_str.len + 8);
+ s = gb_string_append_fmt(s, "{dynamic=^%.*s%s%sAllocator={?^v}}", LIT(type_str), INT_SIZE_ENCODING, INT_SIZE_ENCODING);
+ return make_string_c(s);
+ }
+
+ case Type_Map:
+ return str_lit("{^v^v{Allocator=?^v}}");
+ case Type_Enum:
+ return lb_get_objc_type_encoding(t->Enum.base_type, pointer_depth);
+ case Type_Tuple:
+ // NOTE(harold): Is this type allowed here?
+ return str_lit("?");
+ case Type_Proc:
+ return str_lit("?");
+ case Type_BitSet: {
+ Type *bitset_integer_type = t->BitSet.underlying;
+ if (!bitset_integer_type) {
+ switch (t->cached_size) {
+ case 1: bitset_integer_type = t_u8; break;
+ case 2: bitset_integer_type = t_u16; break;
+ case 4: bitset_integer_type = t_u32; break;
+ case 8: bitset_integer_type = t_u64; break;
+ case 16: bitset_integer_type = t_u128; break;
+ }
+ }
+ GB_ASSERT_MSG(bitset_integer_type, "Could not determine bit_set integer size for objc_type_encoding");
+
+ return lb_get_objc_type_encoding(bitset_integer_type, pointer_depth);
+ }
+
+ case Type_SimdVector: {
+ String type_str = lb_get_objc_type_encoding(t->SimdVector.elem, pointer_depth);
+ gbString s = gb_string_make_reserve(temporary_allocator(), type_str.len + 5);
+ gb_string_append_fmt(s, "[%lld%.*s]", t->SimdVector.count, LIT(type_str));
+ return make_string_c(s);
+ }
+
+ case Type_Matrix: {
+ String type_str = lb_get_objc_type_encoding(t->Matrix.elem, pointer_depth);
+ gbString s = gb_string_make_reserve(temporary_allocator(), type_str.len + 5);
+ i64 element_count = t->Matrix.column_count * t->Matrix.row_count;
+ gb_string_append_fmt(s, "[%lld%.*s]", element_count, LIT(type_str));
+ return make_string_c(s);
+ }
+
+ case Type_BitField:
+ return lb_get_objc_type_encoding(t->BitField.backing_type, pointer_depth);
+ case Type_SoaPointer: {
+ gbString s = gb_string_make_reserve(temporary_allocator(), 8);
+ s = gb_string_append_fmt(s, "{=^v%s}", INT_SIZE_ENCODING);
+ return make_string_c(s);
+ }
+
+ } // End switch t->kind
+ #undef INT_SIZE_ENCODING
+
+ GB_PANIC("Unreachable");
+ return str_lit("");
+}
+
+struct lbObjCGlobalClass {
+ lbObjCGlobal g;
+ union {
+ lbValue class_value; // Local registered class value
+ lbAddr class_global; // Global class pointer. Placeholder for class implementations which are registered in order of definition.
+ };
+};
+
+gb_internal void lb_register_objc_thing(
+ StringSet &handled,
+ lbModule *m,
+ Array<lbValue> &args,
+ Array<lbObjCGlobalClass> &class_impls,
+ StringMap<lbObjCGlobalClass> &class_map,
+ lbProcedure *p,
+ lbObjCGlobal const &g,
+ char const *call
+) {
+ if (string_set_update(&handled, g.name)) {
+ return;
+ }
+
+ lbAddr addr = {};
+ lbValue *found = string_map_get(&m->members, g.global_name);
+ if (found) {
+ addr = lb_addr(*found);
+ } else {
+ lbValue v = {};
+ LLVMTypeRef t = lb_type(m, g.type);
+ v.value = LLVMAddGlobal(m->mod, t, g.global_name);
+ v.type = alloc_type_pointer(g.type);
+ addr = lb_addr(v);
+ LLVMSetInitializer(v.value, LLVMConstNull(t));
+ }
+
+ // If this class requires an implementation, save it for registration below.
+ if (g.class_impl_type != nullptr) {
+
+ // Make sure the superclass has been initialized before us
+ auto &tn = g.class_impl_type->Named.type_name->TypeName;
+ Type *superclass = tn.objc_superclass;
+ if (superclass != nullptr) {
+ auto& superclass_global = string_map_must_get(&class_map, superclass->Named.type_name->TypeName.objc_class_name);
+ lb_register_objc_thing(handled, m, args, class_impls, class_map, p, superclass_global.g, call);
+ GB_ASSERT(superclass_global.class_global.addr.value);
+ }
+
+ lbObjCGlobalClass impl_global = {};
+ impl_global.g = g;
+ impl_global.class_global = addr;
+
+ array_add(&class_impls, impl_global);
+
+ lbObjCGlobalClass* class_global = string_map_get(&class_map, g.name);
+ if (class_global != nullptr) {
+ class_global->class_global = addr;
+ }
+ }
+ else {
+ lbValue class_ptr = {};
+ lbValue class_name = lb_const_value(m, t_cstring, exact_value_string(g.name));
+
+ args.count = 1;
+ args[0] = class_name;
+ class_ptr = lb_emit_runtime_call(p, call, args);
+
+ lb_addr_store(p, addr, class_ptr);
+
+ lbObjCGlobalClass* class_global = string_map_get(&class_map, g.name);
+ if (class_global != nullptr) {
+ class_global->class_value = class_ptr;
+ }
+ }
+}
+
+gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) {
if (p == nullptr) {
return;
}
lbModule *m = p->module;
+ GB_ASSERT(m == &p->module->gen->default_module);
TEMPORARY_ALLOCATOR_GUARD();
- auto args = array_make<lbValue>(temporary_allocator(), 1);
+ StringSet handled = {};
+ string_set_init(&handled);
+ defer (string_set_destroy(&handled));
+
+ auto args = array_make<lbValue>(temporary_allocator(), 3, 8);
+ auto class_impls = array_make<lbObjCGlobalClass>(temporary_allocator(), 0, 16);
+
+ // Register all class implementations unconditionally, even if not statically referenced
+ for (Entity *e = {}; mpsc_dequeue(&gen->info->objc_class_implementations, &e); /**/) {
+ GB_ASSERT(e->kind == Entity_TypeName && e->TypeName.objc_is_implementation);
+ lb_handle_objc_find_or_register_class(p, e->TypeName.objc_class_name, e->type);
+ }
+
+ // Ensure classes that have been implicitly referenced through
+ // the objc_superclass attribute have a global variable available for them.
+ TypeSet class_set{};
+ type_set_init(&class_set, gen->objc_classes.count+16);
+ defer (type_set_destroy(&class_set));
+
+ auto referenced_classes = array_make<lbObjCGlobal>(temporary_allocator());
+ for (lbObjCGlobal g = {}; mpsc_dequeue(&gen->objc_classes, &g); /**/) {
+ array_add(&referenced_classes, g);
+
+ Type *cls = g.class_impl_type;
+ while (cls) {
+ if (type_set_update(&class_set, cls)) {
+ break;
+ }
+ GB_ASSERT(cls->kind == Type_Named);
+
+ cls = cls->Named.type_name->TypeName.objc_superclass;
+ }
+ }
+
+ for (auto pair : class_set) {
+ auto& tn = pair.type->Named.type_name->TypeName;
+ Type *class_impl = !tn.objc_is_implementation ? nullptr : pair.type;
+ lb_handle_objc_find_or_register_class(p, tn.objc_class_name, class_impl);
+ }
+ for (lbObjCGlobal g = {}; mpsc_dequeue(&gen->objc_classes, &g); /**/) {
+ array_add( &referenced_classes, g );
+ }
+
+ // Add all class globals to a map so that we can look them up dynamically
+ // in order to resolve out-of-order because classes that are being implemented
+ // require their superclasses to be registered before them.
+ StringMap<lbObjCGlobalClass> global_class_map{};
+ string_map_init(&global_class_map, (usize)gen->objc_classes.count);
+ defer (string_map_destroy(&global_class_map));
+
+ for (lbObjCGlobal g : referenced_classes) {
+ string_map_set(&global_class_map, g.name, lbObjCGlobalClass{g});
+ }
LLVMSetLinkage(p->value, LLVMInternalLinkage);
lb_begin_procedure_body(p);
- for (auto const &entry : m->objc_classes) {
- String name = entry.key;
- args[0] = lb_const_value(m, t_cstring, exact_value_string(name));
- lbValue ptr = lb_emit_runtime_call(p, "objc_lookUpClass", args);
- lb_addr_store(p, entry.value.local_module_addr, ptr);
+
+ // Register class globals, gathering classes that must be implemented
+ for (auto& kv : global_class_map) {
+ lb_register_objc_thing(handled, m, args, class_impls, global_class_map, p, kv.value.g, "objc_lookUpClass");
+ }
+
+ // Prefetch selectors for implemented methods so that they can also be registered.
+ for (const auto& cd : class_impls) {
+ auto& g = cd.g;
+ Type *class_type = g.class_impl_type;
+
+ Array<ObjcMethodData>* methods = map_get(&m->info->objc_method_implementations, class_type);
+ if (!methods) {
+ continue;
+ }
+
+ for (const ObjcMethodData& md : *methods) {
+ lb_handle_objc_find_or_register_selector(p, md.ac.objc_selector);
+ }
+ }
+
+ // Now we can register all referenced selectors
+ for (lbObjCGlobal g = {}; mpsc_dequeue(&gen->objc_selectors, &g); /**/) {
+ lb_register_objc_thing(handled, m, args, class_impls, global_class_map, p, g, "sel_registerName");
+ }
+
+
+ // Emit method wrapper implementations and registration
+ auto wrapper_args = array_make<Type *>(temporary_allocator(), 2, 8);
+ auto get_context_args = array_make<lbValue>(temporary_allocator(), 1);
+
+
+ PtrMap<Type *, lbObjCGlobal> ivar_map{};
+ map_init(&ivar_map, gen->objc_ivars.count);
+
+ for (lbObjCGlobal g = {}; mpsc_dequeue(&gen->objc_ivars, &g); /**/) {
+ map_set(&ivar_map, g.class_impl_type, g);
}
- for (auto const &entry : m->objc_selectors) {
- String name = entry.key;
- args[0] = lb_const_value(m, t_cstring, exact_value_string(name));
- lbValue ptr = lb_emit_runtime_call(p, "sel_registerName", args);
- lb_addr_store(p, entry.value.local_module_addr, ptr);
+ for (const auto &cd : class_impls) {
+ auto &g = cd.g;
+
+ Type *class_type = g.class_impl_type;
+ Type *class_ptr_type = alloc_type_pointer(class_type);
+
+ // Begin class registration: create class pair and update global reference
+ lbValue class_value = {};
+
+ {
+ lbValue superclass_value = lb_const_nil(m, t_objc_Class);
+
+ auto& tn = class_type->Named.type_name->TypeName;
+ Type *superclass = tn.objc_superclass;
+
+ if (superclass != nullptr) {
+ auto& superclass_global = string_map_must_get(&global_class_map, superclass->Named.type_name->TypeName.objc_class_name);
+ superclass_value = superclass_global.class_value;
+ }
+
+ args.count = 3;
+ args[0] = superclass_value;
+ args[1] = lb_const_value(m, t_cstring, exact_value_string(g.name));
+ args[2] = lb_const_int(m, t_uint, 0);
+ class_value = lb_emit_runtime_call(p, "objc_allocateClassPair", args);
+
+ lbObjCGlobalClass &mapped_global = string_map_must_get(&global_class_map, tn.objc_class_name);
+ lb_addr_store(p, mapped_global.class_global, class_value);
+
+ mapped_global.class_value = class_value;
+ }
+
+
+ Type *ivar_type = class_type->Named.type_name->TypeName.objc_ivar;
+
+ Entity *context_provider = class_type->Named.type_name->TypeName.objc_context_provider;
+ Type *contex_provider_self_ptr_type = nullptr;
+ Type *contex_provider_self_named_type = nullptr;
+ bool is_context_provider_ivar = false;
+ lbValue context_provider_proc_value{};
+
+ if (context_provider) {
+ context_provider_proc_value = lb_find_procedure_value_from_entity(m, context_provider);
+
+ contex_provider_self_ptr_type = base_type(context_provider->type->Proc.params->Tuple.variables[0]->type);
+ GB_ASSERT(contex_provider_self_ptr_type->kind == Type_Pointer);
+ contex_provider_self_named_type = base_named_type(type_deref(contex_provider_self_ptr_type));
+
+ is_context_provider_ivar = ivar_type != nullptr && internal_check_is_assignable_to(contex_provider_self_named_type, ivar_type);
+ }
+
+ Array<ObjcMethodData> *methods = map_get(&m->info->objc_method_implementations, class_type);
+ if (!methods) {
+ continue;
+ }
+
+ // Check if it has any class methods ahead of time so that we know to grab the meta_class
+ lbValue meta_class_value = {};
+ for (const ObjcMethodData &md : *methods) {
+ if (!md.ac.objc_is_class_method) {
+ continue;
+ }
+
+ // Get the meta_class
+ args.count = 1;
+ args[0] = class_value;
+ meta_class_value = lb_emit_runtime_call(p, "object_getClass", args);
+
+ break;
+ }
+
+ for (const ObjcMethodData &md : *methods) {
+ GB_ASSERT( md.proc_entity->kind == Entity_Procedure);
+ Type *method_type = md.proc_entity->type;
+
+ String proc_name = make_string_c("__$objc_method::");
+ proc_name = concatenate_strings(temporary_allocator(), proc_name, g.name);
+ proc_name = concatenate_strings(temporary_allocator(), proc_name, str_lit("::"));
+ proc_name = concatenate_strings( permanent_allocator(), proc_name, md.ac.objc_name);
+
+ wrapper_args.count = 2;
+ wrapper_args[0] = md.ac.objc_is_class_method ? t_objc_Class : class_ptr_type;
+ wrapper_args[1] = t_objc_SEL;
+
+ isize method_param_count = method_type->Proc.param_count;
+ isize method_param_offset = 0;
+
+ if (!md.ac.objc_is_class_method) {
+ GB_ASSERT(method_param_count >= 1);
+ method_param_count -= 1;
+ method_param_offset = 1;
+ }
+
+ for (isize i = 0; i < method_param_count; i++) {
+ array_add(&wrapper_args, method_type->Proc.params->Tuple.variables[method_param_offset+i]->type);
+ }
+
+ Type *wrapper_args_tuple = alloc_type_tuple_from_field_types(wrapper_args.data, wrapper_args.count, false, true);
+ Type *wrapper_results_tuple = nullptr;
+
+ if (method_type->Proc.result_count > 0) {
+ GB_ASSERT(method_type->Proc.result_count == 1);
+ wrapper_results_tuple = alloc_type_tuple_from_field_types(&method_type->Proc.results->Tuple.variables[0]->type, 1, false, true);
+ }
+
+ Type *wrapper_proc_type = alloc_type_proc(nullptr, wrapper_args_tuple, wrapper_args_tuple->Tuple.variables.count,
+ wrapper_results_tuple, method_type->Proc.result_count, false, ProcCC_CDecl);
+
+ lbProcedure *wrapper_proc = lb_create_dummy_procedure(m, proc_name, wrapper_proc_type);
+
+ lb_add_function_type_attributes(wrapper_proc->value, lb_get_function_type(m, wrapper_proc_type), ProcCC_CDecl);
+
+ // Emit the wrapper
+ // LLVMSetLinkage(wrapper_proc->value, LLVMInternalLinkage);
+ LLVMSetDLLStorageClass(wrapper_proc->value, LLVMDLLExportStorageClass);
+ lb_add_attribute_to_proc(wrapper_proc->module, wrapper_proc->value, "nounwind");
+
+ lb_begin_procedure_body(wrapper_proc);
+ {
+ LLVMValueRef context_addr = nullptr;
+ if (method_type->Proc.calling_convention == ProcCC_Odin) {
+ GB_ASSERT(context_provider);
+
+ // Emit the get odin context call
+ get_context_args[0] = lbValue {
+ wrapper_proc->raw_input_parameters[0],
+ contex_provider_self_ptr_type,
+ };
+
+ if (is_context_provider_ivar) {
+ // The context provider takes the ivar's type.
+ // Emit an objc_ivar_get call and use that pointer for 'self' instead.
+ lbValue real_self {
+ wrapper_proc->raw_input_parameters[0],
+ class_ptr_type
+ };
+ get_context_args[0] = lb_handle_objc_ivar_for_objc_object_pointer(wrapper_proc, real_self);
+ }
+
+ lbValue context = lb_emit_call(wrapper_proc, context_provider_proc_value, get_context_args);
+ context_addr = lb_address_from_load(wrapper_proc, context).value;//lb_address_from_load_or_generate_local(wrapper_proc, context));
+ // context_addr = LLVMGetOperand(context.value, 0);
+ }
+
+ isize method_forward_arg_count = method_param_count + method_param_offset;
+ isize method_forward_return_arg_offset = 0;
+ auto raw_method_args = array_make<LLVMValueRef>(temporary_allocator(), 0, method_forward_arg_count+1);
+
+ lbValue method_proc_value = lb_find_procedure_value_from_entity(m, md.proc_entity);
+ lbFunctionType* ft = lb_get_function_type(m, method_type);
+ bool has_return = false;
+ lbArgKind return_kind = {};
+
+ if (wrapper_results_tuple != nullptr) {
+ has_return = true;
+ return_kind = ft->ret.kind;
+
+ if (return_kind == lbArg_Indirect) {
+ method_forward_return_arg_offset = 1;
+ array_add(&raw_method_args, wrapper_proc->return_ptr.addr.value);
+ }
+ }
+
+ if (!md.ac.objc_is_class_method) {
+ array_add(&raw_method_args, wrapper_proc->raw_input_parameters[method_forward_return_arg_offset]);
+ }
+
+ for (isize i = 0; i < method_param_count; i++) {
+ array_add(&raw_method_args, wrapper_proc->raw_input_parameters[i+2+method_forward_return_arg_offset]);
+ }
+
+ if (method_type->Proc.calling_convention == ProcCC_Odin) {
+ array_add(&raw_method_args, context_addr);
+ }
+
+ // Call real procedure for method from here, passing the parameters expected, if any.
+ LLVMTypeRef fnp = lb_type_internal_for_procedures_raw(m, method_type);
+ LLVMValueRef ret_val_raw = LLVMBuildCall2(wrapper_proc->builder, fnp, method_proc_value.value, raw_method_args.data, (unsigned)raw_method_args.count, "");
+
+ if (has_return && return_kind != lbArg_Indirect) {
+ LLVMBuildRet(wrapper_proc->builder, ret_val_raw);
+ }
+ else {
+ LLVMBuildRetVoid(wrapper_proc->builder);
+ }
+ }
+ lb_end_procedure_body(wrapper_proc);
+
+ // Add the method to the class
+ String method_encoding = str_lit("v");
+
+ GB_ASSERT(method_type->Proc.result_count <= 1);
+ if (method_type->Proc.result_count != 0) {
+ method_encoding = lb_get_objc_type_encoding(method_type->Proc.results->Tuple.variables[0]->type);
+ }
+
+ if (!md.ac.objc_is_class_method) {
+ method_encoding = concatenate_strings(temporary_allocator(), method_encoding, str_lit("@:"));
+ } else {
+ method_encoding = concatenate_strings(temporary_allocator(), method_encoding, str_lit("#:"));
+ }
+
+ for (isize i = 0; i < method_param_count; i++) {
+ Type *param_type = method_type->Proc.params->Tuple.variables[i + method_param_offset]->type;
+ String param_encoding = lb_get_objc_type_encoding(param_type);
+
+ method_encoding = concatenate_strings(temporary_allocator(), method_encoding, param_encoding);
+ }
+
+ // Emit method registration
+ lbAddr* sel_address = string_map_get(&m->objc_selectors, md.ac.objc_selector);
+ GB_ASSERT(sel_address);
+ lbValue selector_value = lb_addr_load(p, *sel_address);
+
+ lbValue target_class = !md.ac.objc_is_class_method ? class_value : meta_class_value;
+
+ args.count = 4;
+ args[0] = target_class; // Class
+ args[1] = selector_value; // SEL
+ args[2] = lbValue { wrapper_proc->value, wrapper_proc->type };
+ args[3] = lb_const_value(m, t_cstring, exact_value_string(method_encoding));
+
+ // TODO(harold): Emit check BOOL result and panic if false?
+ lb_emit_runtime_call(p, "class_addMethod", args);
+
+ } // End methods
+
+ // Add ivar if we have one
+ if (ivar_type != nullptr) {
+ // Register a single ivar for this class
+ Type *ivar_base = ivar_type->Named.base;
+
+ // @note(harold): The alignment is supposed to be passed as log2(alignment): https://developer.apple.com/documentation/objectivec/class_addivar(_:_:_:_:_:)?language=objc
+ const i64 size = type_size_of(ivar_base);
+ const i64 alignment = (i64)floor_log2((u64)type_align_of(ivar_base));
+
+ // NOTE(harold): I've opted to not emit the type encoding for ivars in order to keep the data private.
+ // If there is desire in the future to emit the type encoding for introspection through the Obj-C runtime,
+ // then perhaps an option can be added for it then.
+ // Should we pass the actual type encoding? Might not be ideal for obfuscation.
+ String ivar_name = str_lit("__$ivar");
+ String ivar_types = str_lit("{= }"); //lb_get_objc_type_encoding(ivar_type);
+ args.count = 5;
+ args[0] = class_value;
+ args[1] = lb_const_value(m, t_cstring, exact_value_string(ivar_name));
+ args[2] = lb_const_value(m, t_uint, exact_value_u64((u64)size));
+ args[3] = lb_const_value(m, t_u8, exact_value_u64((u64)alignment));
+ args[4] = lb_const_value(m, t_cstring, exact_value_string(ivar_types));
+ lb_emit_runtime_call(p, "class_addIvar", args);
+ }
+
+ // Complete the class registration
+ args.count = 1;
+ args[0] = class_value;
+ lb_emit_runtime_call(p, "objc_registerClassPair", args);
+ }
+
+ // Register ivar offsets for any `objc_ivar_get` expressions emitted.
+ for (auto const& kv : ivar_map) {
+ lbObjCGlobal const& g = kv.value;
+ lbAddr ivar_addr = {};
+ lbValue *found = string_map_get(&m->members, g.global_name);
+
+ if (found) {
+ ivar_addr = lb_addr(*found);
+ GB_ASSERT(ivar_addr.addr.type == t_int_ptr);
+ } else {
+ // Defined in an external package, define it now in the main package
+ LLVMTypeRef t = lb_type(m, t_int);
+
+ lbValue global = {};
+ global.value = LLVMAddGlobal(m->mod, t, g.global_name);
+ global.type = t_int_ptr;
+
+ LLVMSetInitializer(global.value, LLVMConstInt(t, 0, true));
+
+ ivar_addr = lb_addr(global);
+ }
+
+ String class_name = g.class_impl_type->Named.type_name->TypeName.objc_class_name;
+ lbValue class_value = string_map_must_get(&global_class_map, class_name).class_value;
+
+ args.count = 2;
+ args[0] = class_value;
+ args[1] = lb_const_value(m, t_cstring, exact_value_string(str_lit("__$ivar")));
+ lbValue ivar = lb_emit_runtime_call(p, "class_getInstanceVariable", args);
+
+ args.count = 1;
+ args[0] = ivar;
+ lbValue ivar_offset = lb_emit_runtime_call(p, "ivar_getOffset", args);
+ lbValue ivar_offset_int = lb_emit_conv(p, ivar_offset, t_int);
+
+ lb_addr_store(p, ivar_addr, ivar_offset_int);
}
lb_end_procedure_body(p);
@@ -1179,12 +1964,16 @@ gb_internal void lb_verify_function(lbModule *m, lbProcedure *p, bool dump_ll=fa
}
gb_internal WORKER_TASK_PROC(lb_llvm_module_verification_worker_proc) {
+ if (LLVM_IGNORE_VERIFICATION) {
+ return 0;
+ }
+
char *llvm_error = nullptr;
defer (LLVMDisposeMessage(llvm_error));
lbModule *m = cast(lbModule *)data;
if (LLVMVerifyModule(m->mod, LLVMReturnStatusAction, &llvm_error)) {
- gb_printf_err("LLVM Error:\n%s\n", llvm_error);
+ gb_printf_err("LLVM Error in module %s:\n%s\n", m->module_name, llvm_error);
if (build_context.keep_temp_files) {
TIME_SECTION("LLVM Print Module to File");
String filepath_ll = lb_filepath_ll_for_module(m);
@@ -1200,114 +1989,151 @@ gb_internal WORKER_TASK_PROC(lb_llvm_module_verification_worker_proc) {
return 0;
}
+gb_internal bool lb_init_global_var(lbModule *m, lbProcedure *p, Entity *e, Ast *init_expr, lbGlobalVariable &var) {
+ if (init_expr != nullptr) {
+ lbValue init = lb_build_expr(p, init_expr);
+ if (init.value == nullptr) {
+ LLVMTypeRef global_type = llvm_addr_type(p->module, var.var);
+ if (is_type_untyped_nil(init.type)) {
+ LLVMSetInitializer(var.var.value, LLVMConstNull(global_type));
+ var.is_initialized = true;
+ if (e->Variable.is_rodata) {
+ LLVMSetGlobalConstant(var.var.value, true);
+ }
+ return true;
+ }
+ GB_PANIC("Invalid init value, got %s", expr_to_string(init_expr));
+ }
-gb_internal lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProcedure *objc_names, Array<lbGlobalVariable> &global_variables) { // Startup Runtime
- Type *proc_type = alloc_type_proc(nullptr, nullptr, 0, nullptr, 0, false, ProcCC_Odin);
+ if (is_type_any(e->type)) {
+ var.init = init;
+ } else if (lb_is_const_or_global(init)) {
+ if (!var.is_initialized) {
+ if (is_type_proc(init.type)) {
+ init.value = LLVMConstPointerCast(init.value, lb_type(p->module, init.type));
+ }
+ LLVMSetInitializer(var.var.value, init.value);
+ var.is_initialized = true;
+
+ if (e->Variable.is_rodata) {
+ LLVMSetGlobalConstant(var.var.value, true);
+ }
+ return true;
+ }
+ } else {
+ var.init = init;
+ }
+ }
+
+ if (var.init.value != nullptr) {
+ GB_ASSERT(!var.is_initialized);
+ Type *t = type_deref(var.var.type);
+
+ if (is_type_any(t)) {
+ // NOTE(bill): Edge case for 'any' type
+ Type *var_type = default_type(var.init.type);
+ gbString var_name = gb_string_make(permanent_allocator(), "__$global_any::");
+ gbString e_str = string_canonical_entity_name(temporary_allocator(), e);
+ var_name = gb_string_append_length(var_name, e_str, gb_strlen(e_str));
+ lbAddr g = lb_add_global_generated_with_name(m, var_type, {}, make_string_c(var_name));
+ lb_addr_store(p, g, var.init);
+ lbValue gp = lb_addr_get_ptr(p, g);
+
+ lbValue data = lb_emit_struct_ep(p, var.var, 0);
+ lbValue ti = lb_emit_struct_ep(p, var.var, 1);
+ lb_emit_store(p, data, lb_emit_conv(p, gp, t_rawptr));
+ lb_emit_store(p, ti, lb_typeid(p->module, var_type));
+ } else {
+ LLVMTypeRef vt = llvm_addr_type(p->module, var.var);
+ lbValue src0 = lb_emit_conv(p, var.init, t);
+ LLVMValueRef src = OdinLLVMBuildTransmute(p, src0.value, vt);
+ LLVMValueRef dst = var.var.value;
+ LLVMBuildStore(p->builder, src, dst);
+ }
+
+ var.is_initialized = true;
+ }
+ return false;
+}
- lbProcedure *p = lb_create_dummy_procedure(main_module, str_lit(LB_STARTUP_RUNTIME_PROC_NAME), proc_type);
- p->is_startup = true;
- lb_add_attribute_to_proc(p->module, p->value, "optnone");
- lb_add_attribute_to_proc(p->module, p->value, "noinline");
+gb_internal void lb_create_startup_runtime_generate_body(lbModule *m, lbProcedure *p) {
lb_begin_procedure_body(p);
- lb_setup_type_info_data(main_module);
+ lb_setup_type_info_data(m);
- if (objc_names) {
- LLVMBuildCall2(p->builder, lb_type_internal_for_procedures_raw(main_module, objc_names->type), objc_names->value, nullptr, 0, "");
+ if (p->objc_names) {
+ LLVMBuildCall2(p->builder, lb_type_internal_for_procedures_raw(m, p->objc_names->type), p->objc_names->value, nullptr, 0, "");
}
+ Type *dummy_type = alloc_type_proc(nullptr, nullptr, 0, nullptr, 0, false, ProcCC_Odin);
+ LLVMTypeRef raw_dummy_type = lb_type_internal_for_procedures_raw(m, dummy_type);
- for (auto &var : global_variables) {
+ for (auto &var : *p->global_variables) {
if (var.is_initialized) {
continue;
}
- lbModule *entity_module = main_module;
+ lbModule *entity_module = m;
Entity *e = var.decl->entity;
GB_ASSERT(e->kind == Entity_Variable);
e->code_gen_module = entity_module;
-
Ast *init_expr = var.decl->init_expr;
- if (init_expr != nullptr) {
- lbValue init = lb_build_expr(p, init_expr);
- if (init.value == nullptr) {
- LLVMTypeRef global_type = llvm_addr_type(p->module, var.var);
- if (is_type_untyped_nil(init.type)) {
- LLVMSetInitializer(var.var.value, LLVMConstNull(global_type));
- var.is_initialized = true;
-
- if (e->Variable.is_rodata) {
- LLVMSetGlobalConstant(var.var.value, true);
- }
- continue;
- }
- GB_PANIC("Invalid init value, got %s", expr_to_string(init_expr));
- }
-
- if (is_type_any(e->type) || is_type_union(e->type)) {
- var.init = init;
- } else if (lb_is_const_or_global(init)) {
- if (!var.is_initialized) {
- if (is_type_proc(init.type)) {
- init.value = LLVMConstPointerCast(init.value, lb_type(p->module, init.type));
- }
- LLVMSetInitializer(var.var.value, init.value);
- var.is_initialized = true;
- if (e->Variable.is_rodata) {
- LLVMSetGlobalConstant(var.var.value, true);
- }
- continue;
- }
- } else {
- var.init = init;
- }
+ if (init_expr == nullptr && var.init.value == nullptr) {
+ continue;
}
- if (var.init.value != nullptr) {
- GB_ASSERT(!var.is_initialized);
- Type *t = type_deref(var.var.type);
-
- if (is_type_any(t)) {
- // NOTE(bill): Edge case for 'any' type
- Type *var_type = default_type(var.init.type);
- gbString var_name = gb_string_make(permanent_allocator(), "__$global_any::");
- gbString e_str = string_canonical_entity_name(temporary_allocator(), e);
- var_name = gb_string_append_length(var_name, e_str, gb_strlen(e_str));
- lbAddr g = lb_add_global_generated_with_name(main_module, var_type, var.init, make_string_c(var_name));
- lb_addr_store(p, g, var.init);
- lbValue gp = lb_addr_get_ptr(p, g);
-
- lbValue data = lb_emit_struct_ep(p, var.var, 0);
- lbValue ti = lb_emit_struct_ep(p, var.var, 1);
- lb_emit_store(p, data, lb_emit_conv(p, gp, t_rawptr));
- lb_emit_store(p, ti, lb_type_info(p, var_type));
- } else {
- LLVMTypeRef vt = llvm_addr_type(p->module, var.var);
- lbValue src0 = lb_emit_conv(p, var.init, t);
- LLVMValueRef src = OdinLLVMBuildTransmute(p, src0.value, vt);
- LLVMValueRef dst = var.var.value;
- LLVMBuildStore(p->builder, src, dst);
- }
+ if (type_size_of(e->type) > 8) {
+ String ename = lb_get_entity_name(m, e);
+ gbString name = gb_string_make(permanent_allocator(), "");
+ name = gb_string_appendc(name, "__$startup$");
+ name = gb_string_append_length(name, ename.text, ename.len);
- var.is_initialized = true;
- }
+ lbProcedure *dummy = lb_create_dummy_procedure(m, make_string_c(name), dummy_type);
+ LLVMSetVisibility(dummy->value, LLVMHiddenVisibility);
+ LLVMSetLinkage(dummy->value, LLVMWeakAnyLinkage);
+ lb_begin_procedure_body(dummy);
+ lb_init_global_var(m, dummy, e, init_expr, var);
+ lb_end_procedure_body(dummy);
+ LLVMValueRef context_ptr = lb_find_or_generate_context_ptr(p).addr.value;
+ LLVMBuildCall2(p->builder, raw_dummy_type, dummy->value, &context_ptr, 1, "");
+ } else {
+ lb_init_global_var(m, p, e, init_expr, var);
+ }
}
- CheckerInfo *info = main_module->gen->info;
-
+ CheckerInfo *info = m->gen->info;
+
for (Entity *e : info->init_procedures) {
- lbValue value = lb_find_procedure_value_from_entity(main_module, e);
+ lbValue value = lb_find_procedure_value_from_entity(m, e);
lb_emit_call(p, value, {}, ProcInlining_none);
}
lb_end_procedure_body(p);
+}
+
+
+gb_internal lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProcedure *objc_names, Array<lbGlobalVariable> &global_variables) { // Startup Runtime
+ Type *proc_type = alloc_type_proc(nullptr, nullptr, 0, nullptr, 0, false, ProcCC_Odin);
+
+ lbProcedure *p = lb_create_dummy_procedure(main_module, str_lit(LB_STARTUP_RUNTIME_PROC_NAME), proc_type);
+ p->is_startup = true;
+ lb_add_attribute_to_proc(p->module, p->value, "optnone");
+ lb_add_attribute_to_proc(p->module, p->value, "noinline");
+
+ // Make sure shared libraries call their own runtime startup on Linux.
+ LLVMSetVisibility(p->value, LLVMHiddenVisibility);
+ LLVMSetLinkage(p->value, LLVMWeakAnyLinkage);
+
+ p->global_variables = &global_variables;
+ p->objc_names = objc_names;
+
+ lb_create_startup_runtime_generate_body(main_module, p);
- lb_verify_function(main_module, p);
return p;
}
@@ -1319,6 +2145,10 @@ gb_internal lbProcedure *lb_create_cleanup_runtime(lbModule *main_module) { // C
lb_add_attribute_to_proc(p->module, p->value, "optnone");
lb_add_attribute_to_proc(p->module, p->value, "noinline");
+ // Make sure shared libraries call their own runtime cleanup on Linux.
+ LLVMSetVisibility(p->value, LLVMHiddenVisibility);
+ LLVMSetLinkage(p->value, LLVMWeakAnyLinkage);
+
lb_begin_procedure_body(p);
CheckerInfo *info = main_module->gen->info;
@@ -1344,7 +2174,7 @@ gb_internal WORKER_TASK_PROC(lb_generate_procedures_and_types_per_module) {
for (Entity *e : m->global_procedures_to_create) {
(void)lb_get_entity_name(m, e);
- array_add(&m->procedures_to_generate, lb_create_procedure(m, e));
+ mpsc_enqueue(&m->procedures_to_generate, lb_create_procedure(m, e));
}
return 0;
}
@@ -1368,8 +2198,6 @@ gb_internal GB_COMPARE_PROC(llvm_global_entity_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;
-
for (Entity *e : info->entities) {
String name = e->token.string;
Scope * scope = e->scope;
@@ -1393,7 +2221,7 @@ gb_internal void lb_create_global_procedures_and_types(lbGenerator *gen, Checker
break;
case Entity_Constant:
if (build_context.ODIN_DEBUG) {
- add_debug_info_for_global_constant_from_entity(gen, e);
+ lb_add_debug_info_for_global_constant_from_entity(gen, e);
}
break;
}
@@ -1406,18 +2234,28 @@ gb_internal void lb_create_global_procedures_and_types(lbGenerator *gen, Checker
}
}
- if (!polymorphic_struct && !ptr_set_exists(min_dep_set, e)) {
+ if (!polymorphic_struct && e->min_dep_count.load(std::memory_order_relaxed) == 0) {
// NOTE(bill): Nothing depends upon it so doesn't need to be built
continue;
}
+ // if (!polymorphic_struct && !ptr_set_exists(min_dep_set, e)) {
+ // // NOTE(bill): Nothing depends upon it so doesn't need to be built
+ // continue;
+ // }
+
lbModule *m = &gen->default_module;
if (USE_SEPARATE_MODULES) {
- m = lb_module_of_entity(gen, e);
+ m = lb_module_of_entity(gen, e, m);
}
GB_ASSERT(m != nullptr);
if (e->kind == Entity_Procedure) {
+ if (e->Procedure.is_foreign && e->Procedure.is_objc_impl_or_import) {
+ // Do not generate declarations for foreign Objective-C methods. These are called indirectly through the Objective-C runtime.
+ continue;
+ }
+
array_add(&m->global_procedures_to_create, e);
} else if (e->kind == Entity_TypeName) {
array_add(&m->global_types_to_create, e);
@@ -1529,7 +2367,7 @@ gb_internal WORKER_TASK_PROC(lb_llvm_function_pass_per_module) {
lb_llvm_function_pass_per_function_internal(m, m->gen->objc_names);
}
- for (lbProcedure *p : m->procedures_to_generate) {
+ MUTEX_GUARD_BLOCK(&m->generated_procedures_mutex) for (lbProcedure *p : m->generated_procedures) {
if (p->body != nullptr) { // Build Procedure
lbFunctionPassManagerKind pass_manager_kind = lbFunctionPassManager_default;
if (p->flags & lbProcedureFlag_WithoutMemcpyPass) {
@@ -1568,17 +2406,23 @@ gb_internal WORKER_TASK_PROC(lb_llvm_function_pass_per_module) {
}
+void lb_remove_unused_functions_and_globals(lbGenerator *gen) {
+ for (auto &entry : gen->modules) {
+ lbModule *m = entry.value;
+ lb_run_remove_unused_function_pass(m);
+ lb_run_remove_unused_globals_pass(m);
+ }
+}
+
struct lbLLVMModulePassWorkerData {
lbModule *m;
LLVMTargetMachineRef target_machine;
+ bool do_threading;
};
gb_internal WORKER_TASK_PROC(lb_llvm_module_pass_worker_proc) {
auto wd = cast(lbLLVMModulePassWorkerData *)data;
- lb_run_remove_unused_function_pass(wd->m);
- lb_run_remove_unused_globals_pass(wd->m);
-
LLVMPassManagerRef module_pass_manager = LLVMCreatePassManager();
lb_populate_module_pass_manager(wd->target_machine, module_pass_manager, build_context.optimization_level);
LLVMRunPassManager(module_pass_manager, wd->m->mod);
@@ -1591,797 +2435,7 @@ gb_internal WORKER_TASK_PROC(lb_llvm_module_pass_worker_proc) {
LLVMPassBuilderOptionsRef pb_options = LLVMCreatePassBuilderOptions();
defer (LLVMDisposePassBuilderOptions(pb_options));
- switch (build_context.optimization_level) {
- case -1:
- array_add(&passes, "function(annotation-remarks)");
- break;
- case 0:
- array_add(&passes, "always-inline");
- array_add(&passes, "function(annotation-remarks)");
- break;
- case 1:
-// default<Os>
-// Passes removed: coro, openmp, sroa
-#if LLVM_VERSION_MAJOR == 17
- array_add(&passes, u8R"(
-annotation2metadata,
-forceattrs,
-inferattrs,
-function<eager-inv>(
- lower-expect,
- simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;no-switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>,
- early-cse<>
-),
-ipsccp,
-called-value-propagation,
-globalopt,
-function<eager-inv>(
- mem2reg,
- instcombine<max-iterations=1000;no-use-loop-info>,
- simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>
-),
-require<globals-aa>,
-function(
- invalidate<aa>
-),
-require<profile-summary>,
-cgscc(
- devirt<4>(
- inline<only-mandatory>,
- inline,
- function-attrs<skip-non-recursive>,
- function<eager-inv;no-rerun>(
- early-cse<memssa>,
- speculative-execution,
- jump-threading,
- correlated-propagation,
- simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>,
- instcombine<max-iterations=1000;no-use-loop-info>,
- aggressive-instcombine,
- constraint-elimination,
- tailcallelim,
- simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>,
- reassociate,
- loop-mssa(
- loop-instsimplify,
- loop-simplifycfg,
- licm<no-allowspeculation>,
- loop-rotate<header-duplication;no-prepare-for-lto>,
- licm<allowspeculation>,
- simple-loop-unswitch<no-nontrivial;trivial>
- ),
- simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>,
- instcombine<max-iterations=1000;no-use-loop-info>,
- loop(
- loop-idiom,
- indvars,
- loop-deletion,
- loop-unroll-full
- ),
- vector-combine,
- mldst-motion<no-split-footer-bb>,
- gvn<>,
- sccp,
- bdce,
- instcombine<max-iterations=1000;no-use-loop-info>,
- jump-threading,
- correlated-propagation,
- adce,
- memcpyopt,
- dse,
- move-auto-init,
- loop-mssa(
- licm<allowspeculation>
- ),
- simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;hoist-common-insts;sink-common-insts;speculate-blocks;simplify-cond-branch>,
- instcombine<max-iterations=1000;no-use-loop-info>
- ),
- function-attrs,
- function(
- require<should-not-run-function-passes>
- )
- )
-),
-deadargelim,
-globalopt,
-globaldce,
-elim-avail-extern,
-rpo-function-attrs,
-recompute-globalsaa,
-function<eager-inv>(
- float2int,
- lower-constant-intrinsics,
- loop(
- loop-rotate<header-duplication;no-prepare-for-lto>,
- loop-deletion
- ),
- loop-distribute,
- inject-tli-mappings,
- loop-vectorize<no-interleave-forced-only;no-vectorize-forced-only;>,
- loop-load-elim,
- instcombine<max-iterations=1000;no-use-loop-info>,
- simplifycfg<bonus-inst-threshold=1;forward-switch-cond;switch-range-to-icmp;switch-to-lookup;no-keep-loops;hoist-common-insts;sink-common-insts;speculate-blocks;simplify-cond-branch>,
- slp-vectorizer,
- vector-combine,
- instcombine<max-iterations=1000;no-use-loop-info>,
- loop-unroll<O2>,
- transform-warning,
- instcombine<max-iterations=1000;no-use-loop-info>,
- loop-mssa(
- licm<allowspeculation>
- ),
- alignment-from-assumptions,
- loop-sink,
- instsimplify,
- div-rem-pairs,
- tailcallelim,
- simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>
-),
-globaldce,
-constmerge,
-cg-profile,
-rel-lookup-table-converter,
-function(
- annotation-remarks
-),
-verify
-)");
-#else
- array_add(&passes, u8R"(
-annotation2metadata,
-forceattrs,
-inferattrs,
-function<eager-inv>(
- lower-expect,
- simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;no-switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>,
- sroa<modify-cfg>,
- early-cse<>
-),
-ipsccp,
-called-value-propagation,
-globalopt,
-function<eager-inv>(
- mem2reg,
- instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,
- simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>
-),
-always-inline,
-require<globals-aa>,
-function(
- invalidate<aa>
-),
-require<profile-summary>,
-cgscc(
- devirt<4>(
- inline,
- function-attrs<skip-non-recursive-function-attrs>,
- function<eager-inv;no-rerun>(
- sroa<modify-cfg>,
- early-cse<memssa>,
- speculative-execution<only-if-divergent-target>,
- jump-threading,
- correlated-propagation,
- simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>,
- instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,
- aggressive-instcombine,
- tailcallelim,
- simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>,
- reassociate,
- constraint-elimination,
- loop-mssa(
- loop-instsimplify,
- loop-simplifycfg,
- licm<no-allowspeculation>,
- loop-rotate<header-duplication;no-prepare-for-lto>,
- licm<allowspeculation>,
- simple-loop-unswitch<no-nontrivial;trivial>
- ),
- simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>,
- instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,
- loop(
- loop-idiom,
- indvars,
- loop-deletion,
- loop-unroll-full
- ),
- sroa<modify-cfg>,
- vector-combine,
- mldst-motion<no-split-footer-bb>,
- gvn<>,
- sccp,
- bdce,
- instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,
- jump-threading,
- correlated-propagation,
- adce,
- memcpyopt,
- dse,
- move-auto-init,
- loop-mssa(
- licm<allowspeculation>
- ),
- simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;hoist-common-insts;sink-common-insts;speculate-blocks;simplify-cond-branch>,
- instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>
- ),
- function-attrs,
- function(
- require<should-not-run-function-passes>
- )
- )
-),
-deadargelim,
-globalopt,
-globaldce,
-elim-avail-extern,
-rpo-function-attrs,
-recompute-globalsaa,
-function<eager-inv>(
- float2int,
- lower-constant-intrinsics,
- loop(
- loop-rotate<header-duplication;no-prepare-for-lto>,
- loop-deletion
- ),
- loop-distribute,
- inject-tli-mappings,
- loop-vectorize<no-interleave-forced-only;no-vectorize-forced-only;>,
- infer-alignment,
- loop-load-elim,
- instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,
- simplifycfg<bonus-inst-threshold=1;forward-switch-cond;switch-range-to-icmp;switch-to-lookup;no-keep-loops;hoist-common-insts;sink-common-insts;speculate-blocks;simplify-cond-branch>,
- slp-vectorizer,
- vector-combine,
- instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,
- loop-unroll<O2>,
- transform-warning,
- sroa<preserve-cfg>,
- infer-alignment,
- instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,
- loop-mssa(
- licm<allowspeculation>
- ),
- alignment-from-assumptions,
- loop-sink,
- instsimplify,
- div-rem-pairs,
- tailcallelim,
- simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>
-),
-globaldce,
-constmerge,
-cg-profile,
-rel-lookup-table-converter,
-function(
- annotation-remarks
-),
-verify
-)");
-#endif
- break;
-// default<O2>
-// Passes removed: coro, openmp, sroa
- case 2:
-#if LLVM_VERSION_MAJOR == 17
- array_add(&passes, u8R"(
-annotation2metadata,
-forceattrs,
-inferattrs,
-function<eager-inv>(
- lower-expect,
- simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;no-switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>,
- early-cse<>
-),
-ipsccp,
-called-value-propagation,
-globalopt,
-function<eager-inv>(
- mem2reg,
- instcombine<max-iterations=1000;no-use-loop-info>,
- simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>
-),
-require<globals-aa>,
-function(
- invalidate<aa>
-),
-require<profile-summary>,
-cgscc(
- devirt<4>(
- inline<only-mandatory>,
- inline,
- function-attrs<skip-non-recursive>,
- function<eager-inv;no-rerun>(
- early-cse<memssa>,
- speculative-execution,
- jump-threading,
- correlated-propagation,
- simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>,
- instcombine<max-iterations=1000;no-use-loop-info>,
- aggressive-instcombine,
- constraint-elimination,
- libcalls-shrinkwrap,
- tailcallelim,
- simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>,
- reassociate,
- loop-mssa(
- loop-instsimplify,
- loop-simplifycfg,
- licm<no-allowspeculation>,
- loop-rotate<header-duplication;no-prepare-for-lto>,
- licm<allowspeculation>,
- simple-loop-unswitch<no-nontrivial;trivial>
- ),
- simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>,
- instcombine<max-iterations=1000;no-use-loop-info>,
- loop(
- loop-idiom,
- indvars,
- loop-deletion,
- loop-unroll-full
- ),
- vector-combine,
- mldst-motion<no-split-footer-bb>,
- gvn<>,
- sccp,
- bdce,
- instcombine<max-iterations=1000;no-use-loop-info>,
- jump-threading,
- correlated-propagation,
- adce,
- memcpyopt,
- dse,
- move-auto-init,
- loop-mssa(
- licm<allowspeculation>
- ),
- simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;hoist-common-insts;sink-common-insts;speculate-blocks;simplify-cond-branch>,
- instcombine<max-iterations=1000;no-use-loop-info>
- ),
- function-attrs,
- function(
- require<should-not-run-function-passes>
- )
- )
-),
-deadargelim,
-globalopt,
-globaldce,
-elim-avail-extern,
-rpo-function-attrs,
-recompute-globalsaa,
-function<eager-inv>(
- float2int,
- lower-constant-intrinsics,
- loop(
- loop-rotate<header-duplication;no-prepare-for-lto>,
- loop-deletion
- ),
- loop-distribute,
- inject-tli-mappings,
- loop-vectorize<no-interleave-forced-only;no-vectorize-forced-only;>,
- loop-load-elim,
- instcombine<max-iterations=1000;no-use-loop-info>,
- simplifycfg<bonus-inst-threshold=1;forward-switch-cond;switch-range-to-icmp;switch-to-lookup;no-keep-loops;hoist-common-insts;sink-common-insts;speculate-blocks;simplify-cond-branch>,
- slp-vectorizer,
- vector-combine,
- instcombine<max-iterations=1000;no-use-loop-info>,
- loop-unroll<O2>,
- transform-warning,
- instcombine<max-iterations=1000;no-use-loop-info>,
- loop-mssa(
- licm<allowspeculation>
- ),
- alignment-from-assumptions,
- loop-sink,
- instsimplify,
- div-rem-pairs,
- tailcallelim,
- simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>
-),
-globaldce,
-constmerge,
-cg-profile,
-rel-lookup-table-converter,
-function(
- annotation-remarks
-),
-verify
-)");
-#else
- array_add(&passes, u8R"(
-annotation2metadata,
-forceattrs,
-inferattrs,
-function<eager-inv>(
- lower-expect,
- simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;no-switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>,
- sroa<modify-cfg>,
- early-cse<>
-),
-ipsccp,
-called-value-propagation,
-globalopt,
-function<eager-inv>(
- mem2reg,
- instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,
- simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>
-),
-always-inline,
-require<globals-aa>,
-function(
- invalidate<aa>
-),
-require<profile-summary>,
-cgscc(
- devirt<4>(
- inline,
- function-attrs<skip-non-recursive-function-attrs>,
- function<eager-inv;no-rerun>(
- sroa<modify-cfg>,
- early-cse<memssa>,
- speculative-execution<only-if-divergent-target>,
- jump-threading,
- correlated-propagation,
- simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>,
- instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,
- aggressive-instcombine,
- libcalls-shrinkwrap,
- tailcallelim,
- simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>,
- reassociate,
- constraint-elimination,
- loop-mssa(
- loop-instsimplify,
- loop-simplifycfg,
- licm<no-allowspeculation>,
- loop-rotate<header-duplication;no-prepare-for-lto>,
- licm<allowspeculation>,
- simple-loop-unswitch<no-nontrivial;trivial>
- ),
- simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>,
- instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,
- loop(
- loop-idiom,
- indvars,
- loop-deletion,
- loop-unroll-full
- ),
- sroa<modify-cfg>,
- vector-combine,
- mldst-motion<no-split-footer-bb>,
- gvn<>,
- sccp,
- bdce,
- instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,
- jump-threading,
- correlated-propagation,
- adce,
- memcpyopt,
- dse,
- move-auto-init,
- loop-mssa(
- licm<allowspeculation>
- ),
- simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;hoist-common-insts;sink-common-insts;speculate-blocks;simplify-cond-branch>,
- instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>
- ),
- function-attrs,
- function(
- require<should-not-run-function-passes>
- )
- )
-),
-deadargelim,
-globalopt,
-globaldce,
-elim-avail-extern,
-rpo-function-attrs,
-recompute-globalsaa,
-function<eager-inv>(
- float2int,
- lower-constant-intrinsics,
- loop(
- loop-rotate<header-duplication;no-prepare-for-lto>,
- loop-deletion
- ),
- loop-distribute,
- inject-tli-mappings,
- loop-vectorize<no-interleave-forced-only;no-vectorize-forced-only;>,
- infer-alignment,
- loop-load-elim,
- instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,
- simplifycfg<bonus-inst-threshold=1;forward-switch-cond;switch-range-to-icmp;switch-to-lookup;no-keep-loops;hoist-common-insts;sink-common-insts;speculate-blocks;simplify-cond-branch>,
- slp-vectorizer,
- vector-combine,
- instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,
- loop-unroll<O2>,
- transform-warning,
- sroa<modify-cfg>,
- infer-alignment,
- instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,
- loop-mssa(
- licm<allowspeculation>
- ),
- alignment-from-assumptions,
- loop-sink,
- instsimplify,
- div-rem-pairs,
- tailcallelim,
- simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>
-),
-globaldce,
-constmerge,
-cg-profile,
-rel-lookup-table-converter,
-function(
- annotation-remarks
-),
-verify
-)");
-#endif
- break;
-
- case 3:
-// default<O3>
-// Passes removed: coro, openmp, sroa
-#if LLVM_VERSION_MAJOR == 17
- array_add(&passes, u8R"(
-annotation2metadata,
-forceattrs,
-inferattrs,
-function<eager-inv>(
- lower-expect,
- simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;no-switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>,
- early-cse<>,
- callsite-splitting
-),
-ipsccp,
-called-value-propagation,
-globalopt,
-function<eager-inv>(
- mem2reg,
- instcombine<max-iterations=1000;no-use-loop-info>,
- simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>
-),
-require<globals-aa>,
-function(
- invalidate<aa>
-),
-require<profile-summary>,
-cgscc(
- devirt<4>(
- inline<only-mandatory>,
- inline,
- function-attrs<skip-non-recursive>,
- argpromotion,
- function<eager-inv;no-rerun>(
- early-cse<memssa>,
- speculative-execution,
- jump-threading,
- correlated-propagation,
- simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>,
- instcombine<max-iterations=1000;no-use-loop-info>,
- aggressive-instcombine,
- constraint-elimination,
- libcalls-shrinkwrap,
- tailcallelim,
- simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>,
- reassociate,
- loop-mssa(
- loop-instsimplify,
- loop-simplifycfg,
- licm<no-allowspeculation>,
- loop-rotate<header-duplication;no-prepare-for-lto>,
- licm<allowspeculation>,
- simple-loop-unswitch<nontrivial;trivial>
- ),
- simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>,
- instcombine<max-iterations=1000;no-use-loop-info>,
- loop(
- loop-idiom,
- indvars,
- loop-deletion,
- loop-unroll-full
- ),
- vector-combine,
- mldst-motion<no-split-footer-bb>,
- gvn<>,
- sccp,
- bdce,
- instcombine<max-iterations=1000;no-use-loop-info>,
- jump-threading,
- correlated-propagation,
- adce,
- memcpyopt,
- dse,
- move-auto-init,
- loop-mssa(
- licm<allowspeculation>
- ),
- simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;hoist-common-insts;sink-common-insts;speculate-blocks;simplify-cond-branch>,
- instcombine<max-iterations=1000;no-use-loop-info>
- ),
- function-attrs,
- function(
- require<should-not-run-function-passes>
- )
- )
-),
-deadargelim,
-globalopt,
-globaldce,
-elim-avail-extern,
-rpo-function-attrs,
-recompute-globalsaa,
-function<eager-inv>(
- float2int,
- lower-constant-intrinsics,
- chr,
- loop(
- loop-rotate<header-duplication;no-prepare-for-lto>,
- loop-deletion
- ),
- loop-distribute,
- inject-tli-mappings,
- loop-vectorize<no-interleave-forced-only;no-vectorize-forced-only;>,
- loop-load-elim,
- instcombine<max-iterations=1000;no-use-loop-info>,
- simplifycfg<bonus-inst-threshold=1;forward-switch-cond;switch-range-to-icmp;switch-to-lookup;no-keep-loops;hoist-common-insts;sink-common-insts;speculate-blocks;simplify-cond-branch>,
- slp-vectorizer,
- vector-combine,
- instcombine<max-iterations=1000;no-use-loop-info>,
- loop-unroll<O3>,
- transform-warning,
- instcombine<max-iterations=1000;no-use-loop-info>,
- loop-mssa(
- licm<allowspeculation>
- ),
- alignment-from-assumptions,
- loop-sink,
- instsimplify,
- div-rem-pairs,
- tailcallelim,
- simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>
-),
-globaldce,
-constmerge,
-cg-profile,
-rel-lookup-table-converter,
-function(
- annotation-remarks
-),
-verify
-)");
-#else
- array_add(&passes, u8R"(
-annotation2metadata,
-forceattrs,
-inferattrs,
-function<eager-inv>(
- lower-expect,
- simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;no-switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>,
- sroa<modify-cfg>,
- early-cse<>,
- callsite-splitting
-),
-ipsccp,
-called-value-propagation,
-globalopt,
-function<eager-inv>(
- mem2reg,
- instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,
- simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>
-),
-always-inline,
-require<globals-aa>,
-function(invalidate<aa>),
-require<profile-summary>,
-cgscc(
- devirt<4>(
- inline,
- function-attrs<skip-non-recursive-function-attrs>,
- argpromotion,
- function<eager-inv;no-rerun>(
- sroa<modify-cfg>,
- early-cse<memssa>,
- speculative-execution<only-if-divergent-target>,
- jump-threading,
- correlated-propagation,
- simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>,
- instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,
- aggressive-instcombine,
- libcalls-shrinkwrap,
- tailcallelim,
- simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>,
- reassociate,
- constraint-elimination,
- loop-mssa(
- loop-instsimplify,
- loop-simplifycfg,
- licm<no-allowspeculation>,
- loop-rotate<header-duplication;no-prepare-for-lto>,
- licm<allowspeculation>,
- simple-loop-unswitch<nontrivial;trivial>
- ),
- simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>,
- instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,
- loop(
- loop-idiom,
- indvars,
- loop-deletion,
- loop-unroll-full
- ),
- sroa<modify-cfg>,
- vector-combine,
- mldst-motion<no-split-footer-bb>,
- gvn<>,
- sccp,
- bdce,
- instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,
- jump-threading,
- correlated-propagation,
- adce,
- memcpyopt,
- dse,
- move-auto-init,
- loop-mssa(licm<allowspeculation>),
- simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;hoist-common-insts;sink-common-insts;speculate-blocks;simplify-cond-branch>,
- instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>
- ),
- function-attrs,
- function(
- require<should-not-run-function-passes>
- )
- )
-),
-deadargelim,
-globalopt,
-globaldce,
-elim-avail-extern,
-rpo-function-attrs,
-recompute-globalsaa,
-function<eager-inv>(
- float2int,
- lower-constant-intrinsics,
- chr,
- loop(
- loop-rotate<header-duplication;no-prepare-for-lto>,
- loop-deletion
- ),
- loop-distribute,
- inject-tli-mappings,
- loop-vectorize<no-interleave-forced-only;no-vectorize-forced-only;>,
- infer-alignment,
- loop-load-elim,
- instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,
- simplifycfg<bonus-inst-threshold=1;forward-switch-cond;switch-range-to-icmp;switch-to-lookup;no-keep-loops;hoist-common-insts;sink-common-insts;speculate-blocks;simplify-cond-branch>,
- slp-vectorizer,
- vector-combine,
- instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,
- loop-unroll<O3>,
- transform-warning,
- sroa<preserve-cfg>,
- infer-alignment,
- instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,
- loop-mssa(licm<allowspeculation>),
- alignment-from-assumptions,
- loop-sink,
- instsimplify,
- div-rem-pairs,
- tailcallelim,
- simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>
-),
-globaldce,
-constmerge,
-cg-profile,
-rel-lookup-table-converter,
-function(
- annotation-remarks
-),
-verify
-)");
-#endif
- break;
- }
+ #include "llvm_backend_passes.cpp"
// asan - Linux, Darwin, Windows
// msan - linux
@@ -2444,6 +2498,17 @@ verify
return 1;
}
#endif
+
+ if (LLVM_IGNORE_VERIFICATION) {
+ return 0;
+ }
+
+ if (wd->do_threading) {
+ thread_pool_add_task(lb_llvm_module_verification_worker_proc, wd->m);
+ } else {
+ lb_llvm_module_verification_worker_proc(wd->m);
+ }
+
return 0;
}
@@ -2451,8 +2516,7 @@ verify
gb_internal WORKER_TASK_PROC(lb_generate_procedures_worker_proc) {
lbModule *m = cast(lbModule *)data;
- for (isize i = 0; i < m->procedures_to_generate.count; i++) {
- lbProcedure *p = m->procedures_to_generate[i];
+ for (lbProcedure *p = nullptr; mpsc_dequeue(&m->procedures_to_generate, &p); /**/) {
lb_generate_procedure(p->module, p);
}
return 0;
@@ -2476,10 +2540,15 @@ gb_internal void lb_generate_procedures(lbGenerator *gen, bool do_threading) {
gb_internal WORKER_TASK_PROC(lb_generate_missing_procedures_to_check_worker_proc) {
lbModule *m = cast(lbModule *)data;
- for (isize i = 0; i < m->missing_procedures_to_check.count; i++) {
- lbProcedure *p = m->missing_procedures_to_check[i];
- debugf("Generate missing procedure: %.*s module %p\n", LIT(p->name), m);
- lb_generate_procedure(m, p);
+ for (lbProcedure *p = nullptr; mpsc_dequeue(&m->missing_procedures_to_check, &p); /**/) {
+ if (!p->is_done.load(std::memory_order_relaxed)) {
+ debugf("Generate missing procedure: %.*s module %p\n", LIT(p->name), m);
+ lb_generate_procedure(m, p);
+ }
+
+ for (lbProcedure *nested = nullptr; mpsc_dequeue(&m->procedures_to_generate, &nested); /**/) {
+ mpsc_enqueue(&m->missing_procedures_to_check, nested);
+ }
}
return 0;
}
@@ -2499,6 +2568,12 @@ gb_internal void lb_generate_missing_procedures(lbGenerator *gen, bool do_thread
lb_generate_missing_procedures_to_check_worker_proc(m);
}
}
+
+ for (auto const &entry : gen->modules) {
+ lbModule *m = entry.value;
+ GB_ASSERT(m->missing_procedures_to_check.count == 0);
+ GB_ASSERT(m->procedures_to_generate.count == 0);
+ }
}
gb_internal void lb_debug_info_complete_types_and_finalize(lbGenerator *gen) {
@@ -2526,19 +2601,16 @@ gb_internal void lb_llvm_function_passes(lbGenerator *gen, bool do_threading) {
}
-gb_internal void lb_llvm_module_passes(lbGenerator *gen, bool do_threading) {
+gb_internal void lb_llvm_module_passes_and_verification(lbGenerator *gen, bool do_threading) {
if (do_threading) {
for (auto const &entry : gen->modules) {
lbModule *m = entry.value;
auto wd = gb_alloc_item(permanent_allocator(), lbLLVMModulePassWorkerData);
wd->m = m;
wd->target_machine = m->target_machine;
+ wd->do_threading = true;
- if (do_threading) {
- thread_pool_add_task(lb_llvm_module_pass_worker_proc, wd);
- } else {
- lb_llvm_module_pass_worker_proc(wd);
- }
+ thread_pool_add_task(lb_llvm_module_pass_worker_proc, wd);
}
thread_pool_wait();
} else {
@@ -2547,6 +2619,7 @@ gb_internal void lb_llvm_module_passes(lbGenerator *gen, bool do_threading) {
auto wd = gb_alloc_item(permanent_allocator(), lbLLVMModulePassWorkerData);
wd->m = m;
wd->target_machine = m->target_machine;
+ wd->do_threading = false;
lb_llvm_module_pass_worker_proc(wd);
}
}
@@ -2589,10 +2662,8 @@ gb_internal String lb_filepath_obj_for_module(lbModule *m) {
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 (USE_SEPARATE_MODULES) {
GB_ASSERT(m->module_name != nullptr);
String s = make_string_c(m->module_name);
String prefix = str_lit("odin_package");
@@ -2602,6 +2673,8 @@ gb_internal String lb_filepath_obj_for_module(lbModule *m) {
}
path = gb_string_append_length(path, s.text, s.len);
+ } else {
+ path = gb_string_append_length(path, name.text, name.len);
}
if (use_temporary_directory) {
@@ -2612,69 +2685,21 @@ gb_internal String lb_filepath_obj_for_module(lbModule *m) {
String ext = {};
if (build_context.build_mode == BuildMode_Assembly) {
- ext = STR_LIT(".S");
+ ext = STR_LIT("S");
+ } else if (build_context.build_mode == BuildMode_Object) {
+ // Allow a user override for the object extension.
+ ext = build_context.build_paths[BuildPath_Output].ext;
} else {
- if (is_arch_wasm()) {
- ext = STR_LIT(".wasm.o");
- } else {
- switch (build_context.metrics.os) {
- case TargetOs_windows:
- ext = STR_LIT(".obj");
- break;
- default:
- case TargetOs_darwin:
- case TargetOs_linux:
- case TargetOs_essence:
- ext = STR_LIT(".o");
- break;
-
- case TargetOs_freestanding:
- switch (build_context.metrics.abi) {
- default:
- case TargetABI_Default:
- case TargetABI_SysV:
- ext = STR_LIT(".o");
- break;
- case TargetABI_Win64:
- ext = STR_LIT(".obj");
- break;
- }
- break;
- }
- }
+ ext = infer_object_extension_from_build_context();
}
+ path = gb_string_append_length(path, ".", 1);
path = gb_string_append_length(path, ext.text, ext.len);
return make_string(cast(u8 *)path, gb_string_length(path));
}
-
-gb_internal bool lb_llvm_module_verification(lbGenerator *gen, bool do_threading) {
- if (LLVM_IGNORE_VERIFICATION) {
- return true;
- }
-
- if (do_threading) {
- for (auto const &entry : gen->modules) {
- lbModule *m = entry.value;
- thread_pool_add_task(lb_llvm_module_verification_worker_proc, m);
- }
- thread_pool_wait();
-
- } else {
- for (auto const &entry : gen->modules) {
- lbModule *m = entry.value;
- if (lb_llvm_module_verification_worker_proc(m)) {
- return false;
- }
- }
- }
-
- return true;
-}
-
gb_internal void lb_add_foreign_library_paths(lbGenerator *gen) {
for (auto const &entry : gen->modules) {
lbModule *m = entry.value;
@@ -2770,8 +2795,15 @@ gb_internal lbProcedure *lb_create_main_procedure(lbModule *m, lbProcedure *star
params->Tuple.variables[1] = alloc_entity_param(nullptr, make_token_ident("fdwReason"), t_u32, false, true);
params->Tuple.variables[2] = alloc_entity_param(nullptr, make_token_ident("lpReserved"), t_rawptr, false, true);
call_cleanup = false;
- } else if (build_context.metrics.os == TargetOs_windows && (build_context.metrics.arch == TargetArch_i386 || build_context.no_crt)) {
+ } else if (build_context.metrics.os == TargetOs_windows && build_context.no_crt) {
name = str_lit("mainCRTStartup");
+ } else if (build_context.metrics.os == TargetOs_windows && build_context.metrics.arch == TargetArch_i386 && !build_context.no_crt) {
+ // Windows i386 with CRT: libcmt expects _main (main with underscore prefix)
+ name = str_lit("main");
+ has_args = true;
+ slice_init(&params->Tuple.variables, permanent_allocator(), 2);
+ params->Tuple.variables[0] = alloc_entity_param(nullptr, make_token_ident("argc"), t_i32, false, true);
+ params->Tuple.variables[1] = alloc_entity_param(nullptr, make_token_ident("argv"), t_ptr_cstring, false, true);
} else if (is_arch_wasm()) {
name = str_lit("_start");
call_cleanup = false;
@@ -2826,8 +2858,8 @@ gb_internal lbProcedure *lb_create_main_procedure(lbModule *m, lbProcedure *star
if (testing_proc->pkg != nullptr) {
pkg_name = testing_proc->pkg->name;
}
- lbValue v_pkg = lb_find_or_add_entity_string(m, pkg_name);
- lbValue v_name = lb_find_or_add_entity_string(m, name);
+ lbValue v_pkg = lb_find_or_add_entity_string(m, pkg_name, false);
+ lbValue v_name = lb_find_or_add_entity_string(m, name, false);
lbValue v_proc = lb_find_procedure_value_from_entity(m, testing_proc);
indices[1] = LLVMConstInt(lb_type(m, t_int), testing_proc_index++, false);
@@ -2897,18 +2929,20 @@ gb_internal lbProcedure *lb_create_main_procedure(lbModule *m, lbProcedure *star
}
gb_internal void lb_generate_procedure(lbModule *m, lbProcedure *p) {
- if (p->is_done) {
+ if (p->is_done.load(std::memory_order_relaxed)) {
return;
}
+
if (p->body != nullptr) { // Build Procedure
m->curr_procedure = p;
lb_begin_procedure_body(p);
lb_build_stmt(p, p->body);
lb_end_procedure_body(p);
- p->is_done = true;
+ p->is_done.store(true, std::memory_order_relaxed);
m->curr_procedure = nullptr;
+ } else if (p->generate_body != nullptr) {
+ p->generate_body(m, p);
}
- lb_end_procedure(p);
// Add Flags
if (p->entity && p->entity->kind == Entity_Procedure && p->entity->Procedure.is_memcpy_like) {
@@ -2916,6 +2950,9 @@ gb_internal void lb_generate_procedure(lbModule *m, lbProcedure *p) {
}
lb_verify_function(m, p, true);
+
+ MUTEX_GUARD(&m->generated_procedures_mutex);
+ array_add(&m->generated_procedures, p);
}
@@ -2930,8 +2967,6 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
lbModule *default_module = &gen->default_module;
CheckerInfo *info = gen->info;
- auto *min_dep_set = &info->minimum_dependency_set;
-
switch (build_context.metrics.arch) {
case TargetArch_amd64:
case TargetArch_i386:
@@ -2959,13 +2994,24 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
LLVMInitializeWebAssemblyAsmParser();
LLVMInitializeWebAssemblyDisassembler();
break;
+ case TargetArch_riscv64:
+ LLVMInitializeRISCVTargetInfo();
+ LLVMInitializeRISCVTarget();
+ LLVMInitializeRISCVTargetMC();
+ LLVMInitializeRISCVAsmPrinter();
+ LLVMInitializeRISCVAsmParser();
+ LLVMInitializeRISCVDisassembler();
+ break;
+ case TargetArch_arm32:
+ LLVMInitializeARMTargetInfo();
+ LLVMInitializeARMTarget();
+ LLVMInitializeARMTargetMC();
+ LLVMInitializeARMAsmPrinter();
+ LLVMInitializeARMAsmParser();
+ LLVMInitializeARMDisassembler();
+ break;
default:
- LLVMInitializeAllTargetInfos();
- LLVMInitializeAllTargets();
- LLVMInitializeAllTargetMCs();
- LLVMInitializeAllAsmPrinters();
- LLVMInitializeAllAsmParsers();
- LLVMInitializeAllDisassemblers();
+ GB_PANIC("Unimplemented LLVM target initialization");
break;
}
@@ -3206,6 +3252,7 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
LLVMSetInitializer(g, LLVMConstNull(lb_type(m, t)));
LLVMSetLinkage(g, LLVMInternalLinkage);
lb_make_global_private_const(g);
+ lb_set_odin_rtti_section(g);
return lb_addr({g, alloc_type_pointer(t)});
};
@@ -3235,6 +3282,7 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
String link_name = e->Procedure.link_name;
if (e->pkg->kind == Package_Runtime) {
if (link_name == "main" ||
+ link_name == "_main" ||
link_name == "DllMain" ||
link_name == "WinMain" ||
link_name == "wWinMain" ||
@@ -3257,7 +3305,7 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
continue;
}
- if (!ptr_set_exists(min_dep_set, e)) {
+ if (e->min_dep_count.load(std::memory_order_relaxed) == 0) {
continue;
}
@@ -3267,69 +3315,49 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
}
GB_ASSERT(e->kind == Entity_Variable);
+
bool is_foreign = e->Variable.is_foreign;
bool is_export = e->Variable.is_export;
+ lbModule *default_module = &gen->default_module;
- lbModule *m = &gen->default_module;
- String name = lb_get_entity_name(m, e);
+ lbModule *m = default_module;
+ lbModule *e_module = lb_module_of_entity(gen, e, default_module);
- lbValue g = {};
- g.value = LLVMAddGlobal(m->mod, lb_type(m, e->type), alloc_cstring(permanent_allocator(), name));
- g.type = alloc_type_pointer(e->type);
- if (e->Variable.thread_local_model != "") {
- LLVMSetThreadLocal(g.value, true);
-
- String m = e->Variable.thread_local_model;
- LLVMThreadLocalMode mode = LLVMGeneralDynamicTLSModel;
- if (m == "default") {
- mode = LLVMGeneralDynamicTLSModel;
- } else if (m == "localdynamic") {
- mode = LLVMLocalDynamicTLSModel;
- } else if (m == "initialexec") {
- mode = LLVMInitialExecTLSModel;
- } else if (m == "localexec") {
- mode = LLVMLocalExecTLSModel;
- } else {
- GB_PANIC("Unhandled thread local mode %.*s", LIT(m));
- }
- LLVMSetThreadLocalMode(g.value, mode);
- }
- if (is_foreign) {
- LLVMSetLinkage(g.value, LLVMExternalLinkage);
- LLVMSetDLLStorageClass(g.value, LLVMDLLImportStorageClass);
- LLVMSetExternallyInitialized(g.value, true);
- lb_add_foreign_library_path(m, e->Variable.foreign_library);
- } else {
- LLVMSetInitializer(g.value, LLVMConstNull(lb_type(m, e->type)));
- }
- if (is_export) {
- LLVMSetLinkage(g.value, LLVMDLLExportLinkage);
- LLVMSetDLLStorageClass(g.value, LLVMDLLExportStorageClass);
- } else if (!is_foreign) {
- LLVMSetLinkage(g.value, USE_SEPARATE_MODULES ? LLVMWeakAnyLinkage : LLVMInternalLinkage);
- }
- lb_set_linkage_from_entity_flags(m, g.value, e->flags);
-
- if (e->Variable.link_section.len > 0) {
- LLVMSetSection(g.value, alloc_cstring(permanent_allocator(), e->Variable.link_section));
+ bool const split_globals_across_modules = false;
+ if (split_globals_across_modules) {
+ m = e_module;
}
+ String name = lb_get_entity_name(m, e);
+
lbGlobalVariable var = {};
- var.var = g;
var.decl = decl;
+ lbValue g = {};
+ g.type = alloc_type_pointer(e->type);
+ g.value = LLVMAddGlobal(m->mod, lb_type(m, e->type), alloc_cstring(permanent_allocator(), name));
+
if (decl->init_expr != nullptr) {
TypeAndValue tav = type_and_value_of_expr(decl->init_expr);
- if (!is_type_any(e->type) && !is_type_union(e->type)) {
+ if (!is_type_any(e->type)) {
if (tav.mode != Addressing_Invalid) {
if (tav.value.kind != ExactValue_Invalid) {
- bool is_rodata = e->kind == Entity_Variable && e->Variable.is_rodata;
+ auto cc = LB_CONST_CONTEXT_DEFAULT;
+ cc.is_rodata = e->kind == Entity_Variable && e->Variable.is_rodata;
+ cc.allow_local = false;
+ cc.link_section = e->Variable.link_section;
+
ExactValue v = tav.value;
- lbValue init = lb_const_value(m, tav.type, v, false, is_rodata);
+ lbValue init = lb_const_value(m, tav.type, v, cc);
+
+ LLVMDeleteGlobal(g.value);
+ g.value = nullptr;
+ g.value = LLVMAddGlobal(m->mod, LLVMTypeOf(init.value), alloc_cstring(permanent_allocator(), name));
+
LLVMSetInitializer(g.value, init.value);
var.is_initialized = true;
- if (is_rodata) {
+ if (cc.is_rodata) {
LLVMSetGlobalConstant(g.value, true);
}
}
@@ -3344,11 +3372,33 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
} else if (e->kind == Entity_Variable && e->Variable.is_rodata) {
LLVMSetGlobalConstant(g.value, true);
}
- array_add(&global_variables, var);
- lb_add_entity(m, e, g);
- lb_add_member(m, name, g);
+ lb_apply_thread_local_model(g.value, e->Variable.thread_local_model);
+
+ if (is_foreign) {
+ LLVMSetLinkage(g.value, LLVMExternalLinkage);
+ LLVMSetDLLStorageClass(g.value, LLVMDLLImportStorageClass);
+ LLVMSetExternallyInitialized(g.value, true);
+ lb_add_foreign_library_path(m, e->Variable.foreign_library);
+ } else if (LLVMGetInitializer(g.value) == nullptr) {
+ LLVMSetInitializer(g.value, LLVMConstNull(lb_type(m, e->type)));
+ }
+ if (is_export) {
+ LLVMSetLinkage(g.value, LLVMDLLExportLinkage);
+ LLVMSetDLLStorageClass(g.value, LLVMDLLExportStorageClass);
+ } else if (!is_foreign) {
+ LLVMSetLinkage(g.value, USE_SEPARATE_MODULES ? LLVMWeakAnyLinkage : LLVMInternalLinkage);
+ }
+ lb_set_linkage_from_entity_flags(m, g.value, e->flags);
+ LLVMSetAlignment(g.value, cast(u32)type_align_of(e->type));
+
+ if (e->Variable.link_section.len > 0) {
+ LLVMSetSection(g.value, alloc_cstring(permanent_allocator(), e->Variable.link_section));
+ }
+ if (e->flags & EntityFlag_Require) {
+ lb_append_to_compiler_used(m, g.value);
+ }
if (m->debug_builder) {
String global_name = e->token.string;
@@ -3378,6 +3428,49 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
LLVMGlobalSetMetadata(g.value, 0, global_variable_metadata);
}
}
+
+ if (default_module == m) {
+ g.value = LLVMConstPointerCast(g.value, lb_type(m, alloc_type_pointer(e->type)));
+
+ var.var = g;
+ array_add(&global_variables, var);
+ } else {
+ lbValue local_g = {};
+ local_g.type = alloc_type_pointer(e->type);
+ local_g.value = LLVMAddGlobal(default_module->mod, lb_type(default_module, e->type), alloc_cstring(permanent_allocator(), name));
+ LLVMSetLinkage(local_g.value, LLVMExternalLinkage);
+
+ var.var = local_g;
+ array_add(&global_variables, var);
+
+ lb_add_entity(default_module, e, local_g);
+ lb_add_member(default_module, name, local_g);
+ }
+
+ lb_add_entity(m, e, g);
+ lb_add_member(m, name, g);
+ }
+
+ if (build_context.ODIN_DEBUG) {
+ // Custom `.raddbg` section for its debugger
+ if (build_context.metrics.os == TargetOs_windows) {
+ lbModule *m = default_module;
+ LLVMModuleRef mod = m->mod;
+ LLVMContextRef ctx = m->ctx;
+
+ {
+ LLVMTypeRef type = LLVMArrayType(LLVMInt8TypeInContext(ctx), 1);
+ LLVMValueRef global = LLVMAddGlobal(mod, type, "raddbg_is_attached_byte_marker");
+ LLVMSetInitializer(global, LLVMConstNull(type));
+ LLVMSetSection(global, ".raddbg");
+ }
+
+ if (gen->info->entry_point) {
+ String mangled_name = lb_get_entity_name(m, gen->info->entry_point);
+ char const *str = alloc_cstring(temporary_allocator(), mangled_name);
+ lb_add_raddbg_string(m, "entry_point: \"", str, "\"");
+ }
+ }
}
TIME_SECTION("LLVM Runtime Objective-C Names Creation");
@@ -3393,7 +3486,7 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
if (build_context.ODIN_DEBUG) {
for (auto const &entry : builtin_pkg->scope->elements) {
Entity *e = entry.value;
- add_debug_info_for_global_constant_from_entity(gen, e);
+ lb_add_debug_info_for_global_constant_from_entity(gen, e);
}
}
@@ -3417,12 +3510,101 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
if (gen->objc_names) {
TIME_SECTION("Finalize objc names");
- lb_finalize_objc_names(gen->objc_names);
+ lb_finalize_objc_names(gen, gen->objc_names);
}
if (build_context.ODIN_DEBUG) {
TIME_SECTION("LLVM Debug Info Complete Types and Finalize");
lb_debug_info_complete_types_and_finalize(gen);
+
+ // Custom `.raddbg` section for its debugger
+ if (build_context.metrics.os == TargetOs_windows) {
+ lbModule *m = default_module;
+ LLVMModuleRef mod = m->mod;
+ LLVMContextRef ctx = m->ctx;
+
+ lb_add_raddbg_string(m, "type_view: {type: \"[]?\", expr: \"array(data, len)\"}");
+ lb_add_raddbg_string(m, "type_view: {type: \"string\", expr: \"array(data, len)\"}");
+ lb_add_raddbg_string(m, "type_view: {type: \"[dynamic]?\", expr: \"rows($, array(data, len), len, cap, allocator)\"}");
+
+ // column major matrices
+ lb_add_raddbg_string(m, "type_view: {type: \"matrix[1, ?]?\", expr: \"columns($.data, $[0])\"}");
+ lb_add_raddbg_string(m, "type_view: {type: \"matrix[2, ?]?\", expr: \"columns($.data, $[0], $[1])\"}");
+ lb_add_raddbg_string(m, "type_view: {type: \"matrix[3, ?]?\", expr: \"columns($.data, $[0], $[1], $[2])\"}");
+ lb_add_raddbg_string(m, "type_view: {type: \"matrix[4, ?]?\", expr: \"columns($.data, $[0], $[1], $[2], $[3])\"}");
+ lb_add_raddbg_string(m, "type_view: {type: \"matrix[5, ?]?\", expr: \"columns($.data, $[0], $[1], $[2], $[3], $[4])\"}");
+ lb_add_raddbg_string(m, "type_view: {type: \"matrix[6, ?]?\", expr: \"columns($.data, $[0], $[1], $[2], $[3], $[4], $[5])\"}");
+ lb_add_raddbg_string(m, "type_view: {type: \"matrix[7, ?]?\", expr: \"columns($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6])\"}");
+ lb_add_raddbg_string(m, "type_view: {type: \"matrix[8, ?]?\", expr: \"columns($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7])\"}");
+ lb_add_raddbg_string(m, "type_view: {type: \"matrix[9, ?]?\", expr: \"columns($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8])\"}");
+ lb_add_raddbg_string(m, "type_view: {type: \"matrix[10, ?]?\", expr: \"columns($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9])\"}");
+ lb_add_raddbg_string(m, "type_view: {type: \"matrix[11, ?]?\", expr: \"columns($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10])\"}");
+ lb_add_raddbg_string(m, "type_view: {type: \"matrix[12, ?]?\", expr: \"columns($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10], $[11])\"}");
+ lb_add_raddbg_string(m, "type_view: {type: \"matrix[13, ?]?\", expr: \"columns($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10], $[11], $[12])\"}");
+ lb_add_raddbg_string(m, "type_view: {type: \"matrix[14, ?]?\", expr: \"columns($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10], $[11], $[12], $[13])\"}");
+ lb_add_raddbg_string(m, "type_view: {type: \"matrix[15, ?]?\", expr: \"columns($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10], $[11], $[12], $[13], $[14])\"}");
+ lb_add_raddbg_string(m, "type_view: {type: \"matrix[16, ?]?\", expr: \"columns($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10], $[11], $[12], $[13], $[14], $[15])\"}");
+
+ // row major matrices
+ lb_add_raddbg_string(m, "type_view: {type: \"#row_major matrix[?, 1]?\", expr: \"columns($.data, $[0])\"}");
+ lb_add_raddbg_string(m, "type_view: {type: \"#row_major matrix[?, 2]?\", expr: \"columns($.data, $[0], $[1])\"}");
+ lb_add_raddbg_string(m, "type_view: {type: \"#row_major matrix[?, 3]?\", expr: \"columns($.data, $[0], $[1], $[2])\"}");
+ lb_add_raddbg_string(m, "type_view: {type: \"#row_major matrix[?, 4]?\", expr: \"columns($.data, $[0], $[1], $[2], $[3])\"}");
+ lb_add_raddbg_string(m, "type_view: {type: \"#row_major matrix[?, 5]?\", expr: \"columns($.data, $[0], $[1], $[2], $[3], $[4])\"}");
+ lb_add_raddbg_string(m, "type_view: {type: \"#row_major matrix[?, 6]?\", expr: \"columns($.data, $[0], $[1], $[2], $[3], $[4], $[5])\"}");
+ lb_add_raddbg_string(m, "type_view: {type: \"#row_major matrix[?, 7]?\", expr: \"columns($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6])\"}");
+ lb_add_raddbg_string(m, "type_view: {type: \"#row_major matrix[?, 8]?\", expr: \"columns($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7])\"}");
+ lb_add_raddbg_string(m, "type_view: {type: \"#row_major matrix[?, 9]?\", expr: \"columns($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8])\"}");
+ lb_add_raddbg_string(m, "type_view: {type: \"#row_major matrix[?, 10]?\", expr: \"columns($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9])\"}");
+ lb_add_raddbg_string(m, "type_view: {type: \"#row_major matrix[?, 11]?\", expr: \"columns($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10])\"}");
+ lb_add_raddbg_string(m, "type_view: {type: \"#row_major matrix[?, 12]?\", expr: \"columns($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10], $[11])\"}");
+ lb_add_raddbg_string(m, "type_view: {type: \"#row_major matrix[?, 13]?\", expr: \"columns($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10], $[11], $[12])\"}");
+ lb_add_raddbg_string(m, "type_view: {type: \"#row_major matrix[?, 14]?\", expr: \"columns($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10], $[11], $[12], $[13])\"}");
+ lb_add_raddbg_string(m, "type_view: {type: \"#row_major matrix[?, 15]?\", expr: \"columns($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10], $[11], $[12], $[13], $[14])\"}");
+ lb_add_raddbg_string(m, "type_view: {type: \"#row_major matrix[?, 16]?\", expr: \"columns($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10], $[11], $[12], $[13], $[14], $[15])\"}");
+
+
+ TEMPORARY_ALLOCATOR_GUARD();
+ for (RaddbgTypeView const &type_view : gen->info->raddbg_type_views) {
+ if (type_view.type == nullptr) {
+ continue;
+ }
+
+ if (type_view.view.len == 0) {
+ continue;
+ }
+
+ String t_str = type_to_canonical_string(temporary_allocator(), type_view.type);
+
+ gbString s = gb_string_make(temporary_allocator(), "");
+
+ s = gb_string_appendc(s, "type_view: {type: \"");
+ s = gb_string_append_length(s, t_str.text, t_str.len);
+ s = gb_string_appendc(s, "\", expr: \"");
+ s = gb_string_append_length(s, type_view.view.text, type_view.view.len);
+ s = gb_string_appendc(s, "\"}");
+
+ lb_add_raddbg_string(m, s);
+ }
+
+ TEMPORARY_ALLOCATOR_GUARD();
+ u32 global_name_index = 0;
+ for (String str = {}; mpsc_dequeue(&gen->raddebug_section_strings, &str); /**/) {
+ LLVMValueRef data = LLVMConstStringInContext(ctx, cast(char const *)str.text, cast(unsigned)str.len, false);
+ LLVMTypeRef type = LLVMTypeOf(data);
+
+ gbString global_name = gb_string_make(temporary_allocator(), "raddbg_data__");
+ global_name = gb_string_append_fmt(global_name, "%u", global_name_index);
+ global_name_index += 1;
+
+ LLVMValueRef global = LLVMAddGlobal(mod, type, global_name);
+
+ LLVMSetInitializer(global, data);
+ LLVMSetAlignment(global, 1);
+
+ LLVMSetSection(global, ".raddbg");
+ }
+ }
}
if (do_threading) {
@@ -3438,15 +3620,23 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
}
}
+ TIME_SECTION("LLVM Add Foreign Library Paths");
+ lb_add_foreign_library_paths(gen);
+
TIME_SECTION("LLVM Function Pass");
lb_llvm_function_passes(gen, do_threading && !build_context.ODIN_DEBUG);
- TIME_SECTION("LLVM Module Pass");
- lb_llvm_module_passes(gen, do_threading);
+ TIME_SECTION("LLVM Remove Unused Functions and Globals");
+ lb_remove_unused_functions_and_globals(gen);
- TIME_SECTION("LLVM Module Verification");
- if (!lb_llvm_module_verification(gen, do_threading)) {
- return false;
+ TIME_SECTION("LLVM Module Pass and Verification");
+ lb_llvm_module_passes_and_verification(gen, do_threading);
+
+ TIME_SECTION("LLVM Correct Entity Linkage");
+ lb_correct_entity_linkage(gen);
+
+ if (build_context.build_diagnostics) {
+ lb_do_build_diagnostics(gen);
}
llvm_error = nullptr;
@@ -3475,11 +3665,6 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
}
}
- TIME_SECTION("LLVM Add Foreign Library Paths");
- lb_add_foreign_library_paths(gen);
-
- TIME_SECTION("LLVM Correct Entity Linkage");
- lb_correct_entity_linkage(gen);
////////////////////////////////////////////
for (auto const &entry: gen->modules) {
@@ -3505,36 +3690,48 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
if (build_context.sanitizer_flags & SanitizerFlag_Address) {
- if (build_context.metrics.os == TargetOs_windows) {
+ switch (build_context.metrics.os) {
+ case TargetOs_windows: {
auto paths = array_make<String>(heap_allocator(), 0, 1);
String path = concatenate_strings(permanent_allocator(), build_context.ODIN_ROOT, str_lit("\\bin\\llvm\\windows\\clang_rt.asan-x86_64.lib"));
array_add(&paths, path);
Entity *lib = alloc_entity_library_name(nullptr, make_token_ident("asan_lib"), nullptr, slice_from_array(paths), str_lit("asan_lib"));
array_add(&gen->foreign_libraries, lib);
- } else if (build_context.metrics.os == TargetOs_darwin || build_context.metrics.os == TargetOs_linux) {
+ } break;
+ case TargetOs_darwin:
+ case TargetOs_linux:
+ case TargetOs_freebsd:
if (!build_context.extra_linker_flags.text) {
build_context.extra_linker_flags = str_lit("-fsanitize=address");
} else {
build_context.extra_linker_flags = concatenate_strings(permanent_allocator(), build_context.extra_linker_flags, str_lit(" -fsanitize=address"));
}
+ break;
}
}
if (build_context.sanitizer_flags & SanitizerFlag_Memory) {
- if (build_context.metrics.os == TargetOs_darwin || build_context.metrics.os == TargetOs_linux) {
+ switch (build_context.metrics.os) {
+ case TargetOs_linux:
+ case TargetOs_freebsd:
if (!build_context.extra_linker_flags.text) {
build_context.extra_linker_flags = str_lit("-fsanitize=memory");
} else {
build_context.extra_linker_flags = concatenate_strings(permanent_allocator(), build_context.extra_linker_flags, str_lit(" -fsanitize=memory"));
}
+ break;
}
}
if (build_context.sanitizer_flags & SanitizerFlag_Thread) {
- if (build_context.metrics.os == TargetOs_darwin || build_context.metrics.os == TargetOs_linux) {
+ switch (build_context.metrics.os) {
+ case TargetOs_darwin:
+ case TargetOs_linux:
+ case TargetOs_freebsd:
if (!build_context.extra_linker_flags.text) {
build_context.extra_linker_flags = str_lit("-fsanitize=thread");
} else {
build_context.extra_linker_flags = concatenate_strings(permanent_allocator(), build_context.extra_linker_flags, str_lit(" -fsanitize=thread"));
}
+ break;
}
}
diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp
index 4fd1b8d1a..6870f6259 100644
--- a/src/llvm_backend.hpp
+++ b/src/llvm_backend.hpp
@@ -143,18 +143,17 @@ struct lbPadType {
LLVMTypeRef type;
};
-struct lbObjcRef {
- Entity * entity;
- lbAddr local_module_addr;
-};
-
struct lbModule {
LLVMModuleRef mod;
LLVMContextRef ctx;
+ Checker *checker;
+
struct lbGenerator *gen;
LLVMTargetMachineRef target_machine;
+ lbModule *polymorphic_module;
+
CheckerInfo *info;
AstPackage *pkg; // possibly associated
AstFile *file; // possibly associated
@@ -176,18 +175,23 @@ struct lbModule {
StringMap<lbValue> members;
StringMap<lbProcedure *> procedures;
PtrMap<LLVMValueRef, Entity *> procedure_values;
- Array<lbProcedure *> missing_procedures_to_check;
- StringMap<LLVMValueRef> const_strings;
+ MPSCQueue<lbProcedure *> missing_procedures_to_check;
+
+ StringMap<LLVMValueRef> const_strings;
+ String16Map<LLVMValueRef> const_string16s;
PtrMap<u64/*type hash*/, struct lbFunctionType *> function_type_map;
StringMap<lbProcedure *> gen_procs; // key is the canonicalized name
- Array<lbProcedure *> procedures_to_generate;
+ MPSCQueue<lbProcedure *> procedures_to_generate;
Array<Entity *> global_procedures_to_create;
Array<Entity *> global_types_to_create;
+ BlockingMutex generated_procedures_mutex;
+ Array<lbProcedure *> generated_procedures;
+
lbProcedure *curr_procedure;
LLVMBuilderRef const_dummy_builder;
@@ -198,8 +202,11 @@ struct lbModule {
RecursiveMutex debug_values_mutex;
PtrMap<void *, LLVMMetadataRef> debug_values;
- StringMap<lbObjcRef> objc_classes;
- StringMap<lbObjcRef> objc_selectors;
+
+ StringMap<lbAddr> objc_classes;
+ StringMap<lbAddr> objc_selectors;
+ StringMap<lbAddr> objc_ivars;
+ isize objc_next_block_id; // Used to name objective-c blocks, per module
PtrMap<u64/*type hash*/, lbAddr> map_cell_info_map; // address of runtime.Map_Info
PtrMap<u64/*type hash*/, lbAddr> map_info_map; // address of runtime.Map_Cell_Info
@@ -218,6 +225,14 @@ struct lbEntityCorrection {
char const *cname;
};
+struct lbObjCGlobal {
+ lbModule *module;
+ gbString global_name;
+ String name;
+ Type * type;
+ Type * class_impl_type; // This is set when the class has the objc_implement attribute set to true.
+};
+
struct lbGenerator : LinkerData {
CheckerInfo *info;
@@ -225,8 +240,7 @@ struct lbGenerator : LinkerData {
PtrMap<LLVMContextRef, lbModule *> modules_through_ctx;
lbModule default_module;
- RecursiveMutex anonymous_proc_lits_mutex;
- PtrMap<Ast *, lbProcedure *> anonymous_proc_lits;
+ lbModule *equal_module;
isize used_module_count;
@@ -235,6 +249,10 @@ struct lbGenerator : LinkerData {
lbProcedure *objc_names;
MPSCQueue<lbEntityCorrection> entities_to_correct_linkage;
+ MPSCQueue<lbObjCGlobal> objc_selectors;
+ MPSCQueue<lbObjCGlobal> objc_classes;
+ MPSCQueue<lbObjCGlobal> objc_ivars;
+ MPSCQueue<String> raddebug_section_strings;
};
@@ -318,6 +336,14 @@ struct lbVariadicReuseSlices {
lbAddr slice_addr;
};
+struct lbGlobalVariable {
+ lbValue var;
+ lbValue init;
+ DeclInfo *decl;
+ bool is_initialized;
+};
+
+
struct lbProcedure {
u32 flags;
u16 state_flags;
@@ -340,9 +366,9 @@ struct lbProcedure {
lbFunctionType *abi_function_type;
- LLVMValueRef value;
- LLVMBuilderRef builder;
- bool is_done;
+ LLVMValueRef value;
+ LLVMBuilderRef builder;
+ std::atomic<bool> is_done;
lbAddr return_ptr;
Array<lbDefer> defer_stmts;
@@ -378,6 +404,14 @@ struct lbProcedure {
PtrMap<Ast *, lbValue> selector_values;
PtrMap<Ast *, lbAddr> selector_addr;
PtrMap<LLVMValueRef, lbTupleFix> tuple_fix_map;
+
+ Array<lbValue> asan_stack_locals;
+
+ void (*generate_body)(lbModule *m, lbProcedure *p);
+ Array<lbGlobalVariable> *global_variables;
+ lbProcedure *objc_names;
+
+ Type *internal_gen_type; // map_set, map_get, etc.
};
@@ -402,7 +436,6 @@ gb_internal LLVMAttributeRef lb_create_enum_attribute_with_type(LLVMContextRef c
gb_internal void lb_add_proc_attribute_at_index(lbProcedure *p, isize index, char const *name, u64 value);
gb_internal void lb_add_proc_attribute_at_index(lbProcedure *p, isize index, char const *name);
gb_internal lbProcedure *lb_create_procedure(lbModule *module, Entity *entity, bool ignore_body=false);
-gb_internal void lb_end_procedure(lbProcedure *p);
gb_internal LLVMTypeRef lb_type(lbModule *m, Type *type);
@@ -410,9 +443,19 @@ gb_internal LLVMTypeRef llvm_get_element_type(LLVMTypeRef type);
gb_internal lbBlock *lb_create_block(lbProcedure *p, char const *name, bool append=false);
+struct lbConstContext {
+ bool allow_local;
+ bool is_rodata;
+ String link_section;
+};
+
+static lbConstContext const LB_CONST_CONTEXT_DEFAULT = {true, false, {}};
+static lbConstContext const LB_CONST_CONTEXT_DEFAULT_ALLOW_LOCAL = {true, false, {}};
+static lbConstContext const LB_CONST_CONTEXT_DEFAULT_NO_LOCAL = {false, false, {}};
+
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, bool is_rodata=false);
+gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lbConstContext cc = LB_CONST_CONTEXT_DEFAULT, Type *value_type=nullptr);
gb_internal lbValue lb_const_bool(lbModule *m, Type *type, bool value);
gb_internal lbValue lb_const_int(lbModule *m, Type *type, u64 value);
@@ -462,7 +505,10 @@ gb_internal void lb_emit_if(lbProcedure *p, lbValue cond, lbBlock *true_block, l
gb_internal void lb_start_block(lbProcedure *p, lbBlock *b);
gb_internal lbValue lb_build_call_expr(lbProcedure *p, Ast *expr);
-
+gb_internal lbProcedure *lb_create_dummy_procedure(lbModule *m, String link_name, Type *type);
+gb_internal void lb_begin_procedure_body(lbProcedure *p);
+gb_internal void lb_end_procedure_body(lbProcedure *p);
+gb_internal lbValue lb_emit_call(lbProcedure *p, lbValue value, Array<lbValue> const &args, ProcInlining inlining);
gb_internal lbAddr lb_find_or_generate_context_ptr(lbProcedure *p);
gb_internal lbContextData *lb_push_context_onto_stack(lbProcedure *p, lbAddr ctx);
@@ -509,7 +555,7 @@ gb_internal void lb_fill_slice(lbProcedure *p, lbAddr const &slice, lbValue base
gb_internal lbValue lb_type_info(lbProcedure *p, Type *type);
-gb_internal lbValue lb_find_or_add_entity_string(lbModule *m, String const &str);
+gb_internal lbValue lb_find_or_add_entity_string(lbModule *m, String const &str, bool custom_link_section);
gb_internal lbValue lb_generate_anonymous_proc_lit(lbModule *m, String const &prefix_name, Ast *expr, lbProcedure *parent = nullptr);
gb_internal bool lb_is_const(lbValue value);
@@ -558,7 +604,7 @@ gb_internal lbValue lb_emit_logical_binary_expr(lbProcedure *p, TokenKind op, As
gb_internal lbValue lb_build_cond(lbProcedure *p, Ast *cond, lbBlock *true_block, lbBlock *false_block);
gb_internal LLVMValueRef llvm_const_named_struct(lbModule *m, Type *t, LLVMValueRef *values, isize value_count_);
-gb_internal LLVMValueRef llvm_const_named_struct_internal(LLVMTypeRef t, LLVMValueRef *values, isize value_count_);
+gb_internal LLVMValueRef llvm_const_named_struct_internal(lbModule *m, LLVMTypeRef t, LLVMValueRef *values, isize value_count_);
gb_internal void lb_set_entity_from_other_modules_linkage_correctly(lbModule *other_module, Entity *e, String const &name);
gb_internal lbValue lb_expr_untyped_const_to_typed(lbModule *m, Ast *expr, Type *t);
diff --git a/src/llvm_backend_const.cpp b/src/llvm_backend_const.cpp
index b916c0017..193bffe08 100644
--- a/src/llvm_backend_const.cpp
+++ b/src/llvm_backend_const.cpp
@@ -33,7 +33,7 @@ gb_internal bool lb_is_elem_const(Ast *elem, Type *elem_type) {
gb_internal bool lb_is_const_nil(lbValue value) {
LLVMValueRef v = value.value;
- if (LLVMIsConstant(v)) {
+ if (v != nullptr && LLVMIsConstant(v)) {
if (LLVMIsAConstantAggregateZero(v)) {
return true;
} else if (LLVMIsAConstantPointerNull(v)) {
@@ -81,7 +81,7 @@ gb_internal String lb_get_const_string(lbModule *m, lbValue value) {
}
-gb_internal LLVMValueRef llvm_const_cast(LLVMValueRef val, LLVMTypeRef dst) {
+gb_internal LLVMValueRef llvm_const_cast(LLVMValueRef val, LLVMTypeRef dst, bool *failure_) {
LLVMTypeRef src = LLVMTypeOf(val);
if (src == dst) {
return val;
@@ -96,15 +96,12 @@ gb_internal LLVMValueRef llvm_const_cast(LLVMValueRef val, LLVMTypeRef dst) {
case LLVMPointerTypeKind:
return LLVMConstPointerCast(val, dst);
case LLVMStructTypeKind:
- // GB_PANIC("%s -> %s", LLVMPrintValueToString(val), LLVMPrintTypeToString(dst));
- // NOTE(bill): It's not possible to do a bit cast on a struct, why was this code even here in the first place?
- // It seems mostly to exist to get around the "anonymous -> named" struct assignments
- // return LLVMConstBitCast(val, dst);
+ if (LLVMTypeOf(val) != dst) {
+ if (failure_) *failure_ = true;
+ }
return val;
- default:
- GB_PANIC("Unhandled const cast %s to %s", LLVMPrintTypeToString(src), LLVMPrintTypeToString(dst));
}
-
+ if (failure_) *failure_ = true;
return val;
}
@@ -122,19 +119,38 @@ gb_internal lbValue lb_const_ptr_cast(lbModule *m, lbValue value, Type *t) {
gb_internal LLVMValueRef llvm_const_string_internal(lbModule *m, Type *t, LLVMValueRef data, LLVMValueRef len) {
+ GB_ASSERT(!is_type_string16(t));
if (build_context.metrics.ptr_size < build_context.metrics.int_size) {
LLVMValueRef values[3] = {
data,
LLVMConstNull(lb_type(m, t_i32)),
len,
};
- return llvm_const_named_struct_internal(lb_type(m, t), values, 3);
+ return llvm_const_named_struct_internal(m, lb_type(m, t), values, 3);
} else {
LLVMValueRef values[2] = {
data,
len,
};
- return llvm_const_named_struct_internal(lb_type(m, t), values, 2);
+ return llvm_const_named_struct_internal(m, lb_type(m, t), values, 2);
+ }
+}
+
+gb_internal LLVMValueRef llvm_const_string16_internal(lbModule *m, Type *t, LLVMValueRef data, LLVMValueRef len) {
+ GB_ASSERT(is_type_string16(t));
+ if (build_context.metrics.ptr_size < build_context.metrics.int_size) {
+ LLVMValueRef values[3] = {
+ data,
+ LLVMConstNull(lb_type(m, t_i32)),
+ len,
+ };
+ return llvm_const_named_struct_internal(m, lb_type(m, t), values, 3);
+ } else {
+ LLVMValueRef values[2] = {
+ data,
+ len,
+ };
+ return llvm_const_named_struct_internal(m, lb_type(m, t), values, 2);
}
}
@@ -146,10 +162,10 @@ gb_internal LLVMValueRef llvm_const_named_struct(lbModule *m, Type *t, LLVMValue
unsigned value_count = cast(unsigned)value_count_;
unsigned elem_count = LLVMCountStructElementTypes(struct_type);
if (elem_count == value_count) {
- return llvm_const_named_struct_internal(struct_type, values, value_count_);
+ return llvm_const_named_struct_internal(m, struct_type, values, value_count_);
}
Type *bt = base_type(t);
- GB_ASSERT(bt->kind == Type_Struct);
+ GB_ASSERT(bt->kind == Type_Struct || bt->kind == Type_Union);
GB_ASSERT(value_count_ == bt->Struct.fields.count);
@@ -166,25 +182,40 @@ gb_internal LLVMValueRef llvm_const_named_struct(lbModule *m, Type *t, LLVMValue
}
}
- return llvm_const_named_struct_internal(struct_type, values_with_padding, values_with_padding_count);
+ return llvm_const_named_struct_internal(m, struct_type, values_with_padding, values_with_padding_count);
}
-gb_internal LLVMValueRef llvm_const_named_struct_internal(LLVMTypeRef t, LLVMValueRef *values, isize value_count_) {
+gb_internal LLVMValueRef llvm_const_named_struct_internal(lbModule *m, LLVMTypeRef t, LLVMValueRef *values, isize value_count_) {
unsigned value_count = cast(unsigned)value_count_;
unsigned elem_count = LLVMCountStructElementTypes(t);
GB_ASSERT_MSG(value_count == elem_count, "%s %u %u", LLVMPrintTypeToString(t), value_count, elem_count);
+ bool failure = false;
for (unsigned i = 0; i < elem_count; i++) {
LLVMTypeRef elem_type = LLVMStructGetTypeAtIndex(t, i);
- values[i] = llvm_const_cast(values[i], elem_type);
+ values[i] = llvm_const_cast(values[i], elem_type, &failure);
+ }
+
+ if (failure) {
+ return LLVMConstStructInContext(m->ctx, values, value_count, true);
}
return LLVMConstNamedStruct(t, values, value_count);
}
-gb_internal LLVMValueRef llvm_const_array(LLVMTypeRef elem_type, LLVMValueRef *values, isize value_count_) {
+gb_internal LLVMValueRef llvm_const_array(lbModule *m, LLVMTypeRef elem_type, LLVMValueRef *values, isize value_count_) {
unsigned value_count = cast(unsigned)value_count_;
+ bool failure = false;
+ for (unsigned i = 0; i < value_count; i++) {
+ values[i] = llvm_const_cast(values[i], elem_type, &failure);
+ }
+ if (failure) {
+ return LLVMConstStructInContext(m->ctx, values, value_count, false);
+ }
for (unsigned i = 0; i < value_count; i++) {
- values[i] = llvm_const_cast(values[i], elem_type);
+ if (elem_type != LLVMTypeOf(values[i])) {
+ return LLVMConstStructInContext(m->ctx, values, value_count, false);
+ }
}
+
return LLVMConstArray(elem_type, values, value_count);
}
@@ -238,6 +269,10 @@ gb_internal lbValue lb_const_string(lbModule *m, String const &value) {
return lb_const_value(m, t_string, exact_value_string(value));
}
+gb_internal lbValue lb_const_string(lbModule *m, String16 const &value) {
+ return lb_const_value(m, t_string16, exact_value_string16(value));
+}
+
gb_internal lbValue lb_const_bool(lbModule *m, Type *type, bool value) {
lbValue res = {};
@@ -292,19 +327,33 @@ gb_internal lbValue lb_const_source_code_location_const(lbModule *m, String cons
i32 line = pos.line;
i32 column = pos.column;
- if (build_context.obfuscate_source_code_locations) {
+ switch (build_context.source_code_location_info) {
+ case SourceCodeLocationInfo_Normal:
+ break;
+ case SourceCodeLocationInfo_Obfuscated:
file = obfuscate_string(file, "F");
procedure = obfuscate_string(procedure, "P");
- line = obfuscate_i32(line);
- column = obfuscate_i32(column);
+ line = obfuscate_i32(line);
+ column = obfuscate_i32(column);
+ break;
+ case SourceCodeLocationInfo_Filename:
+ file = last_path_element(file);
+ break;
+ case SourceCodeLocationInfo_None:
+ file = str_lit("");
+ procedure = str_lit("");
+
+ line = 0;
+ column = 0;
+ break;
}
LLVMValueRef fields[4] = {};
- fields[0]/*file*/ = lb_find_or_add_entity_string(m, file).value;
+ fields[0]/*file*/ = lb_find_or_add_entity_string(m, file, false).value;
fields[1]/*line*/ = lb_const_int(m, t_i32, line).value;
fields[2]/*column*/ = lb_const_int(m, t_i32, column).value;
- fields[3]/*procedure*/ = lb_find_or_add_entity_string(m, procedure).value;
+ fields[3]/*procedure*/ = lb_find_or_add_entity_string(m, procedure, false).value;
lbValue res = {};
res.value = llvm_const_named_struct(m, t_source_code_location, fields, gb_count_of(fields));
@@ -391,12 +440,12 @@ 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, bool is_rodata) {
- if (allow_local) {
- is_rodata = false;
+gb_internal LLVMValueRef lb_build_constant_array_values(lbModule *m, Type *type, Type *elem_type, isize count, LLVMValueRef *values, lbConstContext cc) {
+ if (cc.allow_local) {
+ cc.is_rodata = false;
}
- bool is_local = allow_local && m->curr_procedure != nullptr;
+ bool is_local = cc.allow_local && m->curr_procedure != nullptr;
bool is_const = true;
if (is_local) {
for (isize i = 0; i < count; i++) {
@@ -424,7 +473,7 @@ gb_internal LLVMValueRef lb_build_constant_array_values(lbModule *m, Type *type,
return lb_addr_load(p, v).value;
}
- return llvm_const_array(lb_type(m, elem_type), values, cast(unsigned int)count);
+ return llvm_const_array(m, lb_type(m, elem_type), values, cast(unsigned int)count);
}
gb_internal LLVMValueRef lb_big_int_to_llvm(lbModule *m, Type *original_type, BigInt const *a) {
@@ -494,15 +543,15 @@ gb_internal bool lb_is_nested_possibly_constant(Type *ft, Selection const &sel,
}
- if (is_type_raw_union(ft) || is_type_typeid(ft)) {
+ if (is_type_raw_union(ft)) {
return false;
}
return lb_is_elem_const(elem, ft);
}
-gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_local, bool is_rodata) {
- if (allow_local) {
- is_rodata = false;
+gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lbConstContext cc, Type *value_type) {
+ if (cc.allow_local) {
+ cc.is_rodata = false;
}
LLVMContextRef ctx = m->ctx;
@@ -515,12 +564,101 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
type = core_type(type);
value = convert_exact_value_for_type(value, type);
- if (value.kind == ExactValue_Typeid) {
- return lb_typeid(m, value.value_typeid);
- }
+ bool is_local = cc.allow_local && m->curr_procedure != nullptr;
- if (value.kind == ExactValue_Invalid) {
- return lb_const_nil(m, original_type);
+ if (is_type_union(type) && is_type_union_constantable(type)) {
+ Type *bt = base_type(type);
+ GB_ASSERT(bt->kind == Type_Union);
+ if (bt->Union.variants.count == 0) {
+ return lb_const_nil(m, original_type);
+ } else if (bt->Union.variants.count == 1) {
+ Type *t = bt->Union.variants[0];
+ lbValue cv = lb_const_value(m, t, value, cc);
+ GB_ASSERT(LLVMIsConstant(cv.value));
+
+ LLVMTypeRef llvm_type = lb_type(m, original_type);
+
+ if (is_type_union_maybe_pointer(type)) {
+ LLVMValueRef values[1] = {cv.value};
+ res.value = llvm_const_named_struct_internal(m, llvm_type, values, 1);
+ res.type = original_type;
+ return res;
+ } else {
+
+ unsigned tag_value = 1;
+ if (bt->Union.kind == UnionType_no_nil) {
+ tag_value = 0;
+ }
+ LLVMValueRef tag = LLVMConstInt(LLVMStructGetTypeAtIndex(llvm_type, 1), tag_value, false);
+ LLVMValueRef padding = nullptr;
+ LLVMValueRef values[3] = {cv.value, tag, padding};
+
+ isize value_count = 2;
+ if (LLVMCountStructElementTypes(llvm_type) > 2) {
+ value_count = 3;
+ padding = LLVMConstNull(LLVMStructGetTypeAtIndex(llvm_type, 2));
+ }
+ res.value = llvm_const_named_struct_internal(m, llvm_type, values, value_count);
+ res.type = original_type;
+ return res;
+ }
+ } else {
+ if (value_type == nullptr) {
+ if (value.kind == ExactValue_Compound) {
+ ast_node(cl, CompoundLit, value.value_compound);
+ if (cl->elems.count == 0) {
+ return lb_const_nil(m, original_type);
+ }
+ } else if (value.kind == ExactValue_Invalid) {
+ return lb_const_nil(m, original_type);
+ }
+ }
+
+ GB_ASSERT_MSG(value_type != nullptr, "%s :: %s", type_to_string(original_type), exact_value_to_string(value));
+
+ i64 block_size = bt->Union.variant_block_size;
+
+ if (are_types_identical(value_type, original_type)) {
+ if (value.kind == ExactValue_Compound) {
+ ast_node(cl, CompoundLit, value.value_compound);
+ if (cl->elems.count == 0) {
+ return lb_const_nil(m, original_type);
+ }
+ } else if (value.kind == ExactValue_Invalid) {
+ return lb_const_nil(m, original_type);
+ }
+
+ GB_PANIC("%s vs %s", type_to_string(value_type), type_to_string(original_type));
+ }
+
+ lbValue cv = lb_const_value(m, value_type, value, cc, value_type);
+ Type *variant_type = cv.type;
+
+ LLVMValueRef values[4] = {};
+ unsigned value_count = 0;
+
+ values[value_count++] = cv.value;
+ if (type_size_of(variant_type) != block_size) {
+ LLVMTypeRef padding_type = lb_type_padding_filler(m, block_size - type_size_of(variant_type), 1);
+ values[value_count++] = LLVMConstNull(padding_type);
+ }
+
+ Type *tag_type = union_tag_type(bt);
+ LLVMTypeRef llvm_tag_type = lb_type(m, tag_type);
+ i64 tag_index = union_variant_index(bt, variant_type);
+ GB_ASSERT(tag_index >= 0);
+ values[value_count++] = LLVMConstInt(llvm_tag_type, tag_index, false);
+ i64 used_size = block_size + type_size_of(tag_type);
+ i64 union_size = type_size_of(bt);
+ i64 padding = union_size - used_size;
+ if (padding > 0) {
+ LLVMTypeRef padding_type = lb_type_padding_filler(m, padding, 1);
+ values[value_count++] = LLVMConstNull(padding_type);
+ }
+
+ res.value = LLVMConstStructInContext(m->ctx, values, value_count, true);
+ return res;
+ }
}
if (value.kind == ExactValue_Procedure) {
@@ -533,7 +671,10 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
Entity *e = entity_from_expr(expr);
res = lb_find_procedure_value_from_entity(m, e);
}
- GB_ASSERT(res.value != nullptr);
+ if (res.value == nullptr) {
+ // This is an unspecialized polymorphic procedure, return nil or dummy value
+ return lb_const_nil(m, original_type);
+ }
GB_ASSERT(LLVMGetValueKind(res.value) == LLVMFunctionValueKind);
if (LLVMGetIntrinsicID(res.value) == 0) {
@@ -543,7 +684,23 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
return res;
}
- bool is_local = allow_local && m->curr_procedure != nullptr;
+ // NOTE(bill): This has to be done AFTER the union stuff
+ if (value.kind == ExactValue_Invalid) {
+ return lb_const_nil(m, original_type);
+ }
+
+
+ if (value.kind == ExactValue_Typeid) {
+ return lb_typeid(m, value.value_typeid);
+ }
+
+ if (value.kind == ExactValue_Compound) {
+ ast_node(cl, CompoundLit, value.value_compound);
+ if (cl->elems.count == 0) {
+ return lb_const_nil(m, original_type);
+ }
+ }
+
// GB_ASSERT_MSG(is_type_typed(type), "%s", type_to_string(type));
@@ -552,6 +709,10 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
GB_ASSERT(is_type_slice(type));
res.value = lb_find_or_add_entity_string_byte_slice_with_type(m, value.value_string, original_type).value;
return res;
+ } else if (value.kind == ExactValue_String16) {
+ GB_ASSERT(is_type_slice(type));
+ res.value = lb_find_or_add_entity_string16_slice_with_type(m, value.value_string16, original_type).value;
+ return res;
} else {
ast_node(cl, CompoundLit, value.value_compound);
@@ -562,19 +723,39 @@ 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, is_rodata);
+ lbValue backing_array = lb_const_value(m, t, value, cc, nullptr);
LLVMValueRef array_data = nullptr;
+
if (is_local) {
// NOTE(bill, 2020-06-08): This is a bit of a hack but a "constant" slice needs
// its backing data on the stack
lbProcedure *p = m->curr_procedure;
LLVMTypeRef llvm_type = lb_type(m, t);
- array_data = llvm_alloca(p, llvm_type, 16);
+ unsigned alignment = cast(unsigned)gb_max(type_align_of(t), 16);
+
+
+ bool do_local_copy = false;
+ if (do_local_copy) {
+ array_data = llvm_alloca(p, llvm_type, alignment);
+
+ LLVMValueRef local_copy = llvm_alloca(p, LLVMTypeOf(backing_array.value), alignment);
+ LLVMBuildStore(p->builder, backing_array.value, local_copy);
+
+ LLVMBuildMemCpy(p->builder,
+ array_data, alignment,
+ local_copy, alignment,
+ LLVMConstInt(lb_type(m, t_int), type_size_of(t), false)
+ );
+ } else {
+ array_data = llvm_alloca(p, LLVMTypeOf(backing_array.value), alignment);
+ LLVMBuildStore(p->builder, backing_array.value, array_data);
+
+ array_data = LLVMBuildPointerCast(p->builder, array_data, LLVMPointerType(llvm_type, 0), "");
+ }
- LLVMBuildStore(p->builder, backing_array.value, array_data);
{
LLVMValueRef indices[2] = {llvm_zero(m), llvm_zero(m)};
@@ -596,23 +777,25 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
String name = make_string(cast(u8 const *)str, gb_string_length(str));
Entity *e = alloc_entity_constant(nullptr, make_token_ident(name), t, value);
- array_data = LLVMAddGlobal(m->mod, lb_type(m, t), str);
+ array_data = LLVMAddGlobal(m->mod, LLVMTypeOf(backing_array.value), str);
LLVMSetInitializer(array_data, backing_array.value);
- if (is_rodata) {
+ if (cc.link_section.len > 0) {
+ LLVMSetSection(array_data, alloc_cstring(permanent_allocator(), cc.link_section));
+ }
+ if (cc.is_rodata) {
LLVMSetGlobalConstant(array_data, true);
}
lbValue g = {};
- g.value = array_data;
+ g.value = LLVMConstPointerCast(array_data, LLVMPointerType(lb_type(m, t), 0));
g.type = t;
lb_add_entity(m, e, g);
lb_add_member(m, name, g);
{
- LLVMValueRef indices[2] = {llvm_zero(m), llvm_zero(m)};
- LLVMValueRef ptr = LLVMConstInBoundsGEP2(lb_type(m, t), array_data, indices, 2);
+ LLVMValueRef ptr = g.value;
LLVMValueRef len = LLVMConstInt(lb_type(m, t_int), count, true);
LLVMValueRef values[2] = {ptr, len};
@@ -645,12 +828,12 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
}
GB_ASSERT(offset == s.len);
- res.value = llvm_const_array(et, elems, cast(unsigned)count);
+ res.value = llvm_const_array(m, et, elems, cast(unsigned)count);
return res;
}
// 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, is_rodata);
+ return lb_const_value(m, core_elem, value, cc);
} else if (is_type_u8_array(type) && value.kind == ExactValue_String) {
GB_ASSERT(type->Array.count == value.value_string.len);
LLVMValueRef data = LLVMConstStringInContext(ctx,
@@ -668,14 +851,14 @@ 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, is_rodata);
+ lbValue single_elem = lb_const_value(m, elem, value, cc);
LLVMValueRef *elems = gb_alloc_array(permanent_allocator(), LLVMValueRef, cast(isize)count);
for (i64 i = 0; i < count; i++) {
elems[i] = single_elem.value;
}
- res.value = llvm_const_array(lb_type(m, elem), elems, cast(unsigned)count);
+ res.value = llvm_const_array(m, lb_type(m, elem), elems, cast(unsigned)count);
return res;
} else if (is_type_matrix(type) &&
value.kind != ExactValue_Invalid &&
@@ -686,8 +869,8 @@ 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, is_rodata);
- single_elem.value = llvm_const_cast(single_elem.value, lb_type(m, elem));
+ lbValue single_elem = lb_const_value(m, elem, value, cc);
+ single_elem.value = llvm_const_cast(single_elem.value, lb_type(m, elem), /*failure_*/nullptr);
i64 total_elem_count = matrix_type_total_internal_elems(type);
LLVMValueRef *elems = gb_alloc_array(permanent_allocator(), LLVMValueRef, cast(isize)total_elem_count);
@@ -708,8 +891,8 @@ 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, is_rodata);
- single_elem.value = llvm_const_cast(single_elem.value, lb_type(m, elem));
+ lbValue single_elem = lb_const_value(m, elem, value, cc);
+ single_elem.value = llvm_const_cast(single_elem.value, lb_type(m, elem), /*failure_*/nullptr);
LLVMValueRef *elems = gb_alloc_array(permanent_allocator(), LLVMValueRef, count);
for (i64 i = 0; i < count; i++) {
@@ -729,24 +912,80 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
return res;
case ExactValue_String:
{
- LLVMValueRef ptr = lb_find_or_add_entity_string_ptr(m, value.value_string);
+ bool custom_link_section = cc.link_section.len > 0;
+
+ LLVMValueRef ptr = nullptr;
+ lbValue res = {};
+ res.type = default_type(original_type);
+
+ isize len = value.value_string.len;
+
+ if (is_type_string16(res.type) || is_type_cstring16(res.type)) {
+ TEMPORARY_ALLOCATOR_GUARD();
+ String16 s16 = string_to_string16(temporary_allocator(), value.value_string);
+ len = s16.len;
+ ptr = lb_find_or_add_entity_string16_ptr(m, s16, custom_link_section);
+ } else {
+ ptr = lb_find_or_add_entity_string_ptr(m, value.value_string, custom_link_section);
+ }
+
+ if (custom_link_section) {
+ LLVMSetSection(ptr, alloc_cstring(permanent_allocator(), cc.link_section));
+ }
+
+ if (is_type_cstring(res.type) || is_type_cstring16(res.type)) {
+ res.value = ptr;
+ } else {
+ if (len == 0) {
+ if (is_type_string16(res.type)) {
+ ptr = LLVMConstNull(lb_type(m, t_u16_ptr));
+ } else {
+ ptr = LLVMConstNull(lb_type(m, t_u8_ptr));
+ }
+ }
+ LLVMValueRef str_len = LLVMConstInt(lb_type(m, t_int), len, true);
+ GB_ASSERT(is_type_string(original_type));
+
+ if (is_type_string16(res.type)) {
+ res.value = llvm_const_string16_internal(m, original_type, ptr, str_len);
+ } else {
+ res.value = llvm_const_string_internal(m, original_type, ptr, str_len);
+ }
+ }
+
+ return res;
+ }
+
+ case ExactValue_String16:
+ {
+ GB_ASSERT(is_type_string16(res.type) || is_type_cstring16(res.type));
+
+ bool custom_link_section = cc.link_section.len > 0;
+
+ LLVMValueRef ptr = lb_find_or_add_entity_string16_ptr(m, value.value_string16, custom_link_section);
lbValue res = {};
res.type = default_type(original_type);
- if (is_type_cstring(res.type)) {
+
+ if (custom_link_section) {
+ LLVMSetSection(ptr, alloc_cstring(permanent_allocator(), cc.link_section));
+ }
+
+ if (is_type_cstring16(res.type)) {
res.value = ptr;
} else {
- if (value.value_string.len == 0) {
+ if (value.value_string16.len == 0) {
ptr = LLVMConstNull(lb_type(m, t_u8_ptr));
}
- LLVMValueRef str_len = LLVMConstInt(lb_type(m, t_int), value.value_string.len, true);
+ LLVMValueRef str_len = LLVMConstInt(lb_type(m, t_int), value.value_string16.len, true);
GB_ASSERT(is_type_string(original_type));
- res.value = llvm_const_string_internal(m, original_type, ptr, str_len);
+ res.value = llvm_const_string16_internal(m, original_type, ptr, str_len);
}
return res;
}
+
case ExactValue_Integer:
if (is_type_pointer(type) || is_type_multi_pointer(type) || is_type_proc(type)) {
LLVMTypeRef t = lb_type(m, original_type);
@@ -837,7 +1076,149 @@ 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, is_rodata);
+ return lb_const_value(m, type, value, cc);
+ } else if (is_type_soa_struct(type)) {
+ GB_ASSERT(type->kind == Type_Struct);
+ GB_ASSERT(type->Struct.soa_kind == StructSoa_Fixed);
+ ast_node(cl, CompoundLit, value.value_compound);
+ Type *elem_type = type->Struct.soa_elem;
+ isize elem_count = cl->elems.count;
+ if (elem_count == 0 || !elem_type_can_be_constant(elem_type)) {
+ return lb_const_nil(m, original_type);
+ }
+ if (cl->elems[0]->kind == Ast_FieldValue) {
+ TEMPORARY_ALLOCATOR_GUARD();
+
+ // TODO(bill): This is O(N*M) and will be quite slow; it should probably be sorted before hand
+
+ isize elem_count = cast(isize)type->Struct.soa_count;
+
+ LLVMValueRef *aos_values = gb_alloc_array(temporary_allocator(), LLVMValueRef, elem_count);
+
+ isize value_index = 0;
+ for (i64 i = 0; i < elem_count; i++) {
+ bool found = false;
+
+ for (isize j = 0; j < elem_count; j++) {
+ Ast *elem = cl->elems[j];
+ ast_node(fv, FieldValue, elem);
+ if (is_ast_range(fv->field)) {
+ ast_node(ie, BinaryExpr, fv->field);
+ TypeAndValue lo_tav = ie->left->tav;
+ TypeAndValue hi_tav = ie->right->tav;
+ GB_ASSERT(lo_tav.mode == Addressing_Constant);
+ GB_ASSERT(hi_tav.mode == Addressing_Constant);
+
+ TokenKind op = ie->op.kind;
+ i64 lo = exact_value_to_i64(lo_tav.value);
+ i64 hi = exact_value_to_i64(hi_tav.value);
+ if (op != Token_RangeHalf) {
+ hi += 1;
+ }
+ if (lo == i) {
+ TypeAndValue tav = fv->value->tav;
+ LLVMValueRef val = lb_const_value(m, elem_type, tav.value, cc, tav.type).value;
+ for (i64 k = lo; k < hi; k++) {
+ aos_values[value_index++] = val;
+ }
+
+ found = true;
+ i += (hi-lo-1);
+ break;
+ }
+ } else {
+ TypeAndValue index_tav = fv->field->tav;
+ GB_ASSERT(index_tav.mode == Addressing_Constant);
+ 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, cc, tav.type).value;
+ aos_values[value_index++] = val;
+ found = true;
+ break;
+ }
+ }
+ }
+
+ if (!found) {
+ aos_values[value_index++] = nullptr;
+ }
+ }
+
+
+ isize field_count = type->Struct.fields.count;
+ LLVMValueRef *soa_values = gb_alloc_array(temporary_allocator(), LLVMValueRef, field_count);
+
+ for (isize i = 0; i < field_count; i++) {
+ TEMPORARY_ALLOCATOR_GUARD();
+
+ LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, elem_count);
+
+ Entity *f = type->Struct.fields[i];
+ Type *array_type = f->type;
+ GB_ASSERT(array_type->kind == Type_Array);
+ Type *field_type = array_type->Array.elem;
+
+ for (isize j = 0; j < elem_count; j++) {
+ LLVMValueRef v = aos_values[j];
+ if (v != nullptr) {
+ values[j] = llvm_const_extract_value(m, v, cast(unsigned)i);
+ } else {
+ values[j] = LLVMConstNull(lb_type(m, field_type));
+ }
+ }
+
+ soa_values[i] = lb_build_constant_array_values(m, array_type, field_type, elem_count, values, cc);
+ }
+
+ res.value = llvm_const_named_struct(m, type, soa_values, field_count);
+ return res;
+ } else {
+ GB_ASSERT_MSG(elem_count == type->Struct.soa_count, "%td != %td", elem_count, type->Struct.soa_count);
+
+ TEMPORARY_ALLOCATOR_GUARD();
+
+ isize elem_count = cast(isize)type->Struct.soa_count;
+
+ LLVMValueRef *aos_values = gb_alloc_array(temporary_allocator(), LLVMValueRef, elem_count);
+
+ for (isize i = 0; i < elem_count; i++) {
+ TypeAndValue tav = cl->elems[i]->tav;
+ GB_ASSERT(tav.mode != Addressing_Invalid);
+ aos_values[i] = lb_const_value(m, elem_type, tav.value, cc, tav.type).value;
+ }
+ for (isize i = elem_count; i < type->Struct.soa_count; i++) {
+ aos_values[i] = nullptr;
+ }
+
+ isize field_count = type->Struct.fields.count;
+ LLVMValueRef *soa_values = gb_alloc_array(temporary_allocator(), LLVMValueRef, field_count);
+
+ for (isize i = 0; i < field_count; i++) {
+ TEMPORARY_ALLOCATOR_GUARD();
+
+ LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, elem_count);
+
+ Entity *f = type->Struct.fields[i];
+ Type *array_type = f->type;
+ GB_ASSERT(array_type->kind == Type_Array);
+ Type *field_type = array_type->Array.elem;
+
+ for (isize j = 0; j < elem_count; j++) {
+ LLVMValueRef v = aos_values[j];
+ if (v != nullptr) {
+ values[j] = llvm_const_extract_value(m, v, cast(unsigned)i);
+ } else {
+ values[j] = LLVMConstNull(lb_type(m, field_type));
+ }
+ }
+
+ soa_values[i] = lb_build_constant_array_values(m, array_type, field_type, elem_count, values, cc);
+ }
+
+ res.value = llvm_const_named_struct(m, type, soa_values, field_count);
+ return res;
+ }
} else if (is_type_array(type)) {
ast_node(cl, CompoundLit, value.value_compound);
Type *elem_type = type->Array.elem;
@@ -871,7 +1252,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, is_rodata).value;
+ LLVMValueRef val = lb_const_value(m, elem_type, tav.value, cc, tav.type).value;
for (i64 k = lo; k < hi; k++) {
values[value_index++] = val;
}
@@ -886,7 +1267,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, is_rodata).value;
+ LLVMValueRef val = lb_const_value(m, elem_type, tav.value, cc, tav.type).value;
values[value_index++] = val;
found = true;
break;
@@ -899,9 +1280,20 @@ 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, is_rodata);
+ res.value = lb_build_constant_array_values(m, type, elem_type, cast(isize)type->Array.count, values, cc);
+ return res;
+ } else if (are_types_identical(value.value_compound->tav.type, elem_type)) {
+ // Compound is of array item type; expand its value to all items in array.
+ LLVMValueRef* values = gb_alloc_array(temporary_allocator(), LLVMValueRef, cast(isize)type->Array.count);
+
+ for (isize i = 0; i < type->Array.count; i++) {
+ values[i] = lb_const_value(m, elem_type, value, cc, elem_type).value;
+ }
+
+ res.value = lb_build_constant_array_values(m, type, elem_type, cast(isize)type->Array.count, values, cc);
return res;
} else {
+ // Assume that compound value is an array literal
GB_ASSERT_MSG(elem_count == type->Array.count, "%td != %td", elem_count, type->Array.count);
LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, cast(isize)type->Array.count);
@@ -909,13 +1301,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, is_rodata).value;
+ values[i] = lb_const_value(m, elem_type, tav.value, cc, tav.type).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, is_rodata);
+ res.value = lb_build_constant_array_values(m, type, elem_type, cast(isize)type->Array.count, values, cc);
return res;
}
} else if (is_type_enumerated_array(type)) {
@@ -955,7 +1347,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, is_rodata).value;
+ LLVMValueRef val = lb_const_value(m, elem_type, tav.value, cc, tav.type).value;
for (i64 k = lo; k < hi; k++) {
values[value_index++] = val;
}
@@ -970,7 +1362,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, is_rodata).value;
+ LLVMValueRef val = lb_const_value(m, elem_type, tav.value, cc, tav.type).value;
values[value_index++] = val;
found = true;
break;
@@ -983,7 +1375,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, is_rodata);
+ res.value = lb_build_constant_array_values(m, type, elem_type, cast(isize)type->EnumeratedArray.count, values, cc);
return res;
} else {
GB_ASSERT_MSG(elem_count == type->EnumeratedArray.count, "%td != %td", elem_count, type->EnumeratedArray.count);
@@ -993,13 +1385,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, is_rodata).value;
+ values[i] = lb_const_value(m, elem_type, tav.value, cc, tav.type).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, is_rodata);
+ res.value = lb_build_constant_array_values(m, type, elem_type, cast(isize)type->EnumeratedArray.count, values, cc);
return res;
}
} else if (is_type_simd_vector(type)) {
@@ -1038,7 +1430,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, is_rodata).value;
+ LLVMValueRef val = lb_const_value(m, elem_type, tav.value, cc, tav.type).value;
for (i64 k = lo; k < hi; k++) {
values[value_index++] = val;
}
@@ -1053,7 +1445,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, is_rodata).value;
+ LLVMValueRef val = lb_const_value(m, elem_type, tav.value, cc, tav.type).value;
values[value_index++] = val;
found = true;
break;
@@ -1072,7 +1464,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, is_rodata).value;
+ values[i] = lb_const_value(m, elem_type, tav.value, cc, tav.type).value;
}
LLVMTypeRef et = lb_type(m, elem_type);
@@ -1080,7 +1472,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
values[i] = LLVMConstNull(et);
}
for (isize i = 0; i < total_elem_count; i++) {
- values[i] = llvm_const_cast(values[i], et);
+ values[i] = llvm_const_cast(values[i], et, /*failure_*/nullptr);
}
res.value = LLVMConstVector(values, cast(unsigned)total_elem_count);
@@ -1094,6 +1486,39 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
}
if (is_type_raw_union(type)) {
+ if (is_type_raw_union_constantable(type)) {
+ GB_ASSERT(cl->elems.count == 1);
+ GB_ASSERT(cl->elems[0]->kind == Ast_FieldValue);
+ ast_node(fv, FieldValue, cl->elems[0]);
+ Entity *f = entity_of_node(fv->field);
+
+ TypeAndValue tav = fv->value->tav;
+ if (tav.value.kind != ExactValue_Invalid) {
+ lbValue value = lb_const_value(m, f->type, tav.value, cc, f->type);
+
+ LLVMValueRef values[2];
+ unsigned value_count = 0;
+
+ values[value_count++] = value.value;
+
+ i64 union_alignment = type_align_of(type);
+ i64 value_alignment = type_align_of(f->type);
+ i64 alignment = gb_max(gb_min(value_alignment, union_alignment), 1);
+
+ i64 union_size = type_size_of(type);
+ i64 value_size = lb_sizeof(LLVMTypeOf(value.value));
+ i64 padding = union_size-value_size;
+ if (padding > 0) {
+ LLVMTypeRef padding_type = lb_type_padding_filler(m, padding, alignment);
+ values[value_count++] = LLVMConstNull(padding_type);
+ }
+
+ LLVMValueRef res = LLVMConstStructInContext(m->ctx, values, value_count, /*packed*/padding > 0);
+
+ return {res, original_type};
+ }
+
+ }
return lb_const_nil(m, original_type);
}
@@ -1121,14 +1546,20 @@ 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, is_rodata).value;
+ lbValue value = lb_const_value(m, f->type, tav.value, cc, tav.type);
+ LLVMTypeRef value_type = LLVMTypeOf(value.value);
+ GB_ASSERT_MSG(lb_sizeof(value_type) == type_size_of(f->type), "%s vs %s", LLVMPrintTypeToString(value_type), type_to_string(f->type));
+ values[index] = value.value;
visited[index] = true;
} else {
if (!visited[index]) {
- values[index] = lb_const_value(m, f->type, {}, false).value;
+ auto new_cc = cc;
+ new_cc.allow_local = false;
+ values[index] = lb_const_value(m, f->type, {}, new_cc).value;
visited[index] = true;
}
+
unsigned idx_list_len = cast(unsigned)sel.index.count-1;
unsigned *idx_list = gb_alloc_array(temporary_allocator(), unsigned, idx_list_len);
@@ -1139,6 +1570,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
i32 index = sel.index[j];
Type *cvt = base_type(cv_type);
+
if (cvt->kind == Type_Struct) {
if (cvt->Struct.is_raw_union) {
// sanity check which should have been caught by `lb_is_nested_possibly_constant`
@@ -1163,9 +1595,41 @@ 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, is_rodata).value;
- if (LLVMIsConstant(elem_value)) {
+ LLVMValueRef elem_value = lb_const_value(m, tav.type, tav.value, cc, tav.type).value;
+ if (LLVMIsConstant(elem_value) && LLVMIsConstant(values[index])) {
values[index] = llvm_const_insert_value(m, values[index], elem_value, idx_list, idx_list_len);
+ } else if (is_local) {
+ #if 1
+ lbProcedure *p = m->curr_procedure;
+ GB_ASSERT(p != nullptr);
+ if (LLVMIsConstant(values[index])) {
+ lbAddr addr = lb_add_local_generated(p, f->type, false);
+ lb_addr_store(p, addr, lbValue{values[index], f->type});
+ values[index] = lb_addr_load(p, addr).value;
+ }
+
+ GB_ASSERT(LLVMIsALoadInst(values[index]));
+
+ LLVMValueRef ptr = LLVMGetOperand(values[index], 0);
+
+ LLVMValueRef *indices = gb_alloc_array(temporary_allocator(), LLVMValueRef, idx_list_len);
+ LLVMTypeRef lt_u32 = lb_type(m, t_u32);
+ for (unsigned i = 0; i < idx_list_len; i++) {
+ indices[i] = LLVMConstInt(lt_u32, idx_list[i], false);
+ }
+
+ ptr = LLVMBuildGEP2(p->builder, lb_type(m, f->type), ptr, indices, idx_list_len, "");
+ ptr = LLVMBuildPointerCast(p->builder, ptr, lb_type(m, alloc_type_pointer(tav.type)), "");
+
+ if (LLVMIsALoadInst(elem_value)) {
+ i64 sz = type_size_of(tav.type);
+ LLVMValueRef src = LLVMGetOperand(elem_value, 0);
+ lb_mem_copy_non_overlapping(p, {ptr, t_rawptr}, {src, t_rawptr}, lb_const_int(m, t_int, sz), false);
+ } else {
+ LLVMBuildStore(p->builder, elem_value, ptr);
+ }
+ #endif
+ is_constant = false;
} else {
is_constant = false;
}
@@ -1185,7 +1649,10 @@ 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, is_rodata).value;
+ lbValue value = lb_const_value(m, f->type, tav.value, cc, tav.type);
+ LLVMTypeRef value_type = LLVMTypeOf(value.value);
+ GB_ASSERT_MSG(lb_sizeof(value_type) == type_size_of(f->type), "%s vs %s", LLVMPrintTypeToString(value_type), type_to_string(f->type));
+ values[index] = value.value;
visited[index] = true;
}
}
@@ -1205,13 +1672,15 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
LLVMValueRef val = values[i];
if (!LLVMIsConstant(val)) {
GB_ASSERT(is_local);
- GB_ASSERT(LLVMGetInstructionOpcode(val) == LLVMLoad);
+ GB_ASSERT(LLVMIsALoadInst(val));
is_constant = false;
}
}
if (is_constant) {
- res.value = llvm_const_named_struct_internal(struct_type, values, cast(unsigned)value_count);
+ res.value = llvm_const_named_struct_internal(m, struct_type, values, cast(unsigned)value_count);
+ LLVMTypeRef res_type = LLVMTypeOf(res.value);
+ GB_ASSERT(lb_sizeof(res_type) == lb_sizeof(struct_type));
return res;
} else {
// TODO(bill): THIS IS HACK BUT IT WORKS FOR WHAT I NEED
@@ -1225,7 +1694,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
new_values[i] = LLVMConstNull(LLVMTypeOf(old_value));
}
}
- LLVMValueRef constant_value = llvm_const_named_struct_internal(struct_type, new_values, cast(unsigned)value_count);
+ LLVMValueRef constant_value = llvm_const_named_struct_internal(m, struct_type, new_values, cast(unsigned)value_count);
GB_ASSERT(is_local);
lbProcedure *p = m->curr_procedure;
@@ -1237,7 +1706,15 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
LLVMValueRef val = old_values[i];
if (!LLVMIsConstant(val)) {
LLVMValueRef dst = LLVMBuildStructGEP2(p->builder, llvm_addr_type(p->module, v.addr), v.addr.value, cast(unsigned)i, "");
+ // if (LLVMIsALoadInst(val)) {
+ // Type *ptr_type = v.addr.type;
+ // i64 sz = type_size_of(type_deref(ptr_type));
+
+ // LLVMValueRef src = LLVMGetOperand(val, 0);
+ // lb_mem_copy_non_overlapping(p, {dst, ptr_type}, {src, ptr_type}, lb_const_int(m, t_int, sz), false);
+ // } else {
LLVMBuildStore(p->builder, val, dst);
+ // }
}
}
return lb_addr_load(p, v);
@@ -1311,7 +1788,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, is_rodata).value;
+ LLVMValueRef val = lb_const_value(m, elem_type, tav.value, cc, tav.type).value;
for (i64 k = lo; k < hi; k++) {
i64 offset = matrix_row_major_index_to_offset(type, k);
GB_ASSERT(values[offset] == nullptr);
@@ -1323,7 +1800,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, is_rodata).value;
+ LLVMValueRef val = lb_const_value(m, elem_type, tav.value, cc, tav.type).value;
i64 offset = matrix_row_major_index_to_offset(type, index);
GB_ASSERT(values[offset] == nullptr);
values[offset] = val;
@@ -1336,7 +1813,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, is_rodata);
+ res.value = lb_build_constant_array_values(m, type, elem_type, cast(isize)total_count, values, cc);
return res;
} else {
GB_ASSERT_MSG(elem_count == max_count, "%td != %td", elem_count, max_count);
@@ -1347,7 +1824,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, is_rodata).value;
+ values[offset] = lb_const_value(m, elem_type, tav.value, cc, tav.type).value;
}
for (isize i = 0; i < total_count; i++) {
if (values[i] == nullptr) {
@@ -1355,7 +1832,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, is_rodata);
+ res.value = lb_build_constant_array_values(m, type, elem_type, cast(isize)total_count, values, cc);
return res;
}
} else {
diff --git a/src/llvm_backend_debug.cpp b/src/llvm_backend_debug.cpp
index 067004bc1..3372165f2 100644
--- a/src/llvm_backend_debug.cpp
+++ b/src/llvm_backend_debug.cpp
@@ -18,6 +18,25 @@ gb_internal void lb_set_llvm_metadata(lbModule *m, void *key, LLVMMetadataRef va
}
}
+gb_internal void lb_add_raddbg_string(lbModule *m, String const &str) {
+ mpsc_enqueue(&m->gen->raddebug_section_strings, copy_string(permanent_allocator(), str));
+}
+
+gb_internal void lb_add_raddbg_string(lbModule *m, char const *cstr) {
+ mpsc_enqueue(&m->gen->raddebug_section_strings, copy_string(permanent_allocator(), make_string_c(cstr)));
+}
+
+gb_internal void lb_add_raddbg_string(lbModule *m, char const *a, char const *b) {
+ String str = concatenate_strings(permanent_allocator(), make_string_c(a), make_string_c(b));
+ mpsc_enqueue(&m->gen->raddebug_section_strings, str);
+}
+
+gb_internal void lb_add_raddbg_string(lbModule *m, char const *a, char const *b, char const *c) {
+ String str = concatenate3_strings(permanent_allocator(), make_string_c(a), make_string_c(b), make_string_c(c));
+ mpsc_enqueue(&m->gen->raddebug_section_strings, str);
+}
+
+
gb_internal LLVMMetadataRef lb_get_current_debug_scope(lbProcedure *p) {
GB_ASSERT_MSG(p->debug_info != nullptr, "missing debug information for %.*s", LIT(p->name));
@@ -564,22 +583,21 @@ gb_internal LLVMMetadataRef lb_debug_bitfield(lbModule *m, Type *type, String na
u64 size_in_bits = 8*type_size_of(bt);
u32 align_in_bits = 8*cast(u32)type_align_of(bt);
- unsigned element_count = cast(unsigned)bt->BitField.fields.count;
- LLVMMetadataRef *elements = gb_alloc_array(permanent_allocator(), LLVMMetadataRef, element_count);
+ unsigned element_count = cast(unsigned)bt->BitField.fields.count;
+ LLVMMetadataRef *elements = gb_alloc_array(permanent_allocator(), LLVMMetadataRef, element_count);
- u64 offset_in_bits = 0;
- for (unsigned i = 0; i < element_count; i++) {
- Entity *f = bt->BitField.fields[i];
- u8 bit_size = bt->BitField.bit_sizes[i];
- GB_ASSERT(f->kind == Entity_Variable);
- String name = f->token.string;
- elements[i] = LLVMDIBuilderCreateBitFieldMemberType(m->debug_builder, scope, cast(char const *)name.text, name.len, file, line,
- bit_size, offset_in_bits, 0,
- LLVMDIFlagZero, lb_debug_type(m, f->type)
- );
-
- offset_in_bits += bit_size;
- }
+ u64 offset_in_bits = 0;
+ for (unsigned i = 0; i < element_count; i++) {
+ Entity *f = bt->BitField.fields[i];
+ u8 bit_size = bt->BitField.bit_sizes[i];
+ GB_ASSERT(f->kind == Entity_Variable);
+ String name = f->token.string;
+ elements[i] = LLVMDIBuilderCreateBitFieldMemberType(m->debug_builder, scope, cast(char const *)name.text, name.len, file, line,
+ bit_size, offset_in_bits, 0,
+ LLVMDIFlagZero, lb_debug_type(m, f->type)
+ );
+ offset_in_bits += bit_size;
+ }
LLVMMetadataRef final_decl = LLVMDIBuilderCreateStructType(
m->debug_builder, scope,
@@ -784,6 +802,20 @@ gb_internal LLVMMetadataRef lb_debug_type_internal(lbModule *m, Type *type) {
LLVMMetadataRef char_type = lb_debug_type_basic_type(m, str_lit("char"), 8, LLVMDWARFTypeEncoding_Unsigned);
return LLVMDIBuilderCreatePointerType(m->debug_builder, char_type, ptr_bits, ptr_bits, 0, "cstring", 7);
}
+
+ case Basic_string16:
+ {
+ LLVMMetadataRef elements[2] = {};
+ elements[0] = lb_debug_struct_field(m, str_lit("data"), t_u16_ptr, 0);
+ elements[1] = lb_debug_struct_field(m, str_lit("len"), t_int, int_bits);
+ return lb_debug_basic_struct(m, str_lit("string16"), 2*int_bits, int_bits, elements, gb_count_of(elements));
+ }
+ case Basic_cstring16:
+ {
+ LLVMMetadataRef char_type = lb_debug_type_basic_type(m, str_lit("wchar_t"), 16, LLVMDWARFTypeEncoding_Unsigned);
+ return LLVMDIBuilderCreatePointerType(m->debug_builder, char_type, ptr_bits, ptr_bits, 0, "cstring16", 7);
+ }
+
case Basic_any:
{
LLVMMetadataRef elements[2] = {};
@@ -924,6 +956,7 @@ gb_internal LLVMMetadataRef lb_debug_type_internal(lbModule *m, Type *type) {
}
case Type_Matrix: {
+ #if 0
LLVMMetadataRef subscripts[1] = {};
subscripts[0] = LLVMDIBuilderGetOrCreateSubrange(m->debug_builder,
0ll,
@@ -935,6 +968,66 @@ gb_internal LLVMMetadataRef lb_debug_type_internal(lbModule *m, Type *type) {
8*cast(unsigned)type_align_of(type),
lb_debug_type(m, type->Matrix.elem),
subscripts, gb_count_of(subscripts));
+ #else
+ LLVMMetadataRef subscripts[2] = {};
+ subscripts[0] = LLVMDIBuilderGetOrCreateSubrange(m->debug_builder, 0ll, type->Matrix.row_count);
+ subscripts[1] = LLVMDIBuilderGetOrCreateSubrange(m->debug_builder, 0ll, type->Matrix.column_count);
+
+ LLVMMetadataRef scope = nullptr;
+ LLVMMetadataRef array_type = nullptr;
+
+ uint64_t size_in_bits = 8*cast(uint64_t)(type_size_of(type));
+ unsigned align_in_bits = 8*cast(unsigned)(type_align_of(type));
+
+ if (type->Matrix.is_row_major) {
+ LLVMMetadataRef base = LLVMDIBuilderCreateArrayType(m->debug_builder,
+ 8*cast(uint64_t)(type_size_of(type->Matrix.elem) * type->Matrix.column_count),
+ 8*cast(unsigned)type_align_of(type->Matrix.elem),
+ lb_debug_type(m, type->Matrix.elem),
+ subscripts+1, 1);
+ array_type = LLVMDIBuilderCreateArrayType(m->debug_builder,
+ size_in_bits,
+ align_in_bits,
+ base,
+ subscripts+0, 1);
+ } else {
+ LLVMMetadataRef base = LLVMDIBuilderCreateArrayType(m->debug_builder,
+ 8*cast(uint64_t)(type_size_of(type->Matrix.elem) * type->Matrix.row_count),
+ 8*cast(unsigned)type_align_of(type->Matrix.elem),
+ lb_debug_type(m, type->Matrix.elem),
+ subscripts+0, 1);
+ array_type = LLVMDIBuilderCreateArrayType(m->debug_builder,
+ size_in_bits,
+ align_in_bits,
+ base,
+ subscripts+1, 1);
+ }
+
+ LLVMMetadataRef elements[1] = {};
+ elements[0] = LLVMDIBuilderCreateMemberType(m->debug_builder, scope,
+ "data", 4,
+ nullptr, 0,
+ size_in_bits, align_in_bits, 0, LLVMDIFlagZero,
+ array_type
+ );
+
+ gbString name = temp_canonical_string(type);
+
+ LLVMMetadataRef final_decl = LLVMDIBuilderCreateStructType(
+ m->debug_builder, scope,
+ name, gb_string_length(name),
+ nullptr, 0,
+ size_in_bits, align_in_bits,
+ LLVMDIFlagZero,
+ nullptr,
+ elements, 1,
+ 0,
+ nullptr,
+ "", 0
+ );
+
+ return final_decl;
+ #endif
}
}
@@ -1085,7 +1178,12 @@ gb_internal void lb_add_debug_local_variable(lbProcedure *p, LLVMValueRef ptr, T
LLVMMetadataRef llvm_debug_loc = lb_debug_location_from_token_pos(p, token.pos);
LLVMMetadataRef llvm_expr = LLVMDIBuilderCreateExpression(m->debug_builder, nullptr, 0);
lb_set_llvm_metadata(m, ptr, llvm_expr);
+
+#if LLVM_VERSION_MAJOR <= 18
LLVMDIBuilderInsertDeclareAtEnd(m->debug_builder, storage, var_info, llvm_expr, llvm_debug_loc, block);
+#else
+ LLVMDIBuilderInsertDeclareRecordAtEnd(m->debug_builder, storage, var_info, llvm_expr, llvm_debug_loc, block);
+#endif
}
gb_internal void lb_add_debug_param_variable(lbProcedure *p, LLVMValueRef ptr, Type *type, Token const &token, unsigned arg_number, lbBlock *block) {
@@ -1181,7 +1279,7 @@ gb_internal void lb_add_debug_context_variable(lbProcedure *p, lbAddr const &ctx
}
-gb_internal String debug_info_mangle_constant_name(Entity *e, gbAllocator const &allocator, bool *did_allocate_) {
+gb_internal String lb_debug_info_mangle_constant_name(Entity *e, gbAllocator const &allocator, bool *did_allocate_) {
String name = e->token.string;
if (e->pkg && e->pkg->name.len > 0) {
gbString s = string_canonical_entity_name(allocator, e);
@@ -1191,7 +1289,7 @@ gb_internal String debug_info_mangle_constant_name(Entity *e, gbAllocator const
return name;
}
-gb_internal void add_debug_info_global_variable_expr(lbModule *m, String const &name, LLVMMetadataRef dtype, LLVMMetadataRef expr) {
+gb_internal void lb_add_debug_info_global_variable_expr(lbModule *m, String const &name, LLVMMetadataRef dtype, LLVMMetadataRef expr) {
LLVMMetadataRef scope = nullptr;
LLVMMetadataRef file = nullptr;
unsigned line = 0;
@@ -1207,20 +1305,20 @@ gb_internal void add_debug_info_global_variable_expr(lbModule *m, String const &
expr, decl, 8/*AlignInBits*/);
}
-gb_internal void add_debug_info_for_global_constant_internal_i64(lbModule *m, Entity *e, LLVMMetadataRef dtype, i64 v) {
+gb_internal void lb_add_debug_info_for_global_constant_internal_i64(lbModule *m, Entity *e, LLVMMetadataRef dtype, i64 v) {
LLVMMetadataRef expr = LLVMDIBuilderCreateConstantValueExpression(m->debug_builder, v);
TEMPORARY_ALLOCATOR_GUARD();
- String name = debug_info_mangle_constant_name(e, temporary_allocator(), nullptr);
+ String name = lb_debug_info_mangle_constant_name(e, temporary_allocator(), nullptr);
- add_debug_info_global_variable_expr(m, name, dtype, expr);
+ lb_add_debug_info_global_variable_expr(m, name, dtype, expr);
if ((e->pkg && e->pkg->kind == Package_Init) ||
(e->scope && (e->scope->flags & ScopeFlag_Global))) {
- add_debug_info_global_variable_expr(m, e->token.string, dtype, expr);
+ lb_add_debug_info_global_variable_expr(m, e->token.string, dtype, expr);
}
}
-gb_internal void add_debug_info_for_global_constant_from_entity(lbGenerator *gen, Entity *e) {
+gb_internal void lb_add_debug_info_for_global_constant_from_entity(lbGenerator *gen, Entity *e) {
if (e == nullptr || e->kind != Entity_Constant) {
return;
}
@@ -1229,7 +1327,7 @@ gb_internal void add_debug_info_for_global_constant_from_entity(lbGenerator *gen
}
lbModule *m = &gen->default_module;
if (USE_SEPARATE_MODULES) {
- m = lb_module_of_entity(gen, e);
+ m = lb_module_of_entity(gen, e, m);
}
GB_ASSERT(m != nullptr);
@@ -1251,14 +1349,14 @@ gb_internal void add_debug_info_for_global_constant_from_entity(lbGenerator *gen
dtype = lb_debug_type(m, e->type);
}
- add_debug_info_for_global_constant_internal_i64(m, e, dtype, v);
+ lb_add_debug_info_for_global_constant_internal_i64(m, e, dtype, v);
}
} else if (is_type_rune(e->type)) {
ExactValue const &value = e->Constant.value;
if (value.kind == ExactValue_Integer) {
LLVMMetadataRef dtype = lb_debug_type(m, t_rune);
i64 v = exact_value_to_i64(value);
- add_debug_info_for_global_constant_internal_i64(m, e, dtype, v);
+ lb_add_debug_info_for_global_constant_internal_i64(m, e, dtype, v);
}
} else if (is_type_boolean(e->type)) {
ExactValue const &value = e->Constant.value;
@@ -1266,7 +1364,7 @@ gb_internal void add_debug_info_for_global_constant_from_entity(lbGenerator *gen
LLVMMetadataRef dtype = lb_debug_type(m, default_type(e->type));
i64 v = cast(i64)value.value_bool;
- add_debug_info_for_global_constant_internal_i64(m, e, dtype, v);
+ lb_add_debug_info_for_global_constant_internal_i64(m, e, dtype, v);
}
} else if (is_type_enum(e->type)) {
ExactValue const &value = e->Constant.value;
@@ -1279,14 +1377,70 @@ gb_internal void add_debug_info_for_global_constant_from_entity(lbGenerator *gen
v = cast(i64)exact_value_to_u64(value);
}
- add_debug_info_for_global_constant_internal_i64(m, e, dtype, v);
+ lb_add_debug_info_for_global_constant_internal_i64(m, e, dtype, v);
}
} else if (is_type_pointer(e->type)) {
ExactValue const &value = e->Constant.value;
if (value.kind == ExactValue_Integer) {
LLVMMetadataRef dtype = lb_debug_type(m, default_type(e->type));
i64 v = cast(i64)exact_value_to_u64(value);
- add_debug_info_for_global_constant_internal_i64(m, e, dtype, v);
+ lb_add_debug_info_for_global_constant_internal_i64(m, e, dtype, v);
}
}
}
+
+gb_internal void lb_add_debug_label(lbProcedure *p, Ast *label, lbBlock *target) {
+// NOTE(tf2spi): LLVM-C DILabel API used only existed for major versions 20+
+#if LLVM_VERSION_MAJOR >= 20
+ if (p == nullptr || p->debug_info == nullptr) {
+ return;
+ }
+ if (target == nullptr || label == nullptr || label->kind != Ast_Label) {
+ return;
+ }
+ Token label_token = label->Label.token;
+ if (is_blank_ident(label_token.string)) {
+ return;
+ }
+ lbModule *m = p->module;
+ if (m == nullptr) {
+ return;
+ }
+
+ AstFile *file = label->file();
+ LLVMMetadataRef llvm_file = lb_get_llvm_metadata(m, file);
+ if (llvm_file == nullptr) {
+ debugf("llvm file not found for label\n");
+ return;
+ }
+ LLVMMetadataRef llvm_scope = p->debug_info;
+ if(llvm_scope == nullptr) {
+ debugf("llvm scope not found for label\n");
+ return;
+ }
+ LLVMMetadataRef llvm_debug_loc = lb_debug_location_from_token_pos(p, label_token.pos);
+ LLVMBasicBlockRef llvm_block = target->block;
+ if (llvm_block == nullptr || llvm_debug_loc == nullptr) {
+ return;
+ }
+ LLVMMetadataRef llvm_label = LLVMDIBuilderCreateLabel(
+ m->debug_builder,
+ llvm_scope,
+ (const char *)label_token.string.text,
+ (size_t)label_token.string.len,
+ llvm_file,
+ label_token.pos.line,
+
+ // NOTE(tf2spi): Defaults to false in LLVM API, but I'd rather not take chances
+ // Always preserve the label no matter what when debugging
+ true
+ );
+ GB_ASSERT(llvm_label != nullptr);
+ (void)LLVMDIBuilderInsertLabelAtEnd(
+ m->debug_builder,
+ llvm_label,
+ llvm_debug_loc,
+ llvm_block
+ );
+#endif
+}
diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp
index 56c7b45ec..187c34595 100644
--- a/src/llvm_backend_expr.cpp
+++ b/src/llvm_backend_expr.cpp
@@ -222,6 +222,7 @@ gb_internal lbValue lb_emit_unary_arith(lbProcedure *p, TokenKind op, lbValue x,
return lb_emit_byte_swap(p, res, type);
}
+ Type* bt = base_type(type);
lbValue res = {};
switch (op) {
@@ -233,6 +234,8 @@ gb_internal lbValue lb_emit_unary_arith(lbProcedure *p, TokenKind op, lbValue x,
case Token_Sub: // Number negation
if (is_type_integer(x.type)) {
res.value = LLVMBuildNeg(p->builder, x.value, "");
+ } else if (bt->kind == Type_Enum && is_type_integer(bt->Enum.base_type)) {
+ res.value = LLVMBuildNeg(p->builder, x.value, "");
} else if (is_type_float(x.type)) {
res.value = LLVMBuildFNeg(p->builder, x.value, "");
} else if (is_type_complex(x.type)) {
@@ -280,6 +283,36 @@ gb_internal lbValue lb_emit_unary_arith(lbProcedure *p, TokenKind op, lbValue x,
return res;
}
+gb_internal IntegerDivisionByZeroKind lb_check_for_integer_division_by_zero_behaviour(lbProcedure *p) {
+ AstFile *file = nullptr;
+
+ if (p->body && p->body->file()) {
+ file = p->body->file();
+ } else if (p->type_expr && p->type_expr->file()) {
+ file = p->type_expr->file();
+ } else if (p->entity && p->entity->file) {
+ file = p->entity->file;
+ }
+
+ if (file != nullptr && file->feature_flags_set) {
+ u64 flags = file->feature_flags;
+ if (flags & OptInFeatureFlag_IntegerDivisionByZero_Trap) {
+ return IntegerDivisionByZero_Trap;
+ }
+ if (flags & OptInFeatureFlag_IntegerDivisionByZero_Zero) {
+ return IntegerDivisionByZero_Zero;
+ }
+ if (flags & OptInFeatureFlag_IntegerDivisionByZero_Self) {
+ return IntegerDivisionByZero_Self;
+ }
+ if (flags & OptInFeatureFlag_IntegerDivisionByZero_AllBits) {
+ return IntegerDivisionByZero_AllBits;
+ }
+ }
+ return build_context.integer_division_by_zero_behaviour;
+}
+
+
gb_internal bool lb_try_direct_vector_arith(lbProcedure *p, TokenKind op, lbValue lhs, lbValue rhs, Type *type, lbValue *res_) {
GB_ASSERT(is_type_array_like(type));
Type *elem_type = base_array_type(type);
@@ -351,7 +384,6 @@ gb_internal bool lb_try_direct_vector_arith(lbProcedure *p, TokenKind op, lbValu
}
} else {
-
switch (op) {
case Token_Add:
z = LLVMBuildAdd(p->builder, x, y, "");
@@ -363,17 +395,15 @@ gb_internal bool lb_try_direct_vector_arith(lbProcedure *p, TokenKind op, lbValu
z = LLVMBuildMul(p->builder, x, y, "");
break;
case Token_Quo:
- if (is_type_unsigned(integral_type)) {
- z = LLVMBuildUDiv(p->builder, x, y, "");
- } else {
- z = LLVMBuildSDiv(p->builder, x, y, "");
+ {
+ auto *call = is_type_unsigned(integral_type) ? LLVMBuildUDiv : LLVMBuildSDiv;
+ z = call(p->builder, x, y, "");
}
break;
case Token_Mod:
- if (is_type_unsigned(integral_type)) {
- z = LLVMBuildURem(p->builder, x, y, "");
- } else {
- z = LLVMBuildSRem(p->builder, x, y, "");
+ {
+ auto *call = is_type_unsigned(integral_type) ? LLVMBuildURem : LLVMBuildSRem;
+ z = call(p->builder, x, y, "");
}
break;
case Token_ModMod:
@@ -1108,6 +1138,303 @@ gb_internal lbValue lb_emit_arith_matrix(lbProcedure *p, TokenKind op, lbValue l
return {};
}
+gb_internal LLVMValueRef lb_integer_division(lbProcedure *p, LLVMValueRef lhs, LLVMValueRef rhs, bool is_signed) {
+ LLVMTypeRef type = LLVMTypeOf(rhs);
+ GB_ASSERT(LLVMTypeOf(lhs) == type);
+
+ LLVMValueRef zero = LLVMConstNull(type);
+ LLVMValueRef all_bits = LLVMConstNot(zero);
+ auto behaviour = lb_check_for_integer_division_by_zero_behaviour(p);
+
+ auto *call = is_signed ? LLVMBuildSDiv : LLVMBuildUDiv;
+
+ if (LLVMIsConstant(rhs)) {
+ if (LLVMIsNull(rhs)) {
+ switch (behaviour) {
+ case IntegerDivisionByZero_Self:
+ return lhs;
+ case IntegerDivisionByZero_Zero:
+ return zero;
+ case IntegerDivisionByZero_AllBits:
+ // return all_bits;
+ break;
+ }
+ } else {
+ if (!is_signed && lb_sizeof(type) <= 8) {
+ u64 v = cast(u64)LLVMConstIntGetZExtValue(rhs);
+ if (v == 1) {
+ return lhs;
+ } else if (is_power_of_two_u64(v)) {
+ u64 n = floor_log2(v);
+ LLVMValueRef bits = LLVMConstInt(type, n, false);
+ return LLVMBuildLShr(p->builder, lhs, bits, "");
+ }
+ }
+
+ return call(p->builder, lhs, rhs, "");
+ }
+ }
+
+ LLVMValueRef incoming_values[2] = {};
+ LLVMBasicBlockRef incoming_blocks[2] = {};
+
+ lbBlock *safe_block = lb_create_block(p, "div.safe");
+ lbBlock *edge_case_block = lb_create_block(p, "div.edge");
+ lbBlock *done_block = lb_create_block(p, "div.done");
+
+ LLVMValueRef dem_check = LLVMBuildICmp(p->builder, LLVMIntNE, rhs, zero, "");
+ lbValue cond = {dem_check, t_untyped_bool};
+
+ lb_emit_if(p, cond, safe_block, edge_case_block);
+
+ lb_start_block(p, safe_block);
+ incoming_values[0] = call(p->builder, lhs, rhs, "");
+ lb_emit_jump(p, done_block);
+
+ lb_start_block(p, edge_case_block);
+
+
+ switch (behaviour) {
+ case IntegerDivisionByZero_Trap:
+ lb_call_intrinsic(p, "llvm.trap", nullptr, 0, nullptr, 0);
+ LLVMBuildUnreachable(p->builder);
+ break;
+ case IntegerDivisionByZero_Zero:
+ incoming_values[1] = zero;
+ break;
+ case IntegerDivisionByZero_Self:
+ incoming_values[1] = lhs;
+ break;
+ case IntegerDivisionByZero_AllBits:
+ incoming_values[1] = all_bits;
+ break;
+ }
+
+ lb_emit_jump(p, done_block);
+ lb_start_block(p, done_block);
+
+ LLVMValueRef res = incoming_values[0];
+
+ switch (behaviour) {
+ case IntegerDivisionByZero_Trap:
+ case IntegerDivisionByZero_Self:
+ res = incoming_values[0];
+ break;
+ case IntegerDivisionByZero_Zero:
+ case IntegerDivisionByZero_AllBits:
+ res = LLVMBuildPhi(p->builder, type, "");
+
+ GB_ASSERT(p->curr_block->preds.count >= 2);
+ incoming_blocks[0] = p->curr_block->preds[0]->block;
+ incoming_blocks[1] = p->curr_block->preds[1]->block;
+
+ LLVMAddIncoming(res, incoming_values, incoming_blocks, 2);
+ break;
+ }
+
+ return res;
+}
+
+gb_internal LLVMValueRef lb_integer_division_intrinsics(lbProcedure *p, LLVMValueRef lhs, LLVMValueRef rhs, LLVMValueRef scale, Type *platform_type, char const *name) {
+ LLVMTypeRef type = LLVMTypeOf(rhs);
+ GB_ASSERT(LLVMTypeOf(lhs) == type);
+
+ LLVMValueRef zero = LLVMConstNull(type);
+ LLVMValueRef all_bits = LLVMConstNot(zero);
+ auto behaviour = lb_check_for_integer_division_by_zero_behaviour(p);
+
+ auto const do_op = [&]() -> LLVMValueRef {
+ LLVMTypeRef types[1] = {lb_type(p->module, platform_type)};
+
+ LLVMValueRef args[3] = {
+ lhs,
+ rhs,
+ scale };
+
+ return lb_call_intrinsic(p, name, args, gb_count_of(args), types, gb_count_of(types));
+ };
+
+ if (LLVMIsConstant(rhs)) {
+ if (LLVMIsNull(rhs)) {
+ switch (behaviour) {
+ case IntegerDivisionByZero_Self:
+ return lhs;
+ case IntegerDivisionByZero_Zero:
+ return zero;
+ }
+ } else {
+ return do_op();
+ }
+ }
+
+ LLVMValueRef incoming_values[2] = {};
+ LLVMBasicBlockRef incoming_blocks[2] = {};
+
+ lbBlock *safe_block = lb_create_block(p, "div.safe");
+ lbBlock *edge_case_block = lb_create_block(p, "div.edge");
+ lbBlock *done_block = lb_create_block(p, "div.done");
+
+ LLVMValueRef dem_check = LLVMBuildICmp(p->builder, LLVMIntNE, rhs, zero, "");
+ lbValue cond = {dem_check, t_untyped_bool};
+
+ lb_emit_if(p, cond, safe_block, edge_case_block);
+
+ lb_start_block(p, safe_block);
+ incoming_values[0] = do_op();
+ lb_emit_jump(p, done_block);
+
+ lb_start_block(p, edge_case_block);
+
+
+ switch (behaviour) {
+ case IntegerDivisionByZero_Trap:
+ lb_call_intrinsic(p, "llvm.trap", nullptr, 0, nullptr, 0);
+ LLVMBuildUnreachable(p->builder);
+ break;
+ case IntegerDivisionByZero_Zero:
+ incoming_values[1] = zero;
+ break;
+ case IntegerDivisionByZero_Self:
+ incoming_values[1] = lhs;
+ break;
+ case IntegerDivisionByZero_AllBits:
+ incoming_values[1] = all_bits;
+ break;
+ }
+
+ lb_emit_jump(p, done_block);
+ lb_start_block(p, done_block);
+
+ LLVMValueRef res = incoming_values[0];
+
+ switch (behaviour) {
+ case IntegerDivisionByZero_Trap:
+ case IntegerDivisionByZero_Self:
+ res = incoming_values[0];
+ break;
+ case IntegerDivisionByZero_Zero:
+ case IntegerDivisionByZero_AllBits:
+ res = LLVMBuildPhi(p->builder, type, "");
+
+ GB_ASSERT(p->curr_block->preds.count >= 2);
+ incoming_blocks[0] = p->curr_block->preds[0]->block;
+ incoming_blocks[1] = p->curr_block->preds[1]->block;
+
+ LLVMAddIncoming(res, incoming_values, incoming_blocks, 2);
+ break;
+ }
+
+ return res;
+}
+
+
+gb_internal LLVMValueRef lb_integer_modulo(lbProcedure *p, LLVMValueRef lhs, LLVMValueRef rhs, bool is_unsigned, bool is_floored) {
+ LLVMTypeRef type = LLVMTypeOf(rhs);
+ GB_ASSERT(LLVMTypeOf(lhs) == type);
+
+ LLVMValueRef zero = LLVMConstNull(type);
+ auto behaviour = lb_check_for_integer_division_by_zero_behaviour(p);
+
+ auto const do_op = [&]() -> LLVMValueRef {
+ if (is_floored) { // %%
+ if (is_unsigned) {
+ return LLVMBuildURem(p->builder, lhs, rhs, "");
+ } else {
+ LLVMValueRef a = LLVMBuildSRem(p->builder, lhs, rhs, "");
+ LLVMValueRef b = LLVMBuildAdd(p->builder, a, rhs, "");
+ LLVMValueRef c = LLVMBuildSRem(p->builder, b, rhs, "");
+ return c;
+ }
+ } else { // %
+ if (is_unsigned) {
+ return LLVMBuildURem(p->builder, lhs, rhs, "");
+ } else {
+ return LLVMBuildSRem(p->builder, lhs, rhs, "");
+ }
+ }
+ };
+
+ if (LLVMIsConstant(rhs)) {
+ if (LLVMIsNull(rhs)) {
+ switch (behaviour) {
+ case IntegerDivisionByZero_Self:
+ return zero;
+ case IntegerDivisionByZero_Zero:
+ case IntegerDivisionByZero_AllBits:
+ return lhs;
+ }
+ } else {
+ return do_op();
+ }
+ }
+
+
+ LLVMValueRef incoming_values[2] = {};
+ LLVMBasicBlockRef incoming_blocks[2] = {};
+
+ lbBlock *safe_block = lb_create_block(p, "mod.safe");
+ lbBlock *edge_case_block = lb_create_block(p, "mod.edge");
+ lbBlock *done_block = lb_create_block(p, "mod.done");
+
+ LLVMValueRef dem_check = LLVMBuildICmp(p->builder, LLVMIntNE, rhs, zero, "");
+ lbValue cond = {dem_check, t_untyped_bool};
+
+ lb_emit_if(p, cond, safe_block, edge_case_block);
+
+ lb_start_block(p, safe_block);
+ incoming_values[0] = do_op();
+ lb_emit_jump(p, done_block);
+
+ lb_start_block(p, edge_case_block);
+
+ /*
+ NOTE(bill): @integer division by zero rules
+
+ truncated: r = a - b*trunc(a/b)
+ floored: r = a - b*floor(a/b)
+
+ IFF a/0 == 0, then (a%0 == a) or (a%%0 == a)
+ IFF a/0 == a, then (a%0 == 0) or (a%%0 == 0)
+ */
+
+ switch (behaviour) {
+ case IntegerDivisionByZero_Trap:
+ lb_call_intrinsic(p, "llvm.trap", nullptr, 0, nullptr, 0);
+ LLVMBuildUnreachable(p->builder);
+ break;
+ case IntegerDivisionByZero_Zero:
+ case IntegerDivisionByZero_AllBits:
+ incoming_values[1] = lhs;
+ break;
+ case IntegerDivisionByZero_Self:
+ incoming_values[1] = zero;
+ break;
+ }
+
+ lb_emit_jump(p, done_block);
+ lb_start_block(p, done_block);
+
+ LLVMValueRef res = incoming_values[0];
+
+ switch (behaviour) {
+ case IntegerDivisionByZero_Trap:
+ case IntegerDivisionByZero_Self:
+ res = incoming_values[0];
+ break;
+ case IntegerDivisionByZero_Zero:
+ case IntegerDivisionByZero_AllBits:
+ res = LLVMBuildPhi(p->builder, type, "");
+
+ GB_ASSERT(p->curr_block->preds.count >= 2);
+ incoming_blocks[0] = p->curr_block->preds[0]->block;
+ incoming_blocks[1] = p->curr_block->preds[1]->block;
+
+ LLVMAddIncoming(res, incoming_values, incoming_blocks, 2);
+ break;
+ }
+
+ return res;
+}
gb_internal lbValue lb_emit_arith(lbProcedure *p, TokenKind op, lbValue lhs, lbValue rhs, Type *type) {
@@ -1347,33 +1674,20 @@ handle_op:;
if (is_type_float(integral_type)) {
res.value = LLVMBuildFDiv(p->builder, lhs.value, rhs.value, "");
return res;
- } else if (is_type_unsigned(integral_type)) {
- res.value = LLVMBuildUDiv(p->builder, lhs.value, rhs.value, "");
+ } else {
+ res.value = lb_integer_division(p, lhs.value, rhs.value, !is_type_unsigned(integral_type));
return res;
}
- res.value = LLVMBuildSDiv(p->builder, lhs.value, rhs.value, "");
- return res;
case Token_Mod:
if (is_type_float(integral_type)) {
res.value = LLVMBuildFRem(p->builder, lhs.value, rhs.value, "");
return res;
- } else if (is_type_unsigned(integral_type)) {
- res.value = LLVMBuildURem(p->builder, lhs.value, rhs.value, "");
- return res;
}
- res.value = LLVMBuildSRem(p->builder, lhs.value, rhs.value, "");
+ res.value = lb_integer_modulo(p, lhs.value, rhs.value, is_type_unsigned(integral_type), /*is_floored*/false);
return res;
case Token_ModMod:
- if (is_type_unsigned(integral_type)) {
- res.value = LLVMBuildURem(p->builder, lhs.value, rhs.value, "");
- return res;
- } else {
- LLVMValueRef a = LLVMBuildSRem(p->builder, lhs.value, rhs.value, "");
- LLVMValueRef b = LLVMBuildAdd(p->builder, a, rhs.value, "");
- LLVMValueRef c = LLVMBuildSRem(p->builder, b, rhs.value, "");
- res.value = c;
- return res;
- }
+ res.value = lb_integer_modulo(p, lhs.value, rhs.value, is_type_unsigned(integral_type), /*is_floored*/true);
+ return res;
case Token_And:
res.value = LLVMBuildAnd(p->builder, lhs.value, rhs.value, "");
@@ -1556,16 +1870,24 @@ gb_internal lbValue lb_build_binary_expr(lbProcedure *p, Ast *expr) {
return lb_emit_conv(p, cmp, type);
} else if (lb_is_empty_string_constant(be->right) && !is_type_union(be->left->tav.type)) {
// `x == ""` or `x != ""`
+ Type *str_type = t_string;
+ if (is_type_string16(be->left->tav.type) || is_type_cstring16(be->left->tav.type)) {
+ str_type = t_string16;
+ }
lbValue s = lb_build_expr(p, be->left);
- s = lb_emit_conv(p, s, t_string);
+ s = lb_emit_conv(p, s, str_type);
lbValue len = lb_string_len(p, s);
lbValue cmp = lb_emit_comp(p, be->op.kind, len, lb_const_int(p->module, t_int, 0));
Type *type = default_type(tv.type);
return lb_emit_conv(p, cmp, type);
} else if (lb_is_empty_string_constant(be->left) && !is_type_union(be->right->tav.type)) {
// `"" == x` or `"" != x`
+ Type *str_type = t_string;
+ if (is_type_string16(be->right->tav.type) || is_type_cstring16(be->right->tav.type)) {
+ str_type = t_string16;
+ }
lbValue s = lb_build_expr(p, be->right);
- s = lb_emit_conv(p, s, t_string);
+ s = lb_emit_conv(p, s, str_type);
lbValue len = lb_string_len(p, s);
lbValue cmp = lb_emit_comp(p, be->op.kind, len, lb_const_int(p->module, t_int, 0));
Type *type = default_type(tv.type);
@@ -1653,6 +1975,8 @@ gb_internal lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
res.type = t;
res.value = llvm_cstring(m, str);
return res;
+ } else if (src->kind == Type_Basic && src->Basic.kind == Basic_string16 && dst->Basic.kind == Basic_cstring16) {
+ GB_PANIC("TODO(bill): UTF-16 string");
}
// if (is_type_float(dst)) {
// return value;
@@ -1792,6 +2116,38 @@ gb_internal lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
}
+
+ if (is_type_cstring16(src) && is_type_u16_ptr(dst)) {
+ return lb_emit_transmute(p, value, dst);
+ }
+ if (is_type_u16_ptr(src) && is_type_cstring16(dst)) {
+ return lb_emit_transmute(p, value, dst);
+ }
+ if (is_type_cstring16(src) && is_type_u16_multi_ptr(dst)) {
+ return lb_emit_transmute(p, value, dst);
+ }
+ if (is_type_u8_multi_ptr(src) && is_type_cstring16(dst)) {
+ return lb_emit_transmute(p, value, dst);
+ }
+ if (is_type_cstring16(src) && is_type_rawptr(dst)) {
+ return lb_emit_transmute(p, value, dst);
+ }
+ if (is_type_rawptr(src) && is_type_cstring16(dst)) {
+ return lb_emit_transmute(p, value, dst);
+ }
+
+ if (are_types_identical(src, t_cstring16) && are_types_identical(dst, t_string16)) {
+ TEMPORARY_ALLOCATOR_GUARD();
+
+ lbValue c = lb_emit_conv(p, value, t_cstring16);
+ auto args = array_make<lbValue>(temporary_allocator(), 1);
+ args[0] = c;
+ lbValue s = lb_emit_runtime_call(p, "cstring16_to_string16", args);
+ return lb_emit_conv(p, s, dst);
+ }
+
+
+
// integer -> boolean
if (is_type_integer(src) && is_type_boolean(dst)) {
lbValue res = {};
@@ -2145,6 +2501,11 @@ gb_internal lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
}
}
for (Type *vt : dst->Union.variants) {
+ if (src_type == t_llvm_bool && is_type_boolean(vt)) {
+ lbAddr parent = lb_add_local_generated(p, t, true);
+ lb_emit_store_union_variant(p, parent.addr, value, vt);
+ return lb_addr_load(p, parent);
+ }
if (are_types_identical(src_type, vt)) {
lbAddr parent = lb_add_local_generated(p, t, true);
lb_emit_store_union_variant(p, parent.addr, value, vt);
@@ -2201,10 +2562,11 @@ gb_internal lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
Type *dt = t;
+ TEMPORARY_ALLOCATOR_GUARD();
+
GB_ASSERT(is_type_struct(st) || is_type_raw_union(st));
Selection sel = {};
- sel.index.allocator = heap_allocator();
- defer (array_free(&sel.index));
+ sel.index.allocator = temporary_allocator();
if (lookup_subtype_polymorphic_selection(t, src_type, &sel)) {
if (sel.entity == nullptr) {
GB_PANIC("invalid subtype cast %s -> ", type_to_string(src_type), type_to_string(t));
@@ -2287,6 +2649,29 @@ gb_internal lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
return res;
}
+ // [^]u16 <-> cstring16
+ if (is_type_u16_multi_ptr(src) && is_type_cstring16(dst)) {
+ return lb_emit_transmute(p, value, t);
+ }
+ if (is_type_cstring16(src) && is_type_u16_multi_ptr(dst)) {
+ return lb_emit_transmute(p, value, t);
+ }
+ if (is_type_u16_ptr(src) && is_type_cstring16(dst)) {
+ return lb_emit_transmute(p, value, t);
+ }
+ if (is_type_cstring16(src) && is_type_u16_ptr(dst)) {
+ return lb_emit_transmute(p, value, t);
+ }
+
+
+ // []u16 <-> string16
+ if (is_type_u16_slice(src) && is_type_string16(dst)) {
+ return lb_emit_transmute(p, value, t);
+ }
+ if (is_type_string16(src) && is_type_u16_slice(dst)) {
+ return lb_emit_transmute(p, value, t);
+ }
+
// []byte/[]u8 <-> string
if (is_type_u8_slice(src) && is_type_string(dst)) {
return lb_emit_transmute(p, value, t);
@@ -2295,6 +2680,7 @@ gb_internal lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
return lb_emit_transmute(p, value, t);
}
+
if (is_type_array_like(dst)) {
Type *elem = base_array_type(dst);
isize index_count = cast(isize)get_array_type_count(dst);
@@ -2346,7 +2732,7 @@ gb_internal lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
Type *elem = base_array_type(dst);
lbValue e = lb_emit_conv(p, value, elem);
lbAddr v = lb_add_local_generated(p, t, false);
- lbValue zero = lb_const_value(p->module, elem, exact_value_i64(0), true);
+ lbValue zero = lb_const_value(p->module, elem, exact_value_i64(0), LB_CONST_CONTEXT_DEFAULT_ALLOW_LOCAL);
for (i64 j = 0; j < dst->Matrix.column_count; j++) {
for (i64 i = 0; i < dst->Matrix.row_count; i++) {
lbValue ptr = lb_emit_matrix_epi(p, v.addr, i, j);
@@ -2383,7 +2769,7 @@ gb_internal lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
lb_emit_store(p, d, s);
} else if (i == j) {
lbValue d = lb_emit_matrix_epi(p, v.addr, i, j);
- lbValue s = lb_const_value(p->module, dst->Matrix.elem, exact_value_i64(1), true);
+ lbValue s = lb_const_value(p->module, dst->Matrix.elem, exact_value_i64(1), LB_CONST_CONTEXT_DEFAULT_ALLOW_LOCAL);
lb_emit_store(p, d, s);
}
}
@@ -2508,7 +2894,7 @@ gb_internal lbValue lb_emit_c_vararg(lbProcedure *p, lbValue arg, Type *type) {
}
gb_internal lbValue lb_compare_records(lbProcedure *p, TokenKind op_kind, lbValue left, lbValue right, Type *type) {
- GB_ASSERT((is_type_struct(type) || is_type_union(type)) && is_type_comparable(type));
+ GB_ASSERT((is_type_struct(type) || is_type_soa_pointer(type) || is_type_union(type)) && is_type_comparable(type));
lbValue left_ptr = lb_address_from_load_or_generate_local(p, left);
lbValue right_ptr = lb_address_from_load_or_generate_local(p, right);
lbValue res = {};
@@ -2701,7 +3087,53 @@ gb_internal lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left
return lb_compare_records(p, op_kind, left, right, b);
}
+
+ if (is_type_string16(a) || is_type_cstring16(a)) {
+ if (is_type_cstring16(a) && is_type_cstring16(b)) {
+ left = lb_emit_conv(p, left, t_cstring16);
+ right = lb_emit_conv(p, right, t_cstring16);
+ char const *runtime_procedure = nullptr;
+ switch (op_kind) {
+ case Token_CmpEq: runtime_procedure = "cstring16_eq"; break;
+ case Token_NotEq: runtime_procedure = "cstring16_ne"; break;
+ case Token_Lt: runtime_procedure = "cstring16_lt"; break;
+ case Token_Gt: runtime_procedure = "cstring16_gt"; break;
+ case Token_LtEq: runtime_procedure = "cstring16_le"; break;
+ case Token_GtEq: runtime_procedure = "cstring16_ge"; break;
+ }
+ GB_ASSERT(runtime_procedure != nullptr);
+
+ auto args = array_make<lbValue>(permanent_allocator(), 2);
+ args[0] = left;
+ args[1] = right;
+ return lb_emit_runtime_call(p, runtime_procedure, args);
+ }
+
+
+ if (is_type_cstring16(a) ^ is_type_cstring16(b)) {
+ left = lb_emit_conv(p, left, t_string16);
+ right = lb_emit_conv(p, right, t_string16);
+ }
+
+ char const *runtime_procedure = nullptr;
+ switch (op_kind) {
+ case Token_CmpEq: runtime_procedure = "string16_eq"; break;
+ case Token_NotEq: runtime_procedure = "string16_ne"; break;
+ case Token_Lt: runtime_procedure = "string16_lt"; break;
+ case Token_Gt: runtime_procedure = "string16_gt"; break;
+ case Token_LtEq: runtime_procedure = "string16_le"; break;
+ case Token_GtEq: runtime_procedure = "string16_ge"; break;
+ }
+ GB_ASSERT(runtime_procedure != nullptr);
+
+ auto args = array_make<lbValue>(permanent_allocator(), 2);
+ args[0] = left;
+ args[1] = right;
+ return lb_emit_runtime_call(p, runtime_procedure, args);
+ }
+
if (is_type_string(a)) {
+
if (is_type_cstring(a) && is_type_cstring(b)) {
left = lb_emit_conv(p, left, t_cstring);
right = lb_emit_conv(p, right, t_cstring);
@@ -2893,6 +3325,7 @@ gb_internal lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left
is_type_proc(a) ||
is_type_enum(a)) {
LLVMIntPredicate pred = {};
+
if (is_type_unsigned(left.type)) {
switch (op_kind) {
case Token_Gt: pred = LLVMIntUGT; break;
@@ -2935,7 +3368,7 @@ gb_internal lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left
case Token_GtEq: pred = LLVMRealOGE; break;
case Token_Lt: pred = LLVMRealOLT; break;
case Token_LtEq: pred = LLVMRealOLE; break;
- case Token_NotEq: pred = LLVMRealONE; break;
+ case Token_NotEq: pred = LLVMRealUNE; break;
}
if (is_type_different_to_arch_endianness(left.type)) {
@@ -2963,7 +3396,7 @@ gb_internal lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left
LLVMRealPredicate pred = {};
switch (op_kind) {
case Token_CmpEq: pred = LLVMRealOEQ; break;
- case Token_NotEq: pred = LLVMRealONE; break;
+ case Token_NotEq: pred = LLVMRealUNE; break;
}
mask = LLVMBuildFCmp(p->builder, pred, left.value, right.value, "");
} else {
@@ -3016,6 +3449,9 @@ gb_internal lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left
return res;
+ } else if (is_type_soa_pointer(a)) {
+ // NOTE(Jeroen): Compare data pointer and index tag as if it were a simple struct.
+ return lb_compare_records(p, op_kind, left, right, a);
} else {
GB_PANIC("Unhandled comparison kind %s (%s) %.*s %s (%s)", type_to_string(left.type), type_to_string(base_type(left.type)), LIT(token_strings[op_kind]), type_to_string(right.type), type_to_string(base_type(right.type)));
}
@@ -3043,6 +3479,13 @@ gb_internal lbValue lb_emit_comp_against_nil(lbProcedure *p, TokenKind op_kind,
res.value = LLVMBuildIsNotNull(p->builder, x.value, "");
}
return res;
+ case Basic_cstring16:
+ 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 Basic_any:
{
// TODO(bill): is this correct behaviour for nil comparison for any?
@@ -3137,6 +3580,18 @@ gb_internal lbValue lb_emit_comp_against_nil(lbProcedure *p, TokenKind op_kind,
}
}
break;
+
+ case Type_SoaPointer:
+ {
+ // NOTE(bill): An SoaPointer is essentially just a pointer for nil comparison
+ lbValue ptr = lb_emit_struct_ev(p, x, 0); // Extract the base pointer component (field 0)
+ if (op_kind == Token_CmpEq) {
+ res.value = LLVMBuildIsNull(p->builder, ptr.value, "");
+ } else if (op_kind == Token_NotEq) {
+ res.value = LLVMBuildIsNotNull(p->builder, ptr.value, "");
+ }
+ return res;
+ }
case Type_Union:
{
@@ -3474,6 +3929,20 @@ gb_internal lbValue lb_build_expr(lbProcedure *p, Ast *expr) {
return res;
}
+gb_internal Type *lb_build_expr_original_const_type(Ast *expr) {
+ expr = unparen_expr(expr);
+ Type *type = type_of_expr(expr);
+ if (is_type_union(type)) {
+ if (expr->kind == Ast_CallExpr) {
+ if (expr->CallExpr.proc->tav.mode == Addressing_Type) {
+ Type *res = lb_build_expr_original_const_type(expr->CallExpr.args[0]);
+ return res;
+ }
+ }
+ }
+ return type_of_expr(expr);
+}
+
gb_internal lbValue lb_build_expr_internal(lbProcedure *p, Ast *expr) {
lbModule *m = p->module;
@@ -3485,9 +3954,11 @@ gb_internal lbValue lb_build_expr_internal(lbProcedure *p, Ast *expr) {
GB_ASSERT_MSG(tv.mode != Addressing_Invalid, "invalid expression '%s' (tv.mode = %d, tv.type = %s) @ %s\n Current Proc: %.*s : %s", expr_to_string(expr), tv.mode, type_to_string(tv.type), token_pos_to_string(expr_pos), LIT(p->name), type_to_string(p->type));
+
if (tv.value.kind != ExactValue_Invalid) {
+ Type *original_type = lb_build_expr_original_const_type(expr);
// NOTE(bill): Short on constant values
- return lb_const_value(p->module, type, tv.value);
+ return lb_const_value(p->module, type, tv.value, LB_CONST_CONTEXT_DEFAULT_ALLOW_LOCAL, original_type);
} else if (tv.mode == Addressing_Type) {
// NOTE(bill, 2023-01-16): is this correct? I hope so at least
return lb_typeid(m, tv.type);
@@ -3568,7 +4039,7 @@ gb_internal lbValue lb_build_expr_internal(lbProcedure *p, Ast *expr) {
TypeAndValue tav = type_and_value_of_expr(expr);
GB_ASSERT(tav.mode == Addressing_Constant);
- return lb_const_value(p->module, type, tv.value);
+ return lb_const_value(p->module, type, tv.value, LB_CONST_CONTEXT_DEFAULT_ALLOW_LOCAL, tv.type);
case_end;
case_ast_node(se, SelectorCallExpr, expr);
@@ -3849,7 +4320,7 @@ gb_internal lbAddr lb_build_addr_from_entity(lbProcedure *p, Entity *e, Ast *exp
GB_ASSERT(e != nullptr);
if (e->kind == Entity_Constant) {
Type *t = default_type(type_of_expr(expr));
- lbValue v = lb_const_value(p->module, t, e->Constant.value);
+ lbValue v = lb_const_value(p->module, t, e->Constant.value, LB_CONST_CONTEXT_DEFAULT_NO_LOCAL, e->type);
if (LLVMIsConstant(v.value)) {
lbAddr g = lb_add_global_generated_from_procedure(p, t, v);
return g;
@@ -4273,12 +4744,13 @@ gb_internal lbAddr lb_build_addr_index_expr(lbProcedure *p, Ast *expr) {
}
- case Type_Basic: { // Basic_string
+ case Type_Basic: { // Basic_string/Basic_string16
lbValue str;
lbValue elem;
lbValue len;
lbValue index;
+
str = lb_build_expr(p, ie->expr);
if (deref) {
str = lb_emit_load(p, str);
@@ -4407,6 +4879,22 @@ gb_internal lbAddr lb_build_addr_slice_expr(lbProcedure *p, Ast *expr) {
}
case Type_Basic: {
+ if (is_type_string16(type)) {
+ GB_ASSERT_MSG(are_types_identical(type, t_string16), "got %s", type_to_string(type));
+ lbValue len = lb_string_len(p, base);
+ if (high.value == nullptr) high = len;
+
+ if (!no_indices) {
+ lb_emit_slice_bounds_check(p, se->open, low, high, len, se->low != nullptr);
+ }
+
+ lbValue elem = lb_emit_ptr_offset(p, lb_string_elem(p, base), low);
+ lbValue new_len = lb_emit_arith(p, Token_Sub, high, low, t_int);
+
+ lbAddr str = lb_add_local_generated(p, t_string16, false);
+ lb_fill_string(p, str, elem, new_len);
+ return str;
+ }
GB_ASSERT_MSG(are_types_identical(type, t_string), "got %s", type_to_string(type));
lbValue len = lb_string_len(p, base);
if (high.value == nullptr) high = len;
@@ -4819,7 +5307,7 @@ gb_internal lbAddr lb_build_addr_compound_lit(lbProcedure *p, Ast *expr) {
if (cl->elems.count == 0) {
break;
}
- GB_ASSERT(expr->file()->feature_flags & OptInFeatureFlag_DynamicLiterals);
+ GB_ASSERT(expr->file()->feature_flags & OptInFeatureFlag_DynamicLiterals || build_context.dynamic_literals);
lbValue err = lb_dynamic_map_reserve(p, v.addr, 2*cl->elems.count, pos);
gb_unused(err);
@@ -4908,7 +5396,7 @@ gb_internal lbAddr lb_build_addr_compound_lit(lbProcedure *p, Ast *expr) {
if (cl->elems.count == 0) {
break;
}
- GB_ASSERT(expr->file()->feature_flags & OptInFeatureFlag_DynamicLiterals);
+ GB_ASSERT(expr->file()->feature_flags & OptInFeatureFlag_DynamicLiterals || build_context.dynamic_literals);
Type *et = bt->DynamicArray.elem;
lbValue size = lb_const_int(p->module, t_int, type_size_of(et));
@@ -5129,8 +5617,6 @@ gb_internal lbAddr lb_build_addr_internal(lbProcedure *p, Ast *expr) {
return lb_build_addr(p, unparen_expr(se->selector));
}
-
- Type *type = base_type(tav.type);
if (tav.mode == Addressing_Type) { // Addressing_Type
Selection sel = lookup_field(tav.type, selector, true);
if (sel.pseudo_field) {
@@ -5165,18 +5651,29 @@ gb_internal lbAddr lb_build_addr_internal(lbProcedure *p, Ast *expr) {
return lb_addr_swizzle(a, type, swizzle_count, swizzle_indices);
}
- Selection sel = lookup_field(type, selector, false);
+ Selection sel = lookup_field(tav.type, selector, false);
GB_ASSERT(sel.entity != nullptr);
- if (sel.pseudo_field) {
- GB_ASSERT(sel.entity->kind == Entity_Procedure || sel.entity->kind == Entity_ProcGroup);
+ if (sel.pseudo_field && (sel.entity->kind == Entity_Procedure || sel.entity->kind == Entity_ProcGroup)) {
Entity *e = entity_of_node(sel_node);
GB_ASSERT(e->kind == Entity_Procedure);
return lb_addr(lb_find_value_from_entity(p->module, e));
}
- if (sel.is_bit_field) {
- lbAddr addr = lb_build_addr(p, se->expr);
+ lbAddr addr = lb_build_addr(p, se->expr);
+ // NOTE(harold): Only allow ivar pseudo field access on indirect selectors.
+ // It is incoherent otherwise as Objective-C objects are zero-sized.
+ Type *deref_type = type_deref(tav.type);
+ if (tav.type->kind == Type_Pointer && deref_type->kind == Type_Named && deref_type->Named.type_name->TypeName.objc_ivar) {
+ // NOTE(harold): We need to load the ivar from the current address and
+ // replace addr with the loaded ivar addr to apply the selector load properly.
+ addr = lb_addr(lb_emit_load(p, addr.addr));
+
+ lbValue ivar_ptr = lb_handle_objc_ivar_for_objc_object_pointer(p, addr.addr);
+ addr = lb_addr(ivar_ptr);
+ }
+
+ if (sel.is_bit_field) {
Selection sub_sel = sel;
sub_sel.index.count -= 1;
@@ -5202,7 +5699,6 @@ gb_internal lbAddr lb_build_addr_internal(lbProcedure *p, Ast *expr) {
}
{
- lbAddr addr = lb_build_addr(p, se->expr);
if (addr.kind == lbAddr_Map) {
lbValue v = lb_addr_load(p, addr);
lbValue a = lb_address_from_load_or_generate_local(p, v);
diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp
index 0705e2e93..39cf70a6a 100644
--- a/src/llvm_backend_general.cpp
+++ b/src/llvm_backend_general.cpp
@@ -15,26 +15,47 @@ gb_global isize lb_global_type_info_member_offsets_index = 0;
gb_global isize lb_global_type_info_member_usings_index = 0;
gb_global isize lb_global_type_info_member_tags_index = 0;
-gb_internal void lb_init_module(lbModule *m, Checker *c) {
+gb_internal WORKER_TASK_PROC(lb_init_module_worker_proc) {
+ lbModule *m = cast(lbModule *)data;
+ Checker *c = m->checker;
m->info = &c->info;
- gbString module_name = gb_string_make(heap_allocator(), "odin_package");
- if (m->file) {
- if (m->pkg) {
+
+ String name = build_context.build_paths[BuildPath_Output].name;
+ gbString module_name = gb_string_make(heap_allocator(), "");
+ module_name = gb_string_append_length(module_name, name.text, name.len);
+
+ if (!USE_SEPARATE_MODULES) {
+ // ignore suffixes
+ } else if (m->file) {
+ if (gb_string_length(module_name)) {
module_name = gb_string_appendc(module_name, "-");
+ }
+ if (m->pkg) {
module_name = gb_string_append_length(module_name, m->pkg->name.text, m->pkg->name.len);
+ module_name = gb_string_appendc(module_name, "-");
}
- module_name = gb_string_appendc(module_name, "-");
String filename = filename_from_path(m->file->filename);
module_name = gb_string_append_length(module_name, filename.text, filename.len);
} else if (m->pkg) {
- module_name = gb_string_appendc(module_name, "-");
+ if (gb_string_length(module_name)) {
+ module_name = gb_string_appendc(module_name, "-");
+ }
module_name = gb_string_append_length(module_name, m->pkg->name.text, m->pkg->name.len);
- } else if (USE_SEPARATE_MODULES) {
- module_name = gb_string_appendc(module_name, "-builtin");
+ } else {
+ if (gb_string_length(module_name)) {
+ module_name = gb_string_appendc(module_name, "-");
+ }
+ module_name = gb_string_appendc(module_name, "builtin");
+ }
+ if (m->polymorphic_module == m) {
+ if (gb_string_length(module_name)) {
+ module_name = gb_string_appendc(module_name, "-");
+ }
+ module_name = gb_string_appendc(module_name, "$parapoly");
}
- m->module_name = module_name ? module_name : "odin_package";
+ m->module_name = module_name;
m->ctx = LLVMContextCreate();
m->mod = LLVMModuleCreateWithNameInContext(m->module_name, m->ctx);
// m->debug_builder = nullptr;
@@ -72,22 +93,28 @@ gb_internal void lb_init_module(lbModule *m, Checker *c) {
string_map_init(&m->members);
string_map_init(&m->procedures);
string_map_init(&m->const_strings);
+ string16_map_init(&m->const_string16s);
map_init(&m->function_type_map);
string_map_init(&m->gen_procs);
if (USE_SEPARATE_MODULES) {
- array_init(&m->procedures_to_generate, a, 0, 1<<10);
+ mpsc_init(&m->procedures_to_generate, a);
map_init(&m->procedure_values, 1<<11);
+ array_init(&m->generated_procedures, a, 0, 1<<10);
} else {
- array_init(&m->procedures_to_generate, a, 0, c->info.all_procedures.count);
+ mpsc_init(&m->procedures_to_generate, a);
map_init(&m->procedure_values, c->info.all_procedures.count*2);
+ array_init(&m->generated_procedures, a, 0, c->info.all_procedures.count*2);
}
+
+
array_init(&m->global_procedures_to_create, a, 0, 1024);
array_init(&m->global_types_to_create, a, 0, 1024);
- array_init(&m->missing_procedures_to_check, a, 0, 16);
+ mpsc_init(&m->missing_procedures_to_check, a);
map_init(&m->debug_values);
string_map_init(&m->objc_classes);
string_map_init(&m->objc_selectors);
+ string_map_init(&m->objc_ivars);
map_init(&m->map_info_map, 0);
map_init(&m->map_cell_info_map, 0);
@@ -98,6 +125,15 @@ gb_internal void lb_init_module(lbModule *m, Checker *c) {
m->const_dummy_builder = LLVMCreateBuilderInContext(m->ctx);
+ return 0;
+}
+
+gb_internal void lb_init_module(lbModule *m, bool do_threading) {
+ if (do_threading) {
+ thread_pool_add_task(lb_init_module_worker_proc, m);
+ } else {
+ lb_init_module_worker_proc(m);
+ }
}
gb_internal bool lb_init_generator(lbGenerator *gen, Checker *c) {
@@ -110,6 +146,10 @@ gb_internal bool lb_init_generator(lbGenerator *gen, Checker *c) {
return false;
}
+ isize thread_count = gb_max(build_context.thread_count, 1);
+ isize worker_count = thread_count-1;
+ bool do_threading = !!(LLVMIsMultithreaded() && USE_SEPARATE_MODULES && MULTITHREAD_OBJECT_GENERATION && worker_count > 0);
+
String init_fullpath = c->parser->init_fullpath;
linker_data_init(gen, &c->info, init_fullpath);
@@ -121,7 +161,6 @@ gb_internal bool lb_init_generator(lbGenerator *gen, Checker *c) {
map_init(&gen->modules, gen->info->packages.count*2);
map_init(&gen->modules_through_ctx, gen->info->packages.count*2);
- 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;
@@ -130,26 +169,94 @@ gb_internal bool lb_init_generator(lbGenerator *gen, Checker *c) {
auto m = gb_alloc_item(permanent_allocator(), lbModule);
m->pkg = pkg;
m->gen = gen;
+ m->checker = c;
map_set(&gen->modules, cast(void *)pkg, m);
- lb_init_module(m, c);
- if (!module_per_file) {
+ lb_init_module(m, do_threading);
+
+ if (LLVM_WEAK_MONOMORPHIZATION) {
+ auto pm = gb_alloc_item(permanent_allocator(), lbModule);
+ pm->pkg = pkg;
+ pm->gen = gen;
+ pm->checker = c;
+ m->polymorphic_module = pm;
+ pm->polymorphic_module = pm;
+
+ map_set(&gen->modules, cast(void *)pm, pm); // point to itself just add it to the list
+
+ lb_init_module(pm, do_threading);
+ }
+
+ bool allow_for_per_file = pkg->kind == Package_Runtime || module_per_file;
+
+ #if 0
+ if (!allow_for_per_file) {
+ if (pkg->files.count >= 20) {
+ isize proc_count = 0;
+ for (Entity *e : gen->info->entities) {
+ if (e->kind != Entity_Procedure) {
+ continue;
+ }
+ if (e->Procedure.is_foreign) {
+ continue;
+ }
+ if (e->pkg == pkg) {
+ proc_count += 1;
+ }
+ }
+
+ if (proc_count >= 300) {
+ allow_for_per_file = true;
+ }
+ }
+ }
+ #endif
+
+ if (!allow_for_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);
+ auto m = gb_alloc_item(permanent_allocator(), lbModule);
m->file = file;
- m->pkg = pkg;
- m->gen = gen;
+ m->pkg = pkg;
+ m->gen = gen;
+ m->checker = c;
map_set(&gen->modules, cast(void *)file, m);
- lb_init_module(m, c);
+ lb_init_module(m, do_threading);
+
+
+ if (LLVM_WEAK_MONOMORPHIZATION) {
+ auto pm = gb_alloc_item(permanent_allocator(), lbModule);
+ pm->file = file;
+ pm->pkg = pkg;
+ pm->gen = gen;
+ pm->checker = c;
+ m->polymorphic_module = pm;
+ pm->polymorphic_module = pm;
+
+ map_set(&gen->modules, cast(void *)pm, pm); // point to itself just add it to the list
+
+ lb_init_module(pm, do_threading);
+ }
}
}
+
+ if (LLVM_WEAK_MONOMORPHIZATION) {
+ lbModule *m = gb_alloc_item(permanent_allocator(), lbModule);
+ gen->equal_module = m;
+ m->gen = gen;
+ m->checker = c;
+ map_set(&gen->modules, cast(void *)m, m); // point to itself just add it to the list
+ lb_init_module(m, do_threading);
+ }
}
gen->default_module.gen = gen;
+ gen->default_module.checker = c;
map_set(&gen->modules, cast(void *)1, &gen->default_module);
- lb_init_module(&gen->default_module, c);
+ lb_init_module(&gen->default_module, do_threading);
+
+ thread_pool_wait();
for (auto const &entry : gen->modules) {
lbModule *m = entry.value;
@@ -158,6 +265,10 @@ gb_internal bool lb_init_generator(lbGenerator *gen, Checker *c) {
}
mpsc_init(&gen->entities_to_correct_linkage, heap_allocator());
+ mpsc_init(&gen->objc_selectors, heap_allocator());
+ mpsc_init(&gen->objc_classes, heap_allocator());
+ mpsc_init(&gen->objc_ivars, heap_allocator());
+ mpsc_init(&gen->raddebug_section_strings, heap_allocator());
return true;
}
@@ -341,7 +452,7 @@ gb_internal LLVMValueRef llvm_const_insert_value(lbModule *m, LLVMValueRef agg,
gb_internal LLVMValueRef llvm_cstring(lbModule *m, String const &str) {
- lbValue v = lb_find_or_add_entity_string(m, str);
+ lbValue v = lb_find_or_add_entity_string(m, str, false);
unsigned indices[1] = {0};
return llvm_const_extract_value(m, v.value, indices, gb_count_of(indices));
}
@@ -384,9 +495,9 @@ gb_internal lbModule *lb_module_of_expr(lbGenerator *gen, Ast *expr) {
return &gen->default_module;
}
-gb_internal lbModule *lb_module_of_entity(lbGenerator *gen, Entity *e) {
- GB_ASSERT(e != nullptr);
+gb_internal lbModule *lb_module_of_entity_internal(lbGenerator *gen, Entity *e, lbModule *curr_module) {
lbModule **found = nullptr;
+
if (e->kind == Entity_Procedure &&
e->decl_info &&
e->decl_info->code_gen_module) {
@@ -409,6 +520,22 @@ gb_internal lbModule *lb_module_of_entity(lbGenerator *gen, Entity *e) {
return &gen->default_module;
}
+
+gb_internal lbModule *lb_module_of_entity(lbGenerator *gen, Entity *e, lbModule *curr_module) {
+ GB_ASSERT(e != nullptr);
+ GB_ASSERT(curr_module != nullptr);
+ lbModule *m = lb_module_of_entity_internal(gen, e, curr_module);
+
+ if (USE_SEPARATE_MODULES) {
+ if (e->kind == Entity_Procedure && e->Procedure.generated_from_polymorphic) {
+ if (m->polymorphic_module) {
+ return m->polymorphic_module;
+ }
+ }
+ }
+ return m;
+}
+
gb_internal lbAddr lb_addr(lbValue addr) {
lbAddr v = {lbAddr_Default, addr};
return v;
@@ -528,8 +655,11 @@ gb_internal lbValue lb_addr_get_ptr(lbProcedure *p, lbAddr const &addr) {
break;
case lbAddr_Swizzle:
+ GB_PANIC("lbAddr_Swizzle should be handled elsewhere");
+ break;
+
case lbAddr_SwizzleLarge:
- // TOOD(bill): is this good enough logic?
+ GB_PANIC("lbAddr_SwizzleLarge should be handled elsewhere");
break;
}
@@ -547,13 +677,25 @@ gb_internal void lb_set_file_line_col(lbProcedure *p, Array<lbValue> arr, TokenP
i32 line = pos.line;
i32 col = pos.column;
- if (build_context.obfuscate_source_code_locations) {
+ switch (build_context.source_code_location_info) {
+ case SourceCodeLocationInfo_Normal:
+ break;
+ case SourceCodeLocationInfo_Obfuscated:
file = obfuscate_string(file, "F");
line = obfuscate_i32(line);
col = obfuscate_i32(col);
+ break;
+ case SourceCodeLocationInfo_Filename:
+ file = last_path_element(file);
+ break;
+ case SourceCodeLocationInfo_None:
+ file = str_lit("");
+ line = 0;
+ col = 0;
+ break;
}
- arr[0] = lb_find_or_add_entity_string(p->module, file);
+ arr[0] = lb_find_or_add_entity_string(p->module, file, false);
arr[1] = lb_const_int(p->module, t_i32, line);
arr[2] = lb_const_int(p->module, t_i32, col);
}
@@ -870,8 +1012,8 @@ gb_internal void lb_addr_store(lbProcedure *p, lbAddr addr, lbValue value) {
Type *t = base_type(type_deref(addr.addr.type));
GB_ASSERT(t->kind == Type_Struct && t->Struct.soa_kind != StructSoa_None);
lbValue len = lb_soa_struct_len(p, addr.addr);
- if (addr.soa.index_expr != nullptr) {
- lb_emit_bounds_check(p, ast_token(addr.soa.index_expr), index, len);
+ if (addr.soa.index_expr != nullptr && (!lb_is_const(addr.soa.index) || t->Struct.soa_kind != StructSoa_Fixed)) {
+ lb_emit_bounds_check(p, ast_token(addr.soa.index_expr), addr.soa.index, len);
}
}
@@ -904,7 +1046,7 @@ gb_internal void lb_addr_store(lbProcedure *p, lbAddr addr, lbValue value) {
GB_ASSERT(value.value != nullptr);
value = lb_emit_conv(p, value, lb_addr_type(addr));
- lbValue dst = lb_addr_get_ptr(p, addr);
+ lbValue dst = addr.addr;
lbValue src = lb_address_from_load_or_generate_local(p, value);
{
lbValue src_ptrs[4] = {};
@@ -930,7 +1072,7 @@ gb_internal void lb_addr_store(lbProcedure *p, lbAddr addr, lbValue value) {
GB_ASSERT(value.value != nullptr);
value = lb_emit_conv(p, value, lb_addr_type(addr));
- lbValue dst = lb_addr_get_ptr(p, addr);
+ lbValue dst = addr.addr;
lbValue src = lb_address_from_load_or_generate_local(p, value);
for_array(i, addr.swizzle_large.indices) {
lbValue src_ptr = lb_emit_array_epi(p, src, i);
@@ -1374,8 +1516,11 @@ gb_internal lbValue lb_emit_union_tag_ptr(lbProcedure *p, lbValue u) {
unsigned element_count = LLVMCountStructElementTypes(uvt);
GB_ASSERT_MSG(element_count >= 2, "element_count=%u (%s) != (%s)", element_count, type_to_string(ut), LLVMPrintTypeToString(uvt));
+ LLVMValueRef ptr = u.value;
+ ptr = LLVMBuildPointerCast(p->builder, ptr, LLVMPointerType(uvt, 0), "");
+
lbValue tag_ptr = {};
- tag_ptr.value = LLVMBuildStructGEP2(p->builder, uvt, u.value, 1, "");
+ tag_ptr.value = LLVMBuildStructGEP2(p->builder, uvt, ptr, 1, "");
tag_ptr.type = alloc_type_pointer(tag_type);
return tag_ptr;
}
@@ -1600,8 +1745,92 @@ gb_internal LLVMTypeRef lb_type_internal_for_procedures_raw(lbModule *m, Type *t
map_set(&m->func_raw_types, type, new_abi_fn_type);
return new_abi_fn_type;
+}
+
+
+gb_internal LLVMTypeRef lb_type_internal_union_block_type(lbModule *m, Type *type) {
+ GB_ASSERT(type->kind == Type_Union);
+
+ if (type->Union.variants.count <= 0) {
+ return nullptr;
+ }
+ if (type->Union.variants.count == 1) {
+ return lb_type(m, type->Union.variants[0]);
+ }
+
+ i64 align = type_align_of(type);
+
+ unsigned block_size = cast(unsigned)type->Union.variant_block_size;
+ if (block_size == 0) {
+ return lb_type_padding_filler(m, block_size, align);
+ }
+
+ bool all_pointers = align == build_context.ptr_size;
+ for (isize i = 0; all_pointers && i < type->Union.variants.count; i++) {
+ Type *t = type->Union.variants[i];
+ if (!is_type_internally_pointer_like(t)) {
+ all_pointers = false;
+ }
+ }
+ if (all_pointers) {
+ return lb_type(m, t_rawptr);
+ }
+
+ {
+ Type *pt = type->Union.variants[0];
+ for (isize i = 1; i < type->Union.variants.count; i++) {
+ Type *t = type->Union.variants[i];
+ if (!are_types_identical(pt, t)) {
+ goto end_check_for_all_the_same;
+ }
+ }
+ return lb_type(m, pt);
+ } end_check_for_all_the_same:;
+
+ {
+ Type *first_different = nullptr;
+ for (isize i = 0; i < type->Union.variants.count; i++) {
+ Type *t = type->Union.variants[i];
+ if (type_size_of(t) == 0) {
+ continue;
+ }
+ if (first_different == nullptr) {
+ first_different = t;
+ } else if (!are_types_identical(first_different, t)) {
+ goto end_rest_zero_except_one;
+ }
+ }
+ if (first_different != nullptr) {
+ return lb_type(m, first_different);
+ }
+ } end_rest_zero_except_one:;
+
+ // {
+ // LLVMTypeRef first_different = nullptr;
+ // for (isize i = 0; i < type->Union.variants.count; i++) {
+ // Type *t = type->Union.variants[i];
+ // if (type_size_of(t) == 0) {
+ // continue;
+ // }
+ // if (first_different == nullptr) {
+ // first_different = lb_type(m, base_type(t));
+ // } else {
+ // LLVMTypeRef llvm_t = lb_type(m, base_type(t));
+ // if (llvm_t != first_different) {
+ // goto end_rest_zero_except_one_llvm_like;
+ // }
+ // }
+ // }
+ // if (first_different != nullptr) {
+ // return first_different;
+ // }
+ // } end_rest_zero_except_one_llvm_like:;
+
+ return lb_type_padding_filler(m, block_size, align);
}
+
+
gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
LLVMContextRef ctx = m->ctx;
i64 size = type_size_of(type); // Check size
@@ -1779,6 +2008,37 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
return type;
}
case Basic_cstring: return LLVMPointerType(LLVMInt8TypeInContext(ctx), 0);
+
+
+ case Basic_string16:
+ {
+ char const *name = "..string16";
+ LLVMTypeRef type = LLVMGetTypeByName(m->mod, name);
+ if (type != nullptr) {
+ return type;
+ }
+ type = LLVMStructCreateNamed(ctx, name);
+
+ if (build_context.metrics.ptr_size < build_context.metrics.int_size) {
+ GB_ASSERT(build_context.metrics.ptr_size == 4);
+ GB_ASSERT(build_context.metrics.int_size == 8);
+ LLVMTypeRef fields[3] = {
+ LLVMPointerType(lb_type(m, t_u16), 0),
+ lb_type(m, t_i32),
+ lb_type(m, t_int),
+ };
+ LLVMStructSetBody(type, fields, 3, false);
+ } else {
+ LLVMTypeRef fields[2] = {
+ LLVMPointerType(lb_type(m, t_u16), 0),
+ lb_type(m, t_int),
+ };
+ LLVMStructSetBody(type, fields, 2, false);
+ }
+ return type;
+ }
+ case Basic_cstring16: return LLVMPointerType(LLVMInt16TypeInContext(ctx), 0);
+
case Basic_any:
{
char const *name = "..any";
@@ -1982,26 +2242,26 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
case Type_Struct:
{
type_set_offsets(type);
-
+
i64 full_type_size = type_size_of(type);
i64 full_type_align = type_align_of(type);
GB_ASSERT(full_type_size % full_type_align == 0);
-
+
if (type->Struct.is_raw_union) {
-
+
lbStructFieldRemapping field_remapping = {};
slice_init(&field_remapping, permanent_allocator(), 1);
-
+
LLVMTypeRef fields[1] = {};
fields[0] = lb_type_padding_filler(m, full_type_size, full_type_align);
field_remapping[0] = 0;
-
+
LLVMTypeRef struct_type = LLVMStructTypeInContext(ctx, fields, gb_count_of(fields), false);
map_set(&m->struct_field_remapping, cast(void *)struct_type, field_remapping);
map_set(&m->struct_field_remapping, cast(void *)type, field_remapping);
return struct_type;
}
-
+
lbStructFieldRemapping field_remapping = {};
slice_init(&field_remapping, permanent_allocator(), type->Struct.fields.count);
@@ -2014,7 +2274,7 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
LLVMTypeRef padding_type = lb_type_padding_filler(m, 0, type_align_of(type));
array_add(&fields, padding_type);
}
-
+
i64 prev_offset = 0;
bool requires_packing = type->Struct.is_packed;
for (i32 field_index : struct_fields_index_by_increasing_offset(temporary_allocator(), type)) {
@@ -2045,7 +2305,7 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
prev_offset = offset + type_size_of(field->type);
}
-
+
i64 end_padding = full_type_size-prev_offset;
if (end_padding > 0) {
array_add(&fields, lb_type_padding_filler(m, end_padding, 1));
@@ -2054,14 +2314,14 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
for_array(i, fields) {
GB_ASSERT(fields[i] != nullptr);
}
-
+
LLVMTypeRef struct_type = LLVMStructTypeInContext(ctx, fields.data, cast(unsigned)fields.count, requires_packing);
map_set(&m->struct_field_remapping, cast(void *)struct_type, field_remapping);
- map_set(&m->struct_field_remapping, cast(void *)type, field_remapping);
+ map_set(&m->struct_field_remapping, cast(void *)type, field_remapping);
#if 0
- GB_ASSERT_MSG(lb_sizeof(struct_type) == full_type_size,
- "(%lld) %s vs (%lld) %s",
- cast(long long)lb_sizeof(struct_type), LLVMPrintTypeToString(struct_type),
+ GB_ASSERT_MSG(lb_sizeof(struct_type) == full_type_size,
+ "(%lld) %s vs (%lld) %s",
+ cast(long long)lb_sizeof(struct_type), LLVMPrintTypeToString(struct_type),
cast(long long)full_type_size, type_to_string(type));
#endif
return struct_type;
@@ -2083,15 +2343,26 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
return LLVMStructTypeInContext(ctx, fields, gb_count_of(fields), false);
}
- unsigned block_size = cast(unsigned)type->Union.variant_block_size;
-
auto fields = array_make<LLVMTypeRef>(temporary_allocator(), 0, 3);
if (is_type_union_maybe_pointer(type)) {
LLVMTypeRef variant = lb_type(m, type->Union.variants[0]);
array_add(&fields, variant);
+ } else if (type->Union.variants.count == 1) {
+ LLVMTypeRef block_type = lb_type(m, type->Union.variants[0]);
+
+ LLVMTypeRef tag_type = lb_type(m, union_tag_type(type));
+ array_add(&fields, block_type);
+ array_add(&fields, tag_type);
+ i64 used_size = lb_sizeof(block_type) + lb_sizeof(tag_type);
+ i64 padding = size - used_size;
+ if (padding > 0) {
+ LLVMTypeRef padding_type = lb_type_padding_filler(m, padding, align);
+ array_add(&fields, padding_type);
+ }
} else {
- LLVMTypeRef block_type = lb_type_padding_filler(m, block_size, align);
- LLVMTypeRef tag_type = lb_type(m, union_tag_type(type));
+ LLVMTypeRef block_type = lb_type_internal_union_block_type(m, type);
+
+ LLVMTypeRef tag_type = lb_type(m, union_tag_type(type));
array_add(&fields, block_type);
array_add(&fields, tag_type);
i64 used_size = lb_sizeof(block_type) + lb_sizeof(tag_type);
@@ -2171,7 +2442,7 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
field_count = 3;
}
LLVMTypeRef *fields = gb_alloc_array(permanent_allocator(), LLVMTypeRef, field_count);
- fields[0] = LLVMPointerType(lb_type(m, type->Pointer.elem), 0);
+ fields[0] = LLVMPointerType(lb_type(m, type->SoaPointer.elem), 0);
if (bigger_int) {
fields[1] = lb_type_padding_filler(m, build_context.ptr_size, build_context.ptr_size);
fields[2] = LLVMIntTypeInContext(ctx, 8*cast(unsigned)build_context.int_size);
@@ -2183,6 +2454,14 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
case Type_BitField:
return lb_type_internal(m, type->BitField.backing_type);
+
+ case Type_Generic:
+ if (type->Generic.specialized) {
+ return lb_type_internal(m, type->Generic.specialized);
+ } else {
+ // For unspecialized generics, use a pointer type as a placeholder
+ return LLVMPointerType(LLVMInt8TypeInContext(m->ctx), 0);
+ }
}
GB_PANIC("Invalid type %s", type_to_string(type));
@@ -2348,6 +2627,29 @@ gb_internal void lb_add_attribute_to_proc_with_string(lbModule *m, LLVMValueRef
}
+gb_internal bool lb_apply_thread_local_model(LLVMValueRef value, String model) {
+ if (model != "") {
+ LLVMSetThreadLocal(value, true);
+
+ LLVMThreadLocalMode mode = LLVMGeneralDynamicTLSModel;
+ if (model == "default") {
+ mode = LLVMGeneralDynamicTLSModel;
+ } else if (model == "localdynamic") {
+ mode = LLVMLocalDynamicTLSModel;
+ } else if (model == "initialexec") {
+ mode = LLVMInitialExecTLSModel;
+ } else if (model == "localexec") {
+ mode = LLVMLocalExecTLSModel;
+ } else {
+ GB_PANIC("Unhandled thread local mode %.*s", LIT(model));
+ }
+ LLVMSetThreadLocalMode(value, mode);
+ return true;
+ }
+
+ return false;
+}
+
gb_internal void lb_add_edge(lbBlock *from, lbBlock *to) {
LLVMValueRef instr = LLVMGetLastInstruction(from->block);
@@ -2486,10 +2788,13 @@ general_end:;
}
}
- src_size = align_formula(src_size, src_align);
- dst_size = align_formula(dst_size, dst_align);
+ // NOTE(laytan): even though this logic seems sound, the Address Sanitizer does not
+ // want you to load/store the space of a value that is there for alignment.
+#if 0
+ i64 aligned_src_size = align_formula(src_size, src_align);
+ i64 aligned_dst_size = align_formula(dst_size, dst_align);
- if (LLVMIsALoadInst(val) && (src_size >= dst_size && src_align >= dst_align)) {
+ if (LLVMIsALoadInst(val) && (aligned_src_size >= aligned_dst_size && src_align >= dst_align)) {
LLVMValueRef val_ptr = LLVMGetOperand(val, 0);
val_ptr = LLVMBuildPointerCast(p->builder, val_ptr, LLVMPointerType(dst_type, 0), "");
LLVMValueRef loaded_val = OdinLLVMBuildLoad(p, dst_type, val_ptr);
@@ -2497,8 +2802,60 @@ general_end:;
// LLVMSetAlignment(loaded_val, gb_min(src_align, dst_align));
return loaded_val;
+ }
+#endif
+
+ if (src_size > dst_size) {
+ GB_ASSERT(p->decl_block != p->curr_block);
+ // NOTE(laytan): src is bigger than dst, need to memcpy the part of src we want.
+
+ LLVMTypeRef llvm_src_type = LLVMPointerType(src_type, 0);
+ LLVMTypeRef llvm_dst_type = LLVMPointerType(dst_type, 0);
+
+ LLVMValueRef val_ptr;
+ if (LLVMIsALoadInst(val)) {
+ val_ptr = LLVMGetOperand(val, 0);
+ } else if (LLVMIsAAllocaInst(val)) {
+ val_ptr = LLVMBuildPointerCast(p->builder, val, llvm_src_type, "");
+ } else {
+ // NOTE(laytan): we need a pointer to memcpy from.
+ LLVMValueRef val_copy = llvm_alloca(p, src_type, src_align);
+ val_ptr = LLVMBuildPointerCast(p->builder, val_copy, llvm_src_type, "");
+ LLVMBuildStore(p->builder, val, val_ptr);
+ }
+
+ i64 max_align = gb_max(lb_alignof(src_type), lb_alignof(dst_type));
+ max_align = gb_max(max_align, 16);
+
+ LLVMValueRef ptr = llvm_alloca(p, dst_type, max_align);
+ LLVMValueRef nptr = LLVMBuildPointerCast(p->builder, ptr, llvm_dst_type, "");
+
+ LLVMTypeRef types[3] = {
+ llvm_dst_type,
+ llvm_src_type,
+ lb_type(p->module, t_int)
+ };
+
+ LLVMValueRef args[4] = {
+ nptr,
+ val_ptr,
+ LLVMConstInt(LLVMIntTypeInContext(p->module->ctx, 8*cast(unsigned)build_context.int_size), dst_size, 0),
+ LLVMConstInt(LLVMInt1TypeInContext(p->module->ctx), 0, 0),
+ };
+
+ lb_call_intrinsic(
+ p,
+ "llvm.memcpy.inline",
+ args,
+ gb_count_of(args),
+ types,
+ gb_count_of(types)
+ );
+
+ return OdinLLVMBuildLoad(p, dst_type, ptr);
} else {
GB_ASSERT(p->decl_block != p->curr_block);
+ GB_ASSERT(dst_size >= src_size);
i64 max_align = gb_max(lb_alignof(src_type), lb_alignof(dst_type));
max_align = gb_max(max_align, 16);
@@ -2514,9 +2871,14 @@ general_end:;
-gb_internal LLVMValueRef lb_find_or_add_entity_string_ptr(lbModule *m, String const &str) {
- StringHashKey key = string_hash_string(str);
- LLVMValueRef *found = string_map_get(&m->const_strings, key);
+gb_internal LLVMValueRef lb_find_or_add_entity_string_ptr(lbModule *m, String const &str, bool custom_link_section) {
+ StringHashKey key = {};
+ LLVMValueRef *found = nullptr;
+
+ if (!custom_link_section) {
+ key = string_hash_string(str);
+ found = string_map_get(&m->const_strings, key);
+ }
if (found != nullptr) {
return *found;
} else {
@@ -2539,15 +2901,68 @@ gb_internal LLVMValueRef lb_find_or_add_entity_string_ptr(lbModule *m, String co
LLVMSetAlignment(global_data, 1);
LLVMValueRef ptr = LLVMConstInBoundsGEP2(type, global_data, indices, 2);
- string_map_set(&m->const_strings, key, ptr);
+ if (!custom_link_section) {
+ string_map_set(&m->const_strings, key, ptr);
+ }
return ptr;
}
}
-gb_internal lbValue lb_find_or_add_entity_string(lbModule *m, String const &str) {
+gb_internal LLVMValueRef lb_find_or_add_entity_string16_ptr(lbModule *m, String16 const &str, bool custom_link_section) {
+ String16HashKey key = {};
+ LLVMValueRef *found = nullptr;
+
+ if (!custom_link_section) {
+ key = string_hash_string(str);
+ found = string16_map_get(&m->const_string16s, key);
+ }
+ if (found != nullptr) {
+ return *found;
+ }
+
+
+
+ LLVMValueRef indices[2] = {llvm_zero(m), llvm_zero(m)};
+
+ LLVMValueRef data = nullptr;
+ {
+ LLVMTypeRef llvm_u16 = LLVMInt16TypeInContext(m->ctx);
+
+ TEMPORARY_ALLOCATOR_GUARD();
+
+ LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, str.len+1);
+
+ for (isize i = 0; i < str.len; i++) {
+ values[i] = LLVMConstInt(llvm_u16, str.text[i], false);
+ }
+ values[str.len] = LLVMConstInt(llvm_u16, 0, false);
+
+ data = LLVMConstArray(llvm_u16, values, cast(unsigned)(str.len+1));
+ }
+
+
+ u32 id = m->global_array_index.fetch_add(1);
+ gbString name = gb_string_make(temporary_allocator(), "csbs$");
+ name = gb_string_appendc(name, m->module_name);
+ name = gb_string_append_fmt(name, "$%x", id);
+
+ LLVMTypeRef type = LLVMTypeOf(data);
+ LLVMValueRef global_data = LLVMAddGlobal(m->mod, type, name);
+ LLVMSetInitializer(global_data, data);
+ lb_make_global_private_const(global_data);
+ LLVMSetAlignment(global_data, 2);
+
+ LLVMValueRef ptr = LLVMConstInBoundsGEP2(type, global_data, indices, 2);
+ if (!custom_link_section) {
+ string16_map_set(&m->const_string16s, key, ptr);
+ }
+ return ptr;
+}
+
+gb_internal lbValue lb_find_or_add_entity_string(lbModule *m, String const &str, bool custom_link_section) {
LLVMValueRef ptr = nullptr;
if (str.len != 0) {
- ptr = lb_find_or_add_entity_string_ptr(m, str);
+ ptr = lb_find_or_add_entity_string_ptr(m, str, custom_link_section);
} else {
ptr = LLVMConstNull(lb_type(m, t_u8_ptr));
}
@@ -2604,6 +3019,60 @@ gb_internal lbValue lb_find_or_add_entity_string_byte_slice_with_type(lbModule *
return res;
}
+gb_internal lbValue lb_find_or_add_entity_string16_slice_with_type(lbModule *m, String16 const &str, Type *slice_type) {
+ GB_ASSERT(is_type_slice(slice_type));
+ LLVMValueRef indices[2] = {llvm_zero(m), llvm_zero(m)};
+ LLVMValueRef data = nullptr;
+ {
+ LLVMTypeRef llvm_u16 = LLVMInt16TypeInContext(m->ctx);
+
+ TEMPORARY_ALLOCATOR_GUARD();
+
+ LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, str.len+1);
+
+ for (isize i = 0; i < str.len; i++) {
+ values[i] = LLVMConstInt(llvm_u16, str.text[i], false);
+ }
+ values[str.len] = LLVMConstInt(llvm_u16, 0, false);
+
+ data = LLVMConstArray(llvm_u16, values, cast(unsigned)(str.len+1));
+ }
+
+ u32 id = m->global_array_index.fetch_add(1);
+ gbString name = gb_string_make(temporary_allocator(), "csba$");
+ name = gb_string_appendc(name, m->module_name);
+ name = gb_string_append_fmt(name, "$%x", id);
+
+ LLVMTypeRef type = LLVMTypeOf(data);
+ LLVMValueRef global_data = LLVMAddGlobal(m->mod, type, name);
+ LLVMSetInitializer(global_data, data);
+ lb_make_global_private_const(global_data);
+ LLVMSetAlignment(global_data, 2);
+
+ i64 data_len = str.len;
+ LLVMValueRef ptr = nullptr;
+ if (data_len != 0) {
+ ptr = LLVMConstInBoundsGEP2(type, global_data, indices, 2);
+ } else {
+ ptr = LLVMConstNull(lb_type(m, t_u8_ptr));
+ }
+ if (!is_type_u16_slice(slice_type)) {
+ Type *bt = base_type(slice_type);
+ Type *elem = bt->Slice.elem;
+ i64 sz = type_size_of(elem);
+ GB_ASSERT(sz > 0);
+ ptr = LLVMConstPointerCast(ptr, lb_type(m, alloc_type_pointer(elem)));
+ data_len /= sz;
+ }
+
+ LLVMValueRef len = LLVMConstInt(lb_type(m, t_int), data_len, true);
+ LLVMValueRef values[2] = {ptr, len};
+
+ lbValue res = {};
+ res.value = llvm_const_named_struct(m, slice_type, values, 2);
+ res.type = slice_type;
+ return res;
+}
gb_internal lbValue lb_find_ident(lbProcedure *p, lbModule *m, Entity *e, Ast *expr) {
@@ -2637,7 +3106,7 @@ gb_internal lbValue lb_find_ident(lbProcedure *p, lbModule *m, Entity *e, Ast *e
return lb_find_procedure_value_from_entity(m, e);
}
if (USE_SEPARATE_MODULES) {
- lbModule *other_module = lb_module_of_entity(m->gen, e);
+ lbModule *other_module = lb_module_of_entity(m->gen, e, m);
if (other_module != m) {
String name = lb_get_entity_name(other_module, e);
@@ -2667,6 +3136,7 @@ gb_internal lbValue lb_find_ident(lbProcedure *p, lbModule *m, Entity *e, Ast *e
gb_internal lbValue lb_find_procedure_value_from_entity(lbModule *m, Entity *e) {
lbGenerator *gen = m->gen;
+ GB_ASSERT(e != nullptr);
GB_ASSERT(is_type_proc(e->type));
e = strip_entity_wrapping(e);
GB_ASSERT(e != nullptr);
@@ -2684,7 +3154,7 @@ gb_internal lbValue lb_find_procedure_value_from_entity(lbModule *m, Entity *e)
lbModule *other_module = m;
if (USE_SEPARATE_MODULES) {
- other_module = lb_module_of_entity(gen, e);
+ other_module = lb_module_of_entity(gen, e, m);
}
if (other_module == m) {
debugf("Missing Procedure (lb_find_procedure_value_from_entity): %.*s module %p\n", LIT(e->token.string), m);
@@ -2692,10 +3162,15 @@ gb_internal lbValue lb_find_procedure_value_from_entity(lbModule *m, Entity *e)
ignore_body = other_module != m;
lbProcedure *missing_proc = lb_create_procedure(m, e, ignore_body);
- if (ignore_body) {
- mutex_lock(&gen->anonymous_proc_lits_mutex);
- defer (mutex_unlock(&gen->anonymous_proc_lits_mutex));
+ if (missing_proc == nullptr) {
+ // This is an unspecialized polymorphic procedure, which should not be codegen'd
+ lbValue dummy = {};
+ dummy.value = nullptr;
+ dummy.type = nullptr;
+ return dummy;
+ }
+ if (ignore_body) {
GB_ASSERT(other_module != nullptr);
rw_mutex_shared_lock(&other_module->values_mutex);
auto *found = map_get(&other_module->values, e);
@@ -2703,10 +3178,10 @@ gb_internal lbValue lb_find_procedure_value_from_entity(lbModule *m, Entity *e)
if (found == nullptr) {
// THIS IS THE RACE CONDITION
lbProcedure *missing_proc_in_other_module = lb_create_procedure(other_module, e, false);
- array_add(&other_module->missing_procedures_to_check, missing_proc_in_other_module);
+ mpsc_enqueue(&other_module->missing_procedures_to_check, missing_proc_in_other_module);
}
} else {
- array_add(&m->missing_procedures_to_check, missing_proc);
+ mpsc_enqueue(&m->missing_procedures_to_check, missing_proc);
}
rw_mutex_shared_lock(&m->values_mutex);
@@ -2724,17 +3199,15 @@ gb_internal lbValue lb_find_procedure_value_from_entity(lbModule *m, Entity *e)
gb_internal lbValue lb_generate_anonymous_proc_lit(lbModule *m, String const &prefix_name, Ast *expr, lbProcedure *parent) {
lbGenerator *gen = m->gen;
+ gb_unused(gen);
- mutex_lock(&gen->anonymous_proc_lits_mutex);
- defer (mutex_unlock(&gen->anonymous_proc_lits_mutex));
+ ast_node(pl, ProcLit, expr);
- TokenPos pos = ast_token(expr).pos;
- lbProcedure **found = map_get(&gen->anonymous_proc_lits, expr);
- if (found) {
- return lb_find_procedure_value_from_entity(m, (*found)->entity);
+ if (pl->decl->entity.load() != nullptr) {
+ return lb_find_procedure_value_from_entity(m, pl->decl->entity.load());
}
- ast_node(pl, ProcLit, expr);
+ TokenPos pos = ast_token(expr).pos;
// NOTE(bill): Generate a new name
// parent$count
@@ -2753,30 +3226,51 @@ gb_internal lbValue lb_generate_anonymous_proc_lit(lbModule *m, String const &pr
token.string = name;
Entity *e = alloc_entity_procedure(nullptr, token, type, pl->tags);
e->file = expr->file();
+ e->scope = e->file->scope;
+
+ lbModule *target_module = m;
+ GB_ASSERT(target_module != nullptr);
// NOTE(bill): this is to prevent a race condition since these procedure literals can be created anywhere at any time
- pl->decl->code_gen_module = m;
+ pl->decl->code_gen_module = target_module;
e->decl_info = pl->decl;
- pl->decl->entity = e;
e->parent_proc_decl = pl->decl->parent;
e->Procedure.is_anonymous = true;
e->flags |= EntityFlag_ProcBodyChecked;
- lbProcedure *p = lb_create_procedure(m, e);
- GB_ASSERT(e->code_gen_module == m);
+ pl->decl->entity.store(e);
- lbValue value = {};
- value.value = p->value;
- value.type = p->type;
- map_set(&gen->anonymous_proc_lits, expr, p);
- array_add(&m->procedures_to_generate, p);
- if (parent != nullptr) {
- array_add(&parent->children, p);
+ if (target_module != m) {
+ rw_mutex_shared_lock(&target_module->values_mutex);
+ lbValue *found = map_get(&target_module->values, e);
+ rw_mutex_shared_unlock(&target_module->values_mutex);
+ if (found == nullptr) {
+ lbProcedure *missing_proc_in_target_module = lb_create_procedure(target_module, e, false);
+ mpsc_enqueue(&target_module->missing_procedures_to_check, missing_proc_in_target_module);
+ }
+
+ lbProcedure *p = lb_create_procedure(m, e, true);
+
+ lbValue value = {};
+ value.value = p->value;
+ value.type = p->type;
+ return value;
} else {
- string_map_set(&m->members, name, value);
+ lbProcedure *p = lb_create_procedure(m, e);
+
+ lbValue value = {};
+ value.value = p->value;
+ value.type = p->type;
+
+ mpsc_enqueue(&m->procedures_to_generate, p);
+ if (parent != nullptr) {
+ array_add(&parent->children, p);
+ } else {
+ string_map_set(&m->members, name, value);
+ }
+ return value;
}
- return value;
}
@@ -2785,15 +3279,18 @@ gb_internal lbAddr lb_add_global_generated_with_name(lbModule *m, Type *type, lb
GB_ASSERT(type != nullptr);
type = default_type(type);
- u8 *str = cast(u8 *)gb_alloc_array(temporary_allocator(), u8, name.len);
- memcpy(str, name.text, name.len);
- str[name.len] = 0;
+ LLVMTypeRef actual_type = lb_type(m, type);
+ if (value.value != nullptr) {
+ LLVMTypeRef value_type = LLVMTypeOf(value.value);
+ GB_ASSERT_MSG(lb_sizeof(actual_type) == lb_sizeof(value_type), "%s vs %s", LLVMPrintTypeToString(actual_type), LLVMPrintTypeToString(value_type));
+ actual_type = value_type;
+ }
Scope *scope = nullptr;
Entity *e = alloc_entity_variable(scope, make_token_ident(name), type);
lbValue g = {};
g.type = alloc_type_pointer(type);
- g.value = LLVMAddGlobal(m->mod, lb_type(m, type), cast(char const *)str);
+ g.value = LLVMAddGlobal(m->mod, actual_type, alloc_cstring(temporary_allocator(), name));
if (value.value != nullptr) {
GB_ASSERT_MSG(LLVMIsConstant(value.value), LLVMPrintValueToString(value.value));
LLVMSetInitializer(g.value, value.value);
@@ -2801,6 +3298,8 @@ gb_internal lbAddr lb_add_global_generated_with_name(lbModule *m, Type *type, lb
LLVMSetInitializer(g.value, LLVMConstNull(lb_type(m, type)));
}
+ g.value = LLVMConstPointerCast(g.value, lb_type(m, g.type));
+
lb_add_entity(m, e, g);
lb_add_member(m, name, g);
@@ -2863,7 +3362,7 @@ gb_internal lbValue lb_find_value_from_entity(lbModule *m, Entity *e) {
}
if (USE_SEPARATE_MODULES) {
- lbModule *other_module = lb_module_of_entity(m->gen, e);
+ lbModule *other_module = lb_module_of_entity(m->gen, e, m);
bool is_external = other_module != m;
if (!is_external) {
@@ -2888,25 +3387,7 @@ gb_internal lbValue lb_find_value_from_entity(lbModule *m, Entity *e) {
lb_set_entity_from_other_modules_linkage_correctly(other_module, e, name);
- if (e->Variable.thread_local_model != "") {
- LLVMSetThreadLocal(g.value, true);
-
- String m = e->Variable.thread_local_model;
- LLVMThreadLocalMode mode = LLVMGeneralDynamicTLSModel;
- if (m == "default") {
- mode = LLVMGeneralDynamicTLSModel;
- } else if (m == "localdynamic") {
- mode = LLVMLocalDynamicTLSModel;
- } else if (m == "initialexec") {
- mode = LLVMInitialExecTLSModel;
- } else if (m == "localexec") {
- mode = LLVMLocalExecTLSModel;
- } else {
- GB_PANIC("Unhandled thread local mode %.*s", LIT(m));
- }
- LLVMSetThreadLocalMode(g.value, mode);
- }
-
+ lb_apply_thread_local_model(g.value, e->Variable.thread_local_model);
return g;
}
@@ -3038,6 +3519,13 @@ gb_internal lbAddr lb_add_local(lbProcedure *p, Type *type, Entity *e, bool zero
if (e != nullptr) {
lb_add_entity(p->module, e, val);
lb_add_debug_local_variable(p, ptr, type, e->token);
+
+ // NOTE(lucas): In LLVM 20 and below we do not have the option to have asan cleanup poisoned stack
+ // locals ourselves. So we need to manually track and unpoison these locals on proc return.
+ // LLVM 21 adds the 'use-after-scope' asan option which does this for us.
+ if (build_context.sanitizer_flags & SanitizerFlag_Address && !p->entity->Procedure.no_sanitize_address) {
+ array_add(&p->asan_stack_locals, val);
+ }
}
if (zero_init) {
diff --git a/src/llvm_backend_opt.cpp b/src/llvm_backend_opt.cpp
index 7fe1359b4..8d5cfcb70 100644
--- a/src/llvm_backend_opt.cpp
+++ b/src/llvm_backend_opt.cpp
@@ -516,7 +516,7 @@ gb_internal void llvm_delete_function(LLVMValueRef func) {
LLVMDeleteFunction(func);
}
-gb_internal void lb_append_to_compiler_used(lbModule *m, LLVMValueRef func) {
+gb_internal void lb_append_to_compiler_used(lbModule *m, LLVMValueRef value) {
LLVMValueRef global = LLVMGetNamedGlobal(m->mod, "llvm.compiler.used");
LLVMValueRef *constants;
@@ -544,7 +544,7 @@ gb_internal void lb_append_to_compiler_used(lbModule *m, LLVMValueRef func) {
LLVMTypeRef Int8PtrTy = LLVMPointerType(LLVMInt8TypeInContext(m->ctx), 0);
LLVMTypeRef ATy = llvm_array_type(Int8PtrTy, operands);
- constants[operands - 1] = LLVMConstBitCast(func, Int8PtrTy);
+ constants[operands - 1] = LLVMConstBitCast(value, Int8PtrTy);
LLVMValueRef initializer = LLVMConstArray(Int8PtrTy, constants, operands);
global = LLVMAddGlobal(m->mod, ATy, "llvm.compiler.used");
diff --git a/src/llvm_backend_passes.cpp b/src/llvm_backend_passes.cpp
new file mode 100644
index 000000000..bea95ce8e
--- /dev/null
+++ b/src/llvm_backend_passes.cpp
@@ -0,0 +1,1195 @@
+ switch (build_context.optimization_level) {
+ case -1:
+ array_add(&passes, "function(annotation-remarks)");
+ break;
+ case 0:
+ array_add(&passes, "always-inline");
+ array_add(&passes, "function(annotation-remarks)");
+ break;
+ case 1:
+// default<Os>
+// Passes removed: coro, openmp, sroa
+#if LLVM_VERSION_MAJOR == 17
+ array_add(&passes, u8R"(
+annotation2metadata,
+forceattrs,
+inferattrs,
+function<eager-inv>(
+ lower-expect,
+ simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;no-switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>,
+ early-cse<>
+),
+ipsccp,
+called-value-propagation,
+globalopt,
+function<eager-inv>(
+ mem2reg,
+ instcombine<max-iterations=1000;no-use-loop-info>,
+ simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>
+),
+require<globals-aa>,
+function(
+ invalidate<aa>
+),
+require<profile-summary>,
+cgscc(
+ devirt<4>(
+ inline<only-mandatory>,
+ inline,
+ function-attrs<skip-non-recursive>,
+ function<eager-inv;no-rerun>(
+ early-cse<memssa>,
+ speculative-execution,
+ jump-threading,
+ correlated-propagation,
+ simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>,
+ instcombine<max-iterations=1000;no-use-loop-info>,
+ aggressive-instcombine,
+ constraint-elimination,
+ tailcallelim,
+ simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>,
+ reassociate,
+ loop-mssa(
+ loop-instsimplify,
+ loop-simplifycfg,
+ licm<no-allowspeculation>,
+ loop-rotate<header-duplication;no-prepare-for-lto>,
+ licm<allowspeculation>,
+ simple-loop-unswitch<no-nontrivial;trivial>
+ ),
+ simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>,
+ instcombine<max-iterations=1000;no-use-loop-info>,
+ loop(
+ loop-idiom,
+ indvars,
+ loop-deletion,
+ loop-unroll-full
+ ),
+ vector-combine,
+ mldst-motion<no-split-footer-bb>,
+ gvn<>,
+ sccp,
+ bdce,
+ instcombine<max-iterations=1000;no-use-loop-info>,
+ jump-threading,
+ correlated-propagation,
+ adce,
+ memcpyopt,
+ dse,
+ move-auto-init,
+ loop-mssa(
+ licm<allowspeculation>
+ ),
+ simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;hoist-common-insts;sink-common-insts;speculate-blocks;simplify-cond-branch>,
+ instcombine<max-iterations=1000;no-use-loop-info>
+ ),
+ function-attrs,
+ function(
+ require<should-not-run-function-passes>
+ )
+ )
+),
+deadargelim,
+globalopt,
+globaldce,
+elim-avail-extern,
+rpo-function-attrs,
+recompute-globalsaa,
+function<eager-inv>(
+ float2int,
+ lower-constant-intrinsics,
+ loop(
+ loop-rotate<header-duplication;no-prepare-for-lto>,
+ loop-deletion
+ ),
+ loop-distribute,
+ inject-tli-mappings,
+ loop-vectorize<no-interleave-forced-only;no-vectorize-forced-only;>,
+ loop-load-elim,
+ instcombine<max-iterations=1000;no-use-loop-info>,
+ simplifycfg<bonus-inst-threshold=1;forward-switch-cond;switch-range-to-icmp;switch-to-lookup;no-keep-loops;hoist-common-insts;sink-common-insts;speculate-blocks;simplify-cond-branch>,
+ slp-vectorizer,
+ vector-combine,
+ instcombine<max-iterations=1000;no-use-loop-info>,
+ loop-unroll<O2>,
+ transform-warning,
+ instcombine<max-iterations=1000;no-use-loop-info>,
+ loop-mssa(
+ licm<allowspeculation>
+ ),
+ alignment-from-assumptions,
+ loop-sink,
+ instsimplify,
+ div-rem-pairs,
+ tailcallelim,
+ simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>
+),
+globaldce,
+constmerge,
+cg-profile,
+rel-lookup-table-converter,
+function(
+ annotation-remarks
+),
+verify
+)");
+#elif LLVM_VERSION_MAJOR < 20
+ array_add(&passes, u8R"(
+annotation2metadata,
+forceattrs,
+inferattrs,
+function<eager-inv>(
+ lower-expect,
+ simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;no-switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>,
+ sroa<modify-cfg>,
+ early-cse<>
+),
+ipsccp,
+called-value-propagation,
+globalopt,
+function<eager-inv>(
+ mem2reg,
+ instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,
+ simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>
+),
+always-inline,
+require<globals-aa>,
+function(
+ invalidate<aa>
+),
+require<profile-summary>,
+cgscc(
+ devirt<4>(
+ inline,
+ function-attrs<skip-non-recursive-function-attrs>,
+ function<eager-inv;no-rerun>(
+ sroa<modify-cfg>,
+ early-cse<memssa>,
+ speculative-execution<only-if-divergent-target>,
+ jump-threading,
+ correlated-propagation,
+ simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>,
+ instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,
+ aggressive-instcombine,
+ tailcallelim,
+ simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>,
+ reassociate,
+ constraint-elimination,
+ loop-mssa(
+ loop-instsimplify,
+ loop-simplifycfg,
+ licm<no-allowspeculation>,
+ loop-rotate<header-duplication;no-prepare-for-lto>,
+ licm<allowspeculation>,
+ simple-loop-unswitch<no-nontrivial;trivial>
+ ),
+ simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>,
+ instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,
+ loop(
+ loop-idiom,
+ indvars,
+ loop-deletion,
+ loop-unroll-full
+ ),
+ sroa<modify-cfg>,
+ vector-combine,
+ mldst-motion<no-split-footer-bb>,
+ gvn<>,
+ sccp,
+ bdce,
+ instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,
+ jump-threading,
+ correlated-propagation,
+ adce,
+ memcpyopt,
+ dse,
+ move-auto-init,
+ loop-mssa(
+ licm<allowspeculation>
+ ),
+ simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;hoist-common-insts;sink-common-insts;speculate-blocks;simplify-cond-branch>,
+ instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>
+ ),
+ function-attrs,
+ function(
+ require<should-not-run-function-passes>
+ )
+ )
+),
+deadargelim,
+globalopt,
+globaldce,
+elim-avail-extern,
+rpo-function-attrs,
+recompute-globalsaa,
+function<eager-inv>(
+ float2int,
+ lower-constant-intrinsics,
+ loop(
+ loop-rotate<header-duplication;no-prepare-for-lto>,
+ loop-deletion
+ ),
+ loop-distribute,
+ inject-tli-mappings,
+ loop-vectorize<no-interleave-forced-only;no-vectorize-forced-only;>,
+ infer-alignment,
+ loop-load-elim,
+ instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,
+ simplifycfg<bonus-inst-threshold=1;forward-switch-cond;switch-range-to-icmp;switch-to-lookup;no-keep-loops;hoist-common-insts;sink-common-insts;speculate-blocks;simplify-cond-branch>,
+ slp-vectorizer,
+ vector-combine,
+ instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,
+ loop-unroll<O2>,
+ transform-warning,
+ sroa<preserve-cfg>,
+ infer-alignment,
+ instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,
+ loop-mssa(
+ licm<allowspeculation>
+ ),
+ alignment-from-assumptions,
+ loop-sink,
+ instsimplify,
+ div-rem-pairs,
+ tailcallelim,
+ simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>
+),
+globaldce,
+constmerge,
+cg-profile,
+rel-lookup-table-converter,
+function(
+ annotation-remarks
+),
+verify
+)");
+#else
+ array_add(&passes, u8R"(
+annotation2metadata,
+forceattrs,
+inferattrs,
+function<eager-inv>(
+ ee-instrument<>,
+ lower-expect,
+ simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;no-switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-hoist-loads-stores-with-cond-faulting;no-sink-common-insts;speculate-blocks;simplify-cond-branch;no-speculate-unpredictables>,
+ sroa<modify-cfg>,
+ early-cse<>
+),
+ipsccp,
+called-value-propagation,
+globalopt,
+function<eager-inv>(
+ mem2reg,
+ instcombine<max-iterations=1;no-verify-fixpoint>,
+ simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-hoist-loads-stores-with-cond-faulting;no-sink-common-insts;speculate-blocks;simplify-cond-branch;no-speculate-unpredictables>
+),
+always-inline,
+require<globals-aa>,
+function(
+ invalidate<aa>
+),
+require<profile-summary>,
+cgscc(
+ devirt<4>(
+ inline,
+ function-attrs<skip-non-recursive-function-attrs>,
+ function<eager-inv;no-rerun>(
+ sroa<modify-cfg>,
+ early-cse<memssa>,
+ speculative-execution<only-if-divergent-target>,
+ jump-threading,
+ correlated-propagation,
+ simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-hoist-loads-stores-with-cond-faulting;no-sink-common-insts;speculate-blocks;simplify-cond-branch;no-speculate-unpredictables>,
+ instcombine<max-iterations=1;no-verify-fixpoint>,
+ aggressive-instcombine,
+ tailcallelim,
+ simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-hoist-loads-stores-with-cond-faulting;no-sink-common-insts;speculate-blocks;simplify-cond-branch;no-speculate-unpredictables>,
+ reassociate,
+ constraint-elimination,
+ loop-mssa(
+ loop-instsimplify,
+ loop-simplifycfg,
+ licm<no-allowspeculation>,
+ loop-rotate<header-duplication;no-prepare-for-lto>,
+ licm<allowspeculation>,
+ simple-loop-unswitch<no-nontrivial;trivial>
+ ),
+ simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-hoist-loads-stores-with-cond-faulting;no-sink-common-insts;speculate-blocks;simplify-cond-branch;no-speculate-unpredictables>,
+ instcombine<max-iterations=1;no-verify-fixpoint>,
+ loop(
+ loop-idiom,
+ indvars,
+ extra-simple-loop-unswitch-passes,
+ loop-deletion,loop-unroll-full
+ ),
+ sroa<modify-cfg>,
+ vector-combine,
+ mldst-motion<no-split-footer-bb>,
+ gvn<>,
+ sccp,
+ bdce,
+ instcombine<max-iterations=1;no-verify-fixpoint>,
+ jump-threading,
+ correlated-propagation,
+ adce,
+ memcpyopt,
+ dse,
+ move-auto-init,
+ loop-mssa(
+ licm<allowspeculation>
+ ),
+ simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;hoist-common-insts;no-hoist-loads-stores-with-cond-faulting;sink-common-insts;speculate-blocks;simplify-cond-branch;no-speculate-unpredictables>,
+ instcombine<max-iterations=1;no-verify-fixpoint>
+ ),
+ function-attrs,
+ function(
+ require<should-not-run-function-passes>
+ )
+ )
+),
+deadargelim,
+globalopt,
+globaldce,
+elim-avail-extern,
+rpo-function-attrs,
+recompute-globalsaa,
+function<eager-inv>(
+ float2int,
+ lower-constant-intrinsics,
+ loop(
+ loop-rotate<header-duplication;no-prepare-for-lto>,
+ loop-deletion
+ ),
+ loop-distribute,
+ inject-tli-mappings,
+ loop-vectorize<no-interleave-forced-only;no-vectorize-forced-only;>,
+ infer-alignment,
+ loop-load-elim,
+ instcombine<max-iterations=1;no-verify-fixpoint>,
+ simplifycfg<bonus-inst-threshold=1;forward-switch-cond;switch-range-to-icmp;switch-to-lookup;no-keep-loops;hoist-common-insts;no-hoist-loads-stores-with-cond-faulting;sink-common-insts;speculate-blocks;simplify-cond-branch;no-speculate-unpredictables>,
+ slp-vectorizer,
+ vector-combine,
+ instcombine<max-iterations=1;no-verify-fixpoint>,
+ loop-unroll<O2>,
+ transform-warning,
+ sroa<preserve-cfg>,
+ infer-alignment,
+ instcombine<max-iterations=1;no-verify-fixpoint>,
+ loop-mssa(
+ licm<allowspeculation>
+ ),
+ alignment-from-assumptions,
+ loop-sink,
+ instsimplify,
+ div-rem-pairs,
+ tailcallelim,
+ simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;hoist-loads-stores-with-cond-faulting;no-sink-common-insts;speculate-blocks;simplify-cond-branch;speculate-unpredictables>
+),
+globaldce,
+constmerge,
+cg-profile,
+rel-lookup-table-converter,
+function(
+ annotation-remarks
+),
+verify
+)");
+
+#endif
+ break;
+// default<O2>
+// Passes removed: coro, openmp, sroa
+ case 2:
+#if LLVM_VERSION_MAJOR == 17
+ array_add(&passes, u8R"(
+annotation2metadata,
+forceattrs,
+inferattrs,
+function<eager-inv>(
+ lower-expect,
+ simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;no-switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>,
+ early-cse<>
+),
+ipsccp,
+called-value-propagation,
+globalopt,
+function<eager-inv>(
+ mem2reg,
+ instcombine<max-iterations=1000;no-use-loop-info>,
+ simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>
+),
+require<globals-aa>,
+function(
+ invalidate<aa>
+),
+require<profile-summary>,
+cgscc(
+ devirt<4>(
+ inline<only-mandatory>,
+ inline,
+ function-attrs<skip-non-recursive>,
+ function<eager-inv;no-rerun>(
+ early-cse<memssa>,
+ speculative-execution,
+ jump-threading,
+ correlated-propagation,
+ simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>,
+ instcombine<max-iterations=1000;no-use-loop-info>,
+ aggressive-instcombine,
+ constraint-elimination,
+ libcalls-shrinkwrap,
+ tailcallelim,
+ simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>,
+ reassociate,
+ loop-mssa(
+ loop-instsimplify,
+ loop-simplifycfg,
+ licm<no-allowspeculation>,
+ loop-rotate<header-duplication;no-prepare-for-lto>,
+ licm<allowspeculation>,
+ simple-loop-unswitch<no-nontrivial;trivial>
+ ),
+ simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>,
+ instcombine<max-iterations=1000;no-use-loop-info>,
+ loop(
+ loop-idiom,
+ indvars,
+ loop-deletion,
+ loop-unroll-full
+ ),
+ vector-combine,
+ mldst-motion<no-split-footer-bb>,
+ gvn<>,
+ sccp,
+ bdce,
+ instcombine<max-iterations=1000;no-use-loop-info>,
+ jump-threading,
+ correlated-propagation,
+ adce,
+ memcpyopt,
+ dse,
+ move-auto-init,
+ loop-mssa(
+ licm<allowspeculation>
+ ),
+ simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;hoist-common-insts;sink-common-insts;speculate-blocks;simplify-cond-branch>,
+ instcombine<max-iterations=1000;no-use-loop-info>
+ ),
+ function-attrs,
+ function(
+ require<should-not-run-function-passes>
+ )
+ )
+),
+deadargelim,
+globalopt,
+globaldce,
+elim-avail-extern,
+rpo-function-attrs,
+recompute-globalsaa,
+function<eager-inv>(
+ float2int,
+ lower-constant-intrinsics,
+ loop(
+ loop-rotate<header-duplication;no-prepare-for-lto>,
+ loop-deletion
+ ),
+ loop-distribute,
+ inject-tli-mappings,
+ loop-vectorize<no-interleave-forced-only;no-vectorize-forced-only;>,
+ loop-load-elim,
+ instcombine<max-iterations=1000;no-use-loop-info>,
+ simplifycfg<bonus-inst-threshold=1;forward-switch-cond;switch-range-to-icmp;switch-to-lookup;no-keep-loops;hoist-common-insts;sink-common-insts;speculate-blocks;simplify-cond-branch>,
+ slp-vectorizer,
+ vector-combine,
+ instcombine<max-iterations=1000;no-use-loop-info>,
+ loop-unroll<O2>,
+ transform-warning,
+ instcombine<max-iterations=1000;no-use-loop-info>,
+ loop-mssa(
+ licm<allowspeculation>
+ ),
+ alignment-from-assumptions,
+ loop-sink,
+ instsimplify,
+ div-rem-pairs,
+ tailcallelim,
+ simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>
+),
+globaldce,
+constmerge,
+cg-profile,
+rel-lookup-table-converter,
+function(
+ annotation-remarks
+),
+verify
+)");
+#elif LLVM_VERSION_MAJOR < 20
+ array_add(&passes, u8R"(
+annotation2metadata,
+forceattrs,
+inferattrs,
+function<eager-inv>(
+ lower-expect,
+ simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;no-switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>,
+ sroa<modify-cfg>,
+ early-cse<>
+),
+ipsccp,
+called-value-propagation,
+globalopt,
+function<eager-inv>(
+ mem2reg,
+ instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,
+ simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>
+),
+always-inline,
+require<globals-aa>,
+function(
+ invalidate<aa>
+),
+require<profile-summary>,
+cgscc(
+ devirt<4>(
+ inline,
+ function-attrs<skip-non-recursive-function-attrs>,
+ function<eager-inv;no-rerun>(
+ sroa<modify-cfg>,
+ early-cse<memssa>,
+ speculative-execution<only-if-divergent-target>,
+ jump-threading,
+ correlated-propagation,
+ simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>,
+ instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,
+ aggressive-instcombine,
+ libcalls-shrinkwrap,
+ tailcallelim,
+ simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>,
+ reassociate,
+ constraint-elimination,
+ loop-mssa(
+ loop-instsimplify,
+ loop-simplifycfg,
+ licm<no-allowspeculation>,
+ loop-rotate<header-duplication;no-prepare-for-lto>,
+ licm<allowspeculation>,
+ simple-loop-unswitch<no-nontrivial;trivial>
+ ),
+ simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>,
+ instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,
+ loop(
+ loop-idiom,
+ indvars,
+ loop-deletion,
+ loop-unroll-full
+ ),
+ sroa<modify-cfg>,
+ vector-combine,
+ mldst-motion<no-split-footer-bb>,
+ gvn<>,
+ sccp,
+ bdce,
+ instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,
+ jump-threading,
+ correlated-propagation,
+ adce,
+ memcpyopt,
+ dse,
+ move-auto-init,
+ loop-mssa(
+ licm<allowspeculation>
+ ),
+ simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;hoist-common-insts;sink-common-insts;speculate-blocks;simplify-cond-branch>,
+ instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>
+ ),
+ function-attrs,
+ function(
+ require<should-not-run-function-passes>
+ )
+ )
+),
+deadargelim,
+globalopt,
+globaldce,
+elim-avail-extern,
+rpo-function-attrs,
+recompute-globalsaa,
+function<eager-inv>(
+ float2int,
+ lower-constant-intrinsics,
+ loop(
+ loop-rotate<header-duplication;no-prepare-for-lto>,
+ loop-deletion
+ ),
+ loop-distribute,
+ inject-tli-mappings,
+ loop-vectorize<no-interleave-forced-only;no-vectorize-forced-only;>,
+ infer-alignment,
+ loop-load-elim,
+ instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,
+ simplifycfg<bonus-inst-threshold=1;forward-switch-cond;switch-range-to-icmp;switch-to-lookup;no-keep-loops;hoist-common-insts;sink-common-insts;speculate-blocks;simplify-cond-branch>,
+ slp-vectorizer,
+ vector-combine,
+ instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,
+ loop-unroll<O2>,
+ transform-warning,
+ sroa<modify-cfg>,
+ infer-alignment,
+ instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,
+ loop-mssa(
+ licm<allowspeculation>
+ ),
+ alignment-from-assumptions,
+ loop-sink,
+ instsimplify,
+ div-rem-pairs,
+ tailcallelim,
+ simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>
+),
+globaldce,
+constmerge,
+cg-profile,
+rel-lookup-table-converter,
+function(
+ annotation-remarks
+),
+verify
+)");
+#else
+ array_add(&passes, u8R"(
+annotation2metadata,
+forceattrs,
+inferattrs,
+function<eager-inv>(
+ ee-instrument<>,
+ lower-expect,
+ simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;no-switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-hoist-loads-stores-with-cond-faulting;no-sink-common-insts;speculate-blocks;simplify-cond-branch;no-speculate-unpredictables>,
+ sroa<modify-cfg>,
+ early-cse<>
+),
+ipsccp,
+called-value-propagation,
+globalopt,
+function<eager-inv>(
+ mem2reg,
+ instcombine<max-iterations=1;no-verify-fixpoint>,
+ simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-hoist-loads-stores-with-cond-faulting;no-sink-common-insts;speculate-blocks;simplify-cond-branch;no-speculate-unpredictables>
+),
+always-inline,
+require<globals-aa>,
+function(
+ invalidate<aa>
+),
+require<profile-summary>,
+cgscc(
+ devirt<4>(
+ inline,
+ function-attrs<skip-non-recursive-function-attrs>,
+ function<eager-inv;no-rerun>(
+ sroa<modify-cfg>,
+ early-cse<memssa>,
+ speculative-execution<only-if-divergent-target>,
+ jump-threading,
+ correlated-propagation,
+ simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-hoist-loads-stores-with-cond-faulting;no-sink-common-insts;speculate-blocks;simplify-cond-branch;no-speculate-unpredictables>,
+ instcombine<max-iterations=1;no-verify-fixpoint>,
+ aggressive-instcombine,
+ libcalls-shrinkwrap,
+ tailcallelim,
+ simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-hoist-loads-stores-with-cond-faulting;no-sink-common-insts;speculate-blocks;simplify-cond-branch;no-speculate-unpredictables>,
+ reassociate,
+ constraint-elimination,
+ loop-mssa(
+ loop-instsimplify,
+ loop-simplifycfg,
+ licm<no-allowspeculation>,
+ loop-rotate<header-duplication;no-prepare-for-lto>,
+ licm<allowspeculation>,
+ simple-loop-unswitch<no-nontrivial;trivial>
+ ),
+ simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-hoist-loads-stores-with-cond-faulting;no-sink-common-insts;speculate-blocks;simplify-cond-branch;no-speculate-unpredictables>,
+ instcombine<max-iterations=1;no-verify-fixpoint>,
+ loop(
+ loop-idiom,
+ indvars,
+ extra-simple-loop-unswitch-passes,
+ loop-deletion,
+ loop-unroll-full
+ ),
+ sroa<modify-cfg>,
+ vector-combine,
+ mldst-motion<no-split-footer-bb>,
+ gvn<>,
+ sccp,
+ bdce,
+ instcombine<max-iterations=1;no-verify-fixpoint>,
+ jump-threading,
+ correlated-propagation,
+ adce,
+ memcpyopt,
+ dse,
+ move-auto-init,
+ loop-mssa(
+ licm<allowspeculation>
+ ),
+ simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;hoist-common-insts;no-hoist-loads-stores-with-cond-faulting;sink-common-insts;speculate-blocks;simplify-cond-branch;no-speculate-unpredictables>,
+ instcombine<max-iterations=1;no-verify-fixpoint>
+ ),
+ function-attrs,
+ function(
+ require<should-not-run-function-passes>
+ )
+ )
+),
+deadargelim,
+globalopt,
+globaldce,
+elim-avail-extern,
+rpo-function-attrs,
+recompute-globalsaa,
+function<eager-inv>(
+ float2int,
+ lower-constant-intrinsics,
+ loop(
+ loop-rotate<header-duplication;no-prepare-for-lto>,
+ loop-deletion
+ ),
+ loop-distribute,
+ inject-tli-mappings,
+ loop-vectorize<no-interleave-forced-only;no-vectorize-forced-only;>,
+ infer-alignment,
+ loop-load-elim,
+ instcombine<max-iterations=1;no-verify-fixpoint>,
+ simplifycfg<bonus-inst-threshold=1;forward-switch-cond;switch-range-to-icmp;switch-to-lookup;no-keep-loops;hoist-common-insts;no-hoist-loads-stores-with-cond-faulting;sink-common-insts;speculate-blocks;simplify-cond-branch;no-speculate-unpredictables>,
+ slp-vectorizer,
+ vector-combine,
+ instcombine<max-iterations=1;no-verify-fixpoint>,
+ loop-unroll<O2>,
+ transform-warning,
+ sroa<preserve-cfg>,
+ infer-alignment,
+ instcombine<max-iterations=1;no-verify-fixpoint>,
+ loop-mssa(
+ licm<allowspeculation>
+ ),
+ alignment-from-assumptions,
+ loop-sink,
+ instsimplify,
+ div-rem-pairs,
+ tailcallelim,
+ simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;hoist-loads-stores-with-cond-faulting;no-sink-common-insts;speculate-blocks;simplify-cond-branch;speculate-unpredictables>
+),
+globaldce,
+constmerge,
+cg-profile,
+rel-lookup-table-converter,
+function(
+ annotation-remarks
+),
+verify
+)");
+#endif
+ break;
+
+ case 3:
+// default<O3>
+// Passes removed: coro, openmp, sroa
+#if LLVM_VERSION_MAJOR == 17
+ array_add(&passes, u8R"(
+annotation2metadata,
+forceattrs,
+inferattrs,
+function<eager-inv>(
+ lower-expect,
+ simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;no-switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>,
+ early-cse<>,
+ callsite-splitting
+),
+ipsccp,
+called-value-propagation,
+globalopt,
+function<eager-inv>(
+ mem2reg,
+ instcombine<max-iterations=1000;no-use-loop-info>,
+ simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>
+),
+require<globals-aa>,
+function(
+ invalidate<aa>
+),
+require<profile-summary>,
+cgscc(
+ devirt<4>(
+ inline<only-mandatory>,
+ inline,
+ function-attrs<skip-non-recursive>,
+ argpromotion,
+ function<eager-inv;no-rerun>(
+ early-cse<memssa>,
+ speculative-execution,
+ jump-threading,
+ correlated-propagation,
+ simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>,
+ instcombine<max-iterations=1000;no-use-loop-info>,
+ aggressive-instcombine,
+ constraint-elimination,
+ libcalls-shrinkwrap,
+ tailcallelim,
+ simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>,
+ reassociate,
+ loop-mssa(
+ loop-instsimplify,
+ loop-simplifycfg,
+ licm<no-allowspeculation>,
+ loop-rotate<header-duplication;no-prepare-for-lto>,
+ licm<allowspeculation>,
+ simple-loop-unswitch<nontrivial;trivial>
+ ),
+ simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>,
+ instcombine<max-iterations=1000;no-use-loop-info>,
+ loop(
+ loop-idiom,
+ indvars,
+ loop-deletion,
+ loop-unroll-full
+ ),
+ vector-combine,
+ mldst-motion<no-split-footer-bb>,
+ gvn<>,
+ sccp,
+ bdce,
+ instcombine<max-iterations=1000;no-use-loop-info>,
+ jump-threading,
+ correlated-propagation,
+ adce,
+ memcpyopt,
+ dse,
+ move-auto-init,
+ loop-mssa(
+ licm<allowspeculation>
+ ),
+ simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;hoist-common-insts;sink-common-insts;speculate-blocks;simplify-cond-branch>,
+ instcombine<max-iterations=1000;no-use-loop-info>
+ ),
+ function-attrs,
+ function(
+ require<should-not-run-function-passes>
+ )
+ )
+),
+deadargelim,
+globalopt,
+globaldce,
+elim-avail-extern,
+rpo-function-attrs,
+recompute-globalsaa,
+function<eager-inv>(
+ float2int,
+ lower-constant-intrinsics,
+ chr,
+ loop(
+ loop-rotate<header-duplication;no-prepare-for-lto>,
+ loop-deletion
+ ),
+ loop-distribute,
+ inject-tli-mappings,
+ loop-vectorize<no-interleave-forced-only;no-vectorize-forced-only;>,
+ loop-load-elim,
+ instcombine<max-iterations=1000;no-use-loop-info>,
+ simplifycfg<bonus-inst-threshold=1;forward-switch-cond;switch-range-to-icmp;switch-to-lookup;no-keep-loops;hoist-common-insts;sink-common-insts;speculate-blocks;simplify-cond-branch>,
+ slp-vectorizer,
+ vector-combine,
+ instcombine<max-iterations=1000;no-use-loop-info>,
+ loop-unroll<O3>,
+ transform-warning,
+ instcombine<max-iterations=1000;no-use-loop-info>,
+ loop-mssa(
+ licm<allowspeculation>
+ ),
+ alignment-from-assumptions,
+ loop-sink,
+ instsimplify,
+ div-rem-pairs,
+ tailcallelim,
+ simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>
+),
+globaldce,
+constmerge,
+cg-profile,
+rel-lookup-table-converter,
+function(
+ annotation-remarks
+),
+verify
+)");
+
+#elif LLVM_VERSION_MAJOR < 20
+ array_add(&passes, u8R"(
+annotation2metadata,
+forceattrs,
+inferattrs,
+function<eager-inv>(
+ lower-expect,
+ simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;no-switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>,
+ sroa<modify-cfg>,
+ early-cse<>,
+ callsite-splitting
+),
+ipsccp,
+called-value-propagation,
+globalopt,
+function<eager-inv>(
+ mem2reg,
+ instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,
+ simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>
+),
+always-inline,
+require<globals-aa>,
+function(invalidate<aa>),
+require<profile-summary>,
+cgscc(
+ devirt<4>(
+ inline,
+ function-attrs<skip-non-recursive-function-attrs>,
+ argpromotion,
+ function<eager-inv;no-rerun>(
+ sroa<modify-cfg>,
+ early-cse<memssa>,
+ speculative-execution<only-if-divergent-target>,
+ jump-threading,
+ correlated-propagation,
+ simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>,
+ instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,
+ aggressive-instcombine,
+ libcalls-shrinkwrap,
+ tailcallelim,
+ simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>,
+ reassociate,
+ constraint-elimination,
+ loop-mssa(
+ loop-instsimplify,
+ loop-simplifycfg,
+ licm<no-allowspeculation>,
+ loop-rotate<header-duplication;no-prepare-for-lto>,
+ licm<allowspeculation>,
+ simple-loop-unswitch<nontrivial;trivial>
+ ),
+ simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>,
+ instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,
+ loop(
+ loop-idiom,
+ indvars,
+ loop-deletion,
+ loop-unroll-full
+ ),
+ sroa<modify-cfg>,
+ vector-combine,
+ mldst-motion<no-split-footer-bb>,
+ gvn<>,
+ sccp,
+ bdce,
+ instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,
+ jump-threading,
+ correlated-propagation,
+ adce,
+ memcpyopt,
+ dse,
+ move-auto-init,
+ loop-mssa(licm<allowspeculation>),
+ simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;hoist-common-insts;sink-common-insts;speculate-blocks;simplify-cond-branch>,
+ instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>
+ ),
+ function-attrs,
+ function(
+ require<should-not-run-function-passes>
+ )
+ )
+),
+deadargelim,
+globalopt,
+globaldce,
+elim-avail-extern,
+rpo-function-attrs,
+recompute-globalsaa,
+function<eager-inv>(
+ float2int,
+ lower-constant-intrinsics,
+ chr,
+ loop(
+ loop-rotate<header-duplication;no-prepare-for-lto>,
+ loop-deletion
+ ),
+ loop-distribute,
+ inject-tli-mappings,
+ loop-vectorize<no-interleave-forced-only;no-vectorize-forced-only;>,
+ infer-alignment,
+ loop-load-elim,
+ instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,
+ simplifycfg<bonus-inst-threshold=1;forward-switch-cond;switch-range-to-icmp;switch-to-lookup;no-keep-loops;hoist-common-insts;sink-common-insts;speculate-blocks;simplify-cond-branch>,
+ slp-vectorizer,
+ vector-combine,
+ instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,
+ loop-unroll<O3>,
+ transform-warning,
+ sroa<preserve-cfg>,
+ infer-alignment,
+ instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,
+ loop-mssa(licm<allowspeculation>),
+ alignment-from-assumptions,
+ loop-sink,
+ instsimplify,
+ div-rem-pairs,
+ tailcallelim,
+ simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>
+),
+globaldce,
+constmerge,
+cg-profile,
+rel-lookup-table-converter,
+function(
+ annotation-remarks
+),
+verify
+)");
+#else
+ array_add(&passes, u8R"(
+annotation2metadata,
+forceattrs,
+inferattrs,
+function<eager-inv>(
+ ee-instrument<>,
+ lower-expect,
+ simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;no-switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-hoist-loads-stores-with-cond-faulting;no-sink-common-insts;speculate-blocks;simplify-cond-branch;no-speculate-unpredictables>,
+ sroa<modify-cfg>,
+ early-cse<>,
+ callsite-splitting
+),
+ipsccp,
+called-value-propagation,
+globalopt,
+function<eager-inv>(
+ mem2reg,
+ instcombine<max-iterations=1;no-verify-fixpoint>,
+ simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-hoist-loads-stores-with-cond-faulting;no-sink-common-insts;speculate-blocks;simplify-cond-branch;no-speculate-unpredictables>
+),
+always-inline,
+require<globals-aa>,
+function(
+ invalidate<aa>
+),
+require<profile-summary>,
+cgscc(
+ devirt<4>(
+ inline,
+ function-attrs<skip-non-recursive-function-attrs>,
+ argpromotion,
+ function<eager-inv;no-rerun>(
+ sroa<modify-cfg>,
+ early-cse<memssa>,
+ speculative-execution<only-if-divergent-target>,
+ jump-threading,
+ correlated-propagation,
+ simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-hoist-loads-stores-with-cond-faulting;no-sink-common-insts;speculate-blocks;simplify-cond-branch;no-speculate-unpredictables>,
+ instcombine<max-iterations=1;no-verify-fixpoint>,
+ aggressive-instcombine,
+ libcalls-shrinkwrap,
+ tailcallelim,
+ simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-hoist-loads-stores-with-cond-faulting;no-sink-common-insts;speculate-blocks;simplify-cond-branch;no-speculate-unpredictables>,
+ reassociate,
+ constraint-elimination,
+ loop-mssa(
+ loop-instsimplify,
+ loop-simplifycfg,
+ licm<no-allowspeculation>,
+ loop-rotate<header-duplication;no-prepare-for-lto>,
+ licm<allowspeculation>,
+ simple-loop-unswitch<nontrivial;trivial>
+ ),
+ simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-hoist-loads-stores-with-cond-faulting;no-sink-common-insts;speculate-blocks;simplify-cond-branch;no-speculate-unpredictables>,
+ instcombine<max-iterations=1;no-verify-fixpoint>,
+ loop(
+ loop-idiom,
+ indvars,
+ extra-simple-loop-unswitch-passes,
+ loop-deletion,
+ loop-unroll-full
+ ),
+ sroa<modify-cfg>,
+ vector-combine,
+ mldst-motion<no-split-footer-bb>,
+ gvn<>,
+ sccp,
+ bdce,
+ instcombine<max-iterations=1;no-verify-fixpoint>,
+ jump-threading,
+ correlated-propagation,
+ adce,
+ memcpyopt,
+ dse,
+ move-auto-init,
+ loop-mssa(
+ licm<allowspeculation>
+ ),
+ simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;hoist-common-insts;no-hoist-loads-stores-with-cond-faulting;sink-common-insts;speculate-blocks;simplify-cond-branch;no-speculate-unpredictables>,
+ instcombine<max-iterations=1;no-verify-fixpoint>
+ ),
+ function-attrs,
+ function(
+ require<should-not-run-function-passes>
+ )
+ )
+),
+deadargelim,
+globalopt,
+globaldce,
+elim-avail-extern,
+rpo-function-attrs,
+recompute-globalsaa,
+function<eager-inv>(
+ float2int,
+ lower-constant-intrinsics,
+ chr,
+ loop(
+ loop-rotate<header-duplication;no-prepare-for-lto>,
+ loop-deletion
+ ),
+ loop-distribute,
+ inject-tli-mappings,
+ loop-vectorize<no-interleave-forced-only;no-vectorize-forced-only;>,
+ infer-alignment,
+ loop-load-elim,
+ instcombine<max-iterations=1;no-verify-fixpoint>,
+ simplifycfg<bonus-inst-threshold=1;forward-switch-cond;switch-range-to-icmp;switch-to-lookup;no-keep-loops;hoist-common-insts;no-hoist-loads-stores-with-cond-faulting;sink-common-insts;speculate-blocks;simplify-cond-branch;no-speculate-unpredictables>,
+ slp-vectorizer,
+ vector-combine,
+ instcombine<max-iterations=1;no-verify-fixpoint>,
+ loop-unroll<O3>,
+ transform-warning,
+ sroa<preserve-cfg>,
+ infer-alignment,
+ instcombine<max-iterations=1;no-verify-fixpoint>,
+ loop-mssa(
+ licm<allowspeculation>
+ ),
+ alignment-from-assumptions,
+ loop-sink,
+ instsimplify,
+ div-rem-pairs,
+ tailcallelim,
+ simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;hoist-loads-stores-with-cond-faulting;no-sink-common-insts;speculate-blocks;simplify-cond-branch;speculate-unpredictables>
+),
+globaldce,
+constmerge,
+cg-profile,
+rel-lookup-table-converter,
+function(
+ annotation-remarks
+),
+verify
+
+)");
+
+#endif
+ break;
+ } \ No newline at end of file
diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp
index a835ae2c8..08ef42f01 100644
--- a/src/llvm_backend_proc.cpp
+++ b/src/llvm_backend_proc.cpp
@@ -67,6 +67,14 @@ gb_internal void lb_mem_copy_non_overlapping(lbProcedure *p, lbValue dst, lbValu
gb_internal lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool ignore_body) {
GB_ASSERT(entity != nullptr);
GB_ASSERT(entity->kind == Entity_Procedure);
+ // Skip codegen for unspecialized polymorphic procedures
+ if (is_type_polymorphic(entity->type) && !entity->Procedure.is_foreign) {
+ Type *bt = base_type(entity->type);
+ if (bt->kind == Type_Proc && bt->Proc.is_polymorphic && !bt->Proc.is_poly_specialized) {
+ // Do not generate code for unspecialized polymorphic procedures
+ return nullptr;
+ }
+ }
if (!entity->Procedure.is_foreign) {
if ((entity->flags & EntityFlag_ProcBodyChecked) == 0) {
GB_PANIC("%.*s :: %s (was parapoly: %d %d)", LIT(entity->token.string), type_to_string(entity->type), is_type_polymorphic(entity->type, true), is_type_polymorphic(entity->type, false));
@@ -76,7 +84,7 @@ gb_internal lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool i
String link_name = {};
if (ignore_body) {
- lbModule *other_module = lb_module_of_entity(m->gen, entity);
+ lbModule *other_module = lb_module_of_entity(m->gen, entity, m);
link_name = lb_get_entity_name(other_module, entity);
} else {
link_name = lb_get_entity_name(m, entity);
@@ -91,7 +99,6 @@ gb_internal lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool i
}
}
-
lbProcedure *p = gb_alloc_item(permanent_allocator(), lbProcedure);
p->module = m;
@@ -115,12 +122,13 @@ gb_internal lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool i
p->is_entry_point = false;
gbAllocator a = heap_allocator();
- p->children.allocator = a;
- p->defer_stmts.allocator = a;
- p->blocks.allocator = a;
- p->branch_blocks.allocator = a;
- p->context_stack.allocator = a;
- p->scope_stack.allocator = a;
+ p->children.allocator = a;
+ p->defer_stmts.allocator = a;
+ p->blocks.allocator = a;
+ p->branch_blocks.allocator = a;
+ p->context_stack.allocator = a;
+ p->scope_stack.allocator = a;
+ p->asan_stack_locals.allocator = a;
// map_init(&p->selector_values, 0);
// map_init(&p->selector_addr, 0);
// map_init(&p->tuple_fix_map, 0);
@@ -333,10 +341,10 @@ gb_internal lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool i
}
if (p->body && entity->pkg && ((entity->pkg->kind == Package_Normal) || (entity->pkg->kind == Package_Init))) {
- if (build_context.sanitizer_flags & SanitizerFlag_Address) {
+ if (build_context.sanitizer_flags & SanitizerFlag_Address && !entity->Procedure.no_sanitize_address) {
lb_add_attribute_to_proc(m, p->value, "sanitize_address");
}
- if (build_context.sanitizer_flags & SanitizerFlag_Memory) {
+ if (build_context.sanitizer_flags & SanitizerFlag_Memory && !entity->Procedure.no_sanitize_memory) {
lb_add_attribute_to_proc(m, p->value, "sanitize_memory");
}
if (build_context.sanitizer_flags & SanitizerFlag_Thread) {
@@ -385,11 +393,12 @@ gb_internal lbProcedure *lb_create_dummy_procedure(lbModule *m, String link_name
p->is_entry_point = false;
gbAllocator a = permanent_allocator();
- p->children.allocator = a;
- p->defer_stmts.allocator = a;
- p->blocks.allocator = a;
- p->branch_blocks.allocator = a;
- p->context_stack.allocator = a;
+ p->children.allocator = a;
+ p->defer_stmts.allocator = a;
+ p->blocks.allocator = a;
+ p->branch_blocks.allocator = a;
+ p->context_stack.allocator = a;
+ p->asan_stack_locals.allocator = a;
map_init(&p->tuple_fix_map, 0);
@@ -536,6 +545,9 @@ gb_internal void lb_begin_procedure_body(lbProcedure *p) {
GB_ASSERT(p->type != nullptr);
lb_ensure_abi_function_type(p->module, p);
+ if (p->type->Proc.calling_convention == ProcCC_Odin) {
+ lb_push_context_onto_stack_from_implicit_parameter(p);
+ }
{
lbFunctionType *ft = p->abi_function_type;
@@ -733,9 +745,6 @@ gb_internal void lb_begin_procedure_body(lbProcedure *p) {
}
}
- if (p->type->Proc.calling_convention == ProcCC_Odin) {
- lb_push_context_onto_stack_from_implicit_parameter(p);
- }
lb_set_debug_position_to_procedure_begin(p);
if (p->debug_info != nullptr) {
@@ -781,17 +790,15 @@ gb_internal void lb_end_procedure_body(lbProcedure *p) {
p->curr_block = nullptr;
p->state_flags = 0;
-}
-gb_internal void lb_end_procedure(lbProcedure *p) {
+
LLVMDisposeBuilder(p->builder);
}
gb_internal void lb_build_nested_proc(lbProcedure *p, AstProcLit *pd, Entity *e) {
GB_ASSERT(pd->body != nullptr);
lbModule *m = p->module;
- auto *min_dep_set = &m->info->minimum_dependency_set;
- if (ptr_set_exists(min_dep_set, e) == false) {
+ if (e->min_dep_count.load(std::memory_order_relaxed) == 0) {
// NOTE(bill): Nothing depends upon it so doesn't need to be built
return;
}
@@ -815,6 +822,10 @@ gb_internal void lb_build_nested_proc(lbProcedure *p, AstProcLit *pd, Entity *e)
e->Procedure.link_name = name;
lbProcedure *nested_proc = lb_create_procedure(p->module, e);
+ if (nested_proc == nullptr) {
+ // This is an unspecialized polymorphic procedure, skip codegen
+ return;
+ }
e->code_gen_procedure = nested_proc;
lbValue value = {};
@@ -823,7 +834,7 @@ gb_internal void lb_build_nested_proc(lbProcedure *p, AstProcLit *pd, Entity *e)
lb_add_entity(m, e, value);
array_add(&p->children, nested_proc);
- array_add(&m->procedures_to_generate, nested_proc);
+ mpsc_enqueue(&m->procedures_to_generate, nested_proc);
}
@@ -910,13 +921,14 @@ gb_internal lbValue lb_emit_call_internal(lbProcedure *p, lbValue value, lbValue
arg_type != param_type) {
LLVMTypeKind arg_kind = LLVMGetTypeKind(arg_type);
LLVMTypeKind param_kind = LLVMGetTypeKind(param_type);
- if (arg_kind == param_kind &&
- arg_kind == LLVMPointerTypeKind) {
- // NOTE(bill): LLVM's newer `ptr` only type system seems to fail at times
- // I don't know why...
- args[i] = LLVMBuildPointerCast(p->builder, args[i], param_type, "");
- arg_type = param_type;
- continue;
+ if (arg_kind == param_kind) {
+ if (arg_kind == LLVMPointerTypeKind) {
+ // NOTE(bill): LLVM's newer `ptr` only type system seems to fail at times
+ // I don't know why...
+ args[i] = LLVMBuildPointerCast(p->builder, args[i], param_type, "");
+ arg_type = param_type;
+ continue;
+ }
}
}
@@ -972,6 +984,7 @@ gb_internal lbValue lb_emit_call_internal(lbProcedure *p, lbValue value, lbValue
gb_internal lbValue lb_lookup_runtime_procedure(lbModule *m, String const &name) {
AstPackage *pkg = m->info->runtime_package;
Entity *e = scope_lookup_current(pkg->scope, name);
+ GB_ASSERT_MSG(e != nullptr, "Runtime procedure not found: %s", name);
return lb_find_procedure_value_from_entity(m, e);
}
@@ -1060,6 +1073,7 @@ gb_internal lbValue lb_emit_call(lbProcedure *p, lbValue value, Array<lbValue> c
lbValue result = {};
+ isize ignored_args = 0;
auto processed_args = array_make<lbValue>(permanent_allocator(), 0, args.count);
{
@@ -1082,6 +1096,7 @@ gb_internal lbValue lb_emit_call(lbProcedure *p, lbValue value, Array<lbValue> c
lbArgType *arg = &ft->args[param_index];
if (arg->kind == lbArg_Ignore) {
param_index += 1;
+ ignored_args += 1;
continue;
}
@@ -1190,7 +1205,7 @@ gb_internal lbValue lb_emit_call(lbProcedure *p, lbValue value, Array<lbValue> c
auto tuple_fix_values = slice_make<lbValue>(permanent_allocator(), ret_count);
auto tuple_geps = slice_make<lbValue>(permanent_allocator(), ret_count);
- isize offset = ft->original_arg_count;
+ isize offset = ft->original_arg_count - ignored_args;
for (isize j = 0; j < ret_count-1; j++) {
lbValue ret_arg_ptr = processed_args[offset + j];
lbValue ret_arg = lb_emit_load(p, ret_arg_ptr);
@@ -1293,6 +1308,23 @@ gb_internal lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAn
lbValue res = {};
res.type = tv.type;
+ switch (builtin_id) {
+ case BuiltinProc_simd_indices: {
+ Type *type = base_type(res.type);
+ GB_ASSERT(type->kind == Type_SimdVector);
+ Type *elem = type->SimdVector.elem;
+
+ i64 count = type->SimdVector.count;
+ LLVMValueRef *scalars = gb_alloc_array(temporary_allocator(), LLVMValueRef, count);
+ for (i64 i = 0; i < count; i++) {
+ scalars[i] = lb_const_value(m, elem, exact_value_i64(i)).value;
+ }
+
+ res.value = LLVMConstVector(scalars, cast(unsigned)count);
+ return res;
+ }
+ }
+
lbValue arg0 = {}; if (ce->args.count > 0) arg0 = lb_build_expr(p, ce->args[0]);
lbValue arg1 = {}; if (ce->args.count > 1) arg1 = lb_build_expr(p, ce->args[1]);
lbValue arg2 = {}; if (ce->args.count > 2) arg2 = lb_build_expr(p, ce->args[2]);
@@ -1442,7 +1474,7 @@ gb_internal lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAn
LLVMRealPredicate pred = cast(LLVMRealPredicate)0;
switch (builtin_id) {
case BuiltinProc_simd_lanes_eq: pred = LLVMRealOEQ; break;
- case BuiltinProc_simd_lanes_ne: pred = LLVMRealONE; break;
+ case BuiltinProc_simd_lanes_ne: pred = LLVMRealUNE; break;
case BuiltinProc_simd_lanes_lt: pred = LLVMRealOLT; break;
case BuiltinProc_simd_lanes_le: pred = LLVMRealOLE; break;
case BuiltinProc_simd_lanes_gt: pred = LLVMRealOGT; break;
@@ -1478,6 +1510,38 @@ gb_internal lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAn
res.value = LLVMBuildInsertElement(p->builder, arg0.value, arg2.value, arg1.value, "");
return res;
+ case BuiltinProc_simd_reduce_add_bisect:
+ case BuiltinProc_simd_reduce_mul_bisect:
+ {
+ GB_ASSERT(arg0.type->kind == Type_SimdVector);
+ i64 num_elems = arg0.type->SimdVector.count;
+
+ LLVMValueRef *indices = gb_alloc_array(temporary_allocator(), LLVMValueRef, num_elems);
+ for (i64 i = 0; i < num_elems; i++) {
+ indices[i] = lb_const_int(m, t_uint, cast(u64)i).value;
+ }
+
+ switch (builtin_id) {
+ case BuiltinProc_simd_reduce_add_bisect: op_code = is_float ? LLVMFAdd : LLVMAdd; break;
+ case BuiltinProc_simd_reduce_mul_bisect: op_code = is_float ? LLVMFMul : LLVMMul; break;
+ }
+
+ LLVMValueRef remaining = arg0.value;
+ i64 num_remaining = num_elems;
+
+ while (num_remaining > 1) {
+ num_remaining /= 2;
+ LLVMValueRef left_indices = LLVMConstVector(&indices[0], cast(unsigned)num_remaining);
+ LLVMValueRef left_value = LLVMBuildShuffleVector(p->builder, remaining, remaining, left_indices, "");
+ LLVMValueRef right_indices = LLVMConstVector(&indices[num_remaining], cast(unsigned)num_remaining);
+ LLVMValueRef right_value = LLVMBuildShuffleVector(p->builder, remaining, remaining, right_indices, "");
+ remaining = LLVMBuildBinOp(p->builder, op_code, left_value, right_value, "");
+ }
+
+ res.value = LLVMBuildExtractElement(p->builder, remaining, indices[0], "");
+ return res;
+ }
+
case BuiltinProc_simd_reduce_add_ordered:
case BuiltinProc_simd_reduce_mul_ordered:
{
@@ -1510,6 +1574,40 @@ gb_internal lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAn
res.value = lb_call_intrinsic(p, name, args, cast(unsigned)args_count, types, gb_count_of(types));
return res;
}
+
+ case BuiltinProc_simd_reduce_add_pairs:
+ case BuiltinProc_simd_reduce_mul_pairs:
+ {
+ GB_ASSERT(arg0.type->kind == Type_SimdVector);
+ i64 num_elems = arg0.type->SimdVector.count;
+
+ LLVMValueRef *indices = gb_alloc_array(temporary_allocator(), LLVMValueRef, num_elems);
+ for (i64 i = 0; i < num_elems/2; i++) {
+ indices[i] = lb_const_int(m, t_uint, cast(u64)(2*i)).value;
+ indices[i+num_elems/2] = lb_const_int(m, t_uint, cast(u64)(2*i+1)).value;
+ }
+
+ switch (builtin_id) {
+ case BuiltinProc_simd_reduce_add_pairs: op_code = is_float ? LLVMFAdd : LLVMAdd; break;
+ case BuiltinProc_simd_reduce_mul_pairs: op_code = is_float ? LLVMFMul : LLVMMul; break;
+ }
+
+ LLVMValueRef remaining = arg0.value;
+ i64 num_remaining = num_elems;
+
+ while (num_remaining > 1) {
+ num_remaining /= 2;
+ LLVMValueRef left_indices = LLVMConstVector(&indices[0], cast(unsigned)num_remaining);
+ LLVMValueRef left_value = LLVMBuildShuffleVector(p->builder, remaining, remaining, left_indices, "");
+ LLVMValueRef right_indices = LLVMConstVector(&indices[num_elems/2], cast(unsigned)num_remaining);
+ LLVMValueRef right_value = LLVMBuildShuffleVector(p->builder, remaining, remaining, right_indices, "");
+ remaining = LLVMBuildBinOp(p->builder, op_code, left_value, right_value, "");
+ }
+
+ res.value = LLVMBuildExtractElement(p->builder, remaining, indices[0], "");
+ return res;
+ }
+
case BuiltinProc_simd_reduce_min:
case BuiltinProc_simd_reduce_max:
case BuiltinProc_simd_reduce_and:
@@ -1625,6 +1723,275 @@ gb_internal lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAn
return res;
}
+ case BuiltinProc_simd_runtime_swizzle:
+ {
+ LLVMValueRef src = arg0.value;
+ LLVMValueRef indices = lb_build_expr(p, ce->args[1]).value;
+
+ Type *vt = arg0.type;
+ GB_ASSERT(vt->kind == Type_SimdVector);
+ i64 count = vt->SimdVector.count;
+ Type *elem_type = vt->SimdVector.elem;
+ i64 elem_size = type_size_of(elem_type);
+
+ // Determine strategy based on element size and target architecture
+ char const *intrinsic_name = nullptr;
+ bool use_hardware_runtime_swizzle = false;
+
+ // 8-bit elements: Use dedicated table lookup instructions
+ if (elem_size == 1) {
+ use_hardware_runtime_swizzle = true;
+
+ if (build_context.metrics.arch == TargetArch_amd64 || build_context.metrics.arch == TargetArch_i386) {
+ // x86/x86-64: Use pshufb intrinsics
+ switch (count) {
+ case 16:
+ intrinsic_name = "llvm.x86.ssse3.pshuf.b.128";
+ break;
+ case 32:
+ intrinsic_name = "llvm.x86.avx2.pshuf.b";
+ break;
+ case 64:
+ intrinsic_name = "llvm.x86.avx512.pshuf.b.512";
+ break;
+ default:
+ use_hardware_runtime_swizzle = false;
+ break;
+ }
+ } else if (build_context.metrics.arch == TargetArch_arm64) {
+ // ARM64: Use NEON tbl intrinsics with automatic table splitting
+ switch (count) {
+ case 16:
+ intrinsic_name = "llvm.aarch64.neon.tbl1";
+ break;
+ case 32:
+ intrinsic_name = "llvm.aarch64.neon.tbl2";
+ break;
+ case 48:
+ intrinsic_name = "llvm.aarch64.neon.tbl3";
+ break;
+ case 64:
+ intrinsic_name = "llvm.aarch64.neon.tbl4";
+ break;
+ default:
+ use_hardware_runtime_swizzle = false;
+ break;
+ }
+ } else if (build_context.metrics.arch == TargetArch_arm32) {
+ // ARM32: Use NEON vtbl intrinsics with automatic table splitting
+ switch (count) {
+ case 8:
+ intrinsic_name = "llvm.arm.neon.vtbl1";
+ break;
+ case 16:
+ intrinsic_name = "llvm.arm.neon.vtbl2";
+ break;
+ case 24:
+ intrinsic_name = "llvm.arm.neon.vtbl3";
+ break;
+ case 32:
+ intrinsic_name = "llvm.arm.neon.vtbl4";
+ break;
+ default:
+ use_hardware_runtime_swizzle = false;
+ break;
+ }
+ } else if (build_context.metrics.arch == TargetArch_wasm32 || build_context.metrics.arch == TargetArch_wasm64p32) {
+ // WebAssembly: Use swizzle (only supports 16-byte vectors)
+ if (count == 16) {
+ intrinsic_name = "llvm.wasm.swizzle";
+ } else {
+ use_hardware_runtime_swizzle = false;
+ }
+ } else {
+ use_hardware_runtime_swizzle = false;
+ }
+ }
+
+ if (use_hardware_runtime_swizzle && intrinsic_name != nullptr) {
+ // Use dedicated hardware swizzle instruction
+
+ // Check if required target features are enabled
+ bool features_enabled = true;
+ if (build_context.metrics.arch == TargetArch_amd64 || build_context.metrics.arch == TargetArch_i386) {
+ // x86/x86-64 feature checking
+ if (count == 16) {
+ // SSE/SSSE3 for 128-bit vectors
+ if (!check_target_feature_is_enabled(str_lit("ssse3"), nullptr)) {
+ features_enabled = false;
+ }
+ } else if (count == 32) {
+ // AVX2 requires ssse3 + avx2 features
+ if (!check_target_feature_is_enabled(str_lit("ssse3"), nullptr) ||
+ !check_target_feature_is_enabled(str_lit("avx2"), nullptr)) {
+ features_enabled = false;
+ }
+ } else if (count == 64) {
+ // AVX512 requires ssse3 + avx2 + avx512f + avx512bw features
+ if (!check_target_feature_is_enabled(str_lit("ssse3"), nullptr) ||
+ !check_target_feature_is_enabled(str_lit("avx2"), nullptr) ||
+ !check_target_feature_is_enabled(str_lit("avx512f"), nullptr) ||
+ !check_target_feature_is_enabled(str_lit("avx512bw"), nullptr)) {
+ features_enabled = false;
+ }
+ }
+ } else if (build_context.metrics.arch == TargetArch_arm64 || build_context.metrics.arch == TargetArch_arm32) {
+ // ARM/ARM64 feature checking - NEON is required for all table/swizzle ops
+ if (!check_target_feature_is_enabled(str_lit("neon"), nullptr)) {
+ features_enabled = false;
+ }
+ }
+
+ if (features_enabled) {
+ // Add target features to function attributes for LLVM instruction selection
+ if (build_context.metrics.arch == TargetArch_amd64 || build_context.metrics.arch == TargetArch_i386) {
+ // x86/x86-64 function attributes
+ if (count == 16) {
+ // SSE/SSSE3 for 128-bit vectors
+ lb_add_attribute_to_proc_with_string(p->module, p->value, str_lit("target-features"), str_lit("+ssse3"));
+ lb_add_attribute_to_proc_with_string(p->module, p->value, str_lit("min-legal-vector-width"), str_lit("128"));
+ } else if (count == 32) {
+ lb_add_attribute_to_proc_with_string(p->module, p->value, str_lit("target-features"), str_lit("+avx,+avx2,+ssse3"));
+ lb_add_attribute_to_proc_with_string(p->module, p->value, str_lit("min-legal-vector-width"), str_lit("256"));
+ } else if (count == 64) {
+ lb_add_attribute_to_proc_with_string(p->module, p->value, str_lit("target-features"), str_lit("+avx,+avx2,+avx512f,+avx512bw,+ssse3"));
+ lb_add_attribute_to_proc_with_string(p->module, p->value, str_lit("min-legal-vector-width"), str_lit("512"));
+ }
+ } else if (build_context.metrics.arch == TargetArch_arm64) {
+ // ARM64 function attributes - enable NEON for swizzle instructions
+ lb_add_attribute_to_proc_with_string(p->module, p->value, str_lit("target-features"), str_lit("+neon"));
+ // Set appropriate vector width for multi-swizzle operations
+ if (count >= 32) {
+ lb_add_attribute_to_proc_with_string(p->module, p->value, str_lit("min-legal-vector-width"), str_lit("256"));
+ }
+ } else if (build_context.metrics.arch == TargetArch_arm32) {
+ // ARM32 function attributes - enable NEON for swizzle instructions
+ lb_add_attribute_to_proc_with_string(p->module, p->value, str_lit("target-features"), str_lit("+neon"));
+ }
+
+ // Handle ARM's multi-swizzle intrinsics by splitting the src vector
+ if (build_context.metrics.arch == TargetArch_arm64 && count > 16) {
+ // ARM64 TBL2/TBL3/TBL4: Split src into multiple 16-byte vectors
+ int num_tables = cast(int)(count / 16);
+ GB_ASSERT_MSG(count % 16 == 0, "ARM64 src size must be multiple of 16 bytes, got %lld bytes", count);
+ GB_ASSERT_MSG(num_tables <= 4, "ARM64 NEON supports maximum 4 tables (tbl4), got %d tables for %lld-byte vector", num_tables, count);
+
+ LLVMValueRef src_parts[4]; // Max 4 tables for tbl4
+ for (int i = 0; i < num_tables; i++) {
+ // Extract 16-byte slice from the larger src
+ LLVMValueRef indices_for_extract[16];
+ for (int j = 0; j < 16; j++) {
+ indices_for_extract[j] = LLVMConstInt(LLVMInt32TypeInContext(p->module->ctx), i * 16 + j, false);
+ }
+ LLVMValueRef extract_mask = LLVMConstVector(indices_for_extract, 16);
+ src_parts[i] = LLVMBuildShuffleVector(p->builder, src, LLVMGetUndef(LLVMTypeOf(src)), extract_mask, "");
+ }
+
+ // Call appropriate ARM64 tbl intrinsic
+ if (count == 32) {
+ LLVMValueRef args[3] = { src_parts[0], src_parts[1], indices };
+ res.value = lb_call_intrinsic(p, intrinsic_name, args, 3, nullptr, 0);
+ } else if (count == 48) {
+ LLVMValueRef args[4] = { src_parts[0], src_parts[1], src_parts[2], indices };
+ res.value = lb_call_intrinsic(p, intrinsic_name, args, 4, nullptr, 0);
+ } else if (count == 64) {
+ LLVMValueRef args[5] = { src_parts[0], src_parts[1], src_parts[2], src_parts[3], indices };
+ res.value = lb_call_intrinsic(p, intrinsic_name, args, 5, nullptr, 0);
+ }
+ } else if (build_context.metrics.arch == TargetArch_arm32 && count > 8) {
+ // ARM32 VTBL2/VTBL3/VTBL4: Split src into multiple 8-byte vectors
+ int num_tables = cast(int)count / 8;
+ GB_ASSERT_MSG(count % 8 == 0, "ARM32 src size must be multiple of 8 bytes, got %lld bytes", count);
+ GB_ASSERT_MSG(num_tables <= 4, "ARM32 NEON supports maximum 4 tables (vtbl4), got %d tables for %lld-byte vector", num_tables, count);
+
+ LLVMValueRef src_parts[4]; // Max 4 tables for vtbl4
+ for (int i = 0; i < num_tables; i++) {
+ // Extract 8-byte slice from the larger src
+ LLVMValueRef indices_for_extract[8];
+ for (int j = 0; j < 8; j++) {
+ indices_for_extract[j] = LLVMConstInt(LLVMInt32TypeInContext(p->module->ctx), i * 8 + j, false);
+ }
+ LLVMValueRef extract_mask = LLVMConstVector(indices_for_extract, 8);
+ src_parts[i] = LLVMBuildShuffleVector(p->builder, src, LLVMGetUndef(LLVMTypeOf(src)), extract_mask, "");
+ }
+
+ // Call appropriate ARM32 vtbl intrinsic
+ if (count == 16) {
+ LLVMValueRef args[3] = { src_parts[0], src_parts[1], indices };
+ res.value = lb_call_intrinsic(p, intrinsic_name, args, 3, nullptr, 0);
+ } else if (count == 24) {
+ LLVMValueRef args[4] = { src_parts[0], src_parts[1], src_parts[2], indices };
+ res.value = lb_call_intrinsic(p, intrinsic_name, args, 4, nullptr, 0);
+ } else if (count == 32) {
+ LLVMValueRef args[5] = { src_parts[0], src_parts[1], src_parts[2], src_parts[3], indices };
+ res.value = lb_call_intrinsic(p, intrinsic_name, args, 5, nullptr, 0);
+ }
+ } else {
+ // Single runtime swizzle case (x86, WebAssembly, ARM single-table)
+ LLVMValueRef args[2] = { src, indices };
+ res.value = lb_call_intrinsic(p, intrinsic_name, args, gb_count_of(args), nullptr, 0);
+ }
+ return res;
+ } else {
+ // Features not enabled, fall back to emulation
+ use_hardware_runtime_swizzle = false;
+ }
+ }
+
+ // Fallback: Emulate with extracts and inserts for all element sizes
+ GB_ASSERT(count > 0 && count <= 64); // Sanity check
+
+ LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, count);
+ LLVMTypeRef i32_type = LLVMInt32TypeInContext(p->module->ctx);
+ LLVMTypeRef elem_llvm_type = lb_type(p->module, elem_type);
+
+ // Calculate mask based on element size and vector count
+ i64 max_index = count - 1;
+ LLVMValueRef index_mask;
+
+ if (elem_size == 1) {
+ // 8-bit: mask to src size (like pshufb behavior)
+ index_mask = LLVMConstInt(elem_llvm_type, max_index, false);
+ } else if (elem_size == 2) {
+ // 16-bit: mask to src size
+ index_mask = LLVMConstInt(elem_llvm_type, max_index, false);
+ } else if (elem_size == 4) {
+ // 32-bit: mask to src size
+ index_mask = LLVMConstInt(elem_llvm_type, max_index, false);
+ } else {
+ // 64-bit: mask to src size
+ index_mask = LLVMConstInt(elem_llvm_type, max_index, false);
+ }
+
+ for (i64 i = 0; i < count; i++) {
+ LLVMValueRef idx_i = LLVMConstInt(i32_type, cast(unsigned)i, false);
+ LLVMValueRef index_elem = LLVMBuildExtractElement(p->builder, indices, idx_i, "");
+
+ // Mask index to valid range
+ LLVMValueRef masked_index = LLVMBuildAnd(p->builder, index_elem, index_mask, "");
+
+ // Convert to i32 for extractelement
+ LLVMValueRef index_i32;
+ if (LLVMGetIntTypeWidth(LLVMTypeOf(masked_index)) < 32) {
+ index_i32 = LLVMBuildZExt(p->builder, masked_index, i32_type, "");
+ } else if (LLVMGetIntTypeWidth(LLVMTypeOf(masked_index)) > 32) {
+ index_i32 = LLVMBuildTrunc(p->builder, masked_index, i32_type, "");
+ } else {
+ index_i32 = masked_index;
+ }
+
+ values[i] = LLVMBuildExtractElement(p->builder, src, index_i32, "");
+ }
+
+ // Build result vector
+ res.value = LLVMGetUndef(LLVMTypeOf(src));
+ for (i64 i = 0; i < count; i++) {
+ LLVMValueRef idx_i = LLVMConstInt(i32_type, cast(unsigned)i, false);
+ res.value = LLVMBuildInsertElement(p->builder, res.value, values[i], idx_i, "");
+ }
+ return res;
+ }
+
case BuiltinProc_simd_ceil:
case BuiltinProc_simd_floor:
case BuiltinProc_simd_trunc:
@@ -1844,7 +2211,7 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
GB_ASSERT(e != nullptr);
if (e->parent_proc_decl != nullptr && e->parent_proc_decl->entity != nullptr) {
- procedure = e->parent_proc_decl->entity->token.string;
+ procedure = e->parent_proc_decl->entity.load()->token.string;
} else {
procedure = str_lit("");
}
@@ -1866,12 +2233,12 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
LLVMValueRef values[2] = {};
values[0] = lb_const_string(m, file_name).value;
- values[1] = lb_const_string(m, file->data).value;
+ values[1] = lb_const_value(m, t_u8_slice, exact_value_string(file->data)).value;
LLVMValueRef element = llvm_const_named_struct(m, t_load_directory_file, values, gb_count_of(values));
elements[i] = element;
}
- LLVMValueRef backing_array = llvm_const_array(lb_type(m, t_load_directory_file), elements, count);
+ LLVMValueRef backing_array = llvm_const_array(m, lb_type(m, t_load_directory_file), elements, count);
Type *array_type = alloc_type_array(t_load_directory_file, count);
lbAddr backing_array_addr = lb_add_global_generated_from_procedure(p, array_type, {backing_array, array_type});
@@ -1922,6 +2289,10 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
}
if (is_type_cstring(t)) {
return lb_cstring_len(p, v);
+ } else if (is_type_cstring16(t)) {
+ return lb_cstring16_len(p, v);
+ } else if (is_type_string16(t)) {
+ return lb_string_len(p, v);
} else if (is_type_string(t)) {
return lb_string_len(p, v);
} else if (is_type_array(t)) {
@@ -2150,6 +2521,68 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
return lb_emit_load(p, tuple);
}
+ case BuiltinProc_compress_values: {
+ isize value_count = 0;
+ for (Ast *arg : ce->args) {
+ Type *t = arg->tav.type;
+ if (is_type_tuple(t)) {
+ value_count += t->Tuple.variables.count;
+ } else {
+ value_count += 1;
+ }
+ }
+
+ if (value_count == 1) {
+ lbValue x = lb_build_expr(p, ce->args[0]);
+ x = lb_emit_conv(p, x, tv.type);
+ return x;
+ }
+
+ Type *dt = base_type(tv.type);
+ lbAddr addr = lb_add_local_generated(p, tv.type, true);
+ if (is_type_struct(dt) || is_type_tuple(dt)) {
+ i32 index = 0;
+ for (Ast *arg : ce->args) {
+ lbValue x = lb_build_expr(p, arg);
+ if (is_type_tuple(x.type)) {
+ for (isize i = 0; i < x.type->Tuple.variables.count; i++) {
+ lbValue y = lb_emit_tuple_ev(p, x, cast(i32)i);
+ lbValue ptr = lb_emit_struct_ep(p, addr.addr, index++);
+ y = lb_emit_conv(p, y, type_deref(ptr.type));
+ lb_emit_store(p, ptr, y);
+ }
+ } else {
+ lbValue ptr = lb_emit_struct_ep(p, addr.addr, index++);
+ x = lb_emit_conv(p, x, type_deref(ptr.type));
+ lb_emit_store(p, ptr, x);
+ }
+ }
+ GB_ASSERT(index == value_count);
+ } else if (is_type_array_like(dt)) {
+ i32 index = 0;
+ for (Ast *arg : ce->args) {
+ lbValue x = lb_build_expr(p, arg);
+ if (is_type_tuple(x.type)) {
+ for (isize i = 0; i < x.type->Tuple.variables.count; i++) {
+ lbValue y = lb_emit_tuple_ev(p, x, cast(i32)i);
+ lbValue ptr = lb_emit_array_epi(p, addr.addr, index++);
+ y = lb_emit_conv(p, y, type_deref(ptr.type));
+ lb_emit_store(p, ptr, y);
+ }
+ } else {
+ lbValue ptr = lb_emit_array_epi(p, addr.addr, index++);
+ x = lb_emit_conv(p, x, type_deref(ptr.type));
+ lb_emit_store(p, ptr, x);
+ }
+ }
+ GB_ASSERT(index == value_count);
+ } else {
+ GB_PANIC("TODO(bill): compress_values -> %s", type_to_string(tv.type));
+ }
+
+ return lb_addr_load(p, addr);
+ }
+
case BuiltinProc_min: {
Type *t = type_of_expr(expr);
if (ce->args.count == 2) {
@@ -2299,6 +2732,11 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
res = lb_emit_conv(p, res, tv.type);
} else if (t->Basic.kind == Basic_cstring) {
res = lb_emit_conv(p, x, tv.type);
+ } else if (t->Basic.kind == Basic_string16) {
+ res = lb_string_elem(p, x);
+ res = lb_emit_conv(p, res, tv.type);
+ } else if (t->Basic.kind == Basic_cstring16) {
+ res = lb_emit_conv(p, x, tv.type);
}
break;
case Type_Pointer:
@@ -2382,6 +2820,21 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
}
return res;
}
+ case BuiltinProc_read_cycle_counter_frequency:
+ {
+ lbValue res = {};
+ res.type = tv.type;
+
+ if (build_context.metrics.arch == TargetArch_arm64) {
+ LLVMTypeRef func_type = LLVMFunctionType(LLVMInt64TypeInContext(p->module->ctx), nullptr, 0, false);
+ bool has_side_effects = false;
+ LLVMValueRef the_asm = llvm_get_inline_asm(func_type, str_lit("mrs $0, cntfrq_el0"), str_lit("=r"), has_side_effects);
+ GB_ASSERT(the_asm != nullptr);
+ res.value = LLVMBuildCall2(p->builder, func_type, the_asm, nullptr, 0, "");
+ }
+
+ return res;
+ }
case BuiltinProc_count_trailing_zeros:
return lb_emit_count_trailing_zeros(p, lb_build_expr(p, ce->args[0]), tv.type);
@@ -2616,15 +3069,18 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
LLVMSetMetadata(instr, kind_id, LLVMMetadataAsValue(p->module->ctx, node));
}
break;
- case BuiltinProc_volatile_store: LLVMSetVolatile(instr, true); break;
- case BuiltinProc_atomic_store: LLVMSetOrdering(instr, LLVMAtomicOrderingSequentiallyConsistent); break;
+ case BuiltinProc_volatile_store:
+ LLVMSetVolatile(instr, true);
+ break;
+ case BuiltinProc_atomic_store:
+ LLVMSetOrdering(instr, LLVMAtomicOrderingSequentiallyConsistent);
+ LLVMSetVolatile(instr, true);
+ break;
case BuiltinProc_atomic_store_explicit:
{
auto ordering = llvm_atomic_ordering_from_odin(ce->args[2]);
LLVMSetOrdering(instr, ordering);
- if (ordering == LLVMAtomicOrderingUnordered) {
- LLVMSetVolatile(instr, true);
- }
+ LLVMSetVolatile(instr, true);
}
break;
}
@@ -2650,15 +3106,18 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
}
break;
break;
- case BuiltinProc_volatile_load: LLVMSetVolatile(instr, true); break;
- case BuiltinProc_atomic_load: LLVMSetOrdering(instr, LLVMAtomicOrderingSequentiallyConsistent); break;
+ case BuiltinProc_volatile_load:
+ LLVMSetVolatile(instr, true);
+ break;
+ case BuiltinProc_atomic_load:
+ LLVMSetOrdering(instr, LLVMAtomicOrderingSequentiallyConsistent);
+ LLVMSetVolatile(instr, true);
+ break;
case BuiltinProc_atomic_load_explicit:
{
auto ordering = llvm_atomic_ordering_from_odin(ce->args[1]);
LLVMSetOrdering(instr, ordering);
- if (ordering == LLVMAtomicOrderingUnordered) {
- LLVMSetVolatile(instr, true);
- }
+ LLVMSetVolatile(instr, true);
}
break;
}
@@ -2743,9 +3202,7 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
lbValue res = {};
res.value = LLVMBuildAtomicRMW(p->builder, op, dst.value, val.value, ordering, false);
res.type = tv.type;
- if (ordering == LLVMAtomicOrderingUnordered) {
- LLVMSetVolatile(res.value, true);
- }
+ LLVMSetVolatile(res.value, true);
return res;
}
@@ -2781,9 +3238,7 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
single_threaded
);
LLVMSetWeak(value, weak);
- if (success_ordering == LLVMAtomicOrderingUnordered || failure_ordering == LLVMAtomicOrderingUnordered) {
- LLVMSetVolatile(value, true);
- }
+ LLVMSetVolatile(value, true);
if (is_type_tuple(tv.type)) {
Type *fix_typed = alloc_type_tuple();
@@ -2846,16 +3301,22 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
}
GB_ASSERT(name != nullptr);
- LLVMTypeRef types[1] = {lb_type(p->module, platform_type)};
lbValue res = {};
+ res.type = platform_type;
- LLVMValueRef args[3] = {
+ if (id == BuiltinProc_fixed_point_div ||
+ id == BuiltinProc_fixed_point_div_sat) {
+ res.value = lb_integer_division_intrinsics(p, x.value, y.value, scale.value, platform_type, name);
+ } else {
+ LLVMTypeRef types[1] = {lb_type(p->module, platform_type)};
+
+ LLVMValueRef args[3] = {
x.value,
y.value,
scale.value };
- res.value = lb_call_intrinsic(p, name, args, gb_count_of(args), types, gb_count_of(types));
- res.type = platform_type;
+ res.value = lb_call_intrinsic(p, name, args, gb_count_of(args), types, gb_count_of(types));
+ }
return lb_emit_conv(p, res, tv.type);
}
@@ -2972,6 +3433,8 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
constraints = gb_string_appendc(constraints, "}");
}
+ constraints = gb_string_appendc(constraints, ",~{memory}");
+
inline_asm = llvm_get_inline_asm(func_type, make_string_c(asm_string), make_string_c(constraints));
}
break;
@@ -3034,6 +3497,8 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
constraints = gb_string_appendc(constraints, "}");
}
+ constraints = gb_string_appendc(constraints, ",~{memory}");
+
inline_asm = llvm_get_inline_asm(func_type, make_string_c(asm_string), make_string_c(constraints));
}
break;
@@ -3059,6 +3524,8 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
constraints = gb_string_appendc(constraints, "}");
}
+ constraints = gb_string_appendc(constraints, ",~{memory}");
+
inline_asm = llvm_get_inline_asm(func_type, make_string_c(asm_string), make_string_c(constraints));
} else {
char asm_string[] = "svc #0";
@@ -3078,6 +3545,8 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
constraints = gb_string_appendc(constraints, "}");
}
+ constraints = gb_string_appendc(constraints, ",~{memory}");
+
inline_asm = llvm_get_inline_asm(func_type, make_string_c(asm_string), make_string_c(constraints));
}
}
@@ -3104,6 +3573,8 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
constraints = gb_string_appendc(constraints, "}");
}
+ constraints = gb_string_appendc(constraints, ",~{memory}");
+
inline_asm = llvm_get_inline_asm(func_type, make_string_c(asm_string), make_string_c(constraints));
}
break;
@@ -3216,11 +3687,28 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
{
GB_ASSERT(arg_count <= 7);
- char asm_string[] = "svc #0; cset x8, cc";
- gbString constraints = gb_string_make(heap_allocator(), "={x0},={x8}");
- for (unsigned i = 0; i < arg_count; i++) {
- constraints = gb_string_appendc(constraints, ",{");
- static char const *regs[] = {
+ char const *asm_string;
+ char const **regs;
+ gbString constraints;
+
+ if (build_context.metrics.os == TargetOs_netbsd) {
+ asm_string = "svc #0; cset x17, cc";
+ constraints = gb_string_make(heap_allocator(), "={x0},={x17}");
+ static char const *_regs[] = {
+ "x17",
+ "x0",
+ "x1",
+ "x2",
+ "x3",
+ "x4",
+ "x5",
+ };
+ regs = _regs;
+ } else {
+ // FreeBSD (tested), OpenBSD (untested).
+ asm_string = "svc #0; cset x8, cc";
+ constraints = gb_string_make(heap_allocator(), "={x0},={x8}");
+ static char const *_regs[] = {
"x8",
"x0",
"x1",
@@ -3229,13 +3717,19 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
"x4",
"x5",
};
+ regs = _regs;
+
+ // FreeBSD clobbered x1 on a call to sysctl.
+ constraints = gb_string_appendc(constraints, ",~{x1}");
+ }
+
+ for (unsigned i = 0; i < arg_count; i++) {
+ constraints = gb_string_appendc(constraints, ",{");
constraints = gb_string_appendc(constraints, regs[i]);
constraints = gb_string_appendc(constraints, "}");
}
- // FreeBSD clobbered x1 on a call to sysctl.
- constraints = gb_string_appendc(constraints, ",~{x1},~{cc},~{memory}");
-
+ constraints = gb_string_appendc(constraints, ",~{cc},~{memory}");
inline_asm = llvm_get_inline_asm(func_type, make_string_c(asm_string), make_string_c(constraints));
}
break;
@@ -3257,6 +3751,9 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
case BuiltinProc_objc_find_class: return lb_handle_objc_find_class(p, expr);
case BuiltinProc_objc_register_selector: return lb_handle_objc_register_selector(p, expr);
case BuiltinProc_objc_register_class: return lb_handle_objc_register_class(p, expr);
+ case BuiltinProc_objc_ivar_get: return lb_handle_objc_ivar_get(p, expr);
+ case BuiltinProc_objc_block: return lb_handle_objc_block(p, expr);
+ case BuiltinProc_objc_super: return lb_handle_objc_super(p, expr);
case BuiltinProc_constant_utf16_cstring:
@@ -3621,25 +4118,28 @@ gb_internal lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr) {
GB_ASSERT(ce->args.count == 1);
lbValue x = lb_build_expr(p, ce->args[0]);
lbValue y = lb_emit_conv(p, x, tv.type);
+ y.type = tv.type;
return y;
}
Ast *proc_expr = unparen_expr(ce->proc);
+ Entity *proc_entity = entity_of_node(proc_expr);
+
if (proc_mode == Addressing_Builtin) {
- Entity *e = entity_of_node(proc_expr);
BuiltinProcId id = BuiltinProc_Invalid;
- if (e != nullptr) {
- id = cast(BuiltinProcId)e->Builtin.id;
+ if (proc_entity != nullptr) {
+ id = cast(BuiltinProcId)proc_entity->Builtin.id;
} else {
id = BuiltinProc_DIRECTIVE;
}
return lb_build_builtin_proc(p, expr, tv, id);
}
+ bool is_objc_call = proc_entity && proc_entity->Procedure.is_objc_impl_or_import;
+
// NOTE(bill): Regular call
lbValue value = {};
- Entity *proc_entity = entity_of_node(proc_expr);
if (proc_entity != nullptr) {
if (proc_entity->flags & EntityFlag_Disabled) {
GB_ASSERT(tv.type == nullptr);
@@ -3673,11 +4173,13 @@ gb_internal lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr) {
}
}
- if (value.value == nullptr) {
+ if (is_objc_call) {
+ value.type = proc_tv.type;
+ } else if (value.value == nullptr) {
value = lb_build_expr(p, proc_expr);
}
- GB_ASSERT(value.value != nullptr);
+ GB_ASSERT(value.value != nullptr || is_objc_call);
Type *proc_type_ = base_type(value.type);
GB_ASSERT(proc_type_->kind == Type_Proc);
TypeProc *pt = &proc_type_->Proc;
@@ -3905,6 +4407,11 @@ gb_internal lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr) {
isize final_count = is_c_vararg ? args.count : pt->param_count;
auto call_args = array_slice(args, 0, final_count);
+
+ if (is_objc_call) {
+ return lb_handle_objc_auto_send(p, expr, slice(call_args, 0, call_args.count));
+ }
+
return lb_emit_call(p, value, call_args, ce->inlining);
}
diff --git a/src/llvm_backend_stmt.cpp b/src/llvm_backend_stmt.cpp
index 758cd353f..f247fa2a7 100644
--- a/src/llvm_backend_stmt.cpp
+++ b/src/llvm_backend_stmt.cpp
@@ -3,8 +3,6 @@ gb_internal void lb_build_constant_value_decl(lbProcedure *p, AstValueDecl *vd)
return;
}
- auto *min_dep_set = &p->module->info->minimum_dependency_set;
-
for (Ast *ident : vd->names) {
GB_ASSERT(ident->kind == Ast_Ident);
Entity *e = entity_of_node(ident);
@@ -21,7 +19,7 @@ gb_internal void lb_build_constant_value_decl(lbProcedure *p, AstValueDecl *vd)
}
}
- if (!polymorphic_struct && !ptr_set_exists(min_dep_set, e)) {
+ if (!polymorphic_struct && e->min_dep_count.load(std::memory_order_relaxed) == 0) {
continue;
}
@@ -56,7 +54,7 @@ gb_internal void lb_build_constant_value_decl(lbProcedure *p, AstValueDecl *vd)
if (gpd) {
rw_mutex_shared_lock(&gpd->mutex);
for (Entity *e : gpd->procs) {
- if (!ptr_set_exists(min_dep_set, e)) {
+ if (e->min_dep_count.load(std::memory_order_relaxed) == 0) {
continue;
}
DeclInfo *d = decl_info_of_entity(e);
@@ -94,7 +92,7 @@ gb_internal void lb_build_constant_value_decl(lbProcedure *p, AstValueDecl *vd)
value.value = nested_proc->value;
value.type = nested_proc->type;
- array_add(&p->module->procedures_to_generate, nested_proc);
+ mpsc_enqueue(&p->module->procedures_to_generate, nested_proc);
array_add(&p->children, nested_proc);
string_map_set(&p->module->members, name, value);
}
@@ -136,7 +134,6 @@ gb_internal lbBranchBlocks lb_lookup_branch_blocks(lbProcedure *p, Ast *ident) {
return empty;
}
-
gb_internal lbTargetList *lb_push_target_list(lbProcedure *p, Ast *label, lbBlock *break_, lbBlock *continue_, lbBlock *fallthrough_) {
lbTargetList *tl = gb_alloc_item(permanent_allocator(), lbTargetList);
tl->prev = p->target_list;
@@ -623,6 +620,121 @@ gb_internal void lb_build_range_string(lbProcedure *p, lbValue expr, Type *val_t
if (done_) *done_ = done;
}
+gb_internal void lb_build_range_string16(lbProcedure *p, lbValue expr, Type *val_type,
+ lbValue *val_, lbValue *idx_, lbBlock **loop_, lbBlock **done_,
+ bool is_reverse) {
+
+ lbModule *m = p->module;
+ lbValue count = lb_const_int(m, t_int, 0);
+ Type *expr_type = base_type(expr.type);
+ switch (expr_type->kind) {
+ case Type_Basic:
+ count = lb_string_len(p, expr);
+ break;
+ default:
+ GB_PANIC("Cannot do range_string of %s", type_to_string(expr_type));
+ break;
+ }
+
+ lbValue val = {};
+ lbValue idx = {};
+ lbBlock *loop = nullptr;
+ lbBlock *done = nullptr;
+ lbBlock *body = nullptr;
+
+ loop = lb_create_block(p, "for.string16.loop");
+ body = lb_create_block(p, "for.string16.body");
+ done = lb_create_block(p, "for.string16.done");
+
+ lbAddr offset_ = lb_add_local_generated(p, t_int, false);
+ lbValue offset = {};
+ lbValue cond = {};
+
+ if (!is_reverse) {
+ /*
+ for c, offset in str {
+ ...
+ }
+
+ offset := 0
+ for offset < len(str) {
+ c, _w := string16_decode_rune(str[offset:])
+ ...
+ offset += _w
+ }
+ */
+ lb_addr_store(p, offset_, lb_const_int(m, t_int, 0));
+
+ lb_emit_jump(p, loop);
+ lb_start_block(p, loop);
+
+
+ offset = lb_addr_load(p, offset_);
+ cond = lb_emit_comp(p, Token_Lt, offset, count);
+ } else {
+ // NOTE(bill): REVERSED LOGIC
+ /*
+ #reverse for c, offset in str {
+ ...
+ }
+
+ offset := len(str)
+ for offset > 0 {
+ c, _w := string16_decode_last_rune(str[:offset])
+ offset -= _w
+ ...
+ }
+ */
+ lb_addr_store(p, offset_, count);
+
+ lb_emit_jump(p, loop);
+ lb_start_block(p, loop);
+
+ offset = lb_addr_load(p, offset_);
+ cond = lb_emit_comp(p, Token_Gt, offset, lb_const_int(m, t_int, 0));
+ }
+ lb_emit_if(p, cond, body, done);
+ lb_start_block(p, body);
+
+
+ lbValue rune_and_len = {};
+ if (!is_reverse) {
+ lbValue str_elem = lb_emit_ptr_offset(p, lb_string_elem(p, expr), offset);
+ lbValue str_len = lb_emit_arith(p, Token_Sub, count, offset, t_int);
+ auto args = array_make<lbValue>(permanent_allocator(), 1);
+ args[0] = lb_emit_string16(p, str_elem, str_len);
+
+ rune_and_len = lb_emit_runtime_call(p, "string16_decode_rune", args);
+ lbValue len = lb_emit_struct_ev(p, rune_and_len, 1);
+ lb_addr_store(p, offset_, lb_emit_arith(p, Token_Add, offset, len, t_int));
+
+ idx = offset;
+ } else {
+ // NOTE(bill): REVERSED LOGIC
+ lbValue str_elem = lb_string_elem(p, expr);
+ lbValue str_len = offset;
+ auto args = array_make<lbValue>(permanent_allocator(), 1);
+ args[0] = lb_emit_string16(p, str_elem, str_len);
+
+ rune_and_len = lb_emit_runtime_call(p, "string16_decode_last_rune", args);
+ lbValue len = lb_emit_struct_ev(p, rune_and_len, 1);
+ lb_addr_store(p, offset_, lb_emit_arith(p, Token_Sub, offset, len, t_int));
+
+ idx = lb_addr_load(p, offset_);
+ }
+
+
+ if (val_type != nullptr) {
+ val = lb_emit_struct_ev(p, rune_and_len, 0);
+ }
+
+ if (val_) *val_ = val;
+ if (idx_) *idx_ = idx;
+ if (loop_) *loop_ = loop;
+ if (done_) *done_ = done;
+}
+
+
gb_internal Ast *lb_strip_and_prefix(Ast *ident) {
if (ident != nullptr) {
@@ -688,6 +800,18 @@ gb_internal void lb_build_range_interval(lbProcedure *p, AstBinaryExpr *node,
lbBlock *body = lb_create_block(p, "for.interval.body");
lbBlock *done = lb_create_block(p, "for.interval.done");
+ // TODO(tf2spi): This is inlined in more than several places.
+ // Putting this in a function might be preferred.
+ // LLVMSetCurrentDebugLocation2 has side effects,
+ // so I didn't want to hide that before it got reviewed.
+ if (rs->label != nullptr && p->debug_info != nullptr) {
+ lbBlock *label = lb_create_block(p, "for.interval.label");
+ lb_emit_jump(p, label);
+ lb_start_block(p, label);
+
+ LLVMSetCurrentDebugLocation2(p->builder, lb_debug_location_from_ast(p, rs->label));
+ lb_add_debug_label(p, rs->label, label);
+ }
lb_emit_jump(p, loop);
lb_start_block(p, loop);
@@ -893,6 +1017,14 @@ gb_internal void lb_build_range_stmt_struct_soa(lbProcedure *p, AstRangeStmt *rs
lbAddr index = lb_add_local_generated(p, t_int, false);
+ if (rs->label != nullptr && p->debug_info != nullptr) {
+ lbBlock *label = lb_create_block(p, "for.soa.label");
+ lb_emit_jump(p, label);
+ lb_start_block(p, label);
+
+ LLVMSetCurrentDebugLocation2(p->builder, lb_debug_location_from_ast(p, rs->label));
+ lb_add_debug_label(p, rs->label, label);
+ }
if (!is_reverse) {
/*
for x, i in array {
@@ -970,7 +1102,6 @@ gb_internal void lb_build_range_stmt_struct_soa(lbProcedure *p, AstRangeStmt *rs
lb_store_range_stmt_val(p, val1, lb_addr_load(p, index));
}
-
lb_push_target_list(p, rs->label, done, loop, nullptr);
lb_build_stmt(p, rs->body);
@@ -1029,6 +1160,15 @@ gb_internal void lb_build_range_stmt(lbProcedure *p, AstRangeStmt *rs, Scope *sc
lbBlock *done = nullptr;
bool is_map = false;
+ if (rs->label != nullptr && p->debug_info != nullptr) {
+ lbBlock *label = lb_create_block(p, "for.range.label");
+ lb_emit_jump(p, label);
+ lb_start_block(p, label);
+
+ LLVMSetCurrentDebugLocation2(p->builder, lb_debug_location_from_ast(p, rs->label));
+ lb_add_debug_label(p, rs->label, label);
+ }
+
if (tav.mode == Addressing_Type) {
lb_build_range_enum(p, type_deref(tav.type), val0_type, &val, &key, &loop, &done);
} else {
@@ -1045,10 +1185,22 @@ gb_internal void lb_build_range_stmt(lbProcedure *p, AstRangeStmt *rs, Scope *sc
break;
}
case Type_Array: {
- lbValue array = lb_build_addr_ptr(p, expr);
- if (is_type_pointer(type_deref(array.type))) {
- array = lb_emit_load(p, array);
+ lbValue array;
+ lbAddr addr = lb_build_addr(p, expr);
+ switch (addr.kind) {
+ case lbAddr_Swizzle:
+ case lbAddr_SwizzleLarge:
+ // NOTE(laytan): apply the swizzle.
+ array = lb_address_from_load(p, lb_addr_load(p, addr));
+ break;
+ default:
+ array = lb_addr_get_ptr(p, addr);
+ if (is_type_pointer(type_deref(array.type))) {
+ array = lb_emit_load(p, array);
+ }
+ break;
}
+
lbAddr count_ptr = lb_add_local_generated(p, t_int, false);
lb_addr_store(p, count_ptr, lb_const_int(p->module, t_int, et->Array.count));
lb_build_range_indexed(p, array, val0_type, count_ptr.addr, &val, &key, &loop, &done, rs->reverse);
@@ -1099,7 +1251,11 @@ gb_internal void lb_build_range_stmt(lbProcedure *p, AstRangeStmt *rs, Scope *sc
}
Type *t = base_type(string.type);
GB_ASSERT(!is_type_cstring(t));
- lb_build_range_string(p, string, val0_type, &val, &key, &loop, &done, rs->reverse);
+ if (is_type_string16(t)) {
+ lb_build_range_string16(p, string, val0_type, &val, &key, &loop, &done, rs->reverse);
+ } else {
+ lb_build_range_string(p, string, val0_type, &val, &key, &loop, &done, rs->reverse);
+ }
break;
}
case Type_Tuple:
@@ -1114,62 +1270,43 @@ gb_internal void lb_build_range_stmt(lbProcedure *p, AstRangeStmt *rs, Scope *sc
}
Type *elem = et->BitSet.elem;
- if (is_type_enum(elem)) {
- i64 enum_count = 0;
- lbValue values = lb_enum_values_slice(p, elem, &enum_count);
- lbValue values_data = lb_slice_elem(p, values);
- lbValue max_count = lb_const_int(m, t_int, enum_count);
-
- lbAddr offset_ = lb_add_local_generated(p, t_int, false);
- lb_addr_store(p, offset_, lb_const_int(m, t_int, 0));
-
- loop = lb_create_block(p, "for.bit_set.enum.loop");
- lb_emit_jump(p, loop);
- lb_start_block(p, loop);
-
- lbBlock *body_check = lb_create_block(p, "for.bit_set.enum.body-check");
- lbBlock *body = lb_create_block(p, "for.bit_set.enum.body");
- done = lb_create_block(p, "for.bit_set.enum.done");
-
- lbValue offset = lb_addr_load(p, offset_);
- lbValue cond = lb_emit_comp(p, Token_Lt, offset, max_count);
- lb_emit_if(p, cond, body_check, done);
- lb_start_block(p, body_check);
-
- lbValue val_ptr = lb_emit_ptr_offset(p, values_data, offset);
- lb_emit_increment(p, offset_.addr);
- val = lb_emit_load(p, val_ptr);
- val = lb_emit_conv(p, val, elem);
-
- lbValue check = lb_build_binary_in(p, val, the_set, Token_in);
- lb_emit_if(p, check, body, loop);
- lb_start_block(p, body);
- } else {
- lbAddr offset_ = lb_add_local_generated(p, t_int, false);
- lb_addr_store(p, offset_, lb_const_int(m, t_int, et->BitSet.lower));
+ Type *mask = bit_set_to_int(et);
+
+ lbValue all_mask = lb_const_value(p->module, mask, exact_bit_set_all_set_mask(et));
+ lbValue initial_mask = lb_emit_arith(p, Token_And, the_set, all_mask, mask);
+
+ if (rs->reverse) {
+ initial_mask = lb_emit_reverse_bits(p, initial_mask, mask);
+ }
- lbValue max_count = lb_const_int(m, t_int, et->BitSet.upper);
+ lbAddr remaining = lb_add_local_generated(p, mask, false);
+ lb_addr_store(p, remaining, initial_mask);
- loop = lb_create_block(p, "for.bit_set.range.loop");
- lb_emit_jump(p, loop);
- lb_start_block(p, loop);
+ loop = lb_create_block(p, "for.bit_set.loop");
+ lbBlock *body = lb_create_block(p, "for.bit_set.body");
+ done = lb_create_block(p, "for.bit_set.done");
- lbBlock *body_check = lb_create_block(p, "for.bit_set.range.body-check");
- lbBlock *body = lb_create_block(p, "for.bit_set.range.body");
- done = lb_create_block(p, "for.bit_set.range.done");
+ lb_emit_jump(p, loop);
+ lb_start_block(p, loop);
- lbValue offset = lb_addr_load(p, offset_);
- lbValue cond = lb_emit_comp(p, Token_LtEq, offset, max_count);
- lb_emit_if(p, cond, body_check, done);
- lb_start_block(p, body_check);
+ lbValue remaining_val = lb_addr_load(p, remaining);
+ lbValue cond = lb_emit_comp(p, Token_NotEq, remaining_val, lb_zero(m, mask));
+ lb_emit_if(p, cond, body, done);
- val = lb_emit_conv(p, offset, elem);
- lb_emit_increment(p, offset_.addr);
+ lb_start_block(p, body);
+ val = lb_emit_count_trailing_zeros(p, remaining_val, mask);
+ val = lb_emit_conv(p, val, elem);
- lbValue check = lb_build_binary_in(p, val, the_set, Token_in);
- lb_emit_if(p, check, body, loop);
- lb_start_block(p, body);
+ if (rs->reverse) {
+ val = lb_emit_arith(p, Token_Sub, lb_const_int(m, elem, et->BitSet.lower + 8*type_size_of(mask) - 1), val, elem);
+ } else {
+ val = lb_emit_arith(p, Token_Add, val, lb_const_int(m, elem, et->BitSet.lower), elem);
}
+
+ lbValue reduce_val = lb_emit_arith(p, Token_Sub, remaining_val, lb_const_int(m, mask, 1), mask);
+ remaining_val = lb_emit_arith(p, Token_And, remaining_val, reduce_val, mask);
+ lb_addr_store(p, remaining, remaining_val);
+
break;
}
default:
@@ -1549,6 +1686,14 @@ gb_internal bool lb_switch_stmt_can_be_trivial_jump_table(AstSwitchStmt *ss, boo
gb_internal void lb_build_switch_stmt(lbProcedure *p, AstSwitchStmt *ss, Scope *scope) {
lb_open_scope(p, scope);
+ if (ss->label != nullptr && p->debug_info != nullptr) {
+ lbBlock *label = lb_create_block(p, "switch.label");
+ lb_emit_jump(p, label);
+ lb_start_block(p, label);
+
+ LLVMSetCurrentDebugLocation2(p->builder, lb_debug_location_from_ast(p, ss->label));
+ lb_add_debug_label(p, ss->label, label);
+ }
if (ss->init != nullptr) {
lb_build_stmt(p, ss->init);
}
@@ -1755,6 +1900,7 @@ gb_internal lbAddr lb_store_range_stmt_val(lbProcedure *p, Ast *stmt_val, lbValu
gb_internal void lb_type_case_body(lbProcedure *p, Ast *label, Ast *clause, lbBlock *body, lbBlock *done) {
ast_node(cc, CaseClause, clause);
+ // NOTE(tf2spi): Debug info for label not generated here on purpose
lb_push_target_list(p, label, done, nullptr, nullptr);
lb_build_stmt_list(p, cc->stmts);
lb_close_scope(p, lbDeferExit_Default, body, clause);
@@ -1957,7 +2103,11 @@ gb_internal void lb_build_type_switch_stmt(lbProcedure *p, AstTypeSwitchStmt *ss
lb_add_entity(p->module, case_entity, ptr);
lb_add_debug_local_variable(p, ptr.value, case_entity->type, case_entity->token);
} else {
- lb_store_type_case_implicit(p, clause, parent_value, false);
+ if (by_reference) {
+ lb_store_type_case_implicit(p, clause, parent_ptr, false);
+ } else {
+ lb_store_type_case_implicit(p, clause, parent_value, false);
+ }
}
lb_type_case_body(p, ss->label, clause, body, done);
@@ -1978,8 +2128,7 @@ gb_internal void lb_build_static_variables(lbProcedure *p, AstValueDecl *vd) {
GB_ASSERT(ast_value->tav.mode == Addressing_Constant ||
ast_value->tav.mode == Addressing_Invalid);
- bool allow_local = false;
- value = lb_const_value(p->module, ast_value->tav.type, ast_value->tav.value, allow_local);
+ value = lb_const_value(p->module, ast_value->tav.type, ast_value->tav.value, LB_CONST_CONTEXT_DEFAULT_NO_LOCAL);
}
Ast *ident = vd->names[i];
@@ -2000,34 +2149,45 @@ gb_internal void lb_build_static_variables(lbProcedure *p, AstValueDecl *vd) {
char *c_name = alloc_cstring(permanent_allocator(), mangled_name);
LLVMValueRef global = LLVMAddGlobal(p->module->mod, lb_type(p->module, e->type), c_name);
+ LLVMSetAlignment(global, cast(u32)type_align_of(e->type));
LLVMSetInitializer(global, LLVMConstNull(lb_type(p->module, e->type)));
- if (value.value != nullptr) {
- LLVMSetInitializer(global, value.value);
- }
+
if (e->Variable.is_rodata) {
LLVMSetGlobalConstant(global, true);
}
- if (e->Variable.thread_local_model != "") {
- LLVMSetThreadLocal(global, true);
-
- String m = e->Variable.thread_local_model;
- LLVMThreadLocalMode mode = LLVMGeneralDynamicTLSModel;
- if (m == "default") {
- mode = LLVMGeneralDynamicTLSModel;
- } else if (m == "localdynamic") {
- mode = LLVMLocalDynamicTLSModel;
- } else if (m == "initialexec") {
- mode = LLVMInitialExecTLSModel;
- } else if (m == "localexec") {
- mode = LLVMLocalExecTLSModel;
- } else {
- GB_PANIC("Unhandled thread local mode %.*s", LIT(m));
- }
- LLVMSetThreadLocalMode(global, mode);
- } else {
+
+ if (!lb_apply_thread_local_model(global, e->Variable.thread_local_model)) {
LLVMSetLinkage(global, LLVMInternalLinkage);
}
+ if (value.value != nullptr) {
+ if (is_type_any(e->type)) {
+ Type *var_type = default_type(value.type);
+
+ gbString var_name = gb_string_make(temporary_allocator(), "__$static_any::");
+ var_name = gb_string_append_length(var_name, mangled_name.text, mangled_name.len);
+
+ lbAddr var_global = lb_add_global_generated_with_name(p->module, var_type, value, make_string_c(var_name), nullptr);
+ LLVMValueRef var_global_ref = var_global.addr.value;
+
+ if (e->Variable.is_rodata) {
+ LLVMSetGlobalConstant(var_global_ref, true);
+ }
+
+ if (!lb_apply_thread_local_model(var_global_ref, e->Variable.thread_local_model)) {
+ LLVMSetLinkage(var_global_ref, LLVMInternalLinkage);
+ }
+
+ LLVMValueRef vals[2] = {
+ lb_emit_conv(p, var_global.addr, t_rawptr).value,
+ lb_typeid(p->module, var_type).value,
+ };
+ LLVMValueRef init = llvm_const_named_struct(p->module, e->type, vals, gb_count_of(vals));
+ LLVMSetInitializer(global, init);
+ } else {
+ LLVMSetInitializer(global, value.value);
+ }
+ }
lbValue global_val = {global, alloc_type_pointer(e->type)};
lb_add_entity(p->module, e, global_val);
@@ -2322,6 +2482,14 @@ gb_internal void lb_build_if_stmt(lbProcedure *p, Ast *node) {
else_ = lb_create_block(p, "if.else");
}
if (is->label != nullptr) {
+ if (p->debug_info != nullptr) {
+ lbBlock *label = lb_create_block(p, "if.label");
+ lb_emit_jump(p, label);
+ lb_start_block(p, label);
+
+ LLVMSetCurrentDebugLocation2(p->builder, lb_debug_location_from_ast(p, is->label));
+ lb_add_debug_label(p, is->label, label);
+ }
lbTargetList *tl = lb_push_target_list(p, is->label, done, nullptr, nullptr);
tl->is_block = true;
}
@@ -2414,12 +2582,19 @@ gb_internal void lb_build_for_stmt(lbProcedure *p, Ast *node) {
lb_push_target_list(p, fs->label, done, post, nullptr);
+ if (fs->label != nullptr && p->debug_info != nullptr) {
+ lbBlock *label = lb_create_block(p, "for.label");
+ lb_emit_jump(p, label);
+ lb_start_block(p, label);
+
+ LLVMSetCurrentDebugLocation2(p->builder, lb_debug_location_from_ast(p, fs->label));
+ lb_add_debug_label(p, fs->label, label);
+ }
if (fs->init != nullptr) {
- #if 1
lbBlock *init = lb_create_block(p, "for.init");
lb_emit_jump(p, init);
lb_start_block(p, init);
- #endif
+
lb_build_stmt(p, fs->init);
}
@@ -2435,7 +2610,6 @@ gb_internal void lb_build_for_stmt(lbProcedure *p, Ast *node) {
lb_start_block(p, body);
}
-
lb_build_stmt(p, fs->body);
lb_pop_target_list(p);
@@ -2709,9 +2883,21 @@ gb_internal void lb_build_stmt(lbProcedure *p, Ast *node) {
case_ast_node(bs, BlockStmt, node);
+ lbBlock *body = nullptr;
lbBlock *done = nullptr;
if (bs->label != nullptr) {
+ if (p->debug_info != nullptr) {
+ lbBlock *label = lb_create_block(p, "block.label");
+ lb_emit_jump(p, label);
+ lb_start_block(p, label);
+
+ LLVMSetCurrentDebugLocation2(p->builder, lb_debug_location_from_ast(p, bs->label));
+ lb_add_debug_label(p, bs->label, label);
+ }
+ body = lb_create_block(p, "block.body");
done = lb_create_block(p, "block.done");
+ lb_emit_jump(p, body);
+ lb_start_block(p, body);
lbTargetList *tl = lb_push_target_list(p, bs->label, done, nullptr, nullptr);
tl->is_block = true;
}
@@ -2932,6 +3118,18 @@ gb_internal void lb_emit_defer_stmts(lbProcedure *p, lbDeferExitKind kind, lbBlo
}
defer (p->branch_location_pos = prev_token_pos);
+ // TODO(lucas): In LLVM 21 use the 'use-after-scope' asan option which does this for us.
+ if (kind == lbDeferExit_Return) {
+ for_array(i, p->asan_stack_locals) {
+ lbValue local = p->asan_stack_locals[i];
+
+ auto args = array_make<lbValue>(temporary_allocator(), 2);
+ args[0] = lb_emit_conv(p, local, t_rawptr);
+ args[1] = lb_const_int(p->module, t_int, type_size_of(local.type->Pointer.elem));
+ lb_emit_runtime_call(p, "__asan_unpoison_memory_region", args);
+ }
+ }
+
isize count = p->defer_stmts.count;
isize i = count;
while (i --> 0) {
diff --git a/src/llvm_backend_type.cpp b/src/llvm_backend_type.cpp
index ad4250f3c..7d412eb15 100644
--- a/src/llvm_backend_type.cpp
+++ b/src/llvm_backend_type.cpp
@@ -1,4 +1,10 @@
+gb_internal void lb_set_odin_rtti_section(LLVMValueRef value) {
+ if (build_context.metrics.os != TargetOs_darwin) {
+ LLVMSetSection(value, ".odinti");
+ }
+}
+
gb_internal isize lb_type_info_index(CheckerInfo *info, TypeInfoPair pair, bool err_on_not_found=true) {
isize index = type_info_index(info, pair, err_on_not_found);
if (index >= 0) {
@@ -221,6 +227,7 @@ gb_internal void lb_setup_type_info_data_giant_array(lbModule *m, i64 global_typ
gb_snprintf(name, 63, "__$ti-%lld", cast(long long)index);
LLVMValueRef g = LLVMAddGlobal(m->mod, type, name);
lb_make_global_private_const(g);
+ lb_set_odin_rtti_section(g);
return g;
};
@@ -295,7 +302,7 @@ gb_internal void lb_setup_type_info_data_giant_array(lbModule *m, i64 global_typ
(name##_values)[i] = LLVMConstNull(elem); \
} \
} \
- LLVMSetInitializer(name.addr.value, llvm_const_array(elem, name##_values, at->Array.count)); \
+ LLVMSetInitializer(name.addr.value, llvm_const_array(m, elem, name##_values, at->Array.count)); \
})
type_info_allocate_values(lb_global_type_info_member_types);
@@ -387,8 +394,9 @@ gb_internal void lb_setup_type_info_data_giant_array(lbModule *m, i64 global_typ
String proc_name = {};
if (t->Named.type_name->parent_proc_decl) {
DeclInfo *decl = t->Named.type_name->parent_proc_decl;
- if (decl->entity && decl->entity->kind == Entity_Procedure) {
- proc_name = decl->entity->token.string;
+ Entity *e = decl->entity.load();
+ if (e && e->kind == Entity_Procedure) {
+ proc_name = e->token.string;
}
}
TokenPos pos = t->Named.type_name->token.pos;
@@ -518,14 +526,48 @@ gb_internal void lb_setup_type_info_data_giant_array(lbModule *m, i64 global_typ
break;
case Basic_string:
- tag_type = t_type_info_string;
+ {
+ tag_type = t_type_info_string;
+ LLVMValueRef vals[2] = {
+ lb_const_bool(m, t_bool, false).value,
+ lb_const_int(m, t_type_info_string_encoding_kind, 0).value,
+ };
+
+ variant_value = llvm_const_named_struct(m, tag_type, vals, gb_count_of(vals));
+ }
break;
case Basic_cstring:
{
tag_type = t_type_info_string;
- LLVMValueRef vals[1] = {
+ LLVMValueRef vals[2] = {
+ lb_const_bool(m, t_bool, true).value,
+ lb_const_int(m, t_type_info_string_encoding_kind, 0).value,
+ };
+
+ variant_value = llvm_const_named_struct(m, tag_type, vals, gb_count_of(vals));
+ }
+ break;
+
+ case Basic_string16:
+ {
+ tag_type = t_type_info_string;
+ LLVMValueRef vals[2] = {
+ lb_const_bool(m, t_bool, false).value,
+ lb_const_int(m, t_type_info_string_encoding_kind, 1).value,
+ };
+
+ variant_value = llvm_const_named_struct(m, tag_type, vals, gb_count_of(vals));
+ }
+ break;
+
+
+ case Basic_cstring16:
+ {
+ tag_type = t_type_info_string;
+ LLVMValueRef vals[2] = {
lb_const_bool(m, t_bool, true).value,
+ lb_const_int(m, t_type_info_string_encoding_kind, 1).value,
};
variant_value = llvm_const_named_struct(m, tag_type, vals, gb_count_of(vals));
@@ -710,12 +752,14 @@ gb_internal void lb_setup_type_info_data_giant_array(lbModule *m, i64 global_typ
value_values[i] = lb_const_value(m, t_i64, fields[i]->Constant.value).value;
}
- LLVMValueRef name_init = llvm_const_array(lb_type(m, t_string), name_values, cast(unsigned)fields.count);
- LLVMValueRef value_init = llvm_const_array(lb_type(m, t_type_info_enum_value), value_values, cast(unsigned)fields.count);
+ LLVMValueRef name_init = llvm_const_array(m, lb_type(m, t_string), name_values, cast(unsigned)fields.count);
+ LLVMValueRef value_init = llvm_const_array(m, lb_type(m, t_type_info_enum_value), value_values, cast(unsigned)fields.count);
LLVMSetInitializer(name_array.value, name_init);
LLVMSetInitializer(value_array.value, value_init);
LLVMSetGlobalConstant(name_array.value, true);
LLVMSetGlobalConstant(value_array.value, true);
+ lb_set_odin_rtti_section(name_array.value);
+ lb_set_odin_rtti_section(value_array.value);
lbValue v_count = lb_const_int(m, t_int, fields.count);
@@ -788,7 +832,7 @@ gb_internal void lb_setup_type_info_data_giant_array(lbModule *m, i64 global_typ
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;
@@ -1056,6 +1100,7 @@ gb_internal void lb_setup_type_info_data_giant_array(lbModule *m, i64 global_typ
LLVMValueRef giant_array = lb_global_type_info_data_ptr(m).value;
LLVMSetInitializer(giant_array, giant_const);
lb_make_global_private_const(giant_array);
+ lb_set_odin_rtti_section(giant_array);
}
diff --git a/src/llvm_backend_utility.cpp b/src/llvm_backend_utility.cpp
index c876169f3..ca7bf34e3 100644
--- a/src/llvm_backend_utility.cpp
+++ b/src/llvm_backend_utility.cpp
@@ -6,6 +6,7 @@ gb_internal bool lb_is_type_aggregate(Type *t) {
case Type_Basic:
switch (t->Basic.kind) {
case Basic_string:
+ case Basic_string16:
case Basic_any:
return true;
@@ -190,6 +191,23 @@ gb_internal lbValue lb_emit_clamp(lbProcedure *p, Type *t, lbValue x, lbValue mi
return z;
}
+gb_internal lbValue lb_emit_string16(lbProcedure *p, lbValue str_elem, lbValue str_len) {
+ if (false && lb_is_const(str_elem) && lb_is_const(str_len)) {
+ LLVMValueRef values[2] = {
+ str_elem.value,
+ str_len.value,
+ };
+ lbValue res = {};
+ res.type = t_string16;
+ res.value = llvm_const_named_struct(p->module, t_string16, values, gb_count_of(values));
+ return res;
+ } else {
+ lbAddr res = lb_add_local_generated(p, t_string16, false);
+ lb_emit_store(p, lb_emit_struct_ep(p, res.addr, 0), str_elem);
+ lb_emit_store(p, lb_emit_struct_ep(p, res.addr, 1), str_len);
+ return lb_addr_load(p, res);
+ }
+}
gb_internal lbValue lb_emit_string(lbProcedure *p, lbValue str_elem, lbValue str_len) {
@@ -268,7 +286,14 @@ gb_internal lbValue lb_emit_transmute(lbProcedure *p, lbValue value, Type *t) {
}
}
+ bool is_simd_vector_bitcastable = false;
if (is_type_simd_vector(src) && is_type_simd_vector(dst)) {
+ if (!is_type_internally_pointer_like(src->SimdVector.elem) && !is_type_internally_pointer_like(dst->SimdVector.elem)) {
+ is_simd_vector_bitcastable = true;
+ }
+ }
+
+ if (is_simd_vector_bitcastable) {
res.value = LLVMBuildBitCast(p->builder, value.value, lb_type(p->module, t), "");
return res;
} else if (is_type_array_like(src) && (is_type_simd_vector(dst) || is_type_integer_128bit(dst))) {
@@ -981,7 +1006,8 @@ gb_internal i32 lb_convert_struct_index(lbModule *m, Type *t, i32 index) {
} else if (build_context.ptr_size != build_context.int_size) {
switch (t->kind) {
case Type_Basic:
- if (t->Basic.kind != Basic_string) {
+ if (t->Basic.kind != Basic_string &&
+ t->Basic.kind != Basic_string16) {
break;
}
/*fallthrough*/
@@ -1160,6 +1186,11 @@ gb_internal lbValue lb_emit_struct_ep(lbProcedure *p, lbValue s, i32 index) {
case 0: result_type = alloc_type_pointer(t->Slice.elem); break;
case 1: result_type = t_int; break;
}
+ } else if (is_type_string16(t)) {
+ switch (index) {
+ case 0: result_type = t_u16_ptr; break;
+ case 1: result_type = t_int; break;
+ }
} else if (is_type_string(t)) {
switch (index) {
case 0: result_type = t_u8_ptr; break;
@@ -1273,6 +1304,12 @@ gb_internal lbValue lb_emit_struct_ev(lbProcedure *p, lbValue s, i32 index) {
switch (t->kind) {
case Type_Basic:
switch (t->Basic.kind) {
+ case Basic_string16:
+ switch (index) {
+ case 0: result_type = t_u16_ptr; break;
+ case 1: result_type = t_int; break;
+ }
+ break;
case Basic_string:
switch (index) {
case 0: result_type = t_u8_ptr; break;
@@ -1440,6 +1477,10 @@ gb_internal lbValue lb_emit_deep_field_gep(lbProcedure *p, lbValue e, Selection
e = lb_emit_struct_ep(p, e, index);
break;
+ case Basic_string16:
+ e = lb_emit_struct_ep(p, e, index);
+ break;
+
default:
GB_PANIC("un-gep-able type %s", type_to_string(type));
break;
@@ -1626,11 +1667,17 @@ gb_internal void lb_fill_string(lbProcedure *p, lbAddr const &string, lbValue ba
gb_internal lbValue lb_string_elem(lbProcedure *p, lbValue string) {
Type *t = base_type(string.type);
+ if (t->kind == Type_Basic && t->Basic.kind == Basic_string16) {
+ return lb_emit_struct_ev(p, string, 0);
+ }
GB_ASSERT(t->kind == Type_Basic && t->Basic.kind == Basic_string);
return lb_emit_struct_ev(p, string, 0);
}
gb_internal lbValue lb_string_len(lbProcedure *p, lbValue string) {
Type *t = base_type(string.type);
+ if (t->kind == Type_Basic && t->Basic.kind == Basic_string16) {
+ return lb_emit_struct_ev(p, string, 1);
+ }
GB_ASSERT_MSG(t->kind == Type_Basic && t->Basic.kind == Basic_string, "%s", type_to_string(t));
return lb_emit_struct_ev(p, string, 1);
}
@@ -1641,6 +1688,12 @@ gb_internal lbValue lb_cstring_len(lbProcedure *p, lbValue value) {
args[0] = lb_emit_conv(p, value, t_cstring);
return lb_emit_runtime_call(p, "cstring_len", args);
}
+gb_internal lbValue lb_cstring16_len(lbProcedure *p, lbValue value) {
+ GB_ASSERT(is_type_cstring16(value.type));
+ auto args = array_make<lbValue>(permanent_allocator(), 1);
+ args[0] = lb_emit_conv(p, value, t_cstring16);
+ return lb_emit_runtime_call(p, "cstring16_len", args);
+}
gb_internal lbValue lb_array_elem(lbProcedure *p, lbValue array_ptr) {
@@ -2094,42 +2147,553 @@ gb_internal void lb_set_wasm_export_attributes(LLVMValueRef value, String export
}
-
gb_internal lbAddr lb_handle_objc_find_or_register_selector(lbProcedure *p, String const &name) {
- lbObjcRef *found = string_map_get(&p->module->objc_selectors, name);
+ lbModule *m = p->module;
+ lbAddr *found = string_map_get(&m->objc_selectors, name);
+ if (found) {
+ return *found;
+ }
+
+ lbModule *default_module = &p->module->gen->default_module;
+
+ gbString global_name = gb_string_make(permanent_allocator(), "__$objc_SEL::");
+ global_name = gb_string_append_length(global_name, name.text, name.len);
+
+ LLVMTypeRef t = lb_type(m, t_objc_SEL);
+ lbValue g = {};
+ g.value = LLVMAddGlobal(m->mod, t, global_name);
+ g.type = alloc_type_pointer(t_objc_SEL);
+
+ if (default_module == m) {
+ LLVMSetInitializer(g.value, LLVMConstNull(t));
+ lb_add_member(m, make_string_c(global_name), g);
+ } else {
+ LLVMSetLinkage(g.value, LLVMExternalLinkage);
+ }
+
+ mpsc_enqueue(&m->gen->objc_selectors, lbObjCGlobal{m, global_name, name, t_objc_SEL});
+
+ lbAddr addr = lb_addr(g);
+ string_map_set(&m->objc_selectors, name, addr);
+ return addr;
+}
+
+gb_internal lbAddr lb_handle_objc_find_or_register_class(lbProcedure *p, String const &name, Type *class_impl_type) {
+ lbModule *m = p->module;
+ lbAddr *found = string_map_get(&m->objc_classes, name);
if (found) {
- return found->local_module_addr;
+ return *found;
}
lbModule *default_module = &p->module->gen->default_module;
- Entity *entity = {};
- if (default_module != p->module) {
- found = string_map_get(&default_module->objc_selectors, name);
- if (found) {
- entity = found->entity;
+ gbString global_name = gb_string_make(permanent_allocator(), "__$objc_Class::");
+ global_name = gb_string_append_length(global_name, name.text, name.len);
+
+ LLVMTypeRef t = lb_type(m, t_objc_Class);
+ lbValue g = {};
+ g.value = LLVMAddGlobal(m->mod, t, global_name);
+ g.type = alloc_type_pointer(t_objc_Class);
+
+ if (default_module == m) {
+ LLVMSetInitializer(g.value, LLVMConstNull(t));
+ lb_add_member(m, make_string_c(global_name), g);
+ } else {
+ LLVMSetLinkage(g.value, LLVMExternalLinkage);
+ }
+ mpsc_enqueue(&m->gen->objc_classes, lbObjCGlobal{m, global_name, name, t_objc_Class, class_impl_type});
+
+ lbAddr addr = lb_addr(g);
+ string_map_set(&m->objc_classes, name, addr);
+ return addr;
+}
+
+gb_internal lbAddr lb_handle_objc_find_or_register_ivar(lbModule *m, Type *self_type) {
+
+ String name = self_type->Named.type_name->TypeName.objc_class_name;
+ GB_ASSERT(name != "");
+
+ lbAddr *found = string_map_get(&m->objc_ivars, name);
+ if (found) {
+ return *found;
+ }
+
+ lbModule *default_module = &m->gen->default_module;
+
+ gbString global_name = gb_string_make(permanent_allocator(), "__$objc_ivar::");
+ global_name = gb_string_append_length(global_name, name.text, name.len);
+
+ // Create a global variable to store offset of the ivar in an instance of an object
+ LLVMTypeRef t = lb_type(m, t_int);
+
+ lbValue g = {};
+ g.value = LLVMAddGlobal(m->mod, t, global_name);
+ g.type = t_int_ptr;
+
+ if (default_module == m) {
+ LLVMSetInitializer(g.value, LLVMConstInt(t, 0, true));
+ lb_add_member(m, make_string_c(global_name), g);
+ } else {
+ LLVMSetLinkage(g.value, LLVMExternalLinkage);
+ }
+
+ mpsc_enqueue(&m->gen->objc_ivars, lbObjCGlobal{m, global_name, name, t_int, self_type});
+
+ lbAddr addr = lb_addr(g);
+ string_map_set(&m->objc_ivars, name, addr);
+ return addr;
+}
+
+gb_internal lbValue lb_handle_objc_ivar_for_objc_object_pointer(lbProcedure *p, lbValue self) {
+ GB_ASSERT(self.type->kind == Type_Pointer && self.type->Pointer.elem->kind == Type_Named);
+
+ Type *self_type = self.type->Pointer.elem;
+
+ lbValue self_uptr = lb_emit_conv(p, self, t_uintptr);
+
+ lbValue ivar_offset = lb_addr_load(p, lb_handle_objc_find_or_register_ivar(p->module, self_type));
+ lbValue ivar_offset_uptr = lb_emit_conv(p, ivar_offset, t_uintptr);
+
+
+ lbValue ivar_uptr = lb_emit_arith(p, Token_Add, self_uptr, ivar_offset_uptr, t_uintptr);
+
+ Type *ivar_type = self_type->Named.type_name->TypeName.objc_ivar;
+ return lb_emit_conv(p, ivar_uptr, alloc_type_pointer(ivar_type));
+}
+
+gb_internal lbValue lb_handle_objc_ivar_get(lbProcedure *p, Ast *expr) {
+ ast_node(ce, CallExpr, expr);
+
+ GB_ASSERT(ce->args[0]->tav.type->kind == Type_Pointer);
+ lbValue self = lb_build_expr(p, ce->args[0]);
+
+ return lb_handle_objc_ivar_for_objc_object_pointer(p, self);
+}
+
+gb_internal void lb_create_objc_block_helper_procs(
+ lbModule *m, LLVMTypeRef block_lit_type, isize capture_field_offset,
+ Slice<lbValue> capture_values, Slice<isize> objc_object_indices,
+ lbProcedure *&out_copy_helper, lbProcedure *&out_dispose_helper
+) {
+ gbString copy_helper_name = gb_string_append_fmt(gb_string_make(temporary_allocator(), ""), "__$objc_block_copy_helper_%lld", m->objc_next_block_id);
+ gbString dispose_helper_name = gb_string_append_fmt(gb_string_make(temporary_allocator(), ""), "__$objc_block_dispose_helper_%lld", m->objc_next_block_id);
+
+ // copy: Block_Literal *dst, Block_Literal *src, i32 field_apropos
+ // dispose: Block_Literal *src, i32 field_apropos
+ Type *types[3] = { t_rawptr, t_rawptr, t_i32 };
+
+ Type *copy_tuple = alloc_type_tuple_from_field_types(types, 3, false, true);
+ Type *dispose_tuple = alloc_type_tuple_from_field_types(&types[1], 2, false, true);
+
+ Type *copy_proc_type = alloc_type_proc(nullptr, copy_tuple, 3, nullptr, 0, false, ProcCC_CDecl);
+ Type *dispose_proc_type = alloc_type_proc(nullptr, dispose_tuple, 2, nullptr, 0, false, ProcCC_CDecl);
+
+ lbProcedure *copy_proc = lb_create_dummy_procedure(m, make_string((u8*)copy_helper_name, gb_string_length(copy_helper_name)), copy_proc_type);
+ lbProcedure *dispose_proc = lb_create_dummy_procedure(m, make_string((u8*)dispose_helper_name, gb_string_length(dispose_helper_name)), dispose_proc_type);
+ LLVMSetLinkage(copy_proc->value, LLVMPrivateLinkage);
+ LLVMSetLinkage(dispose_proc->value, LLVMPrivateLinkage);
+
+
+ const int BLOCK_FIELD_IS_OBJECT = 3; // id, NSObject, __attribute__((NSObject)), block, ...
+ const int BLOCK_FIELD_IS_BLOCK = 7; // a block variable
+
+ Type *block_base_type = find_core_type(m->info->checker, str_lit("Objc_Block"));
+
+ auto is_object_objc_block = [](Type *type, Type *block_base_type) -> bool {
+
+ Type *base = base_type(type_deref(type));
+ GB_ASSERT(base->kind == Type_Struct);
+
+ while (is_type_polymorphic_record_specialized(base)) {
+ if (base->Struct.polymorphic_parent) {
+ base = base->Struct.polymorphic_parent;
+
+ if (base == block_base_type) {
+ return true;
+ }
+ base = base_type(base);
+ GB_ASSERT(base->kind == Type_Struct);
+ }
+ }
+
+ return false;
+ };
+
+ lb_begin_procedure_body(copy_proc);
+ lb_begin_procedure_body(dispose_proc);
+ {
+ for (isize object_index : objc_object_indices) {
+ const auto field_offset = unsigned(capture_field_offset+object_index);
+
+ Type *field_type = capture_values[object_index].type;
+ LLVMTypeRef field_raw_type = lb_type(m, field_type);
+
+ GB_ASSERT(is_type_objc_object(field_type));
+ bool is_block_obj = is_object_objc_block(field_type, block_base_type);
+
+ auto copy_args = array_make<lbValue>(temporary_allocator(), 3, 3);
+ auto dispose_args = array_make<lbValue>(temporary_allocator(), 2, 2);
+
+ // Copy helper
+ {
+ LLVMValueRef dst_field = LLVMBuildStructGEP2(copy_proc->builder, block_lit_type, copy_proc->raw_input_parameters[0], field_offset, "");
+ LLVMValueRef src_field = LLVMBuildStructGEP2(copy_proc->builder, block_lit_type, copy_proc->raw_input_parameters[1], field_offset, "");
+
+ lbValue dst_value = {}, src_value = {};
+ dst_value.type = alloc_type_pointer(field_type);
+ dst_value.value = dst_field;
+
+ src_value.type = field_type;
+ src_value.value = LLVMBuildLoad2(copy_proc->builder, field_raw_type, src_field, "");
+
+ copy_args[0] = dst_value;
+ copy_args[1] = src_value;
+ copy_args[2] = lb_const_int(m, t_i32, u64(is_block_obj ? BLOCK_FIELD_IS_BLOCK : BLOCK_FIELD_IS_OBJECT));
+
+ lb_emit_runtime_call(copy_proc, "_Block_object_assign", copy_args);
+ }
+
+ // Dispose helper
+ {
+ LLVMValueRef src_field = LLVMBuildStructGEP2(dispose_proc->builder, block_lit_type, dispose_proc->raw_input_parameters[0], field_offset, "");
+ lbValue src_value = {};
+ src_value.type = field_type;
+ src_value.value = LLVMBuildLoad2(dispose_proc->builder, field_raw_type, src_field, "");
+
+ dispose_args[0] = src_value;
+ dispose_args[1] = lb_const_int(m, t_i32, u64(is_block_obj ? BLOCK_FIELD_IS_BLOCK : BLOCK_FIELD_IS_OBJECT));
+
+ lb_emit_runtime_call(dispose_proc, "_Block_object_dispose", dispose_args);
+ }
+ }
+ }
+ lb_end_procedure_body(copy_proc);
+ lb_end_procedure_body(dispose_proc);
+
+
+ out_copy_helper = copy_proc;
+ out_dispose_helper = dispose_proc;
+}
+
+gb_internal lbValue lb_handle_objc_block(lbProcedure *p, Ast *expr) {
+ /// #See: https://clang.llvm.org/docs/Block-ABI-Apple.html
+ /// https://www.newosxbook.com/src.php?tree=xnu&file=/libkern/libkern/Block_private.h
+ /// https://github.com/llvm/llvm-project/blob/21f1f9558df3830ffa637def364e3c0cb0dbb3c0/compiler-rt/lib/BlocksRuntime/Block_private.h
+ /// https://github.com/apple-oss-distributions/libclosure/blob/3668b0837f47be3cc1c404fb5e360f4ff178ca13/runtime.cpp
+ ast_node(ce, CallExpr, expr);
+ GB_ASSERT(ce->args.count > 0);
+
+ lbModule *m = p->module;
+
+ m->objc_next_block_id += 1;
+
+ const isize capture_arg_count = ce->args.count - 1;
+
+ Type *block_result_type = type_of_expr(expr);
+ GB_ASSERT(block_result_type != nullptr && block_result_type->kind == Type_Pointer);
+
+ LLVMTypeRef lb_type_rawptr = lb_type(m, t_rawptr);
+ LLVMTypeRef lb_type_i32 = lb_type(m, t_i32);
+ LLVMTypeRef lb_type_int = lb_type(m, t_int);
+
+ // Build user proc
+ // Type * user_proc_type = type_of_expr(ce->args[capture_arg_count]);
+ lbValue user_proc_value = lb_build_expr(p, ce->args[capture_arg_count]);
+ auto& user_proc = user_proc_value.type->Proc;
+ GB_ASSERT(user_proc_value.type->kind == Type_Proc);
+
+ const bool is_global = capture_arg_count == 0 && user_proc.calling_convention != ProcCC_Odin;
+ const isize block_forward_args = user_proc.param_count - capture_arg_count;
+ const isize capture_fields_offset = user_proc.calling_convention != ProcCC_Odin ? 5 : 6;
+
+ Ast *proc_lit = unparen_expr(ce->args[capture_arg_count]);
+ if (proc_lit->kind == Ast_Ident) {
+ proc_lit = proc_lit->Ident.entity->decl_info->proc_lit;
+ }
+ GB_ASSERT(proc_lit->kind == Ast_ProcLit);
+
+ lbProcedure *copy_helper = {}, *dispose_helper = {};
+
+ // Build captured arguments & collect the ones that are Objective-C objects
+ auto captured_values = array_make<lbValue>(temporary_allocator(), capture_arg_count, capture_arg_count);
+ auto objc_captures = array_make<isize>(temporary_allocator());
+
+ for (isize i = 0; i < capture_arg_count; i++) {
+ captured_values[i] = lb_build_expr(p, ce->args[i]);
+
+ if (is_type_pointer(captured_values[i].type) && is_type_objc_object(captured_values[i].type)) {
+ array_add(&objc_captures, i);
+ }
+ }
+
+ const bool has_objc_fields = objc_captures.count > 0;
+
+
+ // Create proc with the block signature
+ // (takes a block literal pointer as the first parameter, followed by any expected ones from the user's proc)
+ gbString block_invoker_name = gb_string_append_fmt(gb_string_make(permanent_allocator(), ""), "__$objc_block_invoker_%lld", m->objc_next_block_id);
+
+ // Add + 1 because the first parameter received is the block literal pointer itself
+ auto invoker_args = array_make<Type *>(temporary_allocator(), block_forward_args + 1, block_forward_args + 1);
+ invoker_args[0] = t_rawptr;
+
+ GB_ASSERT(block_forward_args <= user_proc.param_count);
+ if (user_proc.param_count > 0) {
+ Slice<Entity *> user_proc_param_types = user_proc.params->Tuple.variables;
+ for (isize i = 0; i < block_forward_args; i++) {
+ invoker_args[i+1] = user_proc_param_types[i]->type;
+ }
+ }
+
+ GB_ASSERT(user_proc.result_count <= 1);
+
+ Type *invoker_args_tuple = alloc_type_tuple_from_field_types(invoker_args.data, invoker_args.count, false, true);
+ Type *invoker_results_tuple = nullptr;
+ if (user_proc.result_count > 0) {
+ invoker_results_tuple = alloc_type_tuple_from_field_types(&user_proc.results->Tuple.variables[0]->type, 1, false, true);
+ }
+
+ Type *invoker_proc_type = alloc_type_proc(nullptr, invoker_args_tuple, invoker_args_tuple->Tuple.variables.count,
+ invoker_results_tuple, user_proc.result_count, false, ProcCC_CDecl);
+
+ lbProcedure *invoker_proc = lb_create_dummy_procedure(m, make_string((u8*)block_invoker_name,
+ gb_string_length(block_invoker_name)), invoker_proc_type);
+
+ LLVMSetLinkage(invoker_proc->value, LLVMPrivateLinkage);
+ lb_add_function_type_attributes(invoker_proc->value, lb_get_function_type(m, invoker_proc_type), ProcCC_CDecl);
+
+ // Create the block descriptor and block literal
+ gbString block_lit_type_name = gb_string_make(temporary_allocator(), "__$ObjC_Block_Literal_");
+ block_lit_type_name = gb_string_append_fmt(block_lit_type_name, "%lld", m->objc_next_block_id);
+
+ gbString block_desc_type_name = gb_string_make(temporary_allocator(), "__$ObjC_Block_Descriptor_");
+ block_desc_type_name = gb_string_append_fmt(block_desc_type_name, "%lld", m->objc_next_block_id);
+
+ LLVMTypeRef block_lit_type = {};
+ LLVMTypeRef block_desc_type = {};
+ LLVMValueRef block_desc_initializer = {};
+
+ {
+ block_desc_type = LLVMStructCreateNamed(m->ctx, block_desc_type_name);
+
+ LLVMTypeRef fields_types[4] = {
+ lb_type_int, // Reserved
+ lb_type_int, // Block size
+ lb_type_rawptr, // Copy helper func pointer
+ lb_type_rawptr, // Dispose helper func pointer
+ };
+
+ LLVMStructSetBody(block_desc_type, fields_types, has_objc_fields ? 4 : 2, false);
+ }
+
+ {
+ block_lit_type = LLVMStructCreateNamed(m->ctx, block_lit_type_name);
+
+ auto fields = array_make<LLVMTypeRef>(temporary_allocator());
+
+ array_add(&fields, lb_type_rawptr); // isa
+ array_add(&fields, lb_type_i32); // flags
+ array_add(&fields, lb_type_i32); // reserved
+ array_add(&fields, lb_type_rawptr); // invoke
+ array_add(&fields, block_desc_type); // descriptor
+
+ if (user_proc.calling_convention == ProcCC_Odin) {
+ array_add(&fields, lb_type(m, t_context)); // context
+ }
+
+ // From here on, fields for the captured vars are added
+ for (lbValue cap_arg : captured_values) {
+ array_add(&fields, lb_type(m, cap_arg.type));
+ }
+
+ LLVMStructSetBody(block_lit_type, fields.data, (unsigned)fields.count, false);
+ }
+
+ // Generate copy and dispose helper functions for captured params that are Objective-C objects (or a Block)
+ if (has_objc_fields) {
+ lb_create_objc_block_helper_procs(m, block_lit_type, capture_fields_offset,
+ slice(captured_values, 0, captured_values.count),
+ slice(objc_captures, 0, objc_captures.count),
+ copy_helper, dispose_helper);
+ }
+
+ {
+ LLVMValueRef fields_values[4] = {
+ lb_const_int(m, t_int, 0).value, // Reserved
+ lb_const_int(m, t_int, u64(lb_sizeof(block_lit_type))).value, // Block size
+ has_objc_fields ? copy_helper->value : nullptr, // Copy helper
+ has_objc_fields ? dispose_helper->value : nullptr, // Dispose helper
+ };
+
+ block_desc_initializer = LLVMConstNamedStruct(block_desc_type, fields_values, has_objc_fields ? 4 : 2);
+ }
+
+ // Create global block descriptor
+ gbString desc_global_name = gb_string_make(temporary_allocator(), "__$objc_block_desc_");
+ desc_global_name = gb_string_append_fmt(desc_global_name, "%lld", m->objc_next_block_id);
+
+ LLVMValueRef p_descriptor = LLVMAddGlobal(m->mod, block_desc_type, desc_global_name);
+ LLVMSetInitializer(p_descriptor, block_desc_initializer);
+
+
+ /// Invoker body
+ lb_begin_procedure_body(invoker_proc);
+ {
+ // Reserve 2 extra arguments for: Indirect return values and context.
+ auto call_args = array_make<LLVMValueRef>(temporary_allocator(), 0, user_proc.param_count + 2);
+
+ isize block_literal_arg_index = 0;
+
+ lbFunctionType* user_proc_ft = lb_get_function_type(m, user_proc_value.type);
+
+ lbArgKind return_kind = {};
+
+ GB_ASSERT(user_proc.result_count <= 1);
+ if (user_proc.result_count > 0) {
+ return_kind = user_proc_ft->ret.kind;
+
+ if (return_kind == lbArg_Indirect) {
+ // Forward indirect return value
+ array_add(&call_args, invoker_proc->raw_input_parameters[0]);
+ block_literal_arg_index = 1;
+ }
+ }
+
+ // Forward raw arguments
+ for (isize i = block_literal_arg_index+1; i < invoker_proc->raw_input_parameters.count; i++) {
+ array_add(&call_args, invoker_proc->raw_input_parameters[i]);
+ }
+
+ LLVMValueRef block_literal = invoker_proc->raw_input_parameters[block_literal_arg_index];
+
+ // Copy capture parameters from the block literal
+ isize capture_arg_in_user_proc_start_index = user_proc_ft->args.count - capture_arg_count;
+ if (user_proc.calling_convention == ProcCC_Odin) {
+ capture_arg_in_user_proc_start_index -= 1;
+ }
+
+ for (isize i = 0; i < capture_arg_count; i++) {
+ LLVMValueRef cap_value = LLVMBuildStructGEP2(invoker_proc->builder, block_lit_type, block_literal, unsigned(capture_fields_offset + i), "");
+
+ // Don't emit load if indirect. Pass the pointer as-is
+ isize cap_arg_index_in_user_proc = capture_arg_in_user_proc_start_index + i;
+
+ if (user_proc_ft->args[cap_arg_index_in_user_proc].kind != lbArg_Indirect) {
+ cap_value = OdinLLVMBuildLoad(invoker_proc, lb_type(invoker_proc->module, captured_values[i].type), cap_value);
+ }
+
+ array_add(&call_args, cap_value);
+ }
+
+ // Push context, if needed
+ if (user_proc.calling_convention == ProcCC_Odin) {
+ LLVMValueRef p_context = LLVMBuildStructGEP2(invoker_proc->builder, block_lit_type, block_literal, 5, "context");
+ array_add(&call_args, p_context);
+ }
+
+ LLVMTypeRef fnp = lb_type_internal_for_procedures_raw(m, user_proc_value.type);
+ LLVMValueRef ret_val = LLVMBuildCall2(invoker_proc->builder, fnp, user_proc_value.value, call_args.data, (unsigned)call_args.count, "");
+
+ if (user_proc.result_count > 0 && return_kind != lbArg_Indirect) {
+ LLVMBuildRet(invoker_proc->builder, ret_val);
+ }
+ else {
+ LLVMBuildRetVoid(invoker_proc->builder);
}
}
+ lb_end_procedure_body(invoker_proc);
+
- if (!entity) {
- gbString global_name = gb_string_make(temporary_allocator(), "__$objc_SEL$");
- global_name = gb_string_append_length(global_name, name.text, name.len);
+ /// Create local block literal
+ const int BLOCK_HAS_COPY_DISPOSE = (1 << 25);
+ const int BLOCK_IS_GLOBAL = (1 << 28);
- lbAddr default_addr = lb_add_global_generated_with_name(
- default_module, t_objc_SEL, {},
- make_string(cast(u8 const *)global_name, gb_string_length(global_name)),
- &entity);
- string_map_set(&default_module->objc_selectors, name, lbObjcRef{entity, default_addr});
+ int raw_flags = is_global ? BLOCK_IS_GLOBAL : 0;
+ if (has_objc_fields) {
+ raw_flags |= BLOCK_HAS_COPY_DISPOSE;
}
- lbValue ptr = lb_find_value_from_entity(p->module, entity);
- lbAddr local_addr = lb_addr(ptr);
+ gbString block_var_name = gb_string_make(temporary_allocator(), "__$objc_block_literal_");
+ block_var_name = gb_string_append_fmt(block_var_name, "%lld", m->objc_next_block_id);
+
+ lbValue block_result = {};
+ block_result.type = block_result_type;
- if (default_module != p->module) {
- string_map_set(&p->module->objc_selectors, name, lbObjcRef{entity, local_addr});
+ lbValue isa_val = lb_find_runtime_value(m, is_global ? str_lit("_NSConcreteGlobalBlock") : str_lit("_NSConcreteStackBlock"));
+ lbValue flags_val = lb_const_int(m, t_i32, (u64)raw_flags);
+ lbValue reserved_val = lb_const_int(m, t_i32, 0);
+
+ if (is_global) {
+ LLVMValueRef p_block_lit = LLVMAddGlobal(m->mod, block_lit_type, block_var_name);
+ block_result.value = p_block_lit;
+
+ LLVMValueRef fields_values[5] = {
+ isa_val.value, // isa
+ flags_val.value, // flags
+ reserved_val.value, // reserved
+ invoker_proc->value, // invoke
+ p_descriptor // descriptor
+ };
+
+ LLVMValueRef g_block_lit_initializer = LLVMConstNamedStruct(block_lit_type, fields_values, gb_count_of(fields_values));
+ LLVMSetInitializer(p_block_lit, g_block_lit_initializer);
+
+ } else {
+ LLVMValueRef p_block_lit = llvm_alloca(p, block_lit_type, lb_alignof(block_lit_type), block_var_name);
+ block_result.value = p_block_lit;
+
+ // Initialize it
+ LLVMValueRef f_isa = LLVMBuildStructGEP2(p->builder, block_lit_type, p_block_lit, 0, "isa");
+ LLVMValueRef f_flags = LLVMBuildStructGEP2(p->builder, block_lit_type, p_block_lit, 1, "flags");
+ LLVMValueRef f_reserved = LLVMBuildStructGEP2(p->builder, block_lit_type, p_block_lit, 2, "reserved");
+ LLVMValueRef f_invoke = LLVMBuildStructGEP2(p->builder, block_lit_type, p_block_lit, 3, "invoke");
+ LLVMValueRef f_descriptor = LLVMBuildStructGEP2(p->builder, block_lit_type, p_block_lit, 4, "descriptor");
+
+ LLVMBuildStore(p->builder, isa_val.value, f_isa);
+ LLVMBuildStore(p->builder, flags_val.value, f_flags);
+ LLVMBuildStore(p->builder, reserved_val.value, f_reserved);
+ LLVMBuildStore(p->builder, invoker_proc->value, f_invoke);
+ LLVMBuildStore(p->builder, p_descriptor, f_descriptor);
+
+ // Store current context, if there is one
+ if (user_proc.calling_convention == ProcCC_Odin) {
+ LLVMValueRef f_context = LLVMBuildStructGEP2(p->builder, block_lit_type, p_block_lit, 5, "context");
+ lbAddr p_current_context = lb_find_or_generate_context_ptr(p);
+
+ LLVMValueRef context_size = LLVMConstInt(LLVMInt64TypeInContext(m->ctx), (u64)lb_sizeof(lb_type(m, t_context)), false);
+ LLVMBuildMemCpy(p->builder, f_context, lb_try_get_alignment(f_context, 1),
+ p_current_context.addr.value, lb_try_get_alignment(p_current_context.addr.value, 1), context_size);
+ }
+
+ // Store captured args into the block
+ for (isize i = 0; i < captured_values.count; i++) {
+ lbValue capture_arg = captured_values[i];
+
+ unsigned field_index = unsigned(capture_fields_offset + i);
+ LLVMValueRef f_capture = LLVMBuildStructGEP2(p->builder, block_lit_type, p_block_lit, field_index, "capture_arg");
+
+ lbValue f_capture_val = {};
+ f_capture_val.type = alloc_type_pointer(capture_arg.type);
+ f_capture_val.value = f_capture;
+
+ lb_emit_store(p, f_capture_val, capture_arg);
+ }
}
- return local_addr;
+ return block_result;
+}
+
+gb_internal lbValue lb_handle_objc_block_invoke(lbProcedure *p, Ast *expr) {
+ return {};
+}
+
+gb_internal lbValue lb_handle_objc_super(lbProcedure *p, Ast *expr) {
+ ast_node(ce, CallExpr, expr);
+ GB_ASSERT(ce->args.count == 1);
+
+ // NOTE(harold): This doesn't actually do anything by itself,
+ // it has an effect when used on the left side of a selector call expression.
+ return lb_build_expr(p, ce->args[0]);
}
gb_internal lbValue lb_handle_objc_find_selector(lbProcedure *p, Ast *expr) {
@@ -2158,41 +2722,6 @@ gb_internal lbValue lb_handle_objc_register_selector(lbProcedure *p, Ast *expr)
return lb_addr_load(p, dst);
}
-gb_internal lbAddr lb_handle_objc_find_or_register_class(lbProcedure *p, String const &name) {
- lbObjcRef *found = string_map_get(&p->module->objc_classes, name);
- if (found) {
- return found->local_module_addr;
- }
-
- lbModule *default_module = &p->module->gen->default_module;
- Entity *entity = {};
-
- if (default_module != p->module) {
- found = string_map_get(&default_module->objc_classes, name);
- if (found) {
- entity = found->entity;
- }
- }
-
- if (!entity) {
- gbString global_name = gb_string_make(temporary_allocator(), "__$objc_Class$");
- global_name = gb_string_append_length(global_name, name.text, name.len);
-
- lbAddr default_addr = lb_add_global_generated_with_name(default_module, t_objc_Class, {},
- make_string(cast(u8 const *)global_name, gb_string_length(global_name)),
- &entity);
- string_map_set(&default_module->objc_classes, name, lbObjcRef{entity, default_addr});
- }
-
- lbValue ptr = lb_find_value_from_entity(p->module, entity);
- lbAddr local_addr = lb_addr(ptr);
-
- if (default_module != p->module) {
- string_map_set(&p->module->objc_classes, name, lbObjcRef{entity, local_addr});
- }
-
- return local_addr;
-}
gb_internal lbValue lb_handle_objc_find_class(lbProcedure *p, Ast *expr) {
ast_node(ce, CallExpr, expr);
@@ -2200,7 +2729,7 @@ gb_internal lbValue lb_handle_objc_find_class(lbProcedure *p, Ast *expr) {
auto tav = ce->args[0]->tav;
GB_ASSERT(tav.value.kind == ExactValue_String);
String name = tav.value.value_string;
- return lb_addr_load(p, lb_handle_objc_find_or_register_class(p, name));
+ return lb_addr_load(p, lb_handle_objc_find_or_register_class(p, name, nullptr));
}
gb_internal lbValue lb_handle_objc_register_class(lbProcedure *p, Ast *expr) {
@@ -2210,7 +2739,7 @@ gb_internal lbValue lb_handle_objc_register_class(lbProcedure *p, Ast *expr) {
auto tav = ce->args[0]->tav;
GB_ASSERT(tav.value.kind == ExactValue_String);
String name = tav.value.value_string;
- lbAddr dst = lb_handle_objc_find_or_register_class(p, name);
+ lbAddr dst = lb_handle_objc_find_or_register_class(p, name, nullptr);
auto args = array_make<lbValue>(permanent_allocator(), 3);
args[0] = lb_const_nil(m, t_objc_Class);
@@ -2232,7 +2761,9 @@ gb_internal lbValue lb_handle_objc_id(lbProcedure *p, Ast *expr) {
GB_ASSERT(e->kind == Entity_TypeName);
String name = e->TypeName.objc_class_name;
- return lb_addr_load(p, lb_handle_objc_find_or_register_class(p, name));
+ Type *class_impl_type = e->TypeName.objc_is_implementation ? type : nullptr;
+
+ return lb_addr_load(p, lb_handle_objc_find_or_register_class(p, name, class_impl_type));
}
return lb_build_expr(p, expr);
@@ -2278,7 +2809,118 @@ gb_internal lbValue lb_handle_objc_send(lbProcedure *p, Ast *expr) {
return lb_emit_call(p, the_proc, args);
}
+gb_internal lbValue lb_handle_objc_auto_send(lbProcedure *p, Ast *expr, Slice<lbValue> const arg_values) {
+ ast_node(ce, CallExpr, expr);
+
+ lbModule *m = p->module;
+ CheckerInfo *info = m->info;
+ ObjcMsgData data = map_must_get(&info->objc_msgSend_types, expr);
+
+ Type *proc_type = data.proc_type;
+ GB_ASSERT(proc_type != nullptr);
+
+ Entity *objc_method_ent = entity_of_node(ce->proc);
+ GB_ASSERT(objc_method_ent != nullptr);
+ GB_ASSERT(objc_method_ent->kind == Entity_Procedure);
+ GB_ASSERT(objc_method_ent->Procedure.objc_selector_name.len > 0);
+
+ auto &proc = proc_type->Proc;
+ GB_ASSERT(proc.param_count >= 2);
+
+ Type *objc_super_orig_type = nullptr;
+ if (ce->args.count > 0) {
+ objc_super_orig_type = unparen_expr(ce->args[0])->tav.objc_super_target;
+ }
+ isize arg_offset = 1;
+ lbValue id = {};
+ if (!objc_method_ent->Procedure.is_objc_class_method) {
+ GB_ASSERT(ce->args.count > 0);
+ id = arg_values[0];
+
+ if (objc_super_orig_type) {
+ GB_ASSERT(objc_super_orig_type->kind == Type_Named);
+
+ auto& tn = objc_super_orig_type->Named.type_name->TypeName;
+ lbAddr p_supercls = lb_handle_objc_find_or_register_class(p, tn.objc_class_name, tn.objc_is_implementation ? objc_super_orig_type : nullptr);
+
+ lbValue supercls = lb_addr_load(p, p_supercls);
+ lbAddr p_objc_super = lb_add_local_generated(p, t_objc_super, false);
+
+ lbValue f_id = lb_emit_struct_ep(p, p_objc_super.addr, 0);
+ lbValue f_superclass = lb_emit_struct_ep(p, p_objc_super.addr, 1);
+
+ id = lb_emit_conv(p, id, t_objc_id);
+ lb_emit_store(p, f_id, id);
+ lb_emit_store(p, f_superclass, supercls);
+
+ id = p_objc_super.addr;
+ }
+ } else {
+ Entity *objc_class = objc_method_ent->Procedure.objc_class;
+ if (ce->proc->kind == Ast_SelectorExpr) {
+ // NOTE (harold): If called via a selector expression (ex: Foo.alloc()), then we should use
+ // the lhs-side to determine the class. This allows for class methods to be called
+ // with the correct class as the target, even when the method is defined in a superclass.
+ ast_node(se, SelectorExpr, ce->proc);
+ GB_ASSERT(se->expr->tav.mode == Addressing_Type && se->expr->tav.type->kind == Type_Named);
+
+ objc_class = entity_from_expr(se->expr);
+
+ GB_ASSERT(objc_class);
+ GB_ASSERT(objc_class->kind == Entity_TypeName);
+ GB_ASSERT(objc_class->TypeName.objc_class_name != "");
+ }
+
+ Type *class_impl_type = objc_class->TypeName.objc_is_implementation ? objc_class->type : nullptr;
+
+ id = lb_addr_load(p, lb_handle_objc_find_or_register_class(p, objc_class->TypeName.objc_class_name, class_impl_type));
+ arg_offset = 0;
+ }
+
+ lbValue sel = lb_addr_load(p, lb_handle_objc_find_or_register_selector(p, objc_method_ent->Procedure.objc_selector_name));
+
+ auto args = array_make<lbValue>(permanent_allocator(), 0, arg_values.count + 2 - arg_offset);
+
+ array_add(&args, id);
+ array_add(&args, sel);
+
+ for (isize i = arg_offset; i < ce->args.count; i++) {
+ array_add(&args, arg_values[i]);
+ }
+
+ lbValue the_proc = {};
+
+ if (!objc_super_orig_type) {
+ switch (data.kind) {
+ default:
+ GB_PANIC("unhandled ObjcMsgKind %u", data.kind);
+ break;
+ case ObjcMsg_normal: the_proc = lb_lookup_runtime_procedure(m, str_lit("objc_msgSend")); break;
+ case ObjcMsg_fpret: the_proc = lb_lookup_runtime_procedure(m, str_lit("objc_msgSend_fpret")); break;
+ case ObjcMsg_fp2ret: the_proc = lb_lookup_runtime_procedure(m, str_lit("objc_msgSend_fp2ret")); break;
+ case ObjcMsg_stret: the_proc = lb_lookup_runtime_procedure(m, str_lit("objc_msgSend_stret")); break;
+ }
+ } else {
+ switch (data.kind) {
+ default:
+ GB_PANIC("unhandled ObjcMsgKind %u", data.kind);
+ break;
+ case ObjcMsg_normal:
+ case ObjcMsg_fpret:
+ case ObjcMsg_fp2ret:
+ the_proc = lb_lookup_runtime_procedure(m, str_lit("objc_msgSendSuper2"));
+ break;
+ case ObjcMsg_stret:
+ the_proc = lb_lookup_runtime_procedure(m, str_lit("objc_msgSendSuper2_stret"));
+ break;
+ }
+ }
+
+ the_proc = lb_emit_conv(p, the_proc, data.proc_type);
+
+ return lb_emit_call(p, the_proc, args);
+}
gb_internal LLVMAtomicOrdering llvm_atomic_ordering_from_odin(ExactValue const &value) {
@@ -2301,3 +2943,292 @@ gb_internal LLVMAtomicOrdering llvm_atomic_ordering_from_odin(Ast *expr) {
ExactValue value = type_and_value_of_expr(expr).value;
return llvm_atomic_ordering_from_odin(value);
}
+
+
+
+struct lbDiagParaPolyEntry {
+ Entity *entity;
+ String canonical_name;
+ isize count;
+ isize total_code_size;
+};
+
+gb_internal isize lb_total_code_size(lbProcedure *p) {
+ isize instruction_count = 0;
+
+ LLVMBasicBlockRef first = LLVMGetFirstBasicBlock(p->value);
+
+ for (LLVMBasicBlockRef block = first; block != nullptr; block = LLVMGetNextBasicBlock(block)) {
+ for (LLVMValueRef instr = LLVMGetFirstInstruction(block); instr != nullptr; instr = LLVMGetNextInstruction(instr)) {
+ instruction_count += 1;
+ }
+ }
+ return instruction_count;
+
+}
+
+gb_internal void lb_do_para_poly_diagnostics(lbGenerator *gen) {
+ PtrMap<Entity * /* Parent */, lbDiagParaPolyEntry> procs = {};
+ map_init(&procs);
+ defer (map_destroy(&procs));
+
+ for (auto &entry : gen->modules) {
+ lbModule *m = entry.value;
+ for (lbProcedure *p : m->generated_procedures) {
+ Entity *e = p->entity;
+ if (e == nullptr) {
+ continue;
+ }
+ if (p->builder == nullptr) {
+ continue;
+ }
+
+ DeclInfo *d = e->decl_info;
+ Entity *para_poly_parent = d->para_poly_original;
+ if (para_poly_parent == nullptr) {
+ continue;
+ }
+
+ lbDiagParaPolyEntry *entry = map_get(&procs, para_poly_parent);
+ if (entry == nullptr) {
+ lbDiagParaPolyEntry entry = {};
+ entry.entity = para_poly_parent;
+ entry.count = 0;
+
+
+ gbString w = string_canonical_entity_name(permanent_allocator(), entry.entity);
+ String name = make_string_c(w);
+
+ for (isize i = 0; i < name.len; i++) {
+ String s = substring(name, i, name.len);
+ if (string_starts_with(s, str_lit(":proc"))) {
+ name = substring(name, 0, i);
+ break;
+ }
+ }
+
+ entry.canonical_name = name;
+
+ map_set(&procs, para_poly_parent, entry);
+ }
+ entry = map_get(&procs, para_poly_parent);
+ GB_ASSERT(entry != nullptr);
+ entry->count += 1;
+ entry->total_code_size += lb_total_code_size(p);
+ }
+ }
+
+
+ auto entries = array_make<lbDiagParaPolyEntry>(heap_allocator(), 0, procs.count);
+ defer (array_free(&entries));
+
+ for (auto &entry : procs) {
+ array_add(&entries, entry.value);
+ }
+
+ array_sort(entries, [](void const *a, void const *b) -> int {
+ lbDiagParaPolyEntry *x = cast(lbDiagParaPolyEntry *)a;
+ lbDiagParaPolyEntry *y = cast(lbDiagParaPolyEntry *)b;
+ if (x->total_code_size > y->total_code_size) {
+ return -1;
+ }
+ if (x->total_code_size < y->total_code_size) {
+ return +1;
+ }
+ return string_compare(x->canonical_name, y->canonical_name);
+ });
+
+
+ gb_printf("Parametric Polymorphic Procedure Diagnostics\n");
+ gb_printf("------------------------------------------------------------------------------------------\n");
+
+ gb_printf("Sorted by Total Instruction Count Descending (Top 100)\n\n");
+ gb_printf("Total Instruction Count | Instantiation Count | Average Instruction Count | Procedure Name\n");
+
+ isize max_count = 100;
+ for (auto &entry : entries) {
+ isize code_size = entry.total_code_size;
+ isize count = entry.count;
+ String name = entry.canonical_name;
+
+ f64 average = cast(f64)code_size / cast(f64)gb_max(count, 1);
+
+ gb_printf("%23td | %19td | %25.2f | %.*s\n", code_size, count, average, LIT(name));
+ if (max_count-- <= 0) {
+ break;
+ }
+ }
+
+ gb_printf("------------------------------------------------------------------------------------------\n");
+
+ array_sort(entries, [](void const *a, void const *b) -> int {
+ lbDiagParaPolyEntry *x = cast(lbDiagParaPolyEntry *)a;
+ lbDiagParaPolyEntry *y = cast(lbDiagParaPolyEntry *)b;
+ if (x->count > y->count) {
+ return -1;
+ }
+ if (x->count < y->count) {
+ return +1;
+ }
+
+ return string_compare(x->canonical_name, y->canonical_name);
+ });
+
+ gb_printf("Sorted by Total Instantiation Count Descending (Top 100)\n\n");
+ gb_printf("Instantiation Count | Total Instruction Count | Average Instruction Count | Procedure Name\n");
+
+ max_count = 100;
+ for (auto &entry : entries) {
+ isize code_size = entry.total_code_size;
+ isize count = entry.count;
+ String name = entry.canonical_name;
+
+
+ f64 average = cast(f64)code_size / cast(f64)gb_max(count, 1);
+
+ gb_printf("%19td | %23td | %25.2f | %.*s\n", count, code_size, average, LIT(name));
+ if (max_count-- <= 0) {
+ break;
+ }
+ }
+
+ gb_printf("------------------------------------------------------------------------------------------\n");
+
+
+ array_sort(entries, [](void const *a, void const *b) -> int {
+ lbDiagParaPolyEntry *x = cast(lbDiagParaPolyEntry *)a;
+ lbDiagParaPolyEntry *y = cast(lbDiagParaPolyEntry *)b;
+ if (x->count < y->count) {
+ return -1;
+ }
+ if (x->count > y->count) {
+ return +1;
+ }
+
+ if (x->total_code_size > y->total_code_size) {
+ return -1;
+ }
+ if (x->total_code_size < y->total_code_size) {
+ return +1;
+ }
+
+ return string_compare(x->canonical_name, y->canonical_name);
+ });
+
+ gb_printf("Single Instanced Parametric Polymorphic Procedures\n\n");
+ gb_printf("Instruction Count | Procedure Name\n");
+ for (auto &entry : entries) {
+ isize code_size = entry.total_code_size;
+ isize count = entry.count;
+ String name = entry.canonical_name;
+ if (count != 1) {
+ break;
+ }
+
+ gb_printf("%17td | %.*s\n", code_size, LIT(name));
+ }
+
+}
+
+struct lbDiagModuleEntry {
+ lbModule *m;
+ String name;
+ isize global_internal_count;
+ isize global_external_count;
+ isize proc_internal_count;
+ isize proc_external_count;
+ isize total_instruction_count;
+};
+
+gb_internal void lb_do_module_diagnostics(lbGenerator *gen) {
+ Array<lbDiagModuleEntry> modules = {};
+ array_init(&modules, heap_allocator());
+ defer (array_free(&modules));
+
+ for (auto &em : gen->modules) {
+ lbModule *m = em.value;
+
+ {
+ lbDiagModuleEntry entry = {};
+ entry.m = m;
+ entry.name = make_string_c(m->module_name);
+ array_add(&modules, entry);
+ }
+ lbDiagModuleEntry &entry = modules[modules.count-1];
+
+ for (LLVMValueRef p = LLVMGetFirstFunction(m->mod); p != nullptr; p = LLVMGetNextFunction(p)) {
+ LLVMBasicBlockRef block = LLVMGetFirstBasicBlock(p);
+ if (block == nullptr) {
+ entry.proc_external_count += 1;
+ } else {
+ entry.proc_internal_count += 1;
+
+ for (; block != nullptr; block = LLVMGetNextBasicBlock(block)) {
+ for (LLVMValueRef i = LLVMGetFirstInstruction(block); i != nullptr; i = LLVMGetNextInstruction(i)) {
+ entry.total_instruction_count += 1;
+ }
+ }
+ }
+ }
+
+ for (LLVMValueRef g = LLVMGetFirstGlobal(m->mod); g != nullptr; g = LLVMGetNextGlobal(g)) {
+ LLVMLinkage linkage = LLVMGetLinkage(g);
+ if (linkage == LLVMExternalLinkage) {
+ entry.global_external_count += 1;
+ } else {
+ entry.global_internal_count += 1;
+ }
+ }
+ }
+
+ array_sort(modules, [](void const *a, void const *b) -> int {
+ lbDiagModuleEntry *x = cast(lbDiagModuleEntry *)a;
+ lbDiagModuleEntry *y = cast(lbDiagModuleEntry *)b;
+
+ if (x->total_instruction_count > y->total_instruction_count) {
+ return -1;
+ }
+ if (x->total_instruction_count < y->total_instruction_count) {
+ return +1;
+ }
+
+ return string_compare(x->name, y->name);
+ });
+
+ gb_printf("Module Diagnostics\n\n");
+ gb_printf("Total Instructions | Global Internals | Global Externals | Proc Internals | Proc Externals | Files | Instructions/File | Instructions/Proc | Module Name\n");
+ gb_printf("-------------------+------------------+------------------+----------------+----------------+-------+-------------------+-------------------+------------\n");
+ for (auto &entry : modules) {
+ isize file_count = 1;
+ if (entry.m->file != nullptr) {
+ file_count = 1;
+ } else if (entry.m->pkg) {
+ file_count = entry.m->pkg->files.count;
+ }
+
+ f64 instructions_per_file = cast(f64)entry.total_instruction_count / gb_max(1.0, cast(f64)file_count);
+ f64 instructions_per_proc = cast(f64)entry.total_instruction_count / gb_max(1.0, cast(f64)entry.proc_internal_count);
+
+ gb_printf("%18td | %16td | %16td | %14td | %14td | %5td | %17.1f | %17.1f | %s \n",
+ entry.total_instruction_count,
+ entry.global_internal_count,
+ entry.global_external_count,
+ entry.proc_internal_count,
+ entry.proc_external_count,
+ file_count,
+ instructions_per_file,
+ instructions_per_proc,
+ entry.m->module_name);
+ }
+
+
+}
+
+gb_internal void lb_do_build_diagnostics(lbGenerator *gen) {
+ lb_do_para_poly_diagnostics(gen);
+ gb_printf("------------------------------------------------------------------------------------------\n");
+ gb_printf("------------------------------------------------------------------------------------------\n\n");
+ lb_do_module_diagnostics(gen);
+ gb_printf("------------------------------------------------------------------------------------------\n");
+ gb_printf("------------------------------------------------------------------------------------------\n\n");
+} \ No newline at end of file
diff --git a/src/main.cpp b/src/main.cpp
index 0a24d64a6..6ad20cf26 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -74,6 +74,7 @@ gb_global Timings global_timings = {0};
#include "cached.cpp"
#include "linker.cpp"
+#include "bundle_command.cpp"
#if defined(GB_SYSTEM_WINDOWS) && defined(ODIN_TILDE_BACKEND)
#define ALLOW_TILDE 1
@@ -87,13 +88,6 @@ gb_global Timings global_timings = {0};
#include "llvm_backend.cpp"
-#if defined(GB_SYSTEM_OSX)
- #include <llvm/Config/llvm-config.h>
- #if LLVM_VERSION_MAJOR < 11 || (LLVM_VERSION_MAJOR > 14 && LLVM_VERSION_MAJOR < 17) || LLVM_VERSION_MAJOR > 19
- #error LLVM Version 11..=14 or 17..=19 is required => "brew install llvm@14"
- #endif
-#endif
-
#include "bug_report.cpp"
// NOTE(bill): 'name' is used in debugging and profiling modes
@@ -148,9 +142,9 @@ gb_internal i32 system_exec_command_line_app_internal(bool exit_on_err, char con
}
wcmd = string_to_string16(permanent_allocator(), make_string(cast(u8 *)cmd_line, cmd_len-1));
- if (CreateProcessW(nullptr, wcmd.text,
- nullptr, nullptr, true, 0, nullptr, nullptr,
- &start_info, &pi)) {
+ if (CreateProcessW(nullptr, cast(wchar_t *)wcmd.text,
+ nullptr, nullptr, true, 0, nullptr, nullptr,
+ &start_info, &pi)) {
WaitForSingleObject(pi.hProcess, INFINITE);
GetExitCodeProcess(pi.hProcess, cast(DWORD *)&exit_code);
@@ -238,7 +232,7 @@ gb_internal Array<String> setup_args(int argc, char const **argv) {
wchar_t **wargv = command_line_to_wargv(GetCommandLineW(), &wargc);
auto args = array_make<String>(a, 0, wargc);
for (isize i = 0; i < wargc; i++) {
- wchar_t *warg = wargv[i];
+ u16 *warg = cast(u16 *)wargv[i];
isize wlen = string16_len(warg);
String16 wstr = make_string16(warg, wlen);
String arg = string16_to_string(a, wstr);
@@ -283,10 +277,11 @@ gb_internal void usage(String argv0, String argv1 = {}) {
print_usage_line(1, "build Compiles directory of .odin files, as an executable.");
print_usage_line(1, " One must contain the program's entry point, all must be in the same package.");
print_usage_line(1, "run Same as 'build', but also then runs the newly compiled executable.");
- print_usage_line(1, "check Parses, and type checks a directory of .odin files.");
+ print_usage_line(1, "bundle Bundles a directory in a specific layout for that platform.");
+ print_usage_line(1, "check Parses and type checks a directory of .odin files.");
print_usage_line(1, "strip-semicolon Parses, type checks, and removes unneeded semicolons from the entire program.");
print_usage_line(1, "test Builds and runs procedures with the attribute @(test) in the initial package.");
- print_usage_line(1, "doc Generates documentation on a directory of .odin files.");
+ print_usage_line(1, "doc Generates documentation from a directory of .odin files.");
print_usage_line(1, "version Prints version.");
print_usage_line(1, "report Prints information useful to reporting a bug.");
print_usage_line(1, "root Prints the root path where Odin looks for the builtin collections.");
@@ -317,6 +312,7 @@ enum BuildFlagKind {
BuildFlag_Collection,
BuildFlag_Define,
BuildFlag_BuildMode,
+ BuildFlag_KeepExecutable,
BuildFlag_Target,
BuildFlag_Subtarget,
BuildFlag_Debug,
@@ -324,6 +320,7 @@ enum BuildFlagKind {
BuildFlag_NoBoundsCheck,
BuildFlag_NoTypeAssert,
BuildFlag_NoDynamicLiterals,
+ BuildFlag_DynamicLiterals,
BuildFlag_NoCRT,
BuildFlag_NoRPath,
BuildFlag_NoEntryPoint,
@@ -373,6 +370,7 @@ enum BuildFlagKind {
BuildFlag_NoRTTI,
BuildFlag_DynamicMapCalls,
BuildFlag_ObfuscateSourceCodeLocations,
+ BuildFlag_SourceCodeLocations,
BuildFlag_Compact,
BuildFlag_GlobalDefinitions,
@@ -394,6 +392,10 @@ enum BuildFlagKind {
BuildFlag_PrintLinkerFlags,
+ BuildFlag_IntegerDivisionByZero,
+
+ BuildFlag_BuildDiagnostics,
+
// internal use only
BuildFlag_InternalFastISel,
BuildFlag_InternalIgnoreLazy,
@@ -403,6 +405,8 @@ enum BuildFlagKind {
BuildFlag_InternalCached,
BuildFlag_InternalNoInline,
BuildFlag_InternalByValue,
+ BuildFlag_InternalWeakMonomorphization,
+ BuildFlag_InternalLLVMVerification,
BuildFlag_Tilde,
@@ -415,6 +419,10 @@ enum BuildFlagKind {
BuildFlag_Subsystem,
#endif
+ BuildFlag_AndroidKeystore,
+ BuildFlag_AndroidKeystoreAlias,
+ BuildFlag_AndroidKeystorePassword,
+
BuildFlag_COUNT,
};
@@ -433,12 +441,12 @@ struct BuildFlag {
BuildFlagKind kind;
String name;
BuildFlagParamKind param_kind;
- u32 command_support;
+ u64 command_support;
bool allow_multiple;
};
-gb_internal void add_flag(Array<BuildFlag> *build_flags, BuildFlagKind kind, String name, BuildFlagParamKind param_kind, u32 command_support, bool allow_multiple=false) {
+gb_internal void add_flag(Array<BuildFlag> *build_flags, BuildFlagKind kind, String name, BuildFlagParamKind param_kind, u64 command_support, bool allow_multiple=false) {
BuildFlag flag = {kind, name, param_kind, command_support, allow_multiple};
array_add(build_flags, flag);
}
@@ -531,6 +539,7 @@ gb_internal bool parse_build_flags(Array<String> args) {
add_flag(&build_flags, BuildFlag_Collection, str_lit("collection"), BuildFlagParam_String, Command__does_check);
add_flag(&build_flags, BuildFlag_Define, str_lit("define"), BuildFlagParam_String, Command__does_check, true);
add_flag(&build_flags, BuildFlag_BuildMode, str_lit("build-mode"), BuildFlagParam_String, Command__does_build); // Commands_build is not used to allow for a better error message
+ add_flag(&build_flags, BuildFlag_KeepExecutable, str_lit("keep-executable"), BuildFlagParam_None, Command__does_build | Command_test);
add_flag(&build_flags, BuildFlag_Target, str_lit("target"), BuildFlagParam_String, Command__does_check);
add_flag(&build_flags, BuildFlag_Subtarget, str_lit("subtarget"), BuildFlagParam_String, Command__does_check);
add_flag(&build_flags, BuildFlag_Debug, str_lit("debug"), BuildFlagParam_None, Command__does_check);
@@ -539,6 +548,7 @@ gb_internal bool parse_build_flags(Array<String> args) {
add_flag(&build_flags, BuildFlag_NoTypeAssert, str_lit("no-type-assert"), BuildFlagParam_None, Command__does_check);
add_flag(&build_flags, BuildFlag_NoThreadLocal, str_lit("no-thread-local"), BuildFlagParam_None, Command__does_check);
add_flag(&build_flags, BuildFlag_NoDynamicLiterals, str_lit("no-dynamic-literals"), BuildFlagParam_None, Command__does_check);
+ add_flag(&build_flags, BuildFlag_DynamicLiterals, str_lit("dynamic-literals"), BuildFlagParam_None, Command__does_check);
add_flag(&build_flags, BuildFlag_NoCRT, str_lit("no-crt"), BuildFlagParam_None, Command__does_build);
add_flag(&build_flags, BuildFlag_NoRPath, str_lit("no-rpath"), BuildFlagParam_None, Command__does_build);
add_flag(&build_flags, BuildFlag_NoEntryPoint, str_lit("no-entry-point"), BuildFlagParam_None, Command__does_check &~ Command_test);
@@ -574,7 +584,7 @@ gb_internal bool parse_build_flags(Array<String> args) {
add_flag(&build_flags, BuildFlag_Microarch, str_lit("microarch"), BuildFlagParam_String, Command__does_build);
add_flag(&build_flags, BuildFlag_TargetFeatures, str_lit("target-features"), BuildFlagParam_String, Command__does_build);
add_flag(&build_flags, BuildFlag_StrictTargetFeatures, str_lit("strict-target-features"), BuildFlagParam_None, Command__does_build);
- add_flag(&build_flags, BuildFlag_MinimumOSVersion, str_lit("minimum-os-version"), BuildFlagParam_String, Command__does_build);
+ add_flag(&build_flags, BuildFlag_MinimumOSVersion, str_lit("minimum-os-version"), BuildFlagParam_String, Command__does_build | Command_bundle_android);
add_flag(&build_flags, BuildFlag_RelocMode, str_lit("reloc-mode"), BuildFlagParam_String, Command__does_build);
add_flag(&build_flags, BuildFlag_DisableRedZone, str_lit("disable-red-zone"), BuildFlagParam_None, Command__does_build);
@@ -591,9 +601,10 @@ gb_internal bool parse_build_flags(Array<String> args) {
add_flag(&build_flags, BuildFlag_DynamicMapCalls, str_lit("dynamic-map-calls"), BuildFlagParam_None, Command__does_check);
add_flag(&build_flags, BuildFlag_ObfuscateSourceCodeLocations, str_lit("obfuscate-source-code-locations"), BuildFlagParam_None, Command__does_build);
+ add_flag(&build_flags, BuildFlag_SourceCodeLocations, str_lit("source-code-locations"), BuildFlagParam_String, Command__does_build);
add_flag(&build_flags, BuildFlag_Short, str_lit("short"), BuildFlagParam_None, Command_doc);
- add_flag(&build_flags, BuildFlag_AllPackages, str_lit("all-packages"), BuildFlagParam_None, Command_doc | Command_test);
+ add_flag(&build_flags, BuildFlag_AllPackages, str_lit("all-packages"), BuildFlagParam_None, Command_doc | Command_test | Command_build);
add_flag(&build_flags, BuildFlag_DocFormat, str_lit("doc-format"), BuildFlagParam_None, Command_doc);
add_flag(&build_flags, BuildFlag_IgnoreWarnings, str_lit("ignore-warnings"), BuildFlagParam_None, Command_all);
@@ -608,6 +619,10 @@ gb_internal bool parse_build_flags(Array<String> args) {
add_flag(&build_flags, BuildFlag_PrintLinkerFlags, str_lit("print-linker-flags"), BuildFlagParam_None, Command_build);
+ add_flag(&build_flags, BuildFlag_IntegerDivisionByZero, str_lit("integer-division-by-zero"), BuildFlagParam_String, Command__does_check);
+
+ add_flag(&build_flags, BuildFlag_BuildDiagnostics, str_lit("build-diagnostics"), BuildFlagParam_None, Command__does_build);
+
add_flag(&build_flags, BuildFlag_InternalFastISel, str_lit("internal-fast-isel"), BuildFlagParam_None, Command_all);
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);
@@ -616,6 +631,8 @@ gb_internal bool parse_build_flags(Array<String> args) {
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);
add_flag(&build_flags, BuildFlag_InternalByValue, str_lit("internal-by-value"), BuildFlagParam_None, Command_all);
+ add_flag(&build_flags, BuildFlag_InternalWeakMonomorphization, str_lit("internal-weak-monomorphization"), BuildFlagParam_None, Command_all);
+ add_flag(&build_flags, BuildFlag_InternalLLVMVerification, str_lit("internal-ignore-llvm-verification"), BuildFlagParam_None, Command_all);
#if ALLOW_TILDE
add_flag(&build_flags, BuildFlag_Tilde, str_lit("tilde"), BuildFlagParam_None, Command__does_build);
@@ -631,9 +648,20 @@ gb_internal bool parse_build_flags(Array<String> args) {
add_flag(&build_flags, BuildFlag_Subsystem, str_lit("subsystem"), BuildFlagParam_String, Command__does_build);
#endif
+ add_flag(&build_flags, BuildFlag_AndroidKeystore, str_lit("android-keystore"), BuildFlagParam_String, Command_bundle_android);
+ add_flag(&build_flags, BuildFlag_AndroidKeystoreAlias, str_lit("android-keystore-alias"), BuildFlagParam_String, Command_bundle_android);
+ add_flag(&build_flags, BuildFlag_AndroidKeystorePassword, str_lit("android-keystore-password"), BuildFlagParam_String, Command_bundle_android);
+
- GB_ASSERT(args.count >= 3);
- Array<String> flag_args = array_slice(args, 3, args.count);
+ Array<String> flag_args = {};
+
+ if (build_context.command_kind == Command_bundle_android) {
+ GB_ASSERT(args.count >= 4);
+ flag_args = array_slice(args, 4, args.count);
+ } else {
+ GB_ASSERT(args.count >= 3);
+ flag_args = array_slice(args, 3, args.count);
+ }
bool set_flags[BuildFlag_COUNT] = {};
@@ -1112,8 +1140,9 @@ gb_internal bool parse_build_flags(Array<String> args) {
String str = value.value_string;
bool found = false;
- if (selected_target_metrics->metrics->os != TargetOs_darwin) {
- gb_printf_err("-subtarget can only be used with darwin based targets at the moment\n");
+ if (selected_target_metrics->metrics->os != TargetOs_darwin &&
+ selected_target_metrics->metrics->os != TargetOs_linux ) {
+ gb_printf_err("-subtarget can only be used with darwin and linux based targets at the moment\n");
bad_flags = true;
break;
}
@@ -1180,6 +1209,9 @@ gb_internal bool parse_build_flags(Array<String> args) {
break;
}
+ case BuildFlag_KeepExecutable:
+ build_context.keep_executable = true;
+ break;
case BuildFlag_Debug:
build_context.ODIN_DEBUG = true;
@@ -1196,6 +1228,9 @@ gb_internal bool parse_build_flags(Array<String> args) {
case BuildFlag_NoDynamicLiterals:
gb_printf_err("Warning: Use of -no-dynamic-literals is now redundant\n");
break;
+ case BuildFlag_DynamicLiterals:
+ build_context.dynamic_literals = true;
+ break;
case BuildFlag_NoCRT:
build_context.no_crt = true;
break;
@@ -1401,7 +1436,23 @@ gb_internal bool parse_build_flags(Array<String> args) {
break;
case BuildFlag_ObfuscateSourceCodeLocations:
- build_context.obfuscate_source_code_locations = true;
+ gb_printf_err("'-obfuscate-source-code-locations' is now deprecated in favor of '-source-code-locations:obfuscated'\n");
+ build_context.source_code_location_info = SourceCodeLocationInfo_Obfuscated;
+ break;
+
+ case BuildFlag_SourceCodeLocations:
+ if (str_eq_ignore_case(value.value_string, str_lit("normal"))) {
+ build_context.source_code_location_info = SourceCodeLocationInfo_Normal;
+ } else if (str_eq_ignore_case(value.value_string, str_lit("obfuscated"))) {
+ build_context.source_code_location_info = SourceCodeLocationInfo_Obfuscated;
+ } else if (str_eq_ignore_case(value.value_string, str_lit("filename"))) {
+ build_context.source_code_location_info = SourceCodeLocationInfo_Filename;
+ } else if (str_eq_ignore_case(value.value_string, str_lit("none"))) {
+ build_context.source_code_location_info = SourceCodeLocationInfo_None;
+ } else {
+ gb_printf_err("-source-code-locations:<string> options are 'normal', 'obfuscated', 'filename', and 'none'\n");
+ bad_flags = true;
+ }
break;
case BuildFlag_DefaultToNilAllocator:
@@ -1476,7 +1527,7 @@ gb_internal bool parse_build_flags(Array<String> args) {
} else if (str_eq_ignore_case(value.value_string, str_lit("unix"))) {
build_context.ODIN_ERROR_POS_STYLE = ErrorPosStyle_Unix;
} else {
- gb_printf_err("-error-pos-style options are 'unix', 'odin' and 'default' (odin)\n");
+ gb_printf_err("-error-pos-style options are 'unix', 'odin', and 'default' (odin)\n");
bad_flags = true;
}
break;
@@ -1500,6 +1551,24 @@ gb_internal bool parse_build_flags(Array<String> args) {
build_context.print_linker_flags = true;
break;
+ case BuildFlag_IntegerDivisionByZero:
+ GB_ASSERT(value.kind == ExactValue_String);
+ if (str_eq_ignore_case(value.value_string, "trap")) {
+ build_context.integer_division_by_zero_behaviour = IntegerDivisionByZero_Trap;
+ } else if (str_eq_ignore_case(value.value_string, "zero")) {
+ build_context.integer_division_by_zero_behaviour = IntegerDivisionByZero_Zero;
+ } else if (str_eq_ignore_case(value.value_string, "self")) {
+ build_context.integer_division_by_zero_behaviour = IntegerDivisionByZero_Self;
+ }else {
+ gb_printf_err("-integer-division-by-zero options are 'trap', 'zero', and 'self'.\n");
+ bad_flags = true;
+ }
+ break;
+
+ case BuildFlag_BuildDiagnostics:
+ build_context.build_diagnostics = true;
+ break;
+
case BuildFlag_InternalFastISel:
build_context.fast_isel = true;
break;
@@ -1526,6 +1595,13 @@ gb_internal bool parse_build_flags(Array<String> args) {
case BuildFlag_InternalByValue:
build_context.internal_by_value = true;
break;
+ case BuildFlag_InternalWeakMonomorphization:
+ build_context.internal_weak_monomorphization = true;
+ break;
+ case BuildFlag_InternalLLVMVerification:
+ build_context.internal_ignore_llvm_verification = true;
+ break;
+
case BuildFlag_Tilde:
build_context.tilde_backend = true;
@@ -1534,6 +1610,11 @@ gb_internal bool parse_build_flags(Array<String> args) {
case BuildFlag_Sanitize:
GB_ASSERT(value.kind == ExactValue_String);
+ if (build_context.sanitizer_flags != 0) {
+ gb_printf_err("-sanitize:<string> may only be used once\n");
+ bad_flags = true;
+ }
+
if (str_eq_ignore_case(value.value_string, str_lit("address"))) {
build_context.sanitizer_flags |= SanitizerFlag_Address;
} else if (str_eq_ignore_case(value.value_string, str_lit("memory"))) {
@@ -1606,9 +1687,9 @@ gb_internal bool parse_build_flags(Array<String> args) {
GB_ASSERT(value.kind == ExactValue_String);
String subsystem = value.value_string;
bool subsystem_found = false;
- for (int i = 0; i < Windows_Subsystem_COUNT; i++) {
+ for (int i = 1; i < Windows_Subsystem_COUNT; i++) {
if (str_eq_ignore_case(subsystem, windows_subsystem_names[i])) {
- build_context.ODIN_WINDOWS_SUBSYSTEM = windows_subsystem_names[i];
+ build_context.ODIN_WINDOWS_SUBSYSTEM = Windows_Subsystem(i);
subsystem_found = true;
break;
}
@@ -1617,7 +1698,7 @@ gb_internal bool parse_build_flags(Array<String> args) {
// WINDOW is a hidden alias for WINDOWS. Check it.
String subsystem_windows_alias = str_lit("WINDOW");
if (!subsystem_found && str_eq_ignore_case(subsystem, subsystem_windows_alias)) {
- build_context.ODIN_WINDOWS_SUBSYSTEM = windows_subsystem_names[Windows_Subsystem_WINDOWS];
+ build_context.ODIN_WINDOWS_SUBSYSTEM = Windows_Subsystem_WINDOWS;
subsystem_found = true;
break;
}
@@ -1625,8 +1706,8 @@ gb_internal bool parse_build_flags(Array<String> args) {
if (!subsystem_found) {
gb_printf_err("Invalid -subsystem string, got %.*s. Expected one of:\n", LIT(subsystem));
gb_printf_err("\t");
- for (int i = 0; i < Windows_Subsystem_COUNT; i++) {
- if (i > 0) {
+ for (int i = 1; i < Windows_Subsystem_COUNT; i++) {
+ if (i > 1) {
gb_printf_err(", ");
}
gb_printf_err("%.*s", LIT(windows_subsystem_names[i]));
@@ -1644,6 +1725,20 @@ gb_internal bool parse_build_flags(Array<String> args) {
}
#endif
+ case BuildFlag_AndroidKeystore:
+ GB_ASSERT(value.kind == ExactValue_String);
+ build_context.android_keystore = value.value_string;
+ break;
+
+ case BuildFlag_AndroidKeystoreAlias:
+ GB_ASSERT(value.kind == ExactValue_String);
+ build_context.android_keystore_alias = value.value_string;
+ break;
+
+ case BuildFlag_AndroidKeystorePassword:
+ GB_ASSERT(value.kind == ExactValue_String);
+ build_context.android_keystore_password = value.value_string;
+ break;
}
}
@@ -1659,8 +1754,8 @@ gb_internal bool parse_build_flags(Array<String> args) {
gb_printf_err("'%.*s' is supported with the following commands:\n", LIT(name));
gb_printf_err("\t");
i32 count = 0;
- for (u32 i = 0; i < 32; i++) {
- if (found_bf.command_support & (1<<i)) {
+ for (u64 i = 0; i < 64; i++) {
+ if (found_bf.command_support & (1ull<<i)) {
if (count > 0) {
gb_printf_err(", ");
}
@@ -1698,6 +1793,12 @@ gb_internal bool parse_build_flags(Array<String> args) {
bad_flags = true;
}
+
+ if ((build_context.command_kind & (Command_doc | Command_test)) == 0 && build_context.test_all_packages) {
+ gb_printf_err("`-test-all-packages` can only be used with `odin build -build-mode:test`, `odin test`, or `odin doc`.\n");
+ bad_flags = true;
+ }
+
return !bad_flags;
}
@@ -1950,39 +2051,39 @@ gb_internal void show_timings(Checker *c, Timings *t) {
if (build_context.show_debug_messages && build_context.show_more_timings) {
{
- gb_printf("\n");
- gb_printf("Total Lines - %td\n", lines);
- gb_printf("Total Tokens - %td\n", tokens);
- gb_printf("Total Files - %td\n", files);
- gb_printf("Total Packages - %td\n", packages);
- gb_printf("Total File Size - %td\n", total_file_size);
- gb_printf("\n");
+ gb_printf_err("\n");
+ gb_printf_err("Total Lines - %td\n", lines);
+ gb_printf_err("Total Tokens - %td\n", tokens);
+ gb_printf_err("Total Files - %td\n", files);
+ gb_printf_err("Total Packages - %td\n", packages);
+ gb_printf_err("Total File Size - %td\n", total_file_size);
+ gb_printf_err("\n");
}
{
f64 time = total_tokenizing_time;
- gb_printf("Tokenization Only\n");
- gb_printf("LOC/s - %.3f\n", cast(f64)lines/time);
- gb_printf("us/LOC - %.3f\n", 1.0e6*time/cast(f64)lines);
- gb_printf("Tokens/s - %.3f\n", cast(f64)tokens/time);
- gb_printf("us/Token - %.3f\n", 1.0e6*time/cast(f64)tokens);
- gb_printf("bytes/s - %.3f\n", cast(f64)total_file_size/time);
- gb_printf("MiB/s - %.3f\n", cast(f64)(total_file_size/time)/(1024*1024));
- gb_printf("us/bytes - %.3f\n", 1.0e6*time/cast(f64)total_file_size);
+ gb_printf_err("Tokenization Only\n");
+ gb_printf_err("LOC/s - %.3f\n", cast(f64)lines/time);
+ gb_printf_err("us/LOC - %.3f\n", 1.0e6*time/cast(f64)lines);
+ gb_printf_err("Tokens/s - %.3f\n", cast(f64)tokens/time);
+ gb_printf_err("us/Token - %.3f\n", 1.0e6*time/cast(f64)tokens);
+ gb_printf_err("bytes/s - %.3f\n", cast(f64)total_file_size/time);
+ gb_printf_err("MiB/s - %.3f\n", cast(f64)(total_file_size/time)/(1024*1024));
+ gb_printf_err("us/bytes - %.3f\n", 1.0e6*time/cast(f64)total_file_size);
- gb_printf("\n");
+ gb_printf_err("\n");
}
{
f64 time = total_parsing_time;
- gb_printf("Parsing Only\n");
- gb_printf("LOC/s - %.3f\n", cast(f64)lines/time);
- gb_printf("us/LOC - %.3f\n", 1.0e6*time/cast(f64)lines);
- gb_printf("Tokens/s - %.3f\n", cast(f64)tokens/time);
- gb_printf("us/Token - %.3f\n", 1.0e6*time/cast(f64)tokens);
- gb_printf("bytes/s - %.3f\n", cast(f64)total_file_size/time);
- gb_printf("MiB/s - %.3f\n", cast(f64)(total_file_size/time)/(1024*1024));
- gb_printf("us/bytes - %.3f\n", 1.0e6*time/cast(f64)total_file_size);
+ gb_printf_err("Parsing Only\n");
+ gb_printf_err("LOC/s - %.3f\n", cast(f64)lines/time);
+ gb_printf_err("us/LOC - %.3f\n", 1.0e6*time/cast(f64)lines);
+ gb_printf_err("Tokens/s - %.3f\n", cast(f64)tokens/time);
+ gb_printf_err("us/Token - %.3f\n", 1.0e6*time/cast(f64)tokens);
+ gb_printf_err("bytes/s - %.3f\n", cast(f64)total_file_size/time);
+ gb_printf_err("MiB/s - %.3f\n", cast(f64)(total_file_size/time)/(1024*1024));
+ gb_printf_err("us/bytes - %.3f\n", 1.0e6*time/cast(f64)total_file_size);
- gb_printf("\n");
+ gb_printf_err("\n");
}
{
TimeStamp ts = {};
@@ -1995,16 +2096,16 @@ gb_internal void show_timings(Checker *c, Timings *t) {
GB_ASSERT(ts.label == "parse files");
f64 parse_time = time_stamp_as_s(ts, t->freq);
- gb_printf("Parse pass\n");
- gb_printf("LOC/s - %.3f\n", cast(f64)lines/parse_time);
- gb_printf("us/LOC - %.3f\n", 1.0e6*parse_time/cast(f64)lines);
- gb_printf("Tokens/s - %.3f\n", cast(f64)tokens/parse_time);
- gb_printf("us/Token - %.3f\n", 1.0e6*parse_time/cast(f64)tokens);
- gb_printf("bytes/s - %.3f\n", cast(f64)total_file_size/parse_time);
- gb_printf("MiB/s - %.3f\n", cast(f64)(total_file_size/parse_time)/(1024*1024));
- gb_printf("us/bytes - %.3f\n", 1.0e6*parse_time/cast(f64)total_file_size);
+ gb_printf_err("Parse pass\n");
+ gb_printf_err("LOC/s - %.3f\n", cast(f64)lines/parse_time);
+ gb_printf_err("us/LOC - %.3f\n", 1.0e6*parse_time/cast(f64)lines);
+ gb_printf_err("Tokens/s - %.3f\n", cast(f64)tokens/parse_time);
+ gb_printf_err("us/Token - %.3f\n", 1.0e6*parse_time/cast(f64)tokens);
+ gb_printf_err("bytes/s - %.3f\n", cast(f64)total_file_size/parse_time);
+ gb_printf_err("MiB/s - %.3f\n", cast(f64)(total_file_size/parse_time)/(1024*1024));
+ gb_printf_err("us/bytes - %.3f\n", 1.0e6*parse_time/cast(f64)total_file_size);
- gb_printf("\n");
+ gb_printf_err("\n");
}
{
TimeStamp ts = {};
@@ -2025,27 +2126,27 @@ gb_internal void show_timings(Checker *c, Timings *t) {
ts.finish = ts_end.finish;
f64 parse_time = time_stamp_as_s(ts, t->freq);
- gb_printf("Checker pass\n");
- gb_printf("LOC/s - %.3f\n", cast(f64)lines/parse_time);
- gb_printf("us/LOC - %.3f\n", 1.0e6*parse_time/cast(f64)lines);
- gb_printf("Tokens/s - %.3f\n", cast(f64)tokens/parse_time);
- gb_printf("us/Token - %.3f\n", 1.0e6*parse_time/cast(f64)tokens);
- gb_printf("bytes/s - %.3f\n", cast(f64)total_file_size/parse_time);
- gb_printf("MiB/s - %.3f\n", (cast(f64)total_file_size/parse_time)/(1024*1024));
- gb_printf("us/bytes - %.3f\n", 1.0e6*parse_time/cast(f64)total_file_size);
- gb_printf("\n");
+ gb_printf_err("Checker pass\n");
+ gb_printf_err("LOC/s - %.3f\n", cast(f64)lines/parse_time);
+ gb_printf_err("us/LOC - %.3f\n", 1.0e6*parse_time/cast(f64)lines);
+ gb_printf_err("Tokens/s - %.3f\n", cast(f64)tokens/parse_time);
+ gb_printf_err("us/Token - %.3f\n", 1.0e6*parse_time/cast(f64)tokens);
+ gb_printf_err("bytes/s - %.3f\n", cast(f64)total_file_size/parse_time);
+ gb_printf_err("MiB/s - %.3f\n", (cast(f64)total_file_size/parse_time)/(1024*1024));
+ gb_printf_err("us/bytes - %.3f\n", 1.0e6*parse_time/cast(f64)total_file_size);
+ gb_printf_err("\n");
}
{
f64 total_time = t->total_time_seconds;
- gb_printf("Total pass\n");
- gb_printf("LOC/s - %.3f\n", cast(f64)lines/total_time);
- gb_printf("us/LOC - %.3f\n", 1.0e6*total_time/cast(f64)lines);
- gb_printf("Tokens/s - %.3f\n", cast(f64)tokens/total_time);
- gb_printf("us/Token - %.3f\n", 1.0e6*total_time/cast(f64)tokens);
- gb_printf("bytes/s - %.3f\n", cast(f64)total_file_size/total_time);
- gb_printf("MiB/s - %.3f\n", cast(f64)(total_file_size/total_time)/(1024*1024));
- gb_printf("us/bytes - %.3f\n", 1.0e6*total_time/cast(f64)total_file_size);
- gb_printf("\n");
+ gb_printf_err("Total pass\n");
+ gb_printf_err("LOC/s - %.3f\n", cast(f64)lines/total_time);
+ gb_printf_err("us/LOC - %.3f\n", 1.0e6*total_time/cast(f64)lines);
+ gb_printf_err("Tokens/s - %.3f\n", cast(f64)tokens/total_time);
+ gb_printf_err("us/Token - %.3f\n", 1.0e6*total_time/cast(f64)tokens);
+ gb_printf_err("bytes/s - %.3f\n", cast(f64)total_file_size/total_time);
+ gb_printf_err("MiB/s - %.3f\n", cast(f64)(total_file_size/total_time)/(1024*1024));
+ gb_printf_err("us/bytes - %.3f\n", 1.0e6*total_time/cast(f64)total_file_size);
+ gb_printf_err("\n");
}
}
}
@@ -2176,7 +2277,7 @@ gb_internal void remove_temp_files(lbGenerator *gen) {
return;
}
- TIME_SECTION("remove keep temp files");
+ TIME_SECTION("remove temp files");
for (String const &path : gen->output_temp_paths) {
gb_file_remove(cast(char const *)path.text);
@@ -2196,20 +2297,30 @@ gb_internal void remove_temp_files(lbGenerator *gen) {
}
-gb_internal void print_show_help(String const arg0, String command, String optional_flag = {}) {
+gb_internal int print_show_help(String const arg0, String command, String optional_flag = {}) {
+ bool help_resolved = false;
+ bool printed_usage_header = false;
+ bool printed_flags_header = false;
+
if (command == "help" && optional_flag.len != 0 && optional_flag[0] != '-') {
command = optional_flag;
optional_flag = {};
}
- print_usage_line(0, "%.*s is a tool for managing Odin source code.", LIT(arg0));
- print_usage_line(0, "Usage:");
- print_usage_line(1, "%.*s %.*s [arguments]", LIT(arg0), LIT(command));
- print_usage_line(0, "");
- defer (print_usage_line(0, ""));
-
+ auto const print_usage_header_once = [&help_resolved, &printed_usage_header, arg0, command]() {
+ if (printed_usage_header) {
+ return;
+ }
+ print_usage_line(0, "%.*s is a tool for managing Odin source code.", LIT(arg0));
+ print_usage_line(0, "Usage:");
+ print_usage_line(1, "%.*s %.*s [arguments]", LIT(arg0), LIT(command));
+ print_usage_line(0, "");
+ help_resolved = true;
+ printed_usage_header = true;
+ };
if (command == "build") {
+ print_usage_header_once();
print_usage_line(1, "build Compiles directory of .odin files as an executable.");
print_usage_line(2, "One must contain the program's entry point, all must be in the same package.");
print_usage_line(2, "Use `-file` to build a single file instead.");
@@ -2218,6 +2329,7 @@ gb_internal void print_show_help(String const arg0, String command, String optio
print_usage_line(3, "odin build <dir> Builds package in <dir>.");
print_usage_line(3, "odin build filename.odin -file Builds single-file package, must contain entry point.");
} else if (command == "run") {
+ print_usage_header_once();
print_usage_line(1, "run Same as 'build', but also then runs the newly compiled executable.");
print_usage_line(2, "Append an empty flag and then the args, '-- <args>', to specify args for the output.");
print_usage_line(2, "Examples:");
@@ -2225,24 +2337,40 @@ gb_internal void print_show_help(String const arg0, String command, String optio
print_usage_line(3, "odin run <dir> Builds and runs package in <dir>.");
print_usage_line(3, "odin run filename.odin -file Builds and runs single-file package, must contain entry point.");
} else if (command == "check") {
+ print_usage_header_once();
print_usage_line(1, "check Parses and type checks directory of .odin files.");
print_usage_line(2, "Examples:");
print_usage_line(3, "odin check . Type checks package in current directory.");
print_usage_line(3, "odin check <dir> Type checks package in <dir>.");
print_usage_line(3, "odin check filename.odin -file Type checks single-file package, must contain entry point.");
} else if (command == "test") {
+ print_usage_header_once();
print_usage_line(1, "test Builds and runs procedures with the attribute @(test) in the initial package.");
} else if (command == "doc") {
+ print_usage_header_once();
print_usage_line(1, "doc Generates documentation from a directory of .odin files.");
print_usage_line(2, "Examples:");
print_usage_line(3, "odin doc . Generates documentation on package in current directory.");
print_usage_line(3, "odin doc <dir> Generates documentation on package in <dir>.");
print_usage_line(3, "odin doc filename.odin -file Generates documentation on single-file package.");
} else if (command == "version") {
+ print_usage_header_once();
print_usage_line(1, "version Prints version.");
} else if (command == "strip-semicolon") {
+ print_usage_header_once();
print_usage_line(1, "strip-semicolon");
print_usage_line(2, "Parses and type checks .odin file(s) and then removes unneeded semicolons from the entire project.");
+ } else if (command == "bundle") {
+ print_usage_header_once();
+ print_usage_line(1, "bundle <platform> Bundles a directory in a specific layout for that platform");
+ print_usage_line(2, "Supported platforms:");
+ print_usage_line(3, "android");
+ } else if (command == "report") {
+ print_usage_header_once();
+ print_usage_line(1, "report Prints information useful to reporting a bug.");
+ } else if (command == "root") {
+ print_usage_header_once();
+ print_usage_line(1, "root Prints the root path where Odin looks for the builtin collections.");
}
bool doc = command == "doc";
@@ -2252,6 +2380,7 @@ gb_internal void print_show_help(String const arg0, String command, String optio
bool strip_semicolon = command == "strip-semicolon";
bool check_only = command == "check" || strip_semicolon;
bool check = run_or_build || check_only;
+ bool bundle = command == "bundle";
if (command == "help") {
doc = true;
@@ -2263,13 +2392,10 @@ gb_internal void print_show_help(String const arg0, String command, String optio
check = true;
}
- print_usage_line(0, "");
- print_usage_line(1, "Flags");
- print_usage_line(0, "");
- auto const print_flag = [&optional_flag](char const *flag) -> bool {
+ auto const print_flag = [&optional_flag, &help_resolved, &printed_flags_header, print_usage_header_once](char const *flag) -> bool {
if (optional_flag.len != 0) {
String f = make_string_c(flag);
isize i = string_index_byte(f, ':');
@@ -2280,6 +2406,14 @@ gb_internal void print_show_help(String const arg0, String command, String optio
return false;
}
}
+ print_usage_header_once();
+ if (!printed_flags_header) {
+ print_usage_line(0, "");
+ print_usage_line(1, "Flags");
+ print_usage_line(0, "");
+ printed_flags_header = true;
+ }
+ help_resolved = true;
print_usage_line(0, "");
print_usage_line(1, flag);
return true;
@@ -2301,20 +2435,20 @@ gb_internal void print_show_help(String const arg0, String command, String optio
if (print_flag("-build-mode:<mode>")) {
print_usage_line(2, "Sets the build mode.");
print_usage_line(2, "Available options:");
- print_usage_line(3, "-build-mode:exe Builds as an executable.");
- print_usage_line(3, "-build-mode:test Builds as an executable that executes tests.");
- print_usage_line(3, "-build-mode:dll Builds as a dynamically linked library.");
- print_usage_line(3, "-build-mode:shared Builds as a dynamically linked library.");
- print_usage_line(3, "-build-mode:dynamic 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:obj Builds as an object file.");
- print_usage_line(3, "-build-mode:object Builds as an object file.");
- print_usage_line(3, "-build-mode:assembly Builds as an assembly file.");
- print_usage_line(3, "-build-mode:assembler Builds as an assembly file.");
- print_usage_line(3, "-build-mode:asm Builds as an assembly file.");
- print_usage_line(3, "-build-mode:llvm-ir Builds as an LLVM IR file.");
- print_usage_line(3, "-build-mode:llvm Builds as an LLVM IR file.");
+ print_usage_line(3, "-build-mode:exe Builds as an executable.");
+ print_usage_line(3, "-build-mode:test Builds as an executable that executes tests.");
+ print_usage_line(3, "-build-mode:dll Builds as a dynamically linked library.");
+ print_usage_line(3, "-build-mode:shared Builds as a dynamically linked library.");
+ print_usage_line(3, "-build-mode:dynamic 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:obj Builds as an object file.");
+ print_usage_line(3, "-build-mode:object Builds as an object file.");
+ print_usage_line(3, "-build-mode:assembly Builds as an assembly file.");
+ print_usage_line(3, "-build-mode:assembler Builds as an assembly file.");
+ print_usage_line(3, "-build-mode:asm Builds as an assembly file.");
+ print_usage_line(3, "-build-mode:llvm-ir Builds as an LLVM IR file.");
+ print_usage_line(3, "-build-mode:llvm Builds as an LLVM IR file.");
}
}
@@ -2323,16 +2457,16 @@ gb_internal void print_show_help(String const arg0, String command, String optio
print_usage_line(2, "Defines a library collection used for imports.");
print_usage_line(2, "Example: -collection:shared=dir/to/shared");
print_usage_line(2, "Usage in Code:");
- print_usage_line(3, "import \"shared:foo\"");
+ print_usage_line(3, "import \"shared:foo\"");
}
if (print_flag("-custom-attribute:<string>")) {
print_usage_line(2, "Add a custom attribute which will be ignored if it is unknown.");
print_usage_line(2, "This can be used with metaprogramming tools.");
print_usage_line(2, "Examples:");
- print_usage_line(3, "-custom-attribute:my_tag");
- print_usage_line(3, "-custom-attribute:my_tag,the_other_thing");
- print_usage_line(3, "-custom-attribute:my_tag -custom-attribute:the_other_thing");
+ print_usage_line(3, "-custom-attribute:my_tag");
+ print_usage_line(3, "-custom-attribute:my_tag,the_other_thing");
+ print_usage_line(3, "-custom-attribute:my_tag -custom-attribute:the_other_thing");
}
}
@@ -2355,7 +2489,7 @@ gb_internal void print_show_help(String const arg0, String command, String optio
print_usage_line(2, "Defines a scalar boolean, integer or string as global constant.");
print_usage_line(2, "Example: -define:SPAM=123");
print_usage_line(2, "Usage in code:");
- print_usage_line(3, "#config(SPAM, default_value)");
+ print_usage_line(3, "#config(SPAM, default_value)");
}
}
@@ -2390,9 +2524,9 @@ gb_internal void print_show_help(String const arg0, String command, String optio
if (check) {
if (print_flag("-error-pos-style:<string>")) {
print_usage_line(2, "Available options:");
- print_usage_line(3, "-error-pos-style:unix file/path:45:3:");
- print_usage_line(3, "-error-pos-style:odin file/path(45:3)");
- print_usage_line(3, "-error-pos-style:default (Defaults to 'odin'.)");
+ print_usage_line(3, "-error-pos-style:unix file/path:45:3:");
+ print_usage_line(3, "-error-pos-style:odin file/path(45:3)");
+ print_usage_line(3, "-error-pos-style:default (Defaults to 'odin'.)");
}
if (print_flag("-export-defineables:<filename>")) {
@@ -2403,8 +2537,8 @@ gb_internal void print_show_help(String const arg0, String command, String optio
if (print_flag("-export-dependencies:<format>")) {
print_usage_line(2, "Exports dependencies to one of a few formats. Requires `-export-dependencies-file`.");
print_usage_line(2, "Available options:");
- print_usage_line(3, "-export-dependencies:make Exports in Makefile format");
- print_usage_line(3, "-export-dependencies:json Exports in JSON format");
+ print_usage_line(3, "-export-dependencies:make Exports in Makefile format");
+ print_usage_line(3, "-export-dependencies:json Exports in JSON format");
}
if (print_flag("-export-dependencies-file:<filename>")) {
@@ -2415,8 +2549,8 @@ gb_internal void print_show_help(String const arg0, String command, String optio
if (print_flag("-export-timings:<format>")) {
print_usage_line(2, "Exports timings to one of a few formats. Requires `-show-timings` or `-show-more-timings`.");
print_usage_line(2, "Available options:");
- print_usage_line(3, "-export-timings:json Exports compile time stats to JSON.");
- print_usage_line(3, "-export-timings:csv Exports compile time stats to CSV.");
+ print_usage_line(3, "-export-timings:json Exports compile time stats to JSON.");
+ print_usage_line(3, "-export-timings:csv Exports compile time stats to CSV.");
}
if (print_flag("-export-timings-file:<filename>")) {
@@ -2464,7 +2598,20 @@ gb_internal void print_show_help(String const arg0, String command, String optio
if (print_flag("-ignore-warnings")) {
print_usage_line(2, "Ignores warning messages.");
}
+ }
+
+ if (check) {
+ if (print_flag("-integer-division-by-zero:<string>")) {
+ print_usage_line(2, "Specifies the default behaviour for integer division by zero.");
+ print_usage_line(2, "Available Options:");
+ print_usage_line(3, "-integer-division-by-zero:trap Trap on division/modulo/remainder by zero");
+ print_usage_line(3, "-integer-division-by-zero:zero x/0 == 0 and x%%0 == x and x%%%%0 == x");
+ print_usage_line(3, "-integer-division-by-zero:self x/0 == x and x%%0 == 0 and x%%%%0 == 0");
+ print_usage_line(3, "-integer-division-by-zero:all-bits x/0 == ~T(0) and x%%0 == x and x%%%%0 == x");
+ }
+ }
+ if (check) {
if (print_flag("-json-errors")) {
print_usage_line(2, "Prints the error messages as json to stderr.");
}
@@ -2480,6 +2627,14 @@ gb_internal void print_show_help(String const arg0, String command, String optio
}
}
+ if (test_only || run_or_build) {
+ if (print_flag("-keep-executable")) {
+ print_usage_line(2, "Keep the executable generated by `odin test` or `odin run` after running it. We clean it up by default.");
+ print_usage_line(2, "If you build your program or test using `odin build`, the compiler does not automatically execute");
+ print_usage_line(2, "the resulting program, and this option is not applicable.");
+ }
+ }
+
if (run_or_build) {
if (print_flag("-linker:<string>")) {
print_usage_line(2, "Specify the linker to use.");
@@ -2506,9 +2661,9 @@ gb_internal void print_show_help(String const arg0, String command, String optio
if (print_flag("-microarch:<string>")) {
print_usage_line(2, "Specifies the specific micro-architecture for the build in a string.");
print_usage_line(2, "Examples:");
- print_usage_line(3, "-microarch:sandybridge");
- print_usage_line(3, "-microarch:native");
- print_usage_line(3, "-microarch:\"?\" for a list");
+ print_usage_line(3, "-microarch:sandybridge");
+ print_usage_line(3, "-microarch:native");
+ print_usage_line(3, "-microarch:\"?\" for a list");
}
}
@@ -2519,13 +2674,15 @@ gb_internal void print_show_help(String const arg0, String command, String optio
}
}
- if (run_or_build) {
+ if (run_or_build || bundle) {
if (print_flag("-minimum-os-version:<string>")) {
print_usage_line(2, "Sets the minimum OS version targeted by the application.");
print_usage_line(2, "Default: -minimum-os-version:11.0.0");
- print_usage_line(2, "Only used when target is Darwin, if given, linking mismatched versions will emit a warning.");
+ print_usage_line(2, "Only used when target is Darwin or subtarget is Android, if given, linking mismatched versions will emit a warning.");
}
+ }
+ if (run_or_build) {
if (print_flag("-no-bounds-check")) {
print_usage_line(2, "Disables bounds checking program wide.");
}
@@ -2563,10 +2720,10 @@ gb_internal void print_show_help(String const arg0, String command, String optio
if (print_flag("-o:<string>")) {
print_usage_line(2, "Sets the optimization mode for compilation.");
print_usage_line(2, "Available options:");
- print_usage_line(3, "-o:none");
- print_usage_line(3, "-o:minimal");
- print_usage_line(3, "-o:size");
- print_usage_line(3, "-o:speed");
+ print_usage_line(3, "-o:none");
+ print_usage_line(3, "-o:minimal");
+ print_usage_line(3, "-o:size");
+ print_usage_line(3, "-o:speed");
if (LB_USE_NEW_PASS_SYSTEM) {
print_usage_line(3, "-o:aggressive (use this with caution)");
}
@@ -2574,8 +2731,14 @@ gb_internal void print_show_help(String const arg0, String command, String optio
}
- if (print_flag("-obfuscate-source-code-locations")) {
- print_usage_line(2, "Obfuscate the file and procedure strings, and line and column numbers, stored with a 'runtime.Source_Code_Location' value.");
+ if (print_flag("-source-code-locations:<string>")) {
+ print_usage_line(2, "Processes the file and procedure strings, and line and column numbers, stored with a 'runtime.Source_Code_Location' value.");
+ print_usage_line(2, "Available options:");
+ print_usage_line(3, "-source-code-locations:normal");
+ print_usage_line(3, "-source-code-locations:obfuscated");
+ print_usage_line(3, "-source-code-locations:filename");
+ print_usage_line(3, "-source-code-locations:none");
+ print_usage_line(2, "The default is -source-code-locations:normal.");
}
@@ -2587,7 +2750,7 @@ gb_internal void print_show_help(String const arg0, String command, String optio
if (doc) {
if (print_flag("-out:<filepath>")) {
- print_usage_line(2, "Sets the base name of the resultig .odin-doc file.");
+ print_usage_line(2, "Sets the base name of the resulting .odin-doc file.");
print_usage_line(2, "The extension can be optionally included; the resulting file will always have an extension of '.odin-doc'.");
print_usage_line(2, "Example: -out:foo");
}
@@ -2617,10 +2780,10 @@ gb_internal void print_show_help(String const arg0, String command, String optio
if (print_flag("-reloc-mode:<string>")) {
print_usage_line(2, "Specifies the reloc mode.");
print_usage_line(2, "Available options:");
- print_usage_line(3, "-reloc-mode:default");
- print_usage_line(3, "-reloc-mode:static");
- print_usage_line(3, "-reloc-mode:pic");
- print_usage_line(3, "-reloc-mode:dynamic-no-pic");
+ print_usage_line(3, "-reloc-mode:default");
+ print_usage_line(3, "-reloc-mode:static");
+ print_usage_line(3, "-reloc-mode:pic");
+ print_usage_line(3, "-reloc-mode:dynamic-no-pic");
}
#if defined(GB_SYSTEM_WINDOWS)
@@ -2635,10 +2798,9 @@ gb_internal void print_show_help(String const arg0, String command, String optio
if (print_flag("-sanitize:<string>")) {
print_usage_line(2, "Enables sanitization analysis.");
print_usage_line(2, "Available options:");
- print_usage_line(3, "-sanitize:address");
- print_usage_line(3, "-sanitize:memory");
- print_usage_line(3, "-sanitize:thread");
- print_usage_line(2, "NOTE: This flag can be used multiple times.");
+ print_usage_line(3, "-sanitize:address");
+ print_usage_line(3, "-sanitize:memory");
+ print_usage_line(3, "-sanitize:thread");
}
}
@@ -2682,7 +2844,7 @@ gb_internal void print_show_help(String const arg0, String command, String optio
print_usage_line(2, "Errs on unneeded tokens, such as unneeded semicolons.");
print_usage_line(2, "Errs on missing trailing commas followed by a newline.");
print_usage_line(2, "Errs on deprecated syntax.");
- print_usage_line(2, "Errs when the attached-brace style in not adhered to (also known as 1TBS).");
+ print_usage_line(2, "Errs when the attached-brace style is not adhered to (also known as 1TBS).");
print_usage_line(2, "Errs when 'case' labels are not in the same column as the associated 'switch' token.");
}
}
@@ -2698,17 +2860,32 @@ gb_internal void print_show_help(String const arg0, String command, String optio
print_usage_line(2, "[Windows only]");
print_usage_line(2, "Defines the subsystem for the application.");
print_usage_line(2, "Available options:");
- print_usage_line(3, "-subsystem:console");
- print_usage_line(3, "-subsystem:windows");
+ print_usage_line(3, "-subsystem:console");
+ print_usage_line(3, "-subsystem:windows");
}
#endif
+ }
+ if (build) {
+ if (print_flag("-subtarget:<subtarget>")) {
+ print_usage_line(2, "[Darwin and Linux only]");
+ print_usage_line(2, "Available subtargets:");
+ String prefix = str_lit("-subtarget:");
+ for (u32 i = 1; i < Subtarget_COUNT; i++) {
+ String name = subtarget_strings[i];
+ String help_string = concatenate_strings(temporary_allocator(), prefix, name);
+ print_usage_line(3, (const char *)help_string.text);
+ }
+ }
+ }
+
+ if (run_or_build) {
if (print_flag("-target-features:<string>")) {
print_usage_line(2, "Specifies CPU features to enable on top of the enabled features implied by -microarch.");
print_usage_line(2, "Examples:");
- print_usage_line(3, "-target-features:atomics");
- print_usage_line(3, "-target-features:\"sse2,aes\"");
- print_usage_line(3, "-target-features:\"?\" for a list");
+ print_usage_line(3, "-target-features:atomics");
+ print_usage_line(3, "-target-features:\"sse2,aes\"");
+ print_usage_line(3, "-target-features:\"?\" for a list");
}
}
@@ -2745,11 +2922,11 @@ gb_internal void print_show_help(String const arg0, String command, String optio
if (print_flag("-vet")) {
print_usage_line(2, "Does extra checks on the code.");
print_usage_line(2, "Extra checks include:");
- print_usage_line(3, "-vet-unused");
- print_usage_line(3, "-vet-unused-variables");
- print_usage_line(3, "-vet-unused-imports");
- print_usage_line(3, "-vet-shadowing");
- print_usage_line(3, "-vet-using-stmt");
+ print_usage_line(3, "-vet-unused");
+ print_usage_line(3, "-vet-unused-variables");
+ print_usage_line(3, "-vet-unused-imports");
+ print_usage_line(3, "-vet-shadowing");
+ print_usage_line(3, "-vet-using-stmt");
}
if (print_flag("-vet-cast")) {
@@ -2816,6 +2993,40 @@ gb_internal void print_show_help(String const arg0, String command, String optio
print_usage_line(2, "Treats warning messages as error messages.");
}
}
+
+ if (bundle) {
+ print_usage_line(0, "");
+ print_usage_line(1, "Android-specific flags");
+ print_usage_line(0, "");
+ if (print_flag("-android-keystore:<string>")) {
+ print_usage_line(2, "Specifies the keystore file to use to sign the apk.");
+ }
+
+ if (print_flag("-android-keystore-alias:<string>")) {
+ print_usage_line(2, "Specifies the key alias to use when signing the apk");
+ print_usage_line(2, "Can be omitted if the keystore only contains one key");
+ }
+
+ if (print_flag("-android-keystore-password:<string>")) {
+ print_usage_line(2, "Sets the password to use to unlock the keystore");
+ print_usage_line(2, "If this is omitted, the terminal will prompt you to provide it.");
+ }
+ }
+
+ if (!help_resolved) {
+ usage(arg0);
+ print_usage_line(0, "");
+ if (command == "help") {
+ print_usage_line(0, "'%.*s' is not a recognized flag.", LIT(optional_flag));
+ } else {
+ print_usage_line(0, "'%.*s' is not a recognized command.", LIT(command));
+ }
+ return 1;
+ }
+
+ print_usage_line(0, "");
+
+ return 0;
}
gb_internal void print_show_unused(Checker *c) {
@@ -2857,7 +3068,8 @@ gb_internal void print_show_unused(Checker *c) {
if (e->token.string == "_") {
continue;
}
- if (ptr_set_exists(&info->minimum_dependency_set, e)) {
+
+ if (e->min_dep_count.load(std::memory_order_relaxed) > 0) {
continue;
}
array_add(&unused, e);
@@ -3188,6 +3400,16 @@ int main(int arg_count, char const **arg_ptr) {
String run_args_string = {};
isize last_non_run_arg = args.count;
+ for_array(i, args) {
+ if (args[i] == "--") {
+ break;
+ }
+ if (args[i] == "-help" || args[i] == "--help") {
+ build_context.show_help = true;
+ return print_show_help(args[0], command);
+ }
+ }
+
bool run_output = false;
if (command == "run" || command == "test") {
if (args.count < 3) {
@@ -3281,6 +3503,10 @@ int main(int arg_count, char const **arg_ptr) {
return 1;
#endif
} else if (command == "version") {
+ if (args.count != 2) {
+ usage(args[0]);
+ return 1;
+ }
build_context.command_kind = Command_version;
gb_printf("%.*s version %.*s", LIT(args[0]), LIT(ODIN_VERSION));
@@ -3295,6 +3521,10 @@ int main(int arg_count, char const **arg_ptr) {
gb_printf("\n");
return 0;
} else if (command == "report") {
+ if (args.count != 2) {
+ usage(args[0]);
+ return 1;
+ }
build_context.command_kind = Command_bug_report;
print_bug_report_help();
return 0;
@@ -3303,10 +3533,26 @@ int main(int arg_count, char const **arg_ptr) {
usage(args[0]);
return 1;
} else {
- print_show_help(args[0], args[1], args[2]);
- return 0;
+ return print_show_help(args[0], args[1], args[2]);
+ }
+ } else if (command == "bundle") {
+ if (args.count < 4) {
+ usage(args[0]);
+ return 1;
+ }
+ if (args[2] == "android") {
+ build_context.command_kind = Command_bundle_android;
+ } else {
+ gb_printf_err("Unknown package command: '%.*s'\n", LIT(args[2]));
+ usage(args[0]);
+ return 1;
}
+ init_filename = args[3];
} else if (command == "root") {
+ if (args.count != 2) {
+ usage(args[0]);
+ return 1;
+ }
gb_printf("%.*s", LIT(odin_root_dir()));
return 0;
} else if (command == "clear-cache") {
@@ -3322,11 +3568,6 @@ int main(int arg_count, char const **arg_ptr) {
init_filename = copy_string(permanent_allocator(), init_filename);
- if (init_filename == "-help" ||
- init_filename == "--help") {
- build_context.show_help = true;
- }
-
if (init_filename.len > 0 && !build_context.show_help) {
// The command must be build, run, test, check, or another that takes a directory or filename.
if (!path_is_directory(init_filename)) {
@@ -3340,10 +3581,14 @@ int main(int arg_count, char const **arg_ptr) {
}
if (!single_file_package) {
- gb_printf_err("ERROR: `%.*s %.*s` takes a package as its first argument.\n", LIT(args[0]), LIT(command));
+ gb_printf_err("ERROR: `%.*s %.*s` takes a package/directory as its first argument.\n", LIT(args[0]), LIT(command));
if (init_filename == "-file") {
gb_printf_err("Did you mean `%.*s %.*s <filename.odin> -file`?\n", LIT(args[0]), LIT(command));
} else {
+ if (!gb_file_exists(cast(const char*)init_filename.text)) {
+ gb_printf_err("The file '%.*s' was not found.\n", LIT(init_filename));
+ return 1;
+ }
gb_printf_err("Did you mean `%.*s %.*s %.*s -file`?\n", LIT(args[0]), LIT(command), LIT(init_filename));
}
@@ -3373,8 +3618,11 @@ int main(int arg_count, char const **arg_ptr) {
}
if (build_context.show_help) {
- print_show_help(args[0], command);
- return 0;
+ return print_show_help(args[0], command);
+ }
+
+ if (command == "bundle") {
+ return bundle(init_filename);
}
// NOTE(bill): add 'shared' directory if it is not already set
@@ -3388,6 +3636,11 @@ int main(int arg_count, char const **arg_ptr) {
// print_usage_line(0, "%.*s 32-bit is not yet supported for this platform", LIT(args[0]));
// return 1;
// }
+
+ // Warn about Windows i386 thread-local storage limitations
+ if (build_context.metrics.arch == TargetArch_i386 && build_context.metrics.os == TargetOs_windows) {
+ gb_printf_err("Warning: Thread-local storage is disabled on Windows i386.\n");
+ }
// Check chosen microarchitecture. If not found or ?, print list.
bool print_microarch_list = true;
@@ -3716,6 +3969,21 @@ end_of_code_gen:;
defer (gb_free(heap_allocator(), exe_name.text));
system_must_exec_command_line_app("odin run", "\"%.*s\" %.*s", LIT(exe_name), LIT(run_args_string));
+
+ if (!build_context.keep_executable) {
+ char const *filename = cast(char const *)exe_name.text;
+ gb_file_remove(filename);
+
+ if (build_context.ODIN_DEBUG) {
+ if (build_context.metrics.os == TargetOs_windows || build_context.metrics.os == TargetOs_darwin) {
+ String symbol_path = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_Symbols]);
+ defer (gb_free(heap_allocator(), symbol_path.text));
+
+ filename = cast(char const *)symbol_path.text;
+ gb_file_remove(filename);
+ }
+ }
+ }
}
return 0;
}
diff --git a/src/microsoft_craziness.h b/src/microsoft_craziness.h
index b0fd22a23..933607a2a 100644
--- a/src/microsoft_craziness.h
+++ b/src/microsoft_craziness.h
@@ -59,7 +59,7 @@ struct Find_Result {
};
gb_internal String mc_wstring_to_string(wchar_t const *str) {
- return string16_to_string(mc_allocator, make_string16_c(str));
+ return string16_to_string(mc_allocator, make_string16_c(cast(u16 *)str));
}
gb_internal String16 mc_string_to_wstring(String str) {
@@ -103,7 +103,7 @@ gb_internal HANDLE mc_find_first(String wildcard, MC_Find_Data *find_data) {
String16 wildcard_wide = mc_string_to_wstring(wildcard);
defer (mc_free(wildcard_wide));
- HANDLE handle = FindFirstFileW(wildcard_wide.text, &_find_data);
+ HANDLE handle = FindFirstFileW(cast(wchar_t *)wildcard_wide.text, &_find_data);
if (handle == INVALID_HANDLE_VALUE) return INVALID_HANDLE_VALUE;
find_data->file_attributes = _find_data.dwFileAttributes;
diff --git a/src/name_canonicalization.cpp b/src/name_canonicalization.cpp
index a80dc1996..8bacfabc6 100644
--- a/src/name_canonicalization.cpp
+++ b/src/name_canonicalization.cpp
@@ -1,3 +1,5 @@
+gb_internal bool is_in_doc_writer(void);
+
gb_internal GB_COMPARE_PROC(type_info_pair_cmp) {
TypeInfoPair *x = cast(TypeInfoPair *)a;
TypeInfoPair *y = cast(TypeInfoPair *)b;
@@ -55,10 +57,13 @@ gb_internal isize type_set__find(TypeSet *s, TypeInfoPair pair) {
usize mask = s->capacity-1;
usize hash_index = cast(usize)hash & mask;
for (usize i = 0; i < s->capacity; i++) {
- Type *key = s->keys[hash_index].type;
- if (are_types_identical_unique_tuples(key, pair.type)) {
+ auto *e = &s->keys[hash_index];
+ u64 hash = e->hash;
+ Type *key = e->type;
+ if (hash == pair.hash &&
+ are_types_identical_unique_tuples(key, pair.type)) {
return hash_index;
- } else if (key == 0) {
+ } else if (key == nullptr) {
return -1;
}
hash_index = (hash_index+1)&mask;
@@ -162,6 +167,48 @@ gb_internal bool type_set_update(TypeSet *s, Type *ptr) { // returns true if it
return type_set_update(s, pair);
}
+gb_internal bool type_set_update_with_mutex(TypeSet *s, TypeInfoPair pair, RWSpinLock *m) { // returns true if it previously existsed
+ rwlock_acquire_upgrade(m);
+ if (type_set_exists(s, pair)) {
+ rwlock_release_upgrade(m);
+ return true;
+ }
+
+ rwlock_release_upgrade_and_acquire_write(m);
+ defer (rwlock_release_write(m));
+
+ if (s->keys == nullptr) {
+ type_set_init(s);
+ } else if (type_set__full(s)) {
+ type_set_grow(s);
+ }
+ GB_ASSERT(s->count < s->capacity);
+ GB_ASSERT(s->capacity >= 0);
+
+ usize mask = s->capacity-1;
+ usize hash = cast(usize)pair.hash;
+ usize hash_index = (cast(usize)hash) & mask;
+ GB_ASSERT(hash_index < s->capacity);
+ for (usize i = 0; i < s->capacity; i++) {
+ TypeInfoPair *key = &s->keys[hash_index];
+ GB_ASSERT(!are_types_identical_unique_tuples(key->type, pair.type));
+ if (key->hash == TYPE_SET_TOMBSTONE || key->hash == 0) {
+ *key = pair;
+ s->count++;
+ return false;
+ }
+ hash_index = (hash_index+1)&mask;
+ }
+
+ GB_PANIC("ptr set out of memory");
+ return false;
+}
+
+gb_internal bool type_set_update_with_mutex(TypeSet *s, Type *ptr, RWSpinLock *m) { // returns true if it previously existsed
+ TypeInfoPair pair = {ptr, type_hash_canonical_type(ptr)};
+ return type_set_update_with_mutex(s, pair, m);
+}
+
gb_internal Type *type_set_add(TypeSet *s, Type *ptr) {
type_set_update(s, ptr);
@@ -284,6 +331,23 @@ gb_internal void write_canonical_params(TypeWriter *w, Type *params) {
} else {
write_type_to_canonical_string(w, v->type);
}
+ if (is_in_doc_writer()) {
+ // NOTE(bill): This just exists to make sure the entities default values exist when
+ // writing to the odin doc format
+ Ast *expr = v->Variable.init_expr;
+ if (expr == nullptr) {
+ expr = v->Variable.param_value.original_ast_expr;
+ }
+ if (expr != nullptr) {
+ type_writer_appendc(w, "=");
+ gbString s = write_expr_to_string( // Minor leak
+ gb_string_make(temporary_allocator(), ""),
+ expr,
+ build_context.cmd_doc_flags & CmdDocFlag_Short
+ );
+ type_writer_append(w, s, gb_string_length(s));
+ }
+ }
break;
case Entity_TypeName:
type_writer_appendc(w, CANONICAL_PARAM_TYPEID);
@@ -309,12 +373,20 @@ gb_internal u64 type_hash_canonical_type(Type *type) {
if (type == nullptr) {
return 0;
}
+ u64 prev_hash = type->canonical_hash.load(std::memory_order_relaxed);
+ if (prev_hash != 0) {
+ return prev_hash;
+ }
+
u64 hash = fnv64a(nullptr, 0);
TypeWriter w = {};
type_writer_make_hasher(&w, &hash);
write_type_to_canonical_string(&w, type);
+ hash = hash ? hash : 1;
- return hash ? hash : 1;
+ type->canonical_hash.store(hash, std::memory_order_relaxed);
+
+ return hash;
}
gb_internal String type_to_canonical_string(gbAllocator allocator, Type *type) {
@@ -486,7 +558,13 @@ write_base_name:
Type *params = nullptr;
Entity *parent = type_get_polymorphic_parent(e->type, &params);
- if (parent && (parent->token.string == e->token.string)) {
+ if (parent && (e->token.string == parent->token.string)) {
+ // Check for `distinct` forms
+ type_writer_append(w, parent->token.string.text, parent->token.string.len);
+ write_canonical_params(w, params);
+ } else if (parent && string_starts_with(e->token.string, parent->token.string) &&
+ string_contains_char(e->token.string, '(')) {
+ // Check for named specialization forms
type_writer_append(w, parent->token.string.text, parent->token.string.len);
write_canonical_params(w, params);
} else {
@@ -520,7 +598,6 @@ write_base_name:
return;
}
-gb_internal bool is_in_doc_writer(void);
// NOTE(bill): This exists so that we deterministically hash a type by serializing it to a canonical string
gb_internal void write_type_to_canonical_string(TypeWriter *w, Type *type) {
@@ -631,6 +708,10 @@ gb_internal void write_type_to_canonical_string(TypeWriter *w, Type *type) {
case Type_Union:
type_writer_appendc(w, "union");
+ if (is_in_doc_writer() && type->Union.polymorphic_params) {
+ write_canonical_params(w, type->Union.polymorphic_params);
+ }
+
switch (type->Union.kind) {
case UnionType_no_nil: type_writer_appendc(w, "#no_nil"); break;
case UnionType_shared_nil: type_writer_appendc(w, "#shared_nil"); break;
@@ -658,9 +739,13 @@ gb_internal void write_type_to_canonical_string(TypeWriter *w, Type *type) {
}
type_writer_appendc(w, "struct");
+
+ if (is_in_doc_writer() && type->Struct.polymorphic_params) {
+ write_canonical_params(w, type->Struct.polymorphic_params);
+ }
+
if (type->Struct.is_packed) type_writer_appendc(w, "#packed");
if (type->Struct.is_raw_union) type_writer_appendc(w, "#raw_union");
- if (type->Struct.is_no_copy) type_writer_appendc(w, "#no_copy");
if (type->Struct.custom_min_field_align != 0) type_writer_append_fmt(w, "#min_field_align(%lld)", cast(long long)type->Struct.custom_min_field_align);
if (type->Struct.custom_max_field_align != 0) type_writer_append_fmt(w, "#max_field_align(%lld)", cast(long long)type->Struct.custom_max_field_align);
if (type->Struct.custom_align != 0) type_writer_append_fmt(w, "#align(%lld)", cast(long long)type->Struct.custom_align);
@@ -724,16 +809,23 @@ gb_internal void write_type_to_canonical_string(TypeWriter *w, Type *type) {
if (is_in_doc_writer()) {
type_writer_appendc(w, "$");
type_writer_append(w, type->Generic.name.text, type->Generic.name.len);
- type_writer_append_fmt(w, "%lld", cast(long long)type->Generic.id);
+ type_writer_append_fmt(w, "-%lld", cast(long long)type->Generic.id);
+ if (type->Generic.specialized) {
+ type_writer_appendc(w, "/");
+ write_type_to_canonical_string(w, type->Generic.specialized);
+ }
+ } else if (type->Generic.specialized) {
+ // If we have a specialized type, use that instead of panicking
+ write_type_to_canonical_string(w, type->Generic.specialized);
} else {
- GB_PANIC("Type_Generic should never be hit");
+ // For unspecialized generics, use a generic placeholder string
+ type_writer_appendc(w, "rawptr");
}
return;
case Type_Named:
if (type->Named.type_name != nullptr) {
write_canonical_entity_name(w, type->Named.type_name);
- return;
} else {
type_writer_append(w, type->Named.name.text, type->Named.name.len);
}
diff --git a/src/name_canonicalization.hpp b/src/name_canonicalization.hpp
index 304aff42e..00b450fbe 100644
--- a/src/name_canonicalization.hpp
+++ b/src/name_canonicalization.hpp
@@ -102,6 +102,8 @@ gb_internal Type *type_set_add (TypeSet *s, Type *ptr);
gb_internal Type *type_set_add (TypeSet *s, TypeInfoPair pair);
gb_internal bool type_set_update (TypeSet *s, Type *ptr); // returns true if it previously existed
gb_internal bool type_set_update (TypeSet *s, TypeInfoPair pair); // returns true if it previously existed
+gb_internal bool type_set_update_with_mutex(TypeSet *s, TypeInfoPair pair, RWSpinLock *m);
+gb_internal bool type_set_update_with_mutex(TypeSet *s, Type *ptr, RWSpinLock *m);
gb_internal bool type_set_exists (TypeSet *s, Type *ptr);
gb_internal void type_set_remove (TypeSet *s, Type *ptr);
gb_internal void type_set_clear (TypeSet *s);
diff --git a/src/parser.cpp b/src/parser.cpp
index f38f79607..363eb0c55 100644
--- a/src/parser.cpp
+++ b/src/parser.cpp
@@ -1,5 +1,7 @@
#include "parser_pos.cpp"
+gb_global std::atomic<bool> g_parsing_done;
+
gb_internal bool in_vet_packages(AstFile *file) {
if (file == nullptr) {
return true;
@@ -33,6 +35,10 @@ gb_internal bool ast_file_vet_deprecated(AstFile *f) {
return (ast_file_vet_flags(f) & VetFlag_Deprecated) != 0;
}
+gb_internal bool ast_file_vet_explicit_allocators(AstFile *f) {
+ return (ast_file_vet_flags(f) & VetFlag_ExplicitAllocators) != 0;
+}
+
gb_internal bool file_allow_newline(AstFile *f) {
bool is_strict = build_context.strict_style || ast_file_vet_style(f);
return !is_strict;
@@ -172,7 +178,11 @@ gb_internal Ast *clone_ast(Ast *node, AstFile *f) {
return nullptr;
}
if (f == nullptr) {
- f = node->thread_safe_file();
+ if (g_parsing_done.load(std::memory_order_relaxed)) {
+ f = node->file();
+ } else {
+ f = node->thread_safe_file();
+ }
}
Ast *n = alloc_ast_node(f, node->kind);
gb_memmove(n, node, ast_node_size(node->kind));
@@ -740,6 +750,7 @@ gb_internal Ast *ast_matrix_index_expr(AstFile *f, Ast *expr, Token open, Token
gb_internal Ast *ast_ident(AstFile *f, Token token) {
Ast *result = alloc_ast_node(f, Ast_Ident);
result->Ident.token = token;
+ result->Ident.hash = string_hash(token.string);
return result;
}
@@ -1436,27 +1447,30 @@ gb_internal CommentGroup *consume_comment_group(AstFile *f, isize n, isize *end_
}
gb_internal void consume_comment_groups(AstFile *f, Token prev) {
- if (f->curr_token.kind == Token_Comment) {
- CommentGroup *comment = nullptr;
- isize end_line = 0;
-
- if (f->curr_token.pos.line == prev.pos.line) {
- comment = consume_comment_group(f, 0, &end_line);
- if (f->curr_token.pos.line != end_line || f->curr_token.kind == Token_EOF) {
- f->line_comment = comment;
- }
- }
+ if (f->curr_token.kind != Token_Comment) {
+ return;
+ }
+ CommentGroup *comment = nullptr;
+ isize end_line = 0;
- end_line = -1;
- while (f->curr_token.kind == Token_Comment) {
- comment = consume_comment_group(f, 1, &end_line);
- }
- if (end_line+1 == f->curr_token.pos.line || end_line < 0) {
- f->lead_comment = comment;
+ if (f->curr_token.pos.line == prev.pos.line) {
+ comment = consume_comment_group(f, 0, &end_line);
+ if (f->curr_token.pos.line != end_line ||
+ f->curr_token.pos.line == prev.pos.line+1 ||
+ f->curr_token.kind == Token_EOF) {
+ f->line_comment = comment;
}
+ }
- GB_ASSERT(f->curr_token.kind != Token_Comment);
+ end_line = -1;
+ while (f->curr_token.kind == Token_Comment) {
+ comment = consume_comment_group(f, 1, &end_line);
}
+ if (end_line+1 == f->curr_token.pos.line || end_line < 0) {
+ f->lead_comment = comment;
+ }
+
+ GB_ASSERT(f->curr_token.kind != Token_Comment);
}
gb_internal gb_inline bool ignore_newlines(AstFile *f) {
@@ -3274,6 +3288,8 @@ gb_internal Ast *parse_atom_expr(AstFile *f, Ast *operand, bool lhs) {
case Token_OpenBracket: {
bool prev_allow_range = f->allow_range;
f->allow_range = false;
+ defer (f->allow_range = prev_allow_range);
+
Token open = {}, close = {}, interval = {};
Ast *indices[2] = {};
@@ -3282,6 +3298,13 @@ gb_internal Ast *parse_atom_expr(AstFile *f, Ast *operand, bool lhs) {
f->expr_level++;
open = expect_token(f, Token_OpenBracket);
+ if (f->curr_token.kind == Token_CloseBracket) {
+ error(f->curr_token, "Expected an operand, got ]");
+ close = expect_token(f, Token_CloseBracket);
+ operand = ast_index_expr(f, operand, nullptr, open, close);
+ break;
+ }
+
switch (f->curr_token.kind) {
case Token_Ellipsis:
case Token_RangeFull:
@@ -3331,7 +3354,6 @@ gb_internal Ast *parse_atom_expr(AstFile *f, Ast *operand, bool lhs) {
operand = ast_index_expr(f, operand, indices[0], open, close);
}
- f->allow_range = prev_allow_range;
} break;
case Token_Pointer: // Deference
@@ -5786,7 +5808,7 @@ gb_internal AstPackage *try_add_import_path(Parser *p, String path, String const
for (FileInfo fi : list) {
String name = fi.name;
String ext = path_extension(name);
- if (ext == FILE_EXT) {
+ if (ext == FILE_EXT && !fi.is_dir) {
files_with_ext += 1;
}
if (ext == FILE_EXT && !is_excluded_target_filename(name)) {
@@ -5811,7 +5833,7 @@ gb_internal AstPackage *try_add_import_path(Parser *p, String path, String const
for (FileInfo fi : list) {
String name = fi.name;
String ext = path_extension(name);
- if (ext == FILE_EXT) {
+ if (ext == FILE_EXT && !fi.is_dir) {
if (is_excluded_target_filename(name)) {
continue;
}
@@ -6157,7 +6179,7 @@ gb_internal String build_tag_get_token(String s, String *out) {
isize width = utf8_decode(&s[n], s.len-n, &rune);
if (n == 0 && rune == '!') {
- } else if (!rune_is_letter(rune) && !rune_is_digit(rune)) {
+ } else if (!rune_is_letter(rune) && !rune_is_digit(rune) && rune != ':') {
isize k = gb_max(gb_max(n, width), 1);
*out = substring(s, k, s.len);
return substring(s, 0, k);
@@ -6209,7 +6231,10 @@ gb_internal bool parse_build_tag(Token token_for_pos, String s) {
continue;
}
- TargetOsKind os = get_target_os_from_string(p);
+ Subtarget subtarget = Subtarget_Invalid;
+ String subtarget_str = {};
+
+ TargetOsKind os = get_target_os_from_string(p, &subtarget, &subtarget_str);
TargetArchKind arch = get_target_arch_from_string(p);
num_tokens += 1;
@@ -6220,14 +6245,35 @@ gb_internal bool parse_build_tag(Token token_for_pos, String s) {
break;
}
+ bool is_ios_subtarget = false;
+ if (subtarget == Subtarget_Invalid) {
+ // Special case for pseudo subtarget
+ if (!str_eq_ignore_case(subtarget_str, "ios")) {
+ syntax_error(token_for_pos, "Invalid subtarget '%.*s'.", LIT(subtarget_str));
+ break;
+ }
+
+ is_ios_subtarget = true;
+ }
+
+
if (os != TargetOs_Invalid) {
this_kind_os_seen = true;
+ // NOTE: Only testing for 'default' and not 'generic' because the 'generic' nomenclature implies any subtarget.
+ bool is_explicit_default_subtarget = str_eq_ignore_case(subtarget_str, "default");
+ bool same_subtarget = (subtarget == Subtarget_Default && !is_explicit_default_subtarget) || (subtarget == selected_subtarget);
+
+ // Special case for iPhone or iPhoneSimulator
+ if (is_ios_subtarget && (selected_subtarget == Subtarget_iPhone || selected_subtarget == Subtarget_iPhoneSimulator)) {
+ same_subtarget = true;
+ }
+
GB_ASSERT(arch == TargetArch_Invalid);
if (is_notted) {
- this_kind_correct = this_kind_correct && (os != build_context.metrics.os);
+ this_kind_correct = this_kind_correct && (os != build_context.metrics.os || !same_subtarget);
} else {
- this_kind_correct = this_kind_correct && (os == build_context.metrics.os);
+ this_kind_correct = this_kind_correct && (os == build_context.metrics.os && same_subtarget);
}
} else if (arch != TargetArch_Invalid) {
this_kind_arch_seen = true;
@@ -6250,7 +6296,7 @@ gb_internal bool parse_build_tag(Token token_for_pos, String s) {
return any_correct;
}
-gb_internal String vet_tag_get_token(String s, String *out) {
+gb_internal String vet_tag_get_token(String s, String *out, bool allow_colon) {
s = string_trim_whitespace(s);
isize n = 0;
while (n < s.len) {
@@ -6258,7 +6304,7 @@ gb_internal String vet_tag_get_token(String s, String *out) {
isize width = utf8_decode(&s[n], s.len-n, &rune);
if (n == 0 && rune == '!') {
- } else if (!rune_is_letter(rune) && !rune_is_digit(rune) && rune != '-') {
+ } else if (!rune_is_letter(rune) && !rune_is_digit(rune) && rune != '-' && !(allow_colon && rune == ':')) {
isize k = gb_max(gb_max(n, width), 1);
*out = substring(s, k, s.len);
return substring(s, 0, k);
@@ -6284,7 +6330,7 @@ gb_internal u64 parse_vet_tag(Token token_for_pos, String s) {
u64 vet_not_flags = 0;
while (s.len > 0) {
- String p = string_trim_whitespace(vet_tag_get_token(s, &s));
+ String p = string_trim_whitespace(vet_tag_get_token(s, &s, /*allow_colon*/false));
if (p.len == 0) {
break;
}
@@ -6321,6 +6367,7 @@ gb_internal u64 parse_vet_tag(Token token_for_pos, String s) {
error_line("\textra\n");
error_line("\tcast\n");
error_line("\ttabs\n");
+ error_line("\texplicit-allocators\n");
return build_context.vet_flags;
}
}
@@ -6351,7 +6398,7 @@ gb_internal u64 parse_feature_tag(Token token_for_pos, String s) {
u64 feature_not_flags = 0;
while (s.len > 0) {
- String p = string_trim_whitespace(vet_tag_get_token(s, &s));
+ String p = string_trim_whitespace(vet_tag_get_token(s, &s, /*allow_colon*/true));
if (p.len == 0) {
break;
}
@@ -6373,26 +6420,45 @@ gb_internal u64 parse_feature_tag(Token token_for_pos, String s) {
} else {
feature_flags |= flag;
}
+ if (is_notted) {
+ switch (flag) {
+ case OptInFeatureFlag_IntegerDivisionByZero_Trap:
+ case OptInFeatureFlag_IntegerDivisionByZero_Zero:
+ syntax_error(token_for_pos, "Feature flag does not support notting with '!' - '%.*s'", LIT(p));
+ break;
+ }
+ }
} else {
ERROR_BLOCK();
syntax_error(token_for_pos, "Invalid feature flag name: %.*s", LIT(p));
error_line("\tExpected one of the following\n");
error_line("\tdynamic-literals\n");
+ error_line("\tinteger-division-by-zero:trap\n");
+ error_line("\tinteger-division-by-zero:zero\n");
+ error_line("\tinteger-division-by-zero:self\n");
return OptInFeatureFlag_NONE;
}
}
+ u64 res = OptInFeatureFlag_NONE;
+
if (feature_flags == 0 && feature_not_flags == 0) {
- return OptInFeatureFlag_NONE;
- }
- if (feature_flags == 0 && feature_not_flags != 0) {
- return OptInFeatureFlag_NONE &~ feature_not_flags;
+ res = OptInFeatureFlag_NONE;
+ } else if (feature_flags == 0 && feature_not_flags != 0) {
+ res = OptInFeatureFlag_NONE &~ feature_not_flags;
+ } else if (feature_flags != 0 && feature_not_flags == 0) {
+ res = feature_flags;
+ } else {
+ GB_ASSERT(feature_flags != 0 && feature_not_flags != 0);
+ res = feature_flags &~ feature_not_flags;
}
- if (feature_flags != 0 && feature_not_flags == 0) {
- return feature_flags;
+
+ u64 idbz_count = gb_count_set_bits(res & OptInFeatureFlag_IntegerDivisionByZero_ALL);
+ if (idbz_count > 1) {
+ syntax_error(token_for_pos, "Only one integer-division-by-zero feature flag can be enabled");
}
- GB_ASSERT(feature_flags != 0 && feature_not_flags != 0);
- return feature_flags &~ feature_not_flags;
+
+ return res;
}
gb_internal String dir_from_path(String path) {
@@ -6495,6 +6561,10 @@ gb_internal bool parse_file_tag(const String &lc, const Token &tok, AstFile *f)
} else if (string_starts_with(lc, str_lit("vet"))) {
f->vet_flags = parse_vet_tag(tok, lc);
f->vet_flags_set = true;
+ } else if (string_starts_with(lc, str_lit("test"))) {
+ if ((build_context.command_kind & Command_test) == 0) {
+ return false;
+ }
} else if (string_starts_with(lc, str_lit("ignore"))) {
return false;
} else if (string_starts_with(lc, str_lit("private"))) {
@@ -6865,6 +6935,8 @@ gb_internal ParseFileError parse_packages(Parser *p, String init_filename) {
}
}
+ g_parsing_done.store(true, std::memory_order_relaxed);
+
return ParseFile_None;
}
diff --git a/src/parser.hpp b/src/parser.hpp
index d2dd22667..6127468d4 100644
--- a/src/parser.hpp
+++ b/src/parser.hpp
@@ -27,10 +27,29 @@ enum AddressingMode : u8 {
Addressing_SwizzleVariable = 14, // Swizzle indexed variable
};
+gb_global String const addressing_mode_strings[] = {
+ str_lit("Invalid"),
+ str_lit("NoValue"),
+ str_lit("Value"),
+ str_lit("Context"),
+ str_lit("Variable"),
+ str_lit("Constant"),
+ str_lit("Type"),
+ str_lit("Builtin"),
+ str_lit("ProcGroup"),
+ str_lit("MapIndex"),
+ str_lit("OptionalOk"),
+ str_lit("OptionalOkPtr"),
+ str_lit("SoaVariable"),
+ str_lit("SwizzleValue"),
+ str_lit("SwizzleVariable"),
+};
+
struct TypeAndValue {
Type * type;
AddressingMode mode;
- bool is_lhs; // Debug info
+ bool is_lhs; // Debug info
+ Type * objc_super_target; // Original type of the Obj-C object before being converted to the superclass' type by the objc_super() intrinsic.
ExactValue value;
};
@@ -396,6 +415,7 @@ struct AstSplitArgs {
AST_KIND(Ident, "identifier", struct { \
Token token; \
Entity *entity; \
+ u32 hash; \
}) \
AST_KIND(Implicit, "implicit", Token) \
AST_KIND(Uninit, "uninitialized value", Token) \
diff --git a/src/path.cpp b/src/path.cpp
index 2c08ddd98..2b97a04df 100644
--- a/src/path.cpp
+++ b/src/path.cpp
@@ -30,28 +30,80 @@ gb_internal String remove_directory_from_path(String const &s) {
}
-// NOTE(Mark Naughton): getcwd as String
-#if !defined(GB_SYSTEM_WINDOWS)
-gb_internal String get_current_directory(void) {
- char cwd[256];
- getcwd(cwd, 256);
+#if defined(GB_SYSTEM_WINDOWS)
+gb_global SRWLOCK cwd_lock;
+
+String get_working_directory(gbAllocator allocator) {
+ AcquireSRWLockExclusive(&cwd_lock);
+
+ TEMPORARY_ALLOCATOR_GUARD();
+
+ DWORD sz_utf16 = GetCurrentDirectoryW(0, nullptr);
+ wchar_t *dir_buf_wstr = gb_alloc_array(temporary_allocator(), wchar_t, sz_utf16);
+ if (dir_buf_wstr == nullptr) {
+ ReleaseSRWLockExclusive(&cwd_lock);
+ return {};
+ }
+
+ DWORD n = GetCurrentDirectoryW(sz_utf16, dir_buf_wstr);
+ GB_ASSERT(n+1 == sz_utf16);
+ ReleaseSRWLockExclusive(&cwd_lock);
+
+
+ isize buf_len = sz_utf16*4;
+ u8 *buf = gb_alloc_array(allocator, u8, buf_len);
+ gb_ucs2_to_utf8(buf, buf_len, cast(u16 *)dir_buf_wstr);
+
+ return make_string_c(cast(char const *)buf);
+}
+
+bool set_working_directory(String dir) {
+ bool ok = false;
+ TEMPORARY_ALLOCATOR_GUARD();
+
+ char const *cdir = alloc_cstring(temporary_allocator(), dir);
+ wchar_t *wstr = gb__alloc_utf8_to_ucs2(temporary_allocator(), cdir, nullptr);
- return make_string_c(cwd);
+ AcquireSRWLockExclusive(&cwd_lock);
+
+ ok = SetCurrentDirectoryW(wstr);
+
+ ReleaseSRWLockExclusive(&cwd_lock);
+
+ return ok;
}
#else
-gb_internal String get_current_directory(void) {
- gbAllocator a = heap_allocator();
- wchar_t cwd[256];
- GetCurrentDirectoryW(256, cwd);
+String get_working_directory(gbAllocator allocator) {
+ TEMPORARY_ALLOCATOR_GUARD();
- String16 wstr = make_string16_c(cwd);
+ auto buf = array_make<char>(temporary_allocator());
+ size_t size = PATH_MAX;
- return string16_to_string(a, wstr);
+ char const *cwd = nullptr;
+ for (; cwd == nullptr; size *= 2) {
+ array_resize(&buf, size);
+
+ cwd = getcwd(buf.data, buf.count);
+ if (cwd == nullptr && errno != ERANGE) {
+ return {};
+ }
+ }
+
+ return copy_string(allocator, make_string_c(cwd));
+}
+
+bool set_working_directory(String dir) {
+ TEMPORARY_ALLOCATOR_GUARD();
+ char const *cdir = alloc_cstring(temporary_allocator(), dir);
+ return !chdir(cdir);
}
+
#endif
+
+
gb_internal bool path_is_directory(String path);
gb_internal String directory_from_path(String const &s) {
@@ -78,7 +130,7 @@ gb_internal String directory_from_path(String const &s) {
String16 wstr = string_to_string16(a, path);
defer (gb_free(a, wstr.text));
- i32 attribs = GetFileAttributesW(wstr.text);
+ i32 attribs = GetFileAttributesW(cast(wchar_t *)wstr.text);
if (attribs < 0) return false;
return (attribs & FILE_ATTRIBUTE_DIRECTORY) != 0;
@@ -308,7 +360,7 @@ gb_internal ReadDirectoryError read_directory(String path, Array<FileInfo> *fi)
defer (gb_free(a, wstr.text));
WIN32_FIND_DATAW file_data = {};
- HANDLE find_file = FindFirstFileW(wstr.text, &file_data);
+ HANDLE find_file = FindFirstFileW(cast(wchar_t *)wstr.text, &file_data);
if (find_file == INVALID_HANDLE_VALUE) {
return ReadDirectory_Unknown;
}
@@ -320,7 +372,7 @@ gb_internal ReadDirectoryError read_directory(String path, Array<FileInfo> *fi)
wchar_t *filename_w = file_data.cFileName;
u64 size = cast(u64)file_data.nFileSizeLow;
size |= (cast(u64)file_data.nFileSizeHigh) << 32;
- String name = string16_to_string(a, make_string16_c(filename_w));
+ String name = string16_to_string(a, make_string16_c(cast(u16 *)filename_w));
if (name == "." || name == "..") {
gb_free(a, name.text);
continue;
@@ -442,7 +494,7 @@ gb_internal bool write_directory(String path) {
#else
gb_internal bool write_directory(String path) {
String16 wstr = string_to_string16(heap_allocator(), path);
- LPCWSTR wdirectory_name = wstr.text;
+ LPCWSTR wdirectory_name = cast(wchar_t *)wstr.text;
HANDLE directory = CreateFileW(wdirectory_name,
GENERIC_WRITE,
diff --git a/src/ptr_map.cpp b/src/ptr_map.cpp
index 1c157c386..61f703cf1 100644
--- a/src/ptr_map.cpp
+++ b/src/ptr_map.cpp
@@ -15,7 +15,7 @@ static void *const MAP_TOMBSTONE = (void *)~(uintptr)0;
template <typename K, typename V>
struct PtrMapEntry {
static_assert(sizeof(K) == sizeof(void *), "Key size must be pointer size");
-
+
K key;
V value;
};
@@ -374,7 +374,7 @@ struct PtrMapIterator {
}
bool operator==(PtrMapIterator<K, V> const &other) const noexcept {
- return this->map == other->map && this->index == other->index;
+ return this->map == other.map && this->index == other.index;
}
operator PtrMapEntry<K, V> *() const {
@@ -858,4 +858,4 @@ gb_internal OrderedInsertPtrMapEntry<K, V> *end(OrderedInsertPtrMap<K, V> &m) {
template <typename K, typename V>
gb_internal OrderedInsertPtrMapEntry<K, V> const *end(OrderedInsertPtrMap<K, V> const &m) {
return m.entries + m.count;
-} \ No newline at end of file
+}
diff --git a/src/ptr_set.cpp b/src/ptr_set.cpp
index 5097e2bb6..5b1d2cc19 100644
--- a/src/ptr_set.cpp
+++ b/src/ptr_set.cpp
@@ -16,6 +16,8 @@ template <typename T> gb_internal bool ptr_set_exists (PtrSet<T> *s, T ptr);
template <typename T> gb_internal void ptr_set_remove (PtrSet<T> *s, T ptr);
template <typename T> gb_internal void ptr_set_clear (PtrSet<T> *s);
+#define FOR_PTR_SET(element, set_) for (auto *it = &(set_).keys[0], element = it ? *it : nullptr; (set_).keys != nullptr && it < &(set_).keys[(set_).capacity]; it++) if (element = *it, (*it != nullptr && *it != cast(void *)~(uintptr)(0ull)))
+
gb_internal gbAllocator ptr_set_allocator(void) {
return heap_allocator();
}
@@ -83,7 +85,7 @@ gb_internal gb_inline void ptr_set_grow(PtrSet<T> *old_set) {
PtrSet<T> new_set = {};
ptr_set_init(&new_set, gb_max(old_set->capacity<<1, 16));
- for (T ptr : *old_set) {
+ FOR_PTR_SET(ptr, *old_set) {
bool was_new = ptr_set_update(&new_set, ptr);
GB_ASSERT(!was_new);
}
@@ -135,6 +137,44 @@ gb_internal bool ptr_set_update(PtrSet<T> *s, T ptr) { // returns true if it pre
}
template <typename T>
+gb_internal bool ptr_set_update_with_mutex(PtrSet<T> *s, T ptr, RWSpinLock *m) { // returns true if it previously existsed
+ rwlock_acquire_upgrade(m);
+ if (ptr_set_exists(s, ptr)) {
+ rwlock_release_upgrade(m);
+ return true;
+ }
+
+ rwlock_release_upgrade_and_acquire_write(m);
+ defer (rwlock_release_write(m));
+
+ if (s->keys == nullptr) {
+ ptr_set_init(s);
+ } else if (ptr_set__full(s)) {
+ ptr_set_grow(s);
+ }
+ GB_ASSERT(s->count < s->capacity);
+ GB_ASSERT(s->capacity >= 0);
+
+ usize mask = s->capacity-1;
+ u32 hash = ptr_map_hash_key(ptr);
+ usize hash_index = (cast(usize)hash) & mask;
+ GB_ASSERT(hash_index < s->capacity);
+ for (usize i = 0; i < s->capacity; i++) {
+ T *key = &s->keys[hash_index];
+ GB_ASSERT(*key != ptr);
+ if (*key == (T)PtrSet<T>::TOMBSTONE || *key == 0) {
+ *key = ptr;
+ s->count++;
+ return false;
+ }
+ hash_index = (hash_index+1)&mask;
+ }
+
+ GB_PANIC("ptr set out of memory");
+ return false;
+}
+
+template <typename T>
gb_internal T ptr_set_add(PtrSet<T> *s, T ptr) {
ptr_set_update(s, ptr);
return ptr;
@@ -157,7 +197,7 @@ gb_internal gb_inline void ptr_set_clear(PtrSet<T> *s) {
gb_zero_size(s->keys, s->capacity*gb_size_of(T));
}
-template <typename T>
+/*template <typename T>
struct PtrSetIterator {
PtrSet<T> *set;
usize index;
@@ -201,4 +241,6 @@ gb_internal PtrSetIterator<T> begin(PtrSet<T> &set) noexcept {
template <typename T>
gb_internal PtrSetIterator<T> end(PtrSet<T> &set) noexcept {
return PtrSetIterator<T>{&set, set.capacity};
-} \ No newline at end of file
+}*/
+
+
diff --git a/src/queue.cpp b/src/queue.cpp
index dee9ad1f8..82f82f3e1 100644
--- a/src/queue.cpp
+++ b/src/queue.cpp
@@ -36,7 +36,8 @@ gb_internal void mpsc_destroy(MPSCQueue<T> *q) {
template <typename T>
gb_internal MPSCNode<T> *mpsc_alloc_node(MPSCQueue<T> *q, T const &value) {
- auto new_node = gb_alloc_item(heap_allocator(), MPSCNode<T>);
+ // auto new_node = gb_alloc_item(heap_allocator(), MPSCNode<T>);
+ auto new_node = gb_alloc_item(permanent_allocator(), MPSCNode<T>);
new_node->value = value;
return new_node;
}
diff --git a/src/string.cpp b/src/string.cpp
index b001adf0e..9c08114a7 100644
--- a/src/string.cpp
+++ b/src/string.cpp
@@ -26,15 +26,14 @@ struct String_Iterator {
// NOTE(bill): String16 is only used for Windows due to its file directories
struct String16 {
- wchar_t *text;
- isize len;
- wchar_t const &operator[](isize i) const {
+ u16 * text;
+ isize len;
+ u16 const &operator[](isize i) const {
GB_ASSERT_MSG(0 <= i && i < len, "[%td]", i);
return text[i];
}
};
-
gb_internal gb_inline String make_string(u8 const *text, isize len) {
String s;
s.text = cast(u8 *)text;
@@ -45,19 +44,19 @@ gb_internal gb_inline String make_string(u8 const *text, isize len) {
return s;
}
-
-gb_internal gb_inline String16 make_string16(wchar_t const *text, isize len) {
+gb_internal gb_inline String16 make_string16(u16 const *text, isize len) {
String16 s;
- s.text = cast(wchar_t *)text;
+ s.text = cast(u16 *)text;
s.len = len;
return s;
}
-gb_internal isize string16_len(wchar_t const *s) {
+
+gb_internal isize string16_len(u16 const *s) {
if (s == nullptr) {
return 0;
}
- wchar_t const *p = s;
+ u16 const *p = s;
while (*p) {
p++;
}
@@ -69,7 +68,7 @@ gb_internal gb_inline String make_string_c(char const *text) {
return make_string(cast(u8 *)cast(void *)text, gb_strlen(text));
}
-gb_internal gb_inline String16 make_string16_c(wchar_t const *text) {
+gb_internal gb_inline String16 make_string16_c(u16 const *text) {
return make_string16(text, string16_len(text));
}
@@ -80,6 +79,13 @@ gb_internal String substring(String const &s, isize lo, isize hi) {
return make_string(s.text+lo, hi-lo);
}
+gb_internal String16 substring(String16 const &s, isize lo, isize hi) {
+ isize max = s.len;
+ GB_ASSERT_MSG(lo <= hi && hi <= max, "%td..%td..%td", lo, hi, max);
+
+ return make_string16(s.text+lo, hi-lo);
+}
+
gb_internal char *alloc_cstring(gbAllocator a, String s) {
char *c_str = gb_alloc_array(a, char, s.len+1);
@@ -145,6 +151,27 @@ gb_internal int string_compare(String const &a, String const &b) {
return res;
}
+
+gb_internal int string16_compare(String16 const &a, String16 const &b) {
+ if (a.text == b.text) {
+ return cast(int)(a.len - b.len);
+ }
+ if (a.text == nullptr) {
+ return -1;
+ }
+ if (b.text == nullptr) {
+ return +1;
+ }
+
+ uintptr n = gb_min(a.len, b.len);
+ int res = memcmp(a.text, b.text, n*gb_size_of(u16));
+ if (res == 0) {
+ res = cast(int)(a.len - b.len);
+ }
+ return res;
+}
+
+
gb_internal isize string_index_byte(String const &s, u8 x) {
for (isize i = 0; i < s.len; i++) {
if (s.text[i] == x) {
@@ -182,6 +209,26 @@ template <isize N> gb_internal bool operator >= (String const &a, char const (&b
template <> bool operator == (String const &a, char const (&b)[1]) { return a.len == 0; }
template <> bool operator != (String const &a, char const (&b)[1]) { return a.len != 0; }
+
+gb_internal gb_inline bool str_eq(String16 const &a, String16 const &b) {
+ if (a.len != b.len) return false;
+ if (a.len == 0) return true;
+ return memcmp(a.text, b.text, a.len) == 0;
+}
+gb_internal gb_inline bool str_ne(String16 const &a, String16 const &b) { return !str_eq(a, b); }
+gb_internal gb_inline bool str_lt(String16 const &a, String16 const &b) { return string16_compare(a, b) < 0; }
+gb_internal gb_inline bool str_gt(String16 const &a, String16 const &b) { return string16_compare(a, b) > 0; }
+gb_internal gb_inline bool str_le(String16 const &a, String16 const &b) { return string16_compare(a, b) <= 0; }
+gb_internal gb_inline bool str_ge(String16 const &a, String16 const &b) { return string16_compare(a, b) >= 0; }
+
+gb_internal gb_inline bool operator == (String16 const &a, String16 const &b) { return str_eq(a, b); }
+gb_internal gb_inline bool operator != (String16 const &a, String16 const &b) { return str_ne(a, b); }
+gb_internal gb_inline bool operator < (String16 const &a, String16 const &b) { return str_lt(a, b); }
+gb_internal gb_inline bool operator > (String16 const &a, String16 const &b) { return str_gt(a, b); }
+gb_internal gb_inline bool operator <= (String16 const &a, String16 const &b) { return str_le(a, b); }
+gb_internal gb_inline bool operator >= (String16 const &a, String16 const &b) { return str_ge(a, b); }
+
+
gb_internal gb_inline bool string_starts_with(String const &s, String const &prefix) {
if (prefix.len > s.len) {
return false;
@@ -273,6 +320,15 @@ gb_internal String path_extension(String const &str, bool include_dot = true) {
return substring(str, include_dot ? pos : pos + 1, str.len);
}
+
+gb_internal String path_remove_extension(String const &str) {
+ isize pos = string_extension_position(str);
+ if (pos < 0) {
+ return str;
+ }
+ return substring(str, 0, pos);
+}
+
gb_internal String string_trim_whitespace(String str) {
while (str.len > 0 && rune_is_whitespace(str[str.len-1])) {
str.len--;
@@ -327,6 +383,83 @@ gb_internal Array<String> split_lines_from_array(Array<u8> const &array, gbAlloc
return lines;
}
+enum : u32 { PRIME_RABIN_KARP = 16777619u };
+
+gb_internal u32 hash_str_rabin_karp(String const &s, u32 *pow_) {
+ u32 hash = 0;
+ u32 pow = 1;
+ for (isize i = 0; i < s.len; i++) {
+ hash = hash*PRIME_RABIN_KARP + cast(u32)s.text[i];
+ }
+ u32 sq = PRIME_RABIN_KARP;
+ for (isize i = s.len; i > 0; i >>= 1) {
+ if ((i & 1) != 0) {
+ pow *= sq;
+ }
+ sq *= sq;
+ }
+ if (pow_) *pow_ = pow;
+ return hash;
+
+}
+
+
+gb_internal isize string_index(String const &s, String const &substr) {
+ isize n = substr.len;
+ if (n == 0) {
+ return 0;
+ } else if (n == 1) {
+ return string_index_byte(s, substr[0]);
+ } else if (n == s.len) {
+ if (s == substr) {
+ return 0;
+ }
+ return -1;
+ } else if (n > s.len) {
+ return -1;
+ }
+ u32 pow = 1;
+ u32 hash = hash_str_rabin_karp(s, &pow);
+ u32 h = 0;
+ for (isize i = 0; i < n; i++) {
+ h = h*PRIME_RABIN_KARP + cast(u32)s.text[i];
+ }
+ if (h == hash && substring(s, 0, n) == substr) {
+ return 0;
+ }
+ for (isize i = n; i < s.len; /**/) {
+ h *= PRIME_RABIN_KARP;
+ h += cast(u32)s.text[i];
+ h -= pow * u32(s.text[i-n]);
+ i += 1;
+ if (h == hash && substring(s, i-n, i) == substr) {
+ return i - n;
+ }
+ }
+ return -1;
+}
+
+
+struct StringPartition {
+ String head;
+ String match;
+ String tail;
+};
+
+gb_internal StringPartition string_partition(String const &str, String const &sep) {
+ StringPartition res = {};
+ isize i = string_index(str, sep);
+ if (i < 0) {
+ res.head = str;
+ return res;
+ }
+
+ res.head = substring(str, 0, i);
+ res.match = substring(str, i, i+sep.len);
+ res.tail = substring(str, i+sep.len, str.len);
+ return res;
+}
+
gb_internal bool string_contains_char(String const &s, u8 c) {
isize i;
for (i = 0; i < s.len; i++) {
@@ -500,23 +633,28 @@ gb_internal String normalize_path(gbAllocator a, String const &path, String cons
return WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, widechar_input, input_length, output, output_size, nullptr, nullptr);
}
#elif defined(GB_SYSTEM_UNIX) || defined(GB_SYSTEM_OSX)
-
- #include <iconv.h>
+ #include <wchar.h>
gb_internal int convert_multibyte_to_widechar(char const *multibyte_input, usize input_length, wchar_t *output, usize output_size) {
- iconv_t conv = iconv_open("WCHAR_T", "UTF-8");
- size_t result = iconv(conv, cast(char **)&multibyte_input, &input_length, cast(char **)&output, &output_size);
- iconv_close(conv);
+ String string = copy_string(heap_allocator(), make_string(cast(u8 const*)multibyte_input, input_length)); /* Guarantee NULL terminator */
+ u8* input = string.text;
- return cast(int)result;
+ mbstate_t ps = { 0 };
+ size_t result = mbsrtowcs(output, cast(const char**)&input, output_size, &ps);
+
+ gb_free(heap_allocator(), string.text);
+ return (result == (size_t)-1) ? -1 : (int)result;
}
gb_internal int convert_widechar_to_multibyte(wchar_t const *widechar_input, usize input_length, char* output, usize output_size) {
- iconv_t conv = iconv_open("UTF-8", "WCHAR_T");
- size_t result = iconv(conv, cast(char**) &widechar_input, &input_length, cast(char **)&output, &output_size);
- iconv_close(conv);
+ String string = copy_string(heap_allocator(), make_string(cast(u8 const*)widechar_input, input_length)); /* Guarantee NULL terminator */
+ u8* input = string.text;
+
+ mbstate_t ps = { 0 };
+ size_t result = wcsrtombs(output, cast(const wchar_t**)&input, output_size, &ps);
- return cast(int)result;
+ gb_free(heap_allocator(), string.text);
+ return (result == (size_t)-1) ? -1 : (int)result;
}
#else
#error Implement system
@@ -525,10 +663,9 @@ gb_internal String normalize_path(gbAllocator a, String const &path, String cons
-// TODO(bill): Make this non-windows specific
gb_internal String16 string_to_string16(gbAllocator a, String s) {
int len, len1;
- wchar_t *text;
+ u16 *text;
if (s.len < 1) {
return make_string16(nullptr, 0);
@@ -539,15 +676,14 @@ gb_internal String16 string_to_string16(gbAllocator a, String s) {
return make_string16(nullptr, 0);
}
- text = gb_alloc_array(a, wchar_t, len+1);
+ text = gb_alloc_array(a, u16, len+1);
- len1 = convert_multibyte_to_widechar(cast(char *)s.text, cast(int)s.len, text, cast(int)len);
+ len1 = convert_multibyte_to_widechar(cast(char *)s.text, cast(int)s.len, cast(wchar_t *)text, cast(int)len);
if (len1 == 0) {
gb_free(a, text);
return make_string16(nullptr, 0);
}
text[len] = 0;
-
return make_string16(text, len);
}
@@ -560,7 +696,7 @@ gb_internal String string16_to_string(gbAllocator a, String16 s) {
return make_string(nullptr, 0);
}
- len = convert_widechar_to_multibyte(s.text, cast(int)s.len, nullptr, 0);
+ len = convert_widechar_to_multibyte(cast(wchar_t *)s.text, cast(int)s.len, nullptr, 0);
if (len == 0) {
return make_string(nullptr, 0);
}
@@ -568,7 +704,7 @@ gb_internal String string16_to_string(gbAllocator a, String16 s) {
text = gb_alloc_array(a, u8, len+1);
- len1 = convert_widechar_to_multibyte(s.text, cast(int)s.len, cast(char *)text, cast(int)len);
+ len1 = convert_widechar_to_multibyte(cast(wchar_t *)s.text, cast(int)s.len, cast(char *)text, cast(int)len);
if (len1 == 0) {
gb_free(a, text);
return make_string(nullptr, 0);
@@ -588,9 +724,9 @@ gb_internal String temporary_directory(gbAllocator allocator) {
return String{0};
}
DWORD len = gb_max(MAX_PATH, n);
- wchar_t *b = gb_alloc_array(heap_allocator(), wchar_t, len+1);
+ u16 *b = gb_alloc_array(heap_allocator(), u16, len+1);
defer (gb_free(heap_allocator(), b));
- n = GetTempPathW(len, b);
+ n = GetTempPathW(len, cast(wchar_t *)b);
if (n == 3 && b[1] == ':' && b[2] == '\\') {
} else if (n > 0 && b[n-1] == '\\') {
@@ -705,6 +841,104 @@ gb_internal String quote_to_ascii(gbAllocator a, String str, u8 quote='"') {
return res;
}
+gb_internal Rune decode_surrogate_pair(u16 r1, u16 r2) {
+ static Rune const _surr1 = 0xd800;
+ static Rune const _surr2 = 0xdc00;
+ static Rune const _surr3 = 0xe000;
+ static Rune const _surr_self = 0x10000;
+
+ if (_surr1 <= r1 && r1 < _surr2 && _surr2 <= r2 && r2 < _surr3) {
+ return (((r1-_surr1)<<10) | (r2 - _surr2)) + _surr_self;
+ }
+ return GB_RUNE_INVALID;
+}
+
+gb_internal String quote_to_ascii(gbAllocator a, String16 str, u8 quote='"') {
+ static Rune const _surr1 = 0xd800;
+ static Rune const _surr2 = 0xdc00;
+ static Rune const _surr3 = 0xe000;
+ static Rune const _surr_self = 0x10000;
+
+ u16 *s = cast(u16 *)str.text;
+ isize n = str.len;
+ auto buf = array_make<u8>(a, 0, n*2);
+ array_add(&buf, quote);
+ for (isize width = 0; n > 0; s += width, n -= width) {
+ Rune r = cast(Rune)s[0];
+ width = 1;
+ if (r < _surr1 || _surr3 <= r) {
+ r = cast(Rune)r;
+ } else if (_surr1 <= r && r < _surr2) {
+ if (n>1) {
+ r = decode_surrogate_pair(s[0], s[1]);
+ if (r != GB_RUNE_INVALID) {
+ width = 2;
+ }
+ } else {
+ r = GB_RUNE_INVALID;
+ }
+ }
+ if (width == 1 && r == GB_RUNE_INVALID) {
+ array_add(&buf, cast(u8)'\\');
+ array_add(&buf, cast(u8)'x');
+ array_add(&buf, cast(u8)lower_hex[s[0]>>4]);
+ array_add(&buf, cast(u8)lower_hex[s[0]&0xf]);
+ continue;
+ }
+
+ if (r == quote || r == '\\') {
+ array_add(&buf, cast(u8)'\\');
+ array_add(&buf, u8(r));
+ continue;
+ }
+ if (r < 0x80 && is_printable(r)) {
+ array_add(&buf, u8(r));
+ continue;
+ }
+ switch (r) {
+ case '\a':
+ case '\b':
+ case '\f':
+ case '\n':
+ case '\r':
+ case '\t':
+ case '\v':
+ default:
+ if (r < ' ') {
+ u8 b = cast(u8)r;
+ array_add(&buf, cast(u8)'\\');
+ array_add(&buf, cast(u8)'x');
+ array_add(&buf, cast(u8)lower_hex[b>>4]);
+ array_add(&buf, cast(u8)lower_hex[b&0xf]);
+ }
+ if (r > GB_RUNE_MAX) {
+ r = 0XFFFD;
+ }
+ if (r < 0x10000) {
+ array_add(&buf, cast(u8)'\\');
+ array_add(&buf, cast(u8)'u');
+ for (isize i = 12; i >= 0; i -= 4) {
+ array_add(&buf, cast(u8)lower_hex[(r>>i)&0xf]);
+ }
+ } else {
+ array_add(&buf, cast(u8)'\\');
+ array_add(&buf, cast(u8)'U');
+ for (isize i = 28; i >= 0; i -= 4) {
+ array_add(&buf, cast(u8)lower_hex[(r>>i)&0xf]);
+ }
+ }
+ }
+ }
+
+
+
+ array_add(&buf, quote);
+ String res = {};
+ res.text = buf.data;
+ res.len = buf.count;
+ return res;
+}
+
diff --git a/src/string16_map.cpp b/src/string16_map.cpp
new file mode 100644
index 000000000..c9e2eb817
--- /dev/null
+++ b/src/string16_map.cpp
@@ -0,0 +1,538 @@
+GB_STATIC_ASSERT(sizeof(MapIndex) == sizeof(u32));
+
+
+struct String16HashKey {
+ String16 string;
+ u32 hash;
+
+ operator String16() const noexcept {
+ return this->string;
+ }
+ operator String16 const &() const noexcept {
+ return this->string;
+ }
+};
+gb_internal gb_inline u32 string_hash(String16 const &s) {
+ u32 res = fnv32a(s.text, s.len*gb_size_of(u16)) & 0x7fffffff;
+ return res | (res == 0);
+}
+
+gb_internal gb_inline String16HashKey string_hash_string(String16 const &s) {
+ String16HashKey hash_key = {};
+ hash_key.hash = string_hash(s);
+ hash_key.string = s;
+ return hash_key;
+}
+
+
+#if 1 /* old string map */
+
+template <typename T>
+struct String16MapEntry {
+ String16 key;
+ u32 hash;
+ MapIndex next;
+ T value;
+};
+
+template <typename T>
+struct String16Map {
+ MapIndex * hashes;
+ usize hashes_count;
+ String16MapEntry<T> *entries;
+ u32 count;
+ u32 entries_capacity;
+};
+
+
+template <typename T> gb_internal void string16_map_init (String16Map<T> *h, usize capacity = 16);
+template <typename T> gb_internal void string16_map_destroy (String16Map<T> *h);
+
+template <typename T> gb_internal T * string16_map_get (String16Map<T> *h, String16HashKey const &key);
+template <typename T> gb_internal T & string16_map_must_get(String16Map<T> *h, String16HashKey const &key);
+template <typename T> gb_internal void string16_map_set (String16Map<T> *h, String16HashKey const &key, T const &value);
+
+// template <typename T> gb_internal void string16_map_remove (String16Map<T> *h, String16HashKey const &key);
+template <typename T> gb_internal void string16_map_clear (String16Map<T> *h);
+template <typename T> gb_internal void string16_map_grow (String16Map<T> *h);
+template <typename T> gb_internal void string16_map_reserve (String16Map<T> *h, usize new_count);
+
+gb_internal gbAllocator string16_map_allocator(void) {
+ return heap_allocator();
+}
+
+template <typename T>
+gb_internal gb_inline void string16_map_init(String16Map<T> *h, usize capacity) {
+ capacity = next_pow2_isize(capacity);
+ string16_map_reserve(h, capacity);
+}
+
+template <typename T>
+gb_internal gb_inline void string16_map_destroy(String16Map<T> *h) {
+ gb_free(string16_map_allocator(), h->hashes);
+ gb_free(string16_map_allocator(), h->entries);
+}
+
+
+template <typename T>
+gb_internal void string16_map__resize_hashes(String16Map<T> *h, usize count) {
+ h->hashes_count = cast(u32)resize_array_raw(&h->hashes, string16_map_allocator(), h->hashes_count, count, MAP_CACHE_LINE_SIZE);
+}
+
+
+template <typename T>
+gb_internal void string16_map__reserve_entries(String16Map<T> *h, usize capacity) {
+ h->entries_capacity = cast(u32)resize_array_raw(&h->entries, string16_map_allocator(), h->entries_capacity, capacity, MAP_CACHE_LINE_SIZE);
+}
+
+
+template <typename T>
+gb_internal MapIndex string16_map__add_entry(String16Map<T> *h, u32 hash, String16 const &key) {
+ String16MapEntry<T> e = {};
+ e.key = key;
+ e.hash = hash;
+ e.next = MAP_SENTINEL;
+ if (h->count+1 >= h->entries_capacity) {
+ string16_map__reserve_entries(h, gb_max(h->entries_capacity*2, 4));
+ }
+ h->entries[h->count++] = e;
+ return cast(MapIndex)(h->count-1);
+}
+
+template <typename T>
+gb_internal MapFindResult string16_map__find(String16Map<T> *h, u32 hash, String16 const &key) {
+ MapFindResult fr = {MAP_SENTINEL, MAP_SENTINEL, MAP_SENTINEL};
+ if (h->hashes_count != 0) {
+ fr.hash_index = cast(MapIndex)(hash & (h->hashes_count-1));
+ fr.entry_index = h->hashes[fr.hash_index];
+ while (fr.entry_index != MAP_SENTINEL) {
+ auto *entry = &h->entries[fr.entry_index];
+ if (entry->hash == hash && entry->key == key) {
+ return fr;
+ }
+ fr.entry_prev = fr.entry_index;
+ fr.entry_index = entry->next;
+ }
+ }
+ return fr;
+}
+
+template <typename T>
+gb_internal MapFindResult string16_map__find_from_entry(String16Map<T> *h, String16MapEntry<T> *e) {
+ MapFindResult fr = {MAP_SENTINEL, MAP_SENTINEL, MAP_SENTINEL};
+ if (h->hashes_count != 0) {
+ fr.hash_index = cast(MapIndex)(e->hash & (h->hashes_count-1));
+ fr.entry_index = h->hashes[fr.hash_index];
+ while (fr.entry_index != MAP_SENTINEL) {
+ auto *entry = &h->entries[fr.entry_index];
+ if (entry == e) {
+ return fr;
+ }
+ fr.entry_prev = fr.entry_index;
+ fr.entry_index = entry->next;
+ }
+ }
+ return fr;
+}
+
+template <typename T>
+gb_internal b32 string16_map__full(String16Map<T> *h) {
+ return 0.75f * h->hashes_count <= h->count;
+}
+
+template <typename T>
+gb_inline void string16_map_grow(String16Map<T> *h) {
+ isize new_count = gb_max(h->hashes_count<<1, 16);
+ string16_map_reserve(h, new_count);
+}
+
+
+template <typename T>
+gb_internal void string16_map_reset_entries(String16Map<T> *h) {
+ for (u32 i = 0; i < h->hashes_count; i++) {
+ h->hashes[i] = MAP_SENTINEL;
+ }
+ for (isize i = 0; i < h->count; i++) {
+ MapFindResult fr;
+ String16MapEntry<T> *e = &h->entries[i];
+ e->next = MAP_SENTINEL;
+ fr = string16_map__find_from_entry(h, e);
+ if (fr.entry_prev == MAP_SENTINEL) {
+ h->hashes[fr.hash_index] = cast(MapIndex)i;
+ } else {
+ h->entries[fr.entry_prev].next = cast(MapIndex)i;
+ }
+ }
+}
+
+template <typename T>
+gb_internal void string16_map_reserve(String16Map<T> *h, usize cap) {
+ if (h->count*2 < h->hashes_count) {
+ return;
+ }
+ string16_map__reserve_entries(h, cap);
+ string16_map__resize_hashes(h, cap*2);
+ string16_map_reset_entries(h);
+}
+
+template <typename T>
+gb_internal T *string16_map_get(String16Map<T> *h, u32 hash, String16 const &key) {
+ MapFindResult fr = {MAP_SENTINEL, MAP_SENTINEL, MAP_SENTINEL};
+ if (h->hashes_count != 0) {
+ fr.hash_index = cast(MapIndex)(hash & (h->hashes_count-1));
+ fr.entry_index = h->hashes[fr.hash_index];
+ while (fr.entry_index != MAP_SENTINEL) {
+ auto *entry = &h->entries[fr.entry_index];
+ if (entry->hash == hash && entry->key == key) {
+ return &entry->value;
+ }
+ fr.entry_prev = fr.entry_index;
+ fr.entry_index = entry->next;
+ }
+ }
+ return nullptr;
+}
+
+
+template <typename T>
+gb_internal gb_inline T *string16_map_get(String16Map<T> *h, String16HashKey const &key) {
+ return string16_map_get(h, key.hash, key.string);
+}
+template <typename T>
+gb_internal T &string16_map_must_get(String16Map<T> *h, u32 hash, String16 const &key) {
+ isize index = string16_map__find(h, hash, key).entry_index;
+ GB_ASSERT(index != MAP_SENTINEL);
+ return h->entries[index].value;
+}
+
+template <typename T>
+gb_internal T &string16_map_must_get(String16Map<T> *h, String16HashKey const &key) {
+ return string16_map_must_get(h, key.hash, key.string);
+}
+
+template <typename T>
+gb_internal void string16_map_set(String16Map<T> *h, u32 hash, String16 const &key, T const &value) {
+ MapIndex index;
+ MapFindResult fr;
+ if (h->hashes_count == 0) {
+ string16_map_grow(h);
+ }
+ fr = string16_map__find(h, hash, key);
+ if (fr.entry_index != MAP_SENTINEL) {
+ index = fr.entry_index;
+ } else {
+ index = string16_map__add_entry(h, hash, key);
+ if (fr.entry_prev != MAP_SENTINEL) {
+ h->entries[fr.entry_prev].next = index;
+ } else {
+ h->hashes[fr.hash_index] = index;
+ }
+ }
+ h->entries[index].value = value;
+
+ if (string16_map__full(h)) {
+ string16_map_grow(h);
+ }
+}
+
+template <typename T>
+gb_internal gb_inline void string16_map_set(String16Map<T> *h, String16HashKey const &key, T const &value) {
+ string16_map_set(h, key.hash, key.string, value);
+}
+
+
+template <typename T>
+gb_internal gb_inline void string16_map_clear(String16Map<T> *h) {
+ h->count = 0;
+ for (u32 i = 0; i < h->hashes_count; i++) {
+ h->hashes[i] = MAP_SENTINEL;
+ }
+}
+
+
+
+template <typename T>
+gb_internal String16MapEntry<T> *begin(String16Map<T> &m) noexcept {
+ return m.entries;
+}
+template <typename T>
+gb_internal String16MapEntry<T> const *begin(String16Map<T> const &m) noexcept {
+ return m.entries;
+}
+
+
+template <typename T>
+gb_internal String16MapEntry<T> *end(String16Map<T> &m) noexcept {
+ return m.entries + m.count;
+}
+
+template <typename T>
+gb_internal String16MapEntry<T> const *end(String16Map<T> const &m) noexcept {
+ return m.entries + m.count;
+}
+
+#else /* new string map */
+
+template <typename T>
+struct StringMapEntry {
+ String key;
+ u32 hash;
+ T value;
+};
+
+template <typename T>
+struct StringMap {
+ String16MapEntry<T> *entries;
+ u32 count;
+ u32 capacity;
+};
+
+
+template <typename T> gb_internal void string16_map_init (String16Map<T> *h, usize capacity = 16);
+template <typename T> gb_internal void string16_map_destroy (String16Map<T> *h);
+
+template <typename T> gb_internal T * string16_map_get (String16Map<T> *h, String16 const &key);
+template <typename T> gb_internal T * string16_map_get (String16Map<T> *h, String16HashKey const &key);
+
+template <typename T> gb_internal T & string16_map_must_get(String16Map<T> *h, String16 const &key);
+template <typename T> gb_internal T & string16_map_must_get(String16Map<T> *h, String16HashKey const &key);
+
+template <typename T> gb_internal void string16_map_set (String16Map<T> *h, String16 const &key, T const &value);
+template <typename T> gb_internal void string16_map_set (String16Map<T> *h, String16HashKey const &key, T const &value);
+
+// template <typename T> gb_internal void string16_map_remove (String16Map<T> *h, String16HashKey const &key);
+template <typename T> gb_internal void string16_map_clear (String16Map<T> *h);
+template <typename T> gb_internal void string16_map_grow (String16Map<T> *h);
+template <typename T> gb_internal void string16_map_reserve (String16Map<T> *h, usize new_count);
+
+gb_internal gbAllocator string16_map_allocator(void) {
+ return heap_allocator();
+}
+
+template <typename T>
+gb_internal gb_inline void string16_map_init(String16Map<T> *h, usize capacity) {
+ capacity = next_pow2_isize(capacity);
+ string16_map_reserve(h, capacity);
+}
+
+template <typename T>
+gb_internal gb_inline void string16_map_destroy(String16Map<T> *h) {
+ gb_free(string16_map_allocator(), h->entries);
+}
+
+
+template <typename T>
+gb_internal void string16_map__insert(String16Map<T> *h, u32 hash, String16 const &key, T const &value) {
+ if (h->count+1 >= h->capacity) {
+ string16_map_grow(h);
+ }
+ GB_ASSERT(h->count+1 < h->capacity);
+
+ u32 mask = h->capacity-1;
+ MapIndex index = hash & mask;
+ MapIndex original_index = index;
+ do {
+ auto *entry = h->entries+index;
+ if (entry->hash == 0) {
+ entry->key = key;
+ entry->hash = hash;
+ entry->value = value;
+
+ h->count += 1;
+ return;
+ }
+ index = (index+1)&mask;
+ } while (index != original_index);
+
+ GB_PANIC("Full map");
+}
+
+template <typename T>
+gb_internal b32 string16_map__full(String16Map<T> *h) {
+ return 0.75f * h->count <= h->capacity;
+}
+
+template <typename T>
+gb_inline void string16_map_grow(String16Map<T> *h) {
+ isize new_capacity = gb_max(h->capacity<<1, 16);
+ string16_map_reserve(h, new_capacity);
+}
+
+
+template <typename T>
+gb_internal void string16_map_reserve(String16Map<T> *h, usize cap) {
+ if (cap < h->capacity) {
+ return;
+ }
+ cap = next_pow2_isize(cap);
+
+ String16Map<T> new_h = {};
+ new_h.count = 0;
+ new_h.capacity = cast(u32)cap;
+ new_h.entries = gb_alloc_array(string16_map_allocator(), String16MapEntry<T>, new_h.capacity);
+
+ if (h->count) {
+ for (u32 i = 0; i < h->capacity; i++) {
+ auto *entry = h->entries+i;
+ if (entry->hash) {
+ string16_map__insert(&new_h, entry->hash, entry->key, entry->value);
+ }
+ }
+ }
+ string16_map_destroy(h);
+ *h = new_h;
+}
+
+template <typename T>
+gb_internal T *string16_map_get(String16Map<T> *h, u32 hash, String16 const &key) {
+ if (h->count == 0) {
+ return nullptr;
+ }
+ u32 mask = (h->capacity-1);
+ u32 index = hash & mask;
+ u32 original_index = index;
+ do {
+ auto *entry = h->entries+index;
+ u32 curr_hash = entry->hash;
+ if (curr_hash == 0) {
+ // NOTE(bill): no found, but there isn't any key removal for this hash map
+ return nullptr;
+ } else if (curr_hash == hash && entry->key == key) {
+ return &entry->value;
+ }
+ index = (index+1) & mask;
+ } while (original_index != index);
+ return nullptr;
+}
+
+
+template <typename T>
+gb_internal gb_inline T *string16_map_get(String16Map<T> *h, String16HashKey const &key) {
+ return string16_map_get(h, key.hash, key.string);
+}
+
+template <typename T>
+gb_internal gb_inline T *string16_map_get(String16Map<T> *h, String16 const &key) {
+ return string16_map_get(h, string_hash(key), key);
+}
+
+template <typename T>
+gb_internal T &string16_map_must_get(String16Map<T> *h, u32 hash, String16 const &key) {
+ T *found = string16_map_get(h, hash, key);
+ GB_ASSERT(found != nullptr);
+ return *found;
+}
+
+template <typename T>
+gb_internal T &string16_map_must_get(String16Map<T> *h, String16HashKey const &key) {
+ return string16_map_must_get(h, key.hash, key.string);
+}
+
+template <typename T>
+gb_internal gb_inline T &string16_map_must_get(String16Map<T> *h, String16 const &key) {
+ return string16_map_must_get(h, string_hash(key), key);
+}
+
+template <typename T>
+gb_internal void string16_map_set(String16Map<T> *h, u32 hash, String16 const &key, T const &value) {
+ if (h->count == 0) {
+ string16_map_grow(h);
+ }
+ auto *found = string16_map_get(h, hash, key);
+ if (found) {
+ *found = value;
+ return;
+ }
+ string16_map__insert(h, hash, key, value);
+}
+
+template <typename T>
+gb_internal gb_inline void string16_map_set(String16Map<T> *h, String16 const &key, T const &value) {
+ string16_map_set(h, string_hash_string(key), value);
+}
+
+template <typename T>
+gb_internal gb_inline void string16_map_set(String16Map<T> *h, String16HashKey const &key, T const &value) {
+ string16_map_set(h, key.hash, key.string, value);
+}
+
+
+template <typename T>
+gb_internal gb_inline void string16_map_clear(String16Map<T> *h) {
+ h->count = 0;
+ gb_zero_array(h->entries, h->capacity);
+}
+
+
+template <typename T>
+struct StringMapIterator {
+ String16Map<T> *map;
+ MapIndex index;
+
+ StringMapIterator<T> &operator++() noexcept {
+ for (;;) {
+ ++index;
+ if (map->capacity == index) {
+ return *this;
+ }
+ String16MapEntry<T> *entry = map->entries+index;
+ if (entry->hash != 0) {
+ return *this;
+ }
+ }
+ }
+
+ bool operator==(StringMapIterator<T> const &other) const noexcept {
+ return this->map == other->map && this->index == other->index;
+ }
+
+ operator String16MapEntry<T> *() const {
+ return map->entries+index;
+ }
+};
+
+
+template <typename T>
+gb_internal StringMapIterator<T> end(String16Map<T> &m) noexcept {
+ return StringMapIterator<T>{&m, m.capacity};
+}
+
+template <typename T>
+gb_internal StringMapIterator<T> const end(String16Map<T> const &m) noexcept {
+ return StringMapIterator<T>{&m, m.capacity};
+}
+
+
+
+template <typename T>
+gb_internal StringMapIterator<T> begin(String16Map<T> &m) noexcept {
+ if (m.count == 0) {
+ return end(m);
+ }
+
+ MapIndex index = 0;
+ while (index < m.capacity) {
+ if (m.entries[index].hash) {
+ break;
+ }
+ index++;
+ }
+ return StringMapIterator<T>{&m, index};
+}
+template <typename T>
+gb_internal StringMapIterator<T> const begin(String16Map<T> const &m) noexcept {
+ if (m.count == 0) {
+ return end(m);
+ }
+
+ MapIndex index = 0;
+ while (index < m.capacity) {
+ if (m.entries[index].hash) {
+ break;
+ }
+ index++;
+ }
+ return StringMapIterator<T>{&m, index};
+}
+
+#endif \ No newline at end of file
diff --git a/src/thread_pool.cpp b/src/thread_pool.cpp
index 8363a4553..ca6483fd9 100644
--- a/src/thread_pool.cpp
+++ b/src/thread_pool.cpp
@@ -19,6 +19,11 @@ enum GrabState {
Grab_Failed = 2,
};
+enum BroadcastWaitState {
+ Nobody_Waiting = 0,
+ Someone_Waiting = 1,
+};
+
struct ThreadPool {
gbAllocator threads_allocator;
Slice<Thread> threads;
@@ -54,8 +59,8 @@ 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_acquire);
- futex_broadcast(&pool->tasks_available);
+ pool->tasks_available.store(Nobody_Waiting);
+ futex_broadcast(&t->pool->tasks_available);
thread_join_and_destroy(t);
}
@@ -87,8 +92,10 @@ void thread_pool_queue_push(Thread *thread, WorkerTask task) {
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);
+ i32 state = Someone_Waiting;
+ if (thread->pool->tasks_available.compare_exchange_strong(state, Nobody_Waiting)) {
+ futex_broadcast(&thread->pool->tasks_available);
+ }
}
GrabState thread_pool_queue_take(Thread *thread, WorkerTask *task) {
@@ -230,12 +237,13 @@ 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);
+ pool->tasks_available.store(Someone_Waiting);
if (!pool->running) { break; }
- futex_wait(&pool->tasks_available, state);
+ futex_wait(&pool->tasks_available, Someone_Waiting);
main_loop_continue:;
}
return 0;
}
+
diff --git a/src/threading.cpp b/src/threading.cpp
index af8fd803c..84f09912d 100644
--- a/src/threading.cpp
+++ b/src/threading.cpp
@@ -195,7 +195,13 @@ gb_internal void mutex_lock(RecursiveMutex *m) {
// inside the lock
return;
}
- futex_wait(&m->owner, prev_owner);
+
+ // NOTE(lucas): we are doing spin lock since futex signal is expensive on OSX. The recursive locks are
+ // very short lived so we don't hit this mega often and I see no perform regression on windows (with
+ // a performance uplift on OSX).
+
+ //futex_wait(&m->owner, prev_owner);
+ yield_thread();
}
}
gb_internal bool mutex_try_lock(RecursiveMutex *m) {
@@ -216,7 +222,9 @@ gb_internal void mutex_unlock(RecursiveMutex *m) {
return;
}
m->owner.exchange(0, std::memory_order_release);
- futex_signal(&m->owner);
+ // NOTE(lucas): see comment about spin lock in mutex_lock above
+
+ // futex_signal(&m->owner);
// outside the lock
}
@@ -448,6 +456,44 @@ gb_internal void semaphore_wait(Semaphore *s) {
}
#endif
+static const int RWLOCK_WRITER = 1<<0;
+static const int RWLOCK_UPGRADED = 1<<1;
+static const int RWLOCK_READER = 1<<2;
+struct RWSpinLock {
+ Futex bits;
+};
+
+void rwlock_release_write(RWSpinLock *l) {
+ l->bits.fetch_and(~(RWLOCK_WRITER | RWLOCK_UPGRADED), std::memory_order_release);
+ futex_signal(&l->bits);
+}
+
+bool rwlock_try_acquire_upgrade(RWSpinLock *l) {
+ int value = l->bits.fetch_or(RWLOCK_UPGRADED, std::memory_order_acquire);
+ return (value & (RWLOCK_UPGRADED | RWLOCK_WRITER)) == 0;
+}
+
+void rwlock_acquire_upgrade(RWSpinLock *l) {
+ while (!rwlock_try_acquire_upgrade(l)) {
+ futex_wait(&l->bits, RWLOCK_UPGRADED | RWLOCK_WRITER);
+ }
+}
+void rwlock_release_upgrade(RWSpinLock *l) {
+ l->bits.fetch_add(-RWLOCK_UPGRADED, std::memory_order_acq_rel);
+}
+
+bool rwlock_try_release_upgrade_and_acquire_write(RWSpinLock *l) {
+ int expect = RWLOCK_UPGRADED;
+ return l->bits.compare_exchange_strong(expect, RWLOCK_WRITER, std::memory_order_acq_rel);
+}
+
+void rwlock_release_upgrade_and_acquire_write(RWSpinLock *l) {
+ while (!rwlock_try_release_upgrade_and_acquire_write(l)) {
+ futex_wait(&l->bits, RWLOCK_UPGRADED);
+ }
+}
+
+
struct Parker {
Futex state;
};
@@ -756,7 +802,7 @@ gb_internal void futex_signal(Futex *f) {
perror("Futex wake");
GB_PANIC("futex wake fail");
- } else if (ret == 1) {
+ } else {
return;
}
}
@@ -773,7 +819,7 @@ gb_internal void futex_broadcast(Futex *f) {
perror("Futex wake");
GB_PANIC("futex wake fail");
- } else if (ret == 1) {
+ } else {
return;
}
}
@@ -783,7 +829,7 @@ gb_internal void futex_wait(Futex *f, Footex val) {
for (;;) {
int ret = futex((volatile uint32_t *)f, FUTEX_WAIT | FUTEX_PRIVATE_FLAG, val, NULL, NULL);
if (ret == -1) {
- if (*f != val) {
+ if (errno == EAGAIN) {
return;
}
diff --git a/src/timings.cpp b/src/timings.cpp
index 3f8402b36..f6e86867f 100644
--- a/src/timings.cpp
+++ b/src/timings.cpp
@@ -197,7 +197,7 @@ gb_internal void timings_print_all(Timings *t, TimingUnit unit = TimingUnit_Mill
f64 total_time = time_stamp(t->total, t->freq, unit);
- gb_printf("%.*s%.*s - % 9.3f %s - %6.2f%%\n",
+ gb_printf_err("%.*s%.*s - % 9.3f %s - %6.2f%%\n",
LIT(t->total.label),
cast(int)(max_len-t->total.label.len), SPACES,
total_time,
@@ -207,7 +207,7 @@ gb_internal void timings_print_all(Timings *t, TimingUnit unit = TimingUnit_Mill
for_array(i, t->sections) {
TimeStamp ts = t->sections[i];
f64 section_time = time_stamp(ts, t->freq, unit);
- gb_printf("%.*s%.*s - % 9.3f %s - %6.2f%%\n",
+ gb_printf_err("%.*s%.*s - % 9.3f %s - %6.2f%%\n",
LIT(ts.label),
cast(int)(max_len-ts.label.len), SPACES,
section_time,
diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp
index 53f6135d0..ffa53abb5 100644
--- a/src/tokenizer.cpp
+++ b/src/tokenizer.cpp
@@ -968,6 +968,7 @@ gb_internal void tokenizer_get_token(Tokenizer *t, Token *token, int repeat=0) {
advance_to_next_rune(t);
for (isize comment_scope = 1; comment_scope > 0; /**/) {
if (t->curr_rune == GB_RUNE_EOF) {
+ tokenizer_err(t, "Multi-line comment not terminated");
break;
} else if (t->curr_rune == '/') {
advance_to_next_rune(t);
diff --git a/src/types.cpp b/src/types.cpp
index 43fe625f2..372c2e991 100644
--- a/src/types.cpp
+++ b/src/types.cpp
@@ -41,8 +41,13 @@ enum BasicKind {
Basic_uint,
Basic_uintptr,
Basic_rawptr,
- Basic_string, // ^u8 + int
- Basic_cstring, // ^u8
+
+ Basic_string, // [^]u8 + int
+ Basic_cstring, // [^]u8
+
+ Basic_string16, // [^]u16 + int
+ Basic_cstring16, // [^]u16 + int
+
Basic_any, // rawptr + ^Type_Info
Basic_typeid,
@@ -111,7 +116,7 @@ enum BasicFlag {
BasicFlag_Ordered = BasicFlag_Integer | BasicFlag_Float | BasicFlag_String | BasicFlag_Pointer | BasicFlag_Rune,
BasicFlag_OrderedNumeric = BasicFlag_Integer | BasicFlag_Float | BasicFlag_Rune,
BasicFlag_ConstantType = BasicFlag_Boolean | BasicFlag_Numeric | BasicFlag_String | BasicFlag_Pointer | BasicFlag_Rune,
- BasicFlag_SimpleCompare = BasicFlag_Boolean | BasicFlag_Numeric | BasicFlag_Pointer | BasicFlag_Rune,
+ BasicFlag_SimpleCompare = BasicFlag_Boolean | BasicFlag_Integer | BasicFlag_Pointer | BasicFlag_Rune,
};
struct BasicType {
@@ -152,11 +157,11 @@ struct TypeStruct {
bool is_polymorphic;
bool are_offsets_set : 1;
- bool are_offsets_being_processed : 1;
bool is_packed : 1;
bool is_raw_union : 1;
- bool is_no_copy : 1;
bool is_poly_specialized : 1;
+
+ std::atomic<bool> are_offsets_being_processed;
};
struct TypeUnion {
@@ -201,13 +206,18 @@ struct TypeProc {
bool optional_ok;
};
+struct TypeNamed {
+ String name;
+ Type * base;
+ Entity *type_name; /* Entity_TypeName */
+
+ BlockingMutex gen_types_data_mutex;
+ GenTypesData *gen_types_data;
+};
+
#define TYPE_KINDS \
TYPE_KIND(Basic, BasicType) \
- TYPE_KIND(Named, struct { \
- String name; \
- Type * base; \
- Entity *type_name; /* Entity_TypeName */ \
- }) \
+ TYPE_KIND(Named, TypeNamed) \
TYPE_KIND(Generic, struct { \
i64 id; \
String name; \
@@ -255,7 +265,7 @@ struct TypeProc {
Slice<Entity *> variables; /* Entity_Variable */ \
i64 * offsets; \
BlockingMutex mutex; /* for settings offsets */ \
- bool are_offsets_being_processed; \
+ std::atomic<bool> are_offsets_being_processed; \
bool are_offsets_set; \
bool is_packed; \
}) \
@@ -329,6 +339,7 @@ struct Type {
// NOTE(bill): These need to be at the end to not affect the unionized data
std::atomic<i64> cached_size;
std::atomic<i64> cached_align;
+ std::atomic<u64> canonical_hash;
std::atomic<u32> flags; // TypeFlag
bool failure;
};
@@ -424,11 +435,8 @@ gb_internal Selection make_selection(Entity *entity, Array<i32> index, bool indi
}
gb_internal void selection_add_index(Selection *s, isize index) {
- // IMPORTANT NOTE(bill): this requires a stretchy buffer/dynamic array so it requires some form
- // of heap allocation
- // TODO(bill): Find a way to use a backing buffer for initial use as the general case is probably .count<3
if (s->index.data == nullptr) {
- array_init(&s->index, heap_allocator());
+ array_init(&s->index, permanent_allocator());
}
array_add(&s->index, cast(i32)index);
}
@@ -436,7 +444,7 @@ gb_internal void selection_add_index(Selection *s, isize index) {
gb_internal Selection selection_combine(Selection const &lhs, Selection const &rhs) {
Selection new_sel = lhs;
new_sel.indirect = lhs.indirect || rhs.indirect;
- new_sel.index = array_make<i32>(heap_allocator(), lhs.index.count+rhs.index.count);
+ new_sel.index = array_make<i32>(permanent_allocator(), lhs.index.count+rhs.index.count);
array_copy(&new_sel.index, lhs.index, 0);
array_copy(&new_sel.index, rhs.index, lhs.index.count);
return new_sel;
@@ -501,8 +509,14 @@ gb_global Type basic_types[] = {
{Type_Basic, {Basic_uintptr, BasicFlag_Integer | BasicFlag_Unsigned, -1, STR_LIT("uintptr")}},
{Type_Basic, {Basic_rawptr, BasicFlag_Pointer, -1, STR_LIT("rawptr")}},
+
{Type_Basic, {Basic_string, BasicFlag_String, -1, STR_LIT("string")}},
{Type_Basic, {Basic_cstring, BasicFlag_String, -1, STR_LIT("cstring")}},
+
+ {Type_Basic, {Basic_string16, BasicFlag_String, -1, STR_LIT("string16")}},
+ {Type_Basic, {Basic_cstring16, BasicFlag_String, -1, STR_LIT("cstring16")}},
+
+
{Type_Basic, {Basic_any, 0, 16, STR_LIT("any")}},
{Type_Basic, {Basic_typeid, 0, 8, STR_LIT("typeid")}},
@@ -592,8 +606,12 @@ gb_global Type *t_uint = &basic_types[Basic_uint];
gb_global Type *t_uintptr = &basic_types[Basic_uintptr];
gb_global Type *t_rawptr = &basic_types[Basic_rawptr];
+
gb_global Type *t_string = &basic_types[Basic_string];
gb_global Type *t_cstring = &basic_types[Basic_cstring];
+gb_global Type *t_string16 = &basic_types[Basic_string16];
+gb_global Type *t_cstring16 = &basic_types[Basic_cstring16];
+
gb_global Type *t_any = &basic_types[Basic_any];
gb_global Type *t_typeid = &basic_types[Basic_typeid];
@@ -631,6 +649,8 @@ gb_global Type *t_untyped_uninit = &basic_types[Basic_UntypedUninit];
gb_global Type *t_u8_ptr = nullptr;
gb_global Type *t_u8_multi_ptr = nullptr;
+gb_global Type *t_u16_ptr = nullptr;
+gb_global Type *t_u16_multi_ptr = nullptr;
gb_global Type *t_int_ptr = nullptr;
gb_global Type *t_i64_ptr = nullptr;
gb_global Type *t_f64_ptr = nullptr;
@@ -644,6 +664,8 @@ gb_global Type *t_type_info_enum_value = nullptr;
gb_global Type *t_type_info_ptr = nullptr;
gb_global Type *t_type_info_enum_value_ptr = nullptr;
+gb_global Type *t_type_info_string_encoding_kind = nullptr;
+
gb_global Type *t_type_info_named = nullptr;
gb_global Type *t_type_info_integer = nullptr;
gb_global Type *t_type_info_rune = nullptr;
@@ -729,10 +751,15 @@ gb_global Type *t_map_set_proc = nullptr;
gb_global Type *t_objc_object = nullptr;
gb_global Type *t_objc_selector = nullptr;
gb_global Type *t_objc_class = nullptr;
+gb_global Type *t_objc_ivar = nullptr;
+gb_global Type *t_objc_super = nullptr; // Struct used in lieu of the 'self' instance when calling objc_msgSendSuper.
+gb_global Type *t_objc_super_ptr = nullptr;
gb_global Type *t_objc_id = nullptr;
gb_global Type *t_objc_SEL = nullptr;
gb_global Type *t_objc_Class = nullptr;
+gb_global Type *t_objc_Ivar = nullptr;
+gb_global Type *t_objc_instancetype = nullptr; // Special distinct variant of t_objc_id used mimic auto-typing of instancetype* in Objective-C
enum OdinAtomicMemoryOrder : i32 {
OdinAtomicMemoryOrder_relaxed = 0, // unordered
@@ -872,6 +899,29 @@ gb_internal Type *base_type(Type *t) {
return t;
}
+gb_internal Type *base_named_type(Type *t) {
+ if (t->kind != Type_Named) {
+ return t_invalid;
+ }
+
+ Type *prev_named = t;
+ t = t->Named.base;
+ for (;;) {
+ if (t == nullptr) {
+ break;
+ }
+ if (t->kind != Type_Named) {
+ break;
+ }
+ if (t == t->Named.base) {
+ return t_invalid;
+ }
+ prev_named = t;
+ t = t->Named.base;
+ }
+ return prev_named;
+}
+
gb_internal Type *base_enum_type(Type *t) {
Type *bt = base_type(t);
if (bt != nullptr &&
@@ -1178,6 +1228,7 @@ gb_internal Type *type_deref(Type *t, bool allow_multi_pointer) {
}
gb_internal bool is_type_named(Type *t) {
+ if (t == nullptr) { return false; }
if (t->kind == Type_Basic) {
return true;
}
@@ -1185,16 +1236,16 @@ gb_internal bool is_type_named(Type *t) {
}
gb_internal bool is_type_boolean(Type *t) {
- // t = core_type(t);
t = base_type(t);
+ if (t == nullptr) { return false; }
if (t->kind == Type_Basic) {
return (t->Basic.flags & BasicFlag_Boolean) != 0;
}
return false;
}
gb_internal bool is_type_integer(Type *t) {
- // t = core_type(t);
t = base_type(t);
+ if (t == nullptr) { return false; }
if (t->kind == Type_Basic) {
return (t->Basic.flags & BasicFlag_Integer) != 0;
}
@@ -1202,6 +1253,7 @@ gb_internal bool is_type_integer(Type *t) {
}
gb_internal bool is_type_integer_like(Type *t) {
t = core_type(t);
+ if (t == nullptr) { return false; }
if (t->kind == Type_Basic) {
return (t->Basic.flags & (BasicFlag_Integer|BasicFlag_Boolean)) != 0;
}
@@ -1216,31 +1268,34 @@ gb_internal bool is_type_integer_like(Type *t) {
gb_internal bool is_type_unsigned(Type *t) {
t = base_type(t);
- // t = core_type(t);
+ if (t == nullptr) { return false; }
if (t->kind == Type_Basic) {
return (t->Basic.flags & BasicFlag_Unsigned) != 0;
}
+ if (t->kind == Type_Enum) {
+ return (t->Enum.base_type->Basic.flags & BasicFlag_Unsigned) != 0;
+ }
return false;
}
gb_internal bool is_type_integer_128bit(Type *t) {
- // t = core_type(t);
t = base_type(t);
+ if (t == nullptr) { return false; }
if (t->kind == Type_Basic) {
return (t->Basic.flags & BasicFlag_Integer) != 0 && t->Basic.size == 16;
}
return false;
}
gb_internal bool is_type_rune(Type *t) {
- // t = core_type(t);
t = base_type(t);
+ if (t == nullptr) { return false; }
if (t->kind == Type_Basic) {
return (t->Basic.flags & BasicFlag_Rune) != 0;
}
return false;
}
gb_internal bool is_type_numeric(Type *t) {
- // t = core_type(t);
t = base_type(t);
+ if (t == nullptr) { return false; }
if (t->kind == Type_Basic) {
return (t->Basic.flags & BasicFlag_Numeric) != 0;
} else if (t->kind == Type_Enum) {
@@ -1254,23 +1309,39 @@ gb_internal bool is_type_numeric(Type *t) {
}
gb_internal bool is_type_string(Type *t) {
t = base_type(t);
+ if (t == nullptr) { return false; }
if (t->kind == Type_Basic) {
return (t->Basic.flags & BasicFlag_String) != 0;
}
return false;
}
+gb_internal bool is_type_string16(Type *t) {
+ t = base_type(t);
+ if (t == nullptr) { return false; }
+ if (t->kind == Type_Basic) {
+ return t->Basic.kind == Basic_string16;
+ }
+ return false;
+}
gb_internal bool is_type_cstring(Type *t) {
t = base_type(t);
+ if (t == nullptr) { return false; }
if (t->kind == Type_Basic) {
return t->Basic.kind == Basic_cstring;
}
return false;
}
-gb_internal bool is_type_typed(Type *t) {
+gb_internal bool is_type_cstring16(Type *t) {
t = base_type(t);
- if (t == nullptr) {
- return false;
+ if (t == nullptr) { return false; }
+ if (t->kind == Type_Basic) {
+ return t->Basic.kind == Basic_cstring16;
}
+ return false;
+}
+gb_internal bool is_type_typed(Type *t) {
+ t = base_type(t);
+ if (t == nullptr) { return false; }
if (t->kind == Type_Basic) {
return (t->Basic.flags & BasicFlag_Untyped) == 0;
}
@@ -1278,9 +1349,7 @@ gb_internal bool is_type_typed(Type *t) {
}
gb_internal bool is_type_untyped(Type *t) {
t = base_type(t);
- if (t == nullptr) {
- return false;
- }
+ if (t == nullptr) { return false; }
if (t->kind == Type_Basic) {
return (t->Basic.flags & BasicFlag_Untyped) != 0;
}
@@ -1288,6 +1357,7 @@ gb_internal bool is_type_untyped(Type *t) {
}
gb_internal bool is_type_ordered(Type *t) {
t = core_type(t);
+ if (t == nullptr) { return false; }
switch (t->kind) {
case Type_Basic:
return (t->Basic.flags & BasicFlag_Ordered) != 0;
@@ -1300,6 +1370,7 @@ gb_internal bool is_type_ordered(Type *t) {
}
gb_internal bool is_type_ordered_numeric(Type *t) {
t = core_type(t);
+ if (t == nullptr) { return false; }
switch (t->kind) {
case Type_Basic:
return (t->Basic.flags & BasicFlag_OrderedNumeric) != 0;
@@ -1308,19 +1379,27 @@ gb_internal bool is_type_ordered_numeric(Type *t) {
}
gb_internal bool is_type_constant_type(Type *t) {
t = core_type(t);
- if (t->kind == Type_Basic) {
+ if (t == nullptr) { return false; }
+ switch (t->kind) {
+ case Type_Basic:
+ if (t->Basic.kind == Basic_typeid) {
+ return true;
+ }
return (t->Basic.flags & BasicFlag_ConstantType) != 0;
- }
- if (t->kind == Type_BitSet) {
+ case Type_BitSet:
return true;
- }
- if (t->kind == Type_Proc) {
+ case Type_Proc:
return true;
+ case Type_Array:
+ return is_type_constant_type(t->Array.elem);
+ case Type_EnumeratedArray:
+ return is_type_constant_type(t->EnumeratedArray.elem);
}
return false;
}
gb_internal bool is_type_float(Type *t) {
t = core_type(t);
+ if (t == nullptr) { return false; }
if (t->kind == Type_Basic) {
return (t->Basic.flags & BasicFlag_Float) != 0;
}
@@ -1328,6 +1407,7 @@ gb_internal bool is_type_float(Type *t) {
}
gb_internal bool is_type_complex(Type *t) {
t = core_type(t);
+ if (t == nullptr) { return false; }
if (t->kind == Type_Basic) {
return (t->Basic.flags & BasicFlag_Complex) != 0;
}
@@ -1335,6 +1415,7 @@ gb_internal bool is_type_complex(Type *t) {
}
gb_internal bool is_type_quaternion(Type *t) {
t = core_type(t);
+ if (t == nullptr) { return false; }
if (t->kind == Type_Basic) {
return (t->Basic.flags & BasicFlag_Quaternion) != 0;
}
@@ -1342,6 +1423,7 @@ gb_internal bool is_type_quaternion(Type *t) {
}
gb_internal bool is_type_complex_or_quaternion(Type *t) {
t = core_type(t);
+ if (t == nullptr) { return false; }
if (t->kind == Type_Basic) {
return (t->Basic.flags & (BasicFlag_Complex|BasicFlag_Quaternion)) != 0;
}
@@ -1349,6 +1431,7 @@ gb_internal bool is_type_complex_or_quaternion(Type *t) {
}
gb_internal bool is_type_pointer(Type *t) {
t = base_type(t);
+ if (t == nullptr) { return false; }
if (t->kind == Type_Basic) {
return (t->Basic.flags & BasicFlag_Pointer) != 0;
}
@@ -1356,10 +1439,12 @@ gb_internal bool is_type_pointer(Type *t) {
}
gb_internal bool is_type_soa_pointer(Type *t) {
t = base_type(t);
+ if (t == nullptr) { return false; }
return t->kind == Type_SoaPointer;
}
gb_internal bool is_type_multi_pointer(Type *t) {
t = base_type(t);
+ if (t == nullptr) { return false; }
return t->kind == Type_MultiPointer;
}
gb_internal bool is_type_internally_pointer_like(Type *t) {
@@ -1368,36 +1453,50 @@ gb_internal bool is_type_internally_pointer_like(Type *t) {
gb_internal bool is_type_tuple(Type *t) {
t = base_type(t);
+ if (t == nullptr) { return false; }
return t->kind == Type_Tuple;
}
gb_internal bool is_type_uintptr(Type *t) {
+ if (t == nullptr) { return false; }
if (t->kind == Type_Basic) {
return (t->Basic.kind == Basic_uintptr);
}
return false;
}
gb_internal bool is_type_rawptr(Type *t) {
+ if (t == nullptr) { return false; }
if (t->kind == Type_Basic) {
return t->Basic.kind == Basic_rawptr;
}
return false;
}
gb_internal bool is_type_u8(Type *t) {
+ if (t == nullptr) { return false; }
if (t->kind == Type_Basic) {
return t->Basic.kind == Basic_u8;
}
return false;
}
+gb_internal bool is_type_u16(Type *t) {
+ if (t == nullptr) { return false; }
+ if (t->kind == Type_Basic) {
+ return t->Basic.kind == Basic_u16;
+ }
+ return false;
+}
gb_internal bool is_type_array(Type *t) {
t = base_type(t);
+ if (t == nullptr) { return false; }
return t->kind == Type_Array;
}
gb_internal bool is_type_enumerated_array(Type *t) {
t = base_type(t);
+ if (t == nullptr) { return false; }
return t->kind == Type_EnumeratedArray;
}
gb_internal bool is_type_matrix(Type *t) {
t = base_type(t);
+ if (t == nullptr) { return false; }
return t->kind == Type_Matrix;
}
@@ -1526,6 +1625,7 @@ gb_internal bool is_matrix_square(Type *t) {
gb_internal bool is_type_valid_for_matrix_elems(Type *t) {
t = base_type(t);
+ if (t == nullptr) { return false; }
if (is_type_integer(t)) {
return true;
} else if (is_type_float(t)) {
@@ -1541,22 +1641,27 @@ gb_internal bool is_type_valid_for_matrix_elems(Type *t) {
gb_internal bool is_type_dynamic_array(Type *t) {
t = base_type(t);
+ if (t == nullptr) { return false; }
return t->kind == Type_DynamicArray;
}
gb_internal bool is_type_slice(Type *t) {
t = base_type(t);
+ if (t == nullptr) { return false; }
return t->kind == Type_Slice;
}
gb_internal bool is_type_proc(Type *t) {
t = base_type(t);
+ if (t == nullptr) { return false; }
return t->kind == Type_Proc;
}
gb_internal bool is_type_asm_proc(Type *t) {
t = base_type(t);
+ if (t == nullptr) { return false; }
return t->kind == Type_Proc && t->Proc.calling_convention == ProcCC_InlineAsm;
}
gb_internal bool is_type_simd_vector(Type *t) {
t = base_type(t);
+ if (t == nullptr) { return false; }
return t->kind == Type_SimdVector;
}
@@ -1596,11 +1701,13 @@ gb_internal Type *base_any_array_type(Type *t) {
gb_internal bool is_type_generic(Type *t) {
t = base_type(t);
+ if (t == nullptr) { return false; }
return t->kind == Type_Generic;
}
gb_internal bool is_type_u8_slice(Type *t) {
t = base_type(t);
+ if (t == nullptr) { return false; }
if (t->kind == Type_Slice) {
return is_type_u8(t->Slice.elem);
}
@@ -1608,6 +1715,7 @@ gb_internal bool is_type_u8_slice(Type *t) {
}
gb_internal bool is_type_u8_array(Type *t) {
t = base_type(t);
+ if (t == nullptr) { return false; }
if (t->kind == Type_Array) {
return is_type_u8(t->Array.elem);
}
@@ -1615,6 +1723,7 @@ gb_internal bool is_type_u8_array(Type *t) {
}
gb_internal bool is_type_u8_ptr(Type *t) {
t = base_type(t);
+ if (t == nullptr) { return false; }
if (t->kind == Type_Pointer) {
return is_type_u8(t->Slice.elem);
}
@@ -1622,6 +1731,7 @@ gb_internal bool is_type_u8_ptr(Type *t) {
}
gb_internal bool is_type_u8_multi_ptr(Type *t) {
t = base_type(t);
+ if (t == nullptr) { return false; }
if (t->kind == Type_MultiPointer) {
return is_type_u8(t->Slice.elem);
}
@@ -1629,12 +1739,46 @@ gb_internal bool is_type_u8_multi_ptr(Type *t) {
}
gb_internal bool is_type_rune_array(Type *t) {
t = base_type(t);
+ if (t == nullptr) { return false; }
if (t->kind == Type_Array) {
return is_type_rune(t->Array.elem);
}
return false;
}
+gb_internal bool is_type_u16_slice(Type *t) {
+ t = base_type(t);
+ if (t == nullptr) { return false; }
+ if (t->kind == Type_Slice) {
+ return is_type_u16(t->Slice.elem);
+ }
+ return false;
+}
+gb_internal bool is_type_u16_array(Type *t) {
+ t = base_type(t);
+ if (t == nullptr) { return false; }
+ if (t->kind == Type_Array) {
+ return is_type_u16(t->Array.elem);
+ }
+ return false;
+}
+gb_internal bool is_type_u16_ptr(Type *t) {
+ t = base_type(t);
+ if (t == nullptr) { return false; }
+ if (t->kind == Type_Pointer) {
+ return is_type_u16(t->Slice.elem);
+ }
+ return false;
+}
+gb_internal bool is_type_u16_multi_ptr(Type *t) {
+ t = base_type(t);
+ if (t == nullptr) { return false; }
+ if (t->kind == Type_MultiPointer) {
+ return is_type_u16(t->Slice.elem);
+ }
+ return false;
+}
+
gb_internal bool is_type_array_like(Type *t) {
return is_type_array(t) || is_type_enumerated_array(t);
@@ -1709,44 +1853,49 @@ gb_internal Type *base_complex_elem_type(Type *t) {
gb_internal bool is_type_struct(Type *t) {
t = base_type(t);
+ if (t == nullptr) { return false; }
return t->kind == Type_Struct;
}
gb_internal bool is_type_union(Type *t) {
t = base_type(t);
+ if (t == nullptr) { return false; }
return t->kind == Type_Union;
}
gb_internal bool is_type_soa_struct(Type *t) {
t = base_type(t);
+ if (t == nullptr) { return false; }
return t->kind == Type_Struct && t->Struct.soa_kind != StructSoa_None;
}
gb_internal bool is_type_raw_union(Type *t) {
t = base_type(t);
+ if (t == nullptr) { return false; }
return (t->kind == Type_Struct && t->Struct.is_raw_union);
}
-gb_internal bool is_type_no_copy(Type *t) {
- t = base_type(t);
- return (t->kind == Type_Struct && t->Struct.is_no_copy);
-}
gb_internal bool is_type_enum(Type *t) {
t = base_type(t);
+ if (t == nullptr) { return false; }
return (t->kind == Type_Enum);
}
gb_internal bool is_type_bit_set(Type *t) {
t = base_type(t);
+ if (t == nullptr) { return false; }
return (t->kind == Type_BitSet);
}
gb_internal bool is_type_bit_field(Type *t) {
t = base_type(t);
+ if (t == nullptr) { return false; }
return (t->kind == Type_BitField);
}
gb_internal bool is_type_map(Type *t) {
t = base_type(t);
+ if (t == nullptr) { return false; }
return t->kind == Type_Map;
}
gb_internal bool is_type_union_maybe_pointer(Type *t) {
t = base_type(t);
+ if (t == nullptr) { return false; }
if (t->kind == Type_Union && t->Union.variants.count == 1) {
Type *v = t->Union.variants[0];
return is_type_internally_pointer_like(v);
@@ -1757,6 +1906,7 @@ gb_internal bool is_type_union_maybe_pointer(Type *t) {
gb_internal bool is_type_union_maybe_pointer_original_alignment(Type *t) {
t = base_type(t);
+ if (t == nullptr) { return false; }
if (t->kind == Type_Union && t->Union.variants.count == 1) {
Type *v = t->Union.variants[0];
if (is_type_internally_pointer_like(v)) {
@@ -1791,6 +1941,7 @@ gb_internal TypeEndianKind type_endian_kind_of(Type *t) {
gb_internal bool is_type_endian_big(Type *t) {
t = core_type(t);
+ if (t == nullptr) { return false; }
if (t->kind == Type_Basic) {
if (t->Basic.flags & BasicFlag_EndianBig) {
return true;
@@ -1807,6 +1958,7 @@ gb_internal bool is_type_endian_big(Type *t) {
}
gb_internal bool is_type_endian_little(Type *t) {
t = core_type(t);
+ if (t == nullptr) { return false; }
if (t->kind == Type_Basic) {
if (t->Basic.flags & BasicFlag_EndianLittle) {
return true;
@@ -1824,6 +1976,7 @@ gb_internal bool is_type_endian_little(Type *t) {
gb_internal bool is_type_endian_platform(Type *t) {
t = core_type(t);
+ if (t == nullptr) { return false; }
if (t->kind == Type_Basic) {
return (t->Basic.flags & (BasicFlag_EndianLittle|BasicFlag_EndianBig)) == 0;
} else if (t->kind == Type_BitSet) {
@@ -1839,6 +1992,7 @@ gb_internal bool types_have_same_internal_endian(Type *a, Type *b) {
}
gb_internal bool is_type_endian_specific(Type *t) {
t = core_type(t);
+ if (t == nullptr) { return false; }
if (t->kind == Type_BitSet) {
t = bit_set_to_int(t);
}
@@ -1936,25 +2090,35 @@ gb_internal Type *integer_endian_type_to_platform_type(Type *t) {
gb_internal bool is_type_any(Type *t) {
t = base_type(t);
+ if (t == nullptr) { return false; }
return (t->kind == Type_Basic && t->Basic.kind == Basic_any);
}
gb_internal bool is_type_typeid(Type *t) {
t = base_type(t);
+ if (t == nullptr) { return false; }
return (t->kind == Type_Basic && t->Basic.kind == Basic_typeid);
}
gb_internal bool is_type_untyped_nil(Type *t) {
t = base_type(t);
+ if (t == nullptr) { return false; }
// NOTE(bill): checking for `nil` or `---` at once is just to improve the error handling
return (t->kind == Type_Basic && (t->Basic.kind == Basic_UntypedNil || t->Basic.kind == Basic_UntypedUninit));
}
gb_internal bool is_type_untyped_uninit(Type *t) {
t = base_type(t);
+ if (t == nullptr) { return false; }
// NOTE(bill): checking for `nil` or `---` at once is just to improve the error handling
return (t->kind == Type_Basic && t->Basic.kind == Basic_UntypedUninit);
}
gb_internal bool is_type_empty_union(Type *t) {
+ if (t == nullptr) {
+ return false;
+ }
t = base_type(t);
+ if (t == nullptr) {
+ return false;
+ }
return t->kind == Type_Union && t->Union.variants.count == 0;
}
@@ -2052,7 +2216,7 @@ gb_internal bool is_type_indexable(Type *t) {
Type *bt = base_type(t);
switch (bt->kind) {
case Type_Basic:
- return bt->Basic.kind == Basic_string;
+ return bt->Basic.kind == Basic_string || bt->Basic.kind == Basic_string16;
case Type_Array:
case Type_Slice:
case Type_DynamicArray:
@@ -2072,7 +2236,7 @@ gb_internal bool is_type_sliceable(Type *t) {
Type *bt = base_type(t);
switch (bt->kind) {
case Type_Basic:
- return bt->Basic.kind == Basic_string;
+ return bt->Basic.kind == Basic_string || bt->Basic.kind == Basic_string16;
case Type_Array:
case Type_Slice:
case Type_DynamicArray:
@@ -2319,6 +2483,7 @@ gb_internal bool type_has_nil(Type *t) {
case Basic_any:
return true;
case Basic_cstring:
+ case Basic_cstring16:
return true;
case Basic_typeid:
return true;
@@ -2351,18 +2516,70 @@ gb_internal bool type_has_nil(Type *t) {
return false;
}
+gb_internal bool is_type_union_constantable(Type *type) {
+ Type *bt = base_type(type);
+ GB_ASSERT(bt->kind == Type_Union);
+
+ if (bt->Union.variants.count == 0) {
+ return true;
+ } else if (bt->Union.variants.count == 1) {
+ return is_type_constant_type(bt->Union.variants[0]);
+ }
+
+ for (Type *v : bt->Union.variants) {
+ if (!is_type_constant_type(v)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+gb_internal bool is_type_raw_union_constantable(Type *type) {
+ Type *bt = base_type(type);
+ GB_ASSERT(bt->kind == Type_Struct);
+ GB_ASSERT(bt->Struct.is_raw_union);
+
+ for (Entity *f : bt->Struct.fields) {
+ if (!is_type_constant_type(f->type)) {
+ return false;
+ }
+ }
+ // return true;
+ return false; // Disable raw union constants for the time being
+}
+
gb_internal bool elem_type_can_be_constant(Type *t) {
t = base_type(t);
if (t == t_invalid) {
return false;
}
- if (is_type_any(t) || is_type_union(t) || is_type_raw_union(t)) {
+ if (is_type_any(t)) {
return false;
}
+ if (is_type_raw_union(t)) {
+ return is_type_raw_union_constantable(t);
+ }
+ if (is_type_union(t)) {
+ return is_type_union_constantable(t);
+ }
return true;
}
+gb_internal bool elem_cannot_be_constant(Type *t) {
+ if (is_type_any(t)) {
+ return true;
+ }
+ if (is_type_union(t)) {
+ return !is_type_union_constantable(t);
+ }
+ if (is_type_raw_union(t)) {
+ return !is_type_raw_union_constantable(t);
+ }
+ return false;
+}
+
+
gb_internal bool is_type_lock_free(Type *t) {
t = core_type(t);
if (t == t_invalid) {
@@ -2386,8 +2603,9 @@ gb_internal bool is_type_comparable(Type *t) {
case Basic_rune:
return true;
case Basic_string:
- return true;
case Basic_cstring:
+ case Basic_string16:
+ case Basic_cstring16:
return true;
case Basic_typeid:
return true;
@@ -2502,6 +2720,62 @@ gb_internal bool is_type_simple_compare(Type *t) {
return false;
}
+// NOTE(bill): type can be easily compared using memcmp or contains a float
+gb_internal bool is_type_nearly_simple_compare(Type *t) {
+ t = core_type(t);
+ switch (t->kind) {
+ case Type_Array:
+ return is_type_nearly_simple_compare(t->Array.elem);
+
+ case Type_EnumeratedArray:
+ return is_type_nearly_simple_compare(t->EnumeratedArray.elem);
+
+ case Type_Basic:
+ if (t->Basic.flags & (BasicFlag_SimpleCompare|BasicFlag_Numeric)) {
+ return true;
+ }
+ if (t->Basic.kind == Basic_typeid) {
+ return true;
+ }
+ return false;
+
+ case Type_Pointer:
+ case Type_MultiPointer:
+ case Type_SoaPointer:
+ case Type_Proc:
+ case Type_BitSet:
+ return true;
+
+ case Type_Matrix:
+ return is_type_nearly_simple_compare(t->Matrix.elem);
+
+ case Type_Struct:
+ for_array(i, t->Struct.fields) {
+ Entity *f = t->Struct.fields[i];
+ if (!is_type_nearly_simple_compare(f->type)) {
+ return false;
+ }
+ }
+ return true;
+
+ case Type_Union:
+ for_array(i, t->Union.variants) {
+ Type *v = t->Union.variants[i];
+ if (!is_type_nearly_simple_compare(v)) {
+ return false;
+ }
+ }
+ // make it dumb on purpose
+ return t->Union.variants.count == 1;
+
+ case Type_SimdVector:
+ return is_type_nearly_simple_compare(t->SimdVector.elem);
+
+ }
+
+ return false;
+}
+
gb_internal bool is_type_load_safe(Type *type) {
GB_ASSERT(type != nullptr);
type = core_type(core_array_type(type));
@@ -2643,10 +2917,11 @@ gb_internal bool are_types_identical(Type *x, Type *y) {
y = y->Named.base;
}
}
- if (x->kind != y->kind) {
+ if (x == nullptr || y == nullptr || x->kind != y->kind) {
return false;
}
+ // MUTEX_GUARD(&g_type_mutex);
return are_types_identical_internal(x, y, false);
}
gb_internal bool are_types_identical_unique_tuples(Type *x, Type *y) {
@@ -2674,6 +2949,7 @@ gb_internal bool are_types_identical_unique_tuples(Type *x, Type *y) {
return false;
}
+ // MUTEX_GUARD(&g_type_mutex);
return are_types_identical_internal(x, y, true);
}
@@ -2797,7 +3073,6 @@ gb_internal bool are_types_identical_internal(Type *x, Type *y, bool check_tuple
case Type_Struct:
if (x->Struct.is_raw_union == y->Struct.is_raw_union &&
- x->Struct.is_no_copy == y->Struct.is_no_copy &&
x->Struct.fields.count == y->Struct.fields.count &&
x->Struct.is_packed == y->Struct.is_packed &&
x->Struct.soa_kind == y->Struct.soa_kind &&
@@ -2932,6 +3207,10 @@ gb_internal Type *default_type(Type *type) {
case Basic_UntypedString: return t_string;
case Basic_UntypedRune: return t_rune;
}
+ } else if (type->kind == Type_Generic) {
+ if (type->Generic.specialized) {
+ return default_type(type->Generic.specialized);
+ }
}
return type;
}
@@ -3030,11 +3309,15 @@ gb_internal i64 union_tag_size(Type *u) {
compiler_error("how many variants do you have?! %lld", cast(long long)u->Union.variants.count);
}
- for_array(i, u->Union.variants) {
- Type *variant_type = u->Union.variants[i];
- i64 align = type_align_of(variant_type);
- if (max_align < align) {
- max_align = align;
+ if (u->Union.custom_align > 0) {
+ max_align = gb_max(max_align, u->Union.custom_align);
+ } else {
+ for_array(i, u->Union.variants) {
+ Type *variant_type = u->Union.variants[i];
+ i64 align = type_align_of(variant_type);
+ if (max_align < align) {
+ max_align = align;
+ }
}
}
@@ -3327,6 +3610,15 @@ gb_internal Selection lookup_field_with_selection(Type *type_, String field_name
}
}
}
+
+ Type *objc_ivar_type = e->TypeName.objc_ivar;
+ if (objc_ivar_type != nullptr) {
+ sel = lookup_field_with_selection(objc_ivar_type, field_name, false, sel, allow_blank_ident);
+ if (sel.entity != nullptr) {
+ sel.pseudo_field = true;
+ return sel;
+ }
+ }
}
if (is_type_polymorphic(type)) {
@@ -3701,10 +3993,12 @@ gb_internal i64 type_size_of(Type *t) {
if (t->kind == Type_Basic) {
GB_ASSERT_MSG(is_type_typed(t), "%s", type_to_string(t));
switch (t->Basic.kind) {
- case Basic_string: size = 2*build_context.int_size; break;
- case Basic_cstring: size = build_context.ptr_size; break;
- case Basic_any: size = 16; break;
- case Basic_typeid: size = 8; break;
+ case Basic_string: size = 2*build_context.int_size; break;
+ case Basic_cstring: size = build_context.ptr_size; break;
+ case Basic_string16: size = 2*build_context.int_size; break;
+ case Basic_cstring16: size = build_context.ptr_size; break;
+ case Basic_any: size = 16; break;
+ case Basic_typeid: size = 8; break;
case Basic_int: case Basic_uint:
size = build_context.int_size;
@@ -3724,7 +4018,7 @@ gb_internal i64 type_size_of(Type *t) {
TypePath path{};
type_path_init(&path);
{
- MUTEX_GUARD(&g_type_mutex);
+ // MUTEX_GUARD(&g_type_mutex);
size = type_size_of_internal(t, &path);
t->cached_size.store(size);
}
@@ -3744,7 +4038,7 @@ gb_internal i64 type_align_of(Type *t) {
TypePath path{};
type_path_init(&path);
{
- MUTEX_GUARD(&g_type_mutex);
+ // MUTEX_GUARD(&g_type_mutex);
t->cached_align.store(type_align_of_internal(t, &path));
}
type_path_free(&path);
@@ -3764,10 +4058,12 @@ gb_internal i64 type_align_of_internal(Type *t, TypePath *path) {
case Type_Basic: {
GB_ASSERT(is_type_typed(t));
switch (t->Basic.kind) {
- case Basic_string: return build_context.int_size;
- case Basic_cstring: return build_context.ptr_size;
- case Basic_any: return 8;
- case Basic_typeid: return 8;
+ case Basic_string: return build_context.int_size;
+ case Basic_cstring: return build_context.ptr_size;
+ case Basic_string16: return build_context.int_size;
+ case Basic_cstring16: return build_context.ptr_size;
+ case Basic_any: return 8;
+ case Basic_typeid: return 8;
case Basic_int: case Basic_uint:
return build_context.int_size;
@@ -3970,18 +4266,18 @@ gb_internal bool type_set_offsets(Type *t) {
if (t->kind == Type_Struct) {
MUTEX_GUARD(&t->Struct.offset_mutex);
if (!t->Struct.are_offsets_set) {
- t->Struct.are_offsets_being_processed = true;
+ t->Struct.are_offsets_being_processed.store(true);
t->Struct.offsets = type_set_offsets_of(t->Struct.fields, t->Struct.is_packed, t->Struct.is_raw_union, t->Struct.custom_min_field_align, t->Struct.custom_max_field_align);
- t->Struct.are_offsets_being_processed = false;
+ t->Struct.are_offsets_being_processed.store(false);
t->Struct.are_offsets_set = true;
return true;
}
} else if (is_type_tuple(t)) {
MUTEX_GUARD(&t->Tuple.mutex);
if (!t->Tuple.are_offsets_set) {
- t->Tuple.are_offsets_being_processed = true;
+ t->Tuple.are_offsets_being_processed.store(true);
t->Tuple.offsets = type_set_offsets_of(t->Tuple.variables, t->Tuple.is_packed, false, 1, 0);
- t->Tuple.are_offsets_being_processed = false;
+ t->Tuple.are_offsets_being_processed.store(false);
t->Tuple.are_offsets_set = true;
return true;
}
@@ -4015,10 +4311,12 @@ gb_internal i64 type_size_of_internal(Type *t, TypePath *path) {
return size;
}
switch (kind) {
- case Basic_string: return 2*build_context.int_size;
- case Basic_cstring: return build_context.ptr_size;
- case Basic_any: return 16;
- case Basic_typeid: return 8;
+ case Basic_string: return 2*build_context.int_size;
+ case Basic_cstring: return build_context.ptr_size;
+ case Basic_string16: return 2*build_context.int_size;
+ case Basic_cstring16: return build_context.ptr_size;
+ case Basic_any: return 16;
+ case Basic_typeid: return 8;
case Basic_int: case Basic_uint:
return build_context.int_size;
@@ -4108,10 +4406,10 @@ gb_internal i64 type_size_of_internal(Type *t, TypePath *path) {
}
i64 max = 0;
- i64 field_size = 0;
for_array(i, t->Union.variants) {
Type *variant_type = t->Union.variants[i];
+
i64 size = type_size_of_internal(variant_type, path);
if (max < size) {
max = size;
@@ -4130,7 +4428,7 @@ gb_internal i64 type_size_of_internal(Type *t, TypePath *path) {
size = align_formula(max, tag_size);
// NOTE(bill): Calculate the padding between the common fields and the tag
t->Union.tag_size = cast(i16)tag_size;
- t->Union.variant_block_size = size - field_size;
+ t->Union.variant_block_size = size;
size += tag_size;
}
@@ -4164,9 +4462,12 @@ gb_internal i64 type_size_of_internal(Type *t, TypePath *path) {
if (path->failure) {
return FAILURE_SIZE;
}
- if (t->Struct.are_offsets_being_processed && t->Struct.offsets == nullptr) {
- type_path_print_illegal_cycle(path, path->path.count-1);
- return FAILURE_SIZE;
+ {
+ MUTEX_GUARD(&t->Struct.offset_mutex);
+ if (t->Struct.are_offsets_being_processed.load() && t->Struct.offsets == nullptr) {
+ type_path_print_illegal_cycle(path, path->path.count-1);
+ return FAILURE_SIZE;
+ }
}
type_set_offsets(t);
GB_ASSERT(t->Struct.fields.count == 0 || t->Struct.offsets != nullptr);
@@ -4247,6 +4548,15 @@ gb_internal i64 type_offset_of(Type *t, i64 index, Type **field_type_) {
if (field_type_) *field_type_ = t_int;
return build_context.int_size; // len
}
+ } else if (t->Basic.kind == Basic_string16) {
+ switch (index) {
+ case 0:
+ if (field_type_) *field_type_ = t_u16_ptr;
+ return 0; // data
+ case 1:
+ if (field_type_) *field_type_ = t_int;
+ return build_context.int_size; // len
+ }
} else if (t->Basic.kind == Basic_any) {
switch (index) {
case 0:
@@ -4323,6 +4633,11 @@ gb_internal i64 type_offset_of_from_selection(Type *type, Selection sel) {
case 0: t = t_rawptr; break;
case 1: t = t_int; break;
}
+ } else if (t->Basic.kind == Basic_string16) {
+ switch (index) {
+ case 0: t = t_rawptr; break;
+ case 1: t = t_int; break;
+ }
} else if (t->Basic.kind == Basic_any) {
switch (index) {
case 0: t = t_rawptr; break;
@@ -4423,6 +4738,14 @@ gb_internal bool is_type_objc_object(Type *t) {
return internal_check_is_assignable_to(t, t_objc_object);
}
+gb_internal bool is_type_objc_ptr_to_object(Type *t) {
+ // NOTE (harold): is_type_objc_object() returns true if it's a pointer to an object or the object itself.
+ // This returns true ONLY if Type is a shallow pointer to an Objective-C object.
+
+ Type *elem = type_deref(t);
+ return elem != t && elem->kind == Type_Named && is_type_objc_object(elem);
+}
+
gb_internal Type *get_struct_field_type(Type *t, isize index) {
t = base_type(type_deref(t));
GB_ASSERT(t->kind == Type_Struct);
@@ -4449,7 +4772,7 @@ gb_internal Type *alloc_type_tuple_from_field_types(Type **field_types, isize fi
}
Type *t = alloc_type_tuple();
- t->Tuple.variables = slice_make<Entity *>(heap_allocator(), field_count);
+ t->Tuple.variables = slice_make<Entity *>(permanent_allocator(), field_count);
Scope *scope = nullptr;
for_array(i, t->Tuple.variables) {
@@ -4542,7 +4865,89 @@ gb_internal Type *alloc_type_proc_from_types(Type **param_types, unsigned param_
// return type;
// }
-gb_internal gbString write_type_to_string(gbString str, Type *type, bool shorthand=false) {
+// Index a type that is internally a struct or array.
+gb_internal Type *type_internal_index(Type *t, isize index) {
+ Type *bt = base_type(t);
+ if (bt == nullptr) {
+ return nullptr;
+ }
+
+ switch (bt->kind) {
+ case Type_Basic:
+ {
+ switch (bt->Basic.kind) {
+ case Basic_complex32: return t_f16;
+ case Basic_complex64: return t_f32;
+ case Basic_complex128: return t_f64;
+ case Basic_quaternion64: return t_f16;
+ case Basic_quaternion128: return t_f32;
+ case Basic_quaternion256: return t_f64;
+ case Basic_string:
+ {
+ GB_ASSERT(index == 0 || index == 1);
+ return index == 0 ? t_u8_ptr : t_int;
+ }
+ case Basic_string16:
+ {
+ GB_ASSERT(index == 0 || index == 1);
+ return index == 0 ? t_u16_ptr : t_int;
+ }
+ case Basic_any:
+ {
+ GB_ASSERT(index == 0 || index == 1);
+ return index == 0 ? t_rawptr : t_typeid;
+ }
+ }
+ }
+ break;
+
+ case Type_Array: return bt->Array.elem;
+ case Type_EnumeratedArray: return bt->EnumeratedArray.elem;
+ case Type_SimdVector: return bt->SimdVector.elem;
+ case Type_Slice:
+ {
+ GB_ASSERT(index == 0 || index == 1);
+ return index == 0 ? t_rawptr : t_typeid;
+ }
+ case Type_DynamicArray:
+ {
+ switch (index) {
+ case 0: return t_rawptr;
+ case 1: return t_int;
+ case 2: return t_int;
+ case 3: return t_allocator;
+ default: GB_PANIC("invalid raw dynamic array index");
+ };
+ }
+ case Type_Struct:
+ return get_struct_field_type(bt, index);
+ case Type_Union:
+ if (index < bt->Union.variants.count) {
+ return bt->Union.variants[index];
+ }
+ return union_tag_type(bt);
+ case Type_Tuple:
+ return bt->Tuple.variables[index]->type;
+ case Type_Matrix:
+ return bt->Matrix.elem;
+ case Type_SoaPointer:
+ {
+ GB_ASSERT(index == 0 || index == 1);
+ return index == 0 ? t_rawptr : t_int;
+ }
+ case Type_Map:
+ return type_internal_index(bt->Map.debug_metadata_type, index);
+ case Type_BitField:
+ return type_internal_index(bt->BitField.backing_type, index);
+ case Type_Generic:
+ return type_internal_index(bt->Generic.specialized, index);
+ };
+
+ GB_PANIC("Unhandled type %s", type_to_string(bt));
+ return nullptr;
+};
+
+gb_internal gbString write_type_to_string(gbString str, Type *type, bool shorthand=false, bool allow_polymorphic=false) {
if (type == nullptr) {
return gb_string_appendc(str, "<no type>");
}
@@ -4567,24 +4972,24 @@ gb_internal gbString write_type_to_string(gbString str, Type *type, bool shortha
str = gb_string_append_length(str, name.text, name.len);
if (type->Generic.specialized != nullptr) {
str = gb_string_append_rune(str, '/');
- str = write_type_to_string(str, type->Generic.specialized);
+ str = write_type_to_string(str, type->Generic.specialized, shorthand, allow_polymorphic);
}
}
break;
case Type_Pointer:
str = gb_string_append_rune(str, '^');
- str = write_type_to_string(str, type->Pointer.elem);
+ str = write_type_to_string(str, type->Pointer.elem, shorthand, allow_polymorphic);
break;
case Type_SoaPointer:
str = gb_string_appendc(str, "#soa ^");
- str = write_type_to_string(str, type->SoaPointer.elem);
+ str = write_type_to_string(str, type->SoaPointer.elem, shorthand, allow_polymorphic);
break;
case Type_MultiPointer:
str = gb_string_appendc(str, "[^]");
- str = write_type_to_string(str, type->Pointer.elem);
+ str = write_type_to_string(str, type->Pointer.elem, shorthand, allow_polymorphic);
break;
case Type_EnumeratedArray:
@@ -4592,31 +4997,31 @@ gb_internal gbString write_type_to_string(gbString str, Type *type, bool shortha
str = gb_string_appendc(str, "#sparse");
}
str = gb_string_append_rune(str, '[');
- str = write_type_to_string(str, type->EnumeratedArray.index);
+ str = write_type_to_string(str, type->EnumeratedArray.index, shorthand, allow_polymorphic);
str = gb_string_append_rune(str, ']');
- str = write_type_to_string(str, type->EnumeratedArray.elem);
+ str = write_type_to_string(str, type->EnumeratedArray.elem, shorthand, allow_polymorphic);
break;
case Type_Array:
str = gb_string_appendc(str, gb_bprintf("[%lld]", cast(long long)type->Array.count));
- str = write_type_to_string(str, type->Array.elem);
+ str = write_type_to_string(str, type->Array.elem, shorthand, allow_polymorphic);
break;
case Type_Slice:
str = gb_string_appendc(str, "[]");
- str = write_type_to_string(str, type->Array.elem);
+ str = write_type_to_string(str, type->Array.elem, shorthand, allow_polymorphic);
break;
case Type_DynamicArray:
str = gb_string_appendc(str, "[dynamic]");
- str = write_type_to_string(str, type->DynamicArray.elem);
+ str = write_type_to_string(str, type->DynamicArray.elem, shorthand, allow_polymorphic);
break;
case Type_Enum:
str = gb_string_appendc(str, "enum");
if (type->Enum.base_type != nullptr) {
str = gb_string_appendc(str, " ");
- str = write_type_to_string(str, type->Enum.base_type);
+ str = write_type_to_string(str, type->Enum.base_type, shorthand, allow_polymorphic);
}
str = gb_string_appendc(str, " {");
for_array(i, type->Enum.fields) {
@@ -4633,6 +5038,13 @@ gb_internal gbString write_type_to_string(gbString str, Type *type, bool shortha
case Type_Union:
str = gb_string_appendc(str, "union");
+
+ if (allow_polymorphic && type->Struct.polymorphic_params) {
+ str = gb_string_appendc(str, "(");
+ str = write_type_to_string(str, type->Struct.polymorphic_params, shorthand, allow_polymorphic);
+ str = gb_string_appendc(str, ")");
+ }
+
switch (type->Union.kind) {
case UnionType_no_nil: str = gb_string_appendc(str, " #no_nil"); break;
case UnionType_shared_nil: str = gb_string_appendc(str, " #shared_nil"); break;
@@ -4642,7 +5054,7 @@ gb_internal gbString write_type_to_string(gbString str, Type *type, bool shortha
for_array(i, type->Union.variants) {
Type *t = type->Union.variants[i];
if (i > 0) str = gb_string_appendc(str, ", ");
- str = write_type_to_string(str, t);
+ str = write_type_to_string(str, t, shorthand, allow_polymorphic);
}
str = gb_string_append_rune(str, '}');
break;
@@ -4655,17 +5067,23 @@ gb_internal gbString write_type_to_string(gbString str, Type *type, bool shortha
case StructSoa_Dynamic: str = gb_string_appendc(str, "#soa[dynamic]"); break;
default: GB_PANIC("Unknown StructSoaKind"); break;
}
- str = write_type_to_string(str, type->Struct.soa_elem);
+ str = write_type_to_string(str, type->Struct.soa_elem, shorthand, allow_polymorphic);
break;
}
str = gb_string_appendc(str, "struct");
+
+ if (allow_polymorphic && type->Struct.polymorphic_params) {
+ str = gb_string_appendc(str, "(");
+ str = write_type_to_string(str, type->Struct.polymorphic_params, shorthand, allow_polymorphic);
+ str = gb_string_appendc(str, ")");
+ }
+
if (type->Struct.is_packed) str = gb_string_appendc(str, " #packed");
if (type->Struct.is_raw_union) str = gb_string_appendc(str, " #raw_union");
- if (type->Struct.is_no_copy) str = gb_string_appendc(str, " #no_copy");
if (type->Struct.custom_align != 0) str = gb_string_append_fmt(str, " #align %d", cast(int)type->Struct.custom_align);
- str = gb_string_appendc(str, " {");
+ str = gb_string_appendc(str, " {");
if (shorthand && type->Struct.fields.count > 16) {
str = gb_string_append_fmt(str, "%lld fields...", cast(long long)type->Struct.fields.count);
@@ -4678,7 +5096,7 @@ gb_internal gbString write_type_to_string(gbString str, Type *type, bool shortha
}
str = gb_string_append_length(str, f->token.string.text, f->token.string.len);
str = gb_string_appendc(str, ": ");
- str = write_type_to_string(str, f->type);
+ str = write_type_to_string(str, f->type, shorthand, allow_polymorphic);
}
}
str = gb_string_append_rune(str, '}');
@@ -4686,9 +5104,9 @@ gb_internal gbString write_type_to_string(gbString str, Type *type, bool shortha
case Type_Map: {
str = gb_string_appendc(str, "map[");
- str = write_type_to_string(str, type->Map.key);
+ str = write_type_to_string(str, type->Map.key, shorthand, allow_polymorphic);
str = gb_string_append_rune(str, ']');
- str = write_type_to_string(str, type->Map.value);
+ str = write_type_to_string(str, type->Map.value, shorthand, allow_polymorphic);
} break;
case Type_Named:
@@ -4718,9 +5136,11 @@ gb_internal gbString write_type_to_string(gbString str, Type *type, bool shortha
str = gb_string_append_length(str, name.text, name.len);
if (!is_type_untyped(var->type)) {
str = gb_string_appendc(str, ": ");
- str = write_type_to_string(str, var->type);
- str = gb_string_appendc(str, " = ");
- str = write_exact_value_to_string(str, var->Constant.value);
+ str = write_type_to_string(str, var->type, shorthand, allow_polymorphic);
+ if (var->Constant.value.kind) {
+ str = gb_string_appendc(str, " = ");
+ str = write_exact_value_to_string(str, var->Constant.value);
+ }
} else {
str = gb_string_appendc(str, " := ");
str = write_exact_value_to_string(str, var->Constant.value);
@@ -4736,20 +5156,31 @@ gb_internal gbString write_type_to_string(gbString str, Type *type, bool shortha
Type *slice = base_type(var->type);
str = gb_string_appendc(str, "..");
GB_ASSERT(var->type->kind == Type_Slice);
- str = write_type_to_string(str, slice->Slice.elem);
+ str = write_type_to_string(str, slice->Slice.elem, shorthand, allow_polymorphic);
} else {
- str = write_type_to_string(str, var->type);
+ str = write_type_to_string(str, var->type, shorthand, allow_polymorphic);
}
} else {
GB_ASSERT(var->kind == Entity_TypeName);
if (var->type->kind == Type_Generic) {
- str = gb_string_appendc(str, "typeid/");
- str = write_type_to_string(str, var->type);
+ if (var->token.string.len != 0) {
+ String name = var->token.string;
+ str = gb_string_appendc(str, "$");
+ str = gb_string_append_length(str, name.text, name.len);
+ str = gb_string_appendc(str, ": typeid");
+ if (var->type->Generic.specialized) {
+ str = gb_string_appendc(str, "/");
+ str = write_type_to_string(str, var->type->Generic.specialized, shorthand, allow_polymorphic);
+ }
+ } else {
+ str = gb_string_appendc(str, "typeid/");
+ str = write_type_to_string(str, var->type, shorthand, allow_polymorphic);
+ }
} else {
str = gb_string_appendc(str, "$");
str = gb_string_append_length(str, name.text, name.len);
str = gb_string_appendc(str, "=");
- str = write_type_to_string(str, var->type);
+ str = write_type_to_string(str, var->type, shorthand, allow_polymorphic);
}
}
}
@@ -4795,7 +5226,7 @@ gb_internal gbString write_type_to_string(gbString str, Type *type, bool shortha
}
str = gb_string_appendc(str, "(");
if (type->Proc.params) {
- str = write_type_to_string(str, type->Proc.params);
+ str = write_type_to_string(str, type->Proc.params, shorthand, allow_polymorphic);
}
str = gb_string_appendc(str, ")");
if (type->Proc.results) {
@@ -4803,7 +5234,7 @@ gb_internal gbString write_type_to_string(gbString str, Type *type, bool shortha
if (type->Proc.results->Tuple.variables.count > 1) {
str = gb_string_appendc(str, "(");
}
- str = write_type_to_string(str, type->Proc.results);
+ str = write_type_to_string(str, type->Proc.results, shorthand, allow_polymorphic);
if (type->Proc.results->Tuple.variables.count > 1) {
str = gb_string_appendc(str, ")");
}
@@ -4815,7 +5246,7 @@ gb_internal gbString write_type_to_string(gbString str, Type *type, bool shortha
if (type->BitSet.elem == nullptr) {
str = gb_string_appendc(str, "<unresolved>");
} else if (is_type_enum(type->BitSet.elem)) {
- str = write_type_to_string(str, type->BitSet.elem);
+ str = write_type_to_string(str, type->BitSet.elem, shorthand, allow_polymorphic);
} else {
str = gb_string_append_fmt(str, "%lld", type->BitSet.lower);
str = gb_string_append_fmt(str, "..=");
@@ -4823,14 +5254,14 @@ gb_internal gbString write_type_to_string(gbString str, Type *type, bool shortha
}
if (type->BitSet.underlying != nullptr) {
str = gb_string_appendc(str, "; ");
- str = write_type_to_string(str, type->BitSet.underlying);
+ str = write_type_to_string(str, type->BitSet.underlying, shorthand, allow_polymorphic);
}
str = gb_string_appendc(str, "]");
break;
case Type_SimdVector:
str = gb_string_append_fmt(str, "#simd[%d]", cast(int)type->SimdVector.count);
- str = write_type_to_string(str, type->SimdVector.elem);
+ str = write_type_to_string(str, type->SimdVector.elem, shorthand, allow_polymorphic);
break;
case Type_Matrix:
@@ -4838,12 +5269,12 @@ gb_internal gbString write_type_to_string(gbString str, Type *type, bool shortha
str = gb_string_appendc(str, "#row_major ");
}
str = gb_string_appendc(str, gb_bprintf("matrix[%d, %d]", cast(int)type->Matrix.row_count, cast(int)type->Matrix.column_count));
- str = write_type_to_string(str, type->Matrix.elem);
+ str = write_type_to_string(str, type->Matrix.elem, shorthand, allow_polymorphic);
break;
case Type_BitField:
str = gb_string_appendc(str, "bit_field ");
- str = write_type_to_string(str, type->BitField.backing_type);
+ str = write_type_to_string(str, type->BitField.backing_type, shorthand, allow_polymorphic);
str = gb_string_appendc(str, " {");
for (isize i = 0; i < type->BitField.fields.count; i++) {
Entity *f = type->BitField.fields[i];
@@ -4852,7 +5283,7 @@ gb_internal gbString write_type_to_string(gbString str, Type *type, bool shortha
}
str = gb_string_append_length(str, f->token.string.text, f->token.string.len);
str = gb_string_appendc(str, ": ");
- str = write_type_to_string(str, f->type);
+ str = write_type_to_string(str, f->type, shorthand, allow_polymorphic);
str = gb_string_append_fmt(str, " | %u", type->BitField.bit_sizes[i]);
}
str = gb_string_appendc(str, " }");
@@ -4870,6 +5301,11 @@ gb_internal gbString type_to_string(Type *type, bool shorthand) {
return write_type_to_string(gb_string_make(heap_allocator(), ""), type, shorthand);
}
+gb_internal gbString type_to_string_polymorphic(Type *type) {
+ return write_type_to_string(gb_string_make(heap_allocator(), ""), type, false, true);
+}
+
+
gb_internal gbString type_to_string_shorthand(Type *type) {
return type_to_string(type, true);
}