aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorgingerBill <gingerBill@users.noreply.github.com>2025-01-06 13:43:01 +0000
committerGitHub <noreply@github.com>2025-01-06 13:43:01 +0000
commit6e49bbb66853b5d824ac5bbd534ae3e81c4f39aa (patch)
tree50886a3be8f2fcfab053e07cfe9e15f50fa5f9f6 /src
parentbd96cd0af761994210018ca647eb843dfeb71494 (diff)
parent98efb03934b464a1b23759b5695a12ff37588357 (diff)
Merge branch 'master' into d3d11-annotations
Diffstat (limited to 'src')
-rw-r--r--src/array.cpp7
-rw-r--r--src/big_int.cpp21
-rw-r--r--src/bug_report.cpp1699
-rw-r--r--src/build_cpuid.cpp35
-rw-r--r--src/build_settings.cpp888
-rw-r--r--src/build_settings_microarch.cpp516
-rw-r--r--src/cached.cpp474
-rw-r--r--src/check_builtin.cpp1578
-rw-r--r--src/check_decl.cpp621
-rw-r--r--src/check_expr.cpp1809
-rw-r--r--src/check_stmt.cpp553
-rw-r--r--src/check_type.cpp1487
-rw-r--r--src/checker.cpp1263
-rw-r--r--src/checker.hpp120
-rw-r--r--src/checker_builtin_procs.hpp111
-rw-r--r--src/common.cpp25
-rw-r--r--src/common_memory.cpp101
-rw-r--r--src/docs.cpp4
-rw-r--r--src/docs_format.cpp28
-rw-r--r--src/docs_writer.cpp116
-rw-r--r--src/entity.cpp50
-rw-r--r--src/error.cpp841
-rw-r--r--src/exact_value.cpp73
-rw-r--r--src/gb/gb.h152
-rw-r--r--src/linker.cpp479
-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/AsmParsers.def4
-rw-r--r--src/llvm-c/Config/AsmPrinters.def4
-rw-r--r--src/llvm-c/Config/Disassemblers.def4
-rw-r--r--src/llvm-c/Config/TargetExegesis.def33
-rw-r--r--src/llvm-c/Config/TargetMCAs.def32
-rw-r--r--src/llvm-c/Config/Targets.def4
-rw-r--r--src/llvm-c/Config/abi-breaking.h4
-rw-r--r--src/llvm-c/Config/llvm-config.h132
-rw-r--r--src/llvm-c/Core.h273
-rw-r--r--src/llvm-c/DebugInfo.h4
-rw-r--r--src/llvm-c/Disassembler.h4
-rw-r--r--src/llvm-c/DisassemblerTypes.h2
-rw-r--r--src/llvm-c/Error.h2
-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.h10
-rw-r--r--src/llvm-c/LLJITUtils.h52
-rw-r--r--src/llvm-c/Linker.h4
-rw-r--r--src/llvm-c/Object.h6
-rw-r--r--src/llvm-c/Orc.h16
-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.h30
-rw-r--r--src/llvm-c/TargetMachine.h77
-rw-r--r--src/llvm-c/Transforms/PassBuilder.h6
-rw-r--r--src/llvm-c/Types.h9
-rw-r--r--src/llvm-c/lto.h2
-rw-r--r--src/llvm_abi.cpp456
-rw-r--r--src/llvm_backend.cpp1099
-rw-r--r--src/llvm_backend.hpp99
-rw-r--r--src/llvm_backend_const.cpp135
-rw-r--r--src/llvm_backend_debug.cpp1110
-rw-r--r--src/llvm_backend_expr.cpp1089
-rw-r--r--src/llvm_backend_general.cpp491
-rw-r--r--src/llvm_backend_opt.cpp98
-rw-r--r--src/llvm_backend_proc.cpp777
-rw-r--r--src/llvm_backend_stmt.cpp376
-rw-r--r--src/llvm_backend_type.cpp1090
-rw-r--r--src/llvm_backend_utility.cpp285
-rw-r--r--src/main.cpp1803
-rw-r--r--src/microsoft_craziness.h10
-rw-r--r--src/parser.cpp990
-rw-r--r--src/parser.hpp55
-rw-r--r--src/parser_pos.cpp5
-rw-r--r--src/path.cpp926
-rw-r--r--src/ptr_map.cpp501
-rw-r--r--src/queue.cpp2
-rw-r--r--src/string.cpp216
-rw-r--r--src/string_map.cpp328
-rw-r--r--src/string_set.cpp4
-rw-r--r--src/thread_pool.cpp134
-rw-r--r--src/threading.cpp384
-rw-r--r--src/tilde.cpp10
-rw-r--r--src/tilde.hpp4
-rw-r--r--src/tilde/tb.h372
-rw-r--r--src/tilde/tb.libbin4265424 -> 2347218 bytes
-rw-r--r--src/tilde/tb_coff.h6
-rw-r--r--src/tilde/tb_x64.h31
-rw-r--r--src/tilde_const.cpp2
-rw-r--r--src/tilde_expr.cpp2
-rw-r--r--src/tilde_proc.cpp12
-rw-r--r--src/tilde_stmt.cpp17
-rw-r--r--src/tilde_type_info.cpp13
-rw-r--r--src/timings.cpp30
-rw-r--r--src/tokenizer.cpp57
-rw-r--r--src/types.cpp646
-rw-r--r--src/ucg/ucg.c686
-rw-r--r--src/ucg/ucg_tables.h2629
-rw-r--r--src/unicode.cpp18
100 files changed, 23085 insertions, 7728 deletions
diff --git a/src/array.cpp b/src/array.cpp
index 4583a31a9..ec2c97d0e 100644
--- a/src/array.cpp
+++ b/src/array.cpp
@@ -52,6 +52,13 @@ template <typename T> gb_internal T *array_end_ptr(Array<T> *array);
template <typename T>
+gb_internal void array_sort(Array<T> &array, gbCompareProc compare_proc) {
+ gb_sort_array(array.data, array.count, compare_proc);
+}
+
+
+
+template <typename T>
struct Slice {
T *data;
isize count;
diff --git a/src/big_int.cpp b/src/big_int.cpp
index e350687b4..8e476f090 100644
--- a/src/big_int.cpp
+++ b/src/big_int.cpp
@@ -62,6 +62,7 @@ gb_internal void big_int_shl (BigInt *dst, BigInt const *x, BigInt const *y);
gb_internal void big_int_shr (BigInt *dst, BigInt const *x, BigInt const *y);
gb_internal void big_int_mul (BigInt *dst, BigInt const *x, BigInt const *y);
gb_internal void big_int_mul_u64(BigInt *dst, BigInt const *x, u64 y);
+gb_internal void big_int_exp_u64(BigInt *dst, BigInt const *x, u64 y, bool *success);
gb_internal void big_int_quo_rem(BigInt const *x, BigInt const *y, BigInt *q, BigInt *r);
gb_internal void big_int_quo (BigInt *z, BigInt const *x, BigInt const *y);
@@ -250,9 +251,7 @@ gb_internal void big_int_from_string(BigInt *dst, String const &s, bool *success
exp *= 10;
exp += v;
}
- for (u64 x = 0; x < exp; x++) {
- big_int_mul_eq(dst, &b);
- }
+ big_int_exp_u64(dst, &b, exp, success);
}
if (is_negative) {
@@ -328,6 +327,18 @@ gb_internal void big_int_mul_u64(BigInt *dst, BigInt const *x, u64 y) {
big_int_dealloc(&d);
}
+gb_internal void big_int_exp_u64(BigInt *dst, BigInt const *x, u64 y, bool *success) {
+ if (y > INT_MAX) {
+ *success = false;
+ return;
+ }
+
+ // Note: The cutoff for square-multiply being faster than the naive
+ // for loop is when exp > 4, but it probably isn't worth adding
+ // a fast path.
+ mp_err err = mp_expt_n(x, int(y), dst);
+ *success = err == MP_OKAY;
+}
gb_internal void big_int_mul(BigInt *dst, BigInt const *x, BigInt const *y) {
mp_mul(x, y, dst);
@@ -621,3 +632,7 @@ gb_internal String big_int_to_string(gbAllocator allocator, BigInt const *x, u64
}
return make_string(cast(u8 *)buf.data, buf.count);
}
+
+gb_internal int big_int_log2(BigInt const *x) {
+ return mp_count_bits(x) - 1;
+}
diff --git a/src/bug_report.cpp b/src/bug_report.cpp
index fbf616efb..ca5d0a395 100644
--- a/src/bug_report.cpp
+++ b/src/bug_report.cpp
@@ -1,1007 +1,692 @@
-/*
- Gather and print platform and version info to help with reporting Odin bugs.
-*/
-
-#if !defined(GB_COMPILER_MSVC)
- #if defined(GB_CPU_X86)
- #include <cpuid.h>
- #endif
-#endif
-
-#if defined(GB_SYSTEM_LINUX)
- #include <sys/utsname.h>
- #include <sys/sysinfo.h>
-#endif
-
-#if defined(GB_SYSTEM_OSX)
- #include <sys/sysctl.h>
-#endif
-
-#if defined(GB_SYSTEM_OPENBSD)
- #include <sys/sysctl.h>
- #include <sys/utsname.h>
-#endif
-
-#if defined(GB_SYSTEM_FREEBSD)
- #include <sys/sysctl.h>
-#endif
-
-/*
- NOTE(Jeroen): This prints the Windows product edition only, to be called from `print_platform_details`.
-*/
-#if defined(GB_SYSTEM_WINDOWS)
-gb_internal void report_windows_product_type(DWORD ProductType) {
- switch (ProductType) {
- case PRODUCT_ULTIMATE:
- gb_printf("Ultimate");
- break;
-
- case PRODUCT_HOME_BASIC:
- gb_printf("Home Basic");
- break;
-
- case PRODUCT_HOME_PREMIUM:
- gb_printf("Home Premium");
- break;
-
- case PRODUCT_ENTERPRISE:
- gb_printf("Enterprise");
- break;
-
- case PRODUCT_CORE:
- gb_printf("Home Basic");
- break;
-
- case PRODUCT_HOME_BASIC_N:
- gb_printf("Home Basic N");
- break;
-
- case PRODUCT_EDUCATION:
- gb_printf("Education");
- break;
-
- case PRODUCT_EDUCATION_N:
- gb_printf("Education N");
- break;
-
- case PRODUCT_BUSINESS:
- gb_printf("Business");
- break;
-
- case PRODUCT_STANDARD_SERVER:
- gb_printf("Standard Server");
- break;
-
- case PRODUCT_DATACENTER_SERVER:
- gb_printf("Datacenter");
- break;
-
- case PRODUCT_SMALLBUSINESS_SERVER:
- gb_printf("Windows Small Business Server");
- break;
-
- case PRODUCT_ENTERPRISE_SERVER:
- gb_printf("Enterprise Server");
- break;
-
- case PRODUCT_STARTER:
- gb_printf("Starter");
- break;
-
- case PRODUCT_DATACENTER_SERVER_CORE:
- gb_printf("Datacenter Server Core");
- break;
-
- case PRODUCT_STANDARD_SERVER_CORE:
- gb_printf("Server Standard Core");
- break;
-
- case PRODUCT_ENTERPRISE_SERVER_CORE:
- gb_printf("Enterprise Server Core");
- break;
-
- case PRODUCT_BUSINESS_N:
- gb_printf("Business N");
- break;
-
- case PRODUCT_HOME_SERVER:
- gb_printf("Home Server");
- break;
-
- case PRODUCT_SERVER_FOR_SMALLBUSINESS:
- gb_printf("Windows Server 2008 for Windows Essential Server Solutions");
- break;
-
- case PRODUCT_SMALLBUSINESS_SERVER_PREMIUM:
- gb_printf("Small Business Server Premium");
- break;
-
- case PRODUCT_HOME_PREMIUM_N:
- gb_printf("Home Premium N");
- break;
-
- case PRODUCT_ENTERPRISE_N:
- gb_printf("Enterprise N");
- break;
-
- case PRODUCT_ULTIMATE_N:
- gb_printf("Ultimate N");
- break;
-
- case PRODUCT_HYPERV:
- gb_printf("HyperV");
- break;
-
- case PRODUCT_STARTER_N:
- gb_printf("Starter N");
- break;
-
- case PRODUCT_PROFESSIONAL:
- gb_printf("Professional");
- break;
-
- case PRODUCT_PROFESSIONAL_N:
- gb_printf("Professional N");
- break;
-
- case PRODUCT_UNLICENSED:
- gb_printf("Unlicensed");
- break;
-
- default:
- gb_printf("Unknown Edition (%08x)", cast(unsigned)ProductType);
- }
-}
-#endif
-
-gb_internal void odin_cpuid(int leaf, int result[]) {
- #if defined(GB_CPU_ARM)
- return;
-
- #elif defined(GB_CPU_X86)
-
- #if defined(GB_COMPILER_MSVC)
- __cpuid(result, leaf);
- #else
- __get_cpuid(leaf, (unsigned int*)&result[0], (unsigned int*)&result[1], (unsigned int*)&result[2], (unsigned int*)&result[3]);
- #endif
-
- #endif
-}
-
-gb_internal void report_cpu_info() {
- gb_printf("\tCPU: ");
-
- #if defined(GB_CPU_X86)
-
- /*
- Get extended leaf info
- */
- int cpu[4];
-
- odin_cpuid(0x80000000, &cpu[0]);
- int number_of_extended_ids = cpu[0];
-
- int brand[0x12] = {};
-
- /*
- Read CPU brand if supported.
- */
- if (number_of_extended_ids >= 0x80000004) {
- odin_cpuid(0x80000002, &brand[0]);
- odin_cpuid(0x80000003, &brand[4]);
- odin_cpuid(0x80000004, &brand[8]);
-
- /*
- Some CPUs like ` Intel(R) Xeon(R) CPU E5-1650 v2 @ 3.50GHz` may include leading spaces. Trim them.
- */
- char * brand_name = (char *)&brand[0];
- for (; brand_name[0] == ' '; brand_name++) {}
-
- gb_printf("%s\n", brand_name);
- } else {
- gb_printf("Unable to retrieve.\n");
- }
-
- #elif defined(GB_CPU_ARM)
- /*
- TODO(Jeroen): On *nix, perhaps query `/proc/cpuinfo`.
- */
- #if defined(GB_ARCH_64_BIT)
- gb_printf("ARM64\n");
- #else
- gb_printf("ARM\n");
- #endif
- #else
- gb_printf("Unknown\n");
- #endif
-}
-
-/*
- Report the amount of installed RAM.
-*/
-gb_internal void report_ram_info() {
- gb_printf("\tRAM: ");
-
- #if defined(GB_SYSTEM_WINDOWS)
- MEMORYSTATUSEX statex;
- statex.dwLength = sizeof(statex);
- GlobalMemoryStatusEx (&statex);
-
- gb_printf("%lld MiB\n", statex.ullTotalPhys / gb_megabytes(1));
-
- #elif defined(GB_SYSTEM_LINUX)
- /*
- Retrieve RAM info using `sysinfo()`,
- */
- struct sysinfo info;
- int result = sysinfo(&info);
-
- if (result == 0x0) {
- gb_printf("%lu MiB\n", info.totalram * info.mem_unit / gb_megabytes(1));
- } else {
- gb_printf("Unknown.\n");
- }
- #elif defined(GB_SYSTEM_OSX)
- uint64_t ram_amount;
- size_t val_size = sizeof(ram_amount);
-
- int mibs[] = { CTL_HW, HW_MEMSIZE };
- if (sysctl(mibs, 2, &ram_amount, &val_size, NULL, 0) != -1) {
- gb_printf("%lld MiB\n", ram_amount / gb_megabytes(1));
- }
- #elif defined(GB_SYSTEM_OPENBSD)
- uint64_t ram_amount;
- size_t val_size = sizeof(ram_amount);
-
- int mibs[] = { CTL_HW, HW_PHYSMEM64 };
- if (sysctl(mibs, 2, &ram_amount, &val_size, NULL, 0) != -1) {
- gb_printf("%lld MiB\n", ram_amount / gb_megabytes(1));
- }
- #elif defined(GB_SYSTEM_FREEBSD)
- uint64_t ram_amount;
- size_t val_size = sizeof(ram_amount);
-
- int mibs[] = { CTL_HW, HW_PHYSMEM };
- if (sysctl(mibs, 2, &ram_amount, &val_size, NULL, 0) != -1) {
- gb_printf("%lu MiB\n", ram_amount / gb_megabytes(1));
- }
- #else
- gb_printf("Unknown.\n");
- #endif
-}
-
-gb_internal void report_os_info() {
- gb_printf("\tOS: ");
-
- #if defined(GB_SYSTEM_WINDOWS)
- /*
- NOTE(Jeroen):
- `GetVersionEx` will return 6.2 for Windows 10 unless the program is manifested for Windows 10.
- `RtlGetVersion` will return the true version.
-
- Rather than include the WinDDK, we ask the kernel directly.
-
- `HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion` is for the minor build version (Update Build Release)
-
- */
- OSVERSIONINFOEXW osvi;
- ZeroMemory(&osvi, sizeof(OSVERSIONINFOEXW));
- osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW);
-
- typedef NTSTATUS (WINAPI* RtlGetVersionPtr)(OSVERSIONINFOW*);
- typedef BOOL (WINAPI* GetProductInfoPtr)(DWORD dwOSMajorVersion, DWORD dwOSMinorVersion, DWORD dwSpMajorVersion, DWORD dwSpMinorVersion, PDWORD pdwReturnedProductType);
-
- // https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/nf-wdm-rtlgetversion
- RtlGetVersionPtr RtlGetVersion = (RtlGetVersionPtr)GetProcAddress(GetModuleHandle(TEXT("ntdll.dll")), "RtlGetVersion");
- // https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getproductinfo
- GetProductInfoPtr GetProductInfo = (GetProductInfoPtr)GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "GetProductInfo");
-
- NTSTATUS status = {};
- DWORD ProductType = {};
- if (RtlGetVersion != nullptr) {
- status = RtlGetVersion((OSVERSIONINFOW*)&osvi);
- }
-
- if (RtlGetVersion == nullptr || status != 0x0) {
- gb_printf("Windows (Unknown Version)");
- } else {
- if (GetProductInfo != nullptr) {
- GetProductInfo(osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.wServicePackMajor, osvi.wServicePackMinor, &ProductType);
- }
-
- if (false) {
- gb_printf("dwMajorVersion: %u\n", cast(unsigned)osvi.dwMajorVersion);
- gb_printf("dwMinorVersion: %u\n", cast(unsigned)osvi.dwMinorVersion);
- gb_printf("dwBuildNumber: %u\n", cast(unsigned)osvi.dwBuildNumber);
- gb_printf("dwPlatformId: %u\n", cast(unsigned)osvi.dwPlatformId);
- gb_printf("wServicePackMajor: %u\n", cast(unsigned)osvi.wServicePackMajor);
- gb_printf("wServicePackMinor: %u\n", cast(unsigned)osvi.wServicePackMinor);
- gb_printf("wSuiteMask: %u\n", cast(unsigned)osvi.wSuiteMask);
- gb_printf("wProductType: %u\n", cast(unsigned)osvi.wProductType);
- }
-
- gb_printf("Windows ");
-
- switch (osvi.dwMajorVersion) {
- case 10:
- /*
- Windows 10 (Pro), Windows 2016 Server, Windows 2019 Server, Windows 2022 Server
- */
- switch (osvi.wProductType) {
- case VER_NT_WORKSTATION: // Workstation
- if (osvi.dwBuildNumber < 22000) {
- gb_printf("10 ");
- } else {
- gb_printf("11 ");
- }
-
- report_windows_product_type(ProductType);
-
- break;
- default: // Server or Domain Controller
- switch(osvi.dwBuildNumber) {
- case 14393:
- gb_printf("2016 Server");
- break;
- case 17763:
- gb_printf("2019 Server");
- break;
- case 20348:
- gb_printf("2022 Server");
- break;
- default:
- gb_printf("Unknown Server");
- break;
- }
- }
- break;
- case 6:
- switch (osvi.dwMinorVersion) {
- case 0:
- switch (osvi.wProductType) {
- case VER_NT_WORKSTATION:
- gb_printf("Windows Vista ");
- report_windows_product_type(ProductType);
- break;
- case 3:
- gb_printf("Windows Server 2008");
- break;
- }
- break;
-
- case 1:
- switch (osvi.wProductType) {
- case VER_NT_WORKSTATION:
- gb_printf("Windows 7 ");
- report_windows_product_type(ProductType);
- break;
- case 3:
- gb_printf("Windows Server 2008 R2");
- break;
- }
- break;
- case 2:
- switch (osvi.wProductType) {
- case VER_NT_WORKSTATION:
- gb_printf("Windows 8 ");
- report_windows_product_type(ProductType);
- break;
- case 3:
- gb_printf("Windows Server 2012");
- break;
- }
- break;
- case 3:
- switch (osvi.wProductType) {
- case VER_NT_WORKSTATION:
- gb_printf("Windows 8.1 ");
- report_windows_product_type(ProductType);
- break;
- case 3:
- gb_printf("Windows Server 2012 R2");
- break;
- }
- break;
- }
- break;
- case 5:
- switch (osvi.dwMinorVersion) {
- case 0:
- gb_printf("Windows 2000");
- break;
- case 1:
- gb_printf("Windows XP");
- break;
- case 2:
- gb_printf("Windows Server 2003");
- break;
- }
- break;
- default:
- break;
- }
-
- /*
- Grab Windows DisplayVersion (like 20H02)
- */
- LPDWORD ValueType = {};
- DWORD UBR;
- char DisplayVersion[256];
- DWORD ValueSize = 256;
-
- status = RegGetValue(
- HKEY_LOCAL_MACHINE,
- TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"),
- TEXT("DisplayVersion"),
- RRF_RT_REG_SZ,
- ValueType,
- DisplayVersion,
- &ValueSize
- );
-
- if (status == 0x0) {
- gb_printf(" (version: %s)", DisplayVersion);
- }
-
- /*
- Now print build number.
- */
- gb_printf(", build %u", cast(unsigned)osvi.dwBuildNumber);
-
- ValueSize = sizeof(UBR);
- status = RegGetValue(
- HKEY_LOCAL_MACHINE,
- TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"),
- TEXT("UBR"),
- RRF_RT_REG_DWORD,
- ValueType,
- &UBR,
- &ValueSize
- );
-
- if (status == 0x0) {
- gb_printf(".%u", cast(unsigned)UBR);
- }
- gb_printf("\n");
- }
- #elif defined(GB_SYSTEM_LINUX)
- /*
- Try to parse `/etc/os-release` for `PRETTY_NAME="Ubuntu 20.04.3 LTS`
- */
- gbAllocator a = heap_allocator();
-
- gbFileContents release = gb_file_read_contents(a, 1, "/etc/os-release");
- defer (gb_file_free_contents(&release));
-
- b32 found = 0;
- if (release.size) {
- char *start = (char *)release.data;
- char *end = (char *)release.data + release.size;
- const char *needle = "PRETTY_NAME=\"";
- isize needle_len = gb_strlen((needle));
-
- char *c = start;
- for (; c < end; c++) {
- if (gb_strncmp(c, needle, needle_len) == 0) {
- found = 1;
- start = c + needle_len;
- break;
- }
- }
-
- if (found) {
- for (c = start; c < end; c++) {
- if (*c == '"') {
- // Found the closing quote. Replace it with \0
- *c = 0;
- gb_printf("%s", (char *)start);
- break;
- } else if (*c == '\n') {
- found = 0;
- }
- }
- }
- }
-
- if (!found) {
- gb_printf("Unknown Linux Distro");
- }
-
- /*
- Print kernel info using `uname()` syscall, https://linux.die.net/man/2/uname
- */
- char buffer[1024];
- uname((struct utsname *)&buffer[0]);
-
- struct utsname *info;
- info = (struct utsname *)&buffer[0];
-
- gb_printf(", %s %s\n", info->sysname, info->release);
-
- #elif defined(GB_SYSTEM_OSX)
- struct Darwin_To_Release {
- const char* build; // 21G83
- int darwin[3]; // Darwin kernel triplet
- const char* os_name; // OS X, MacOS
- struct {
- const char* name; // Monterey, Mojave, etc.
- int version[3]; // 12.4, etc.
- } release;
- };
-
- Darwin_To_Release macos_release_map[] = {
- {"8A428", { 8, 0, 0}, "macOS", {"Tiger", {10, 4, 0}}},
- {"8A432", { 8, 0, 0}, "macOS", {"Tiger", {10, 4, 0}}},
- {"8B15", { 8, 1, 0}, "macOS", {"Tiger", {10, 4, 1}}},
- {"8B17", { 8, 1, 0}, "macOS", {"Tiger", {10, 4, 1}}},
- {"8C46", { 8, 2, 0}, "macOS", {"Tiger", {10, 4, 2}}},
- {"8C47", { 8, 2, 0}, "macOS", {"Tiger", {10, 4, 2}}},
- {"8E102", { 8, 2, 0}, "macOS", {"Tiger", {10, 4, 2}}},
- {"8E45", { 8, 2, 0}, "macOS", {"Tiger", {10, 4, 2}}},
- {"8E90", { 8, 2, 0}, "macOS", {"Tiger", {10, 4, 2}}},
- {"8F46", { 8, 3, 0}, "macOS", {"Tiger", {10, 4, 3}}},
- {"8G32", { 8, 4, 0}, "macOS", {"Tiger", {10, 4, 4}}},
- {"8G1165", { 8, 4, 0}, "macOS", {"Tiger", {10, 4, 4}}},
- {"8H14", { 8, 5, 0}, "macOS", {"Tiger", {10, 4, 5}}},
- {"8G1454", { 8, 5, 0}, "macOS", {"Tiger", {10, 4, 5}}},
- {"8I127", { 8, 6, 0}, "macOS", {"Tiger", {10, 4, 6}}},
- {"8I1119", { 8, 6, 0}, "macOS", {"Tiger", {10, 4, 6}}},
- {"8J135", { 8, 7, 0}, "macOS", {"Tiger", {10, 4, 7}}},
- {"8J2135a", { 8, 7, 0}, "macOS", {"Tiger", {10, 4, 7}}},
- {"8K1079", { 8, 7, 0}, "macOS", {"Tiger", {10, 4, 7}}},
- {"8N5107", { 8, 7, 0}, "macOS", {"Tiger", {10, 4, 7}}},
- {"8L127", { 8, 8, 0}, "macOS", {"Tiger", {10, 4, 8}}},
- {"8L2127", { 8, 8, 0}, "macOS", {"Tiger", {10, 4, 8}}},
- {"8P135", { 8, 9, 0}, "macOS", {"Tiger", {10, 4, 9}}},
- {"8P2137", { 8, 9, 0}, "macOS", {"Tiger", {10, 4, 9}}},
- {"8R218", { 8, 10, 0}, "macOS", {"Tiger", {10, 4, 10}}},
- {"8R2218", { 8, 10, 0}, "macOS", {"Tiger", {10, 4, 10}}},
- {"8R2232", { 8, 10, 0}, "macOS", {"Tiger", {10, 4, 10}}},
- {"8S165", { 8, 11, 0}, "macOS", {"Tiger", {10, 4, 11}}},
- {"8S2167", { 8, 11, 0}, "macOS", {"Tiger", {10, 4, 11}}},
- {"9A581", { 9, 0, 0}, "macOS", {"Leopard", {10, 5, 0}}},
- {"9B18", { 9, 1, 0}, "macOS", {"Leopard", {10, 5, 1}}},
- {"9B2117", { 9, 1, 1}, "macOS", {"Leopard", {10, 5, 1}}},
- {"9C31", { 9, 2, 0}, "macOS", {"Leopard", {10, 5, 2}}},
- {"9C7010", { 9, 2, 0}, "macOS", {"Leopard", {10, 5, 2}}},
- {"9D34", { 9, 3, 0}, "macOS", {"Leopard", {10, 5, 3}}},
- {"9E17", { 9, 4, 0}, "macOS", {"Leopard", {10, 5, 4}}},
- {"9F33", { 9, 5, 0}, "macOS", {"Leopard", {10, 5, 5}}},
- {"9G55", { 9, 6, 0}, "macOS", {"Leopard", {10, 5, 6}}},
- {"9G66", { 9, 6, 0}, "macOS", {"Leopard", {10, 5, 6}}},
- {"9G71", { 9, 6, 0}, "macOS", {"Leopard", {10, 5, 6}}},
- {"9J61", { 9, 7, 0}, "macOS", {"Leopard", {10, 5, 7}}},
- {"9L30", { 9, 8, 0}, "macOS", {"Leopard", {10, 5, 8}}},
- {"9L34", { 9, 8, 0}, "macOS", {"Leopard", {10, 5, 8}}},
- {"10A432", {10, 0, 0}, "macOS", {"Snow Leopard", {10, 6, 0}}},
- {"10A433", {10, 0, 0}, "macOS", {"Snow Leopard", {10, 6, 0}}},
- {"10B504", {10, 1, 0}, "macOS", {"Snow Leopard", {10, 6, 1}}},
- {"10C540", {10, 2, 0}, "macOS", {"Snow Leopard", {10, 6, 2}}},
- {"10D573", {10, 3, 0}, "macOS", {"Snow Leopard", {10, 6, 3}}},
- {"10D575", {10, 3, 0}, "macOS", {"Snow Leopard", {10, 6, 3}}},
- {"10D578", {10, 3, 0}, "macOS", {"Snow Leopard", {10, 6, 3}}},
- {"10F569", {10, 4, 0}, "macOS", {"Snow Leopard", {10, 6, 4}}},
- {"10H574", {10, 5, 0}, "macOS", {"Snow Leopard", {10, 6, 5}}},
- {"10J567", {10, 6, 0}, "macOS", {"Snow Leopard", {10, 6, 6}}},
- {"10J869", {10, 7, 0}, "macOS", {"Snow Leopard", {10, 6, 7}}},
- {"10J3250", {10, 7, 0}, "macOS", {"Snow Leopard", {10, 6, 7}}},
- {"10J4138", {10, 7, 0}, "macOS", {"Snow Leopard", {10, 6, 7}}},
- {"10K540", {10, 8, 0}, "macOS", {"Snow Leopard", {10, 6, 8}}},
- {"10K549", {10, 8, 0}, "macOS", {"Snow Leopard", {10, 6, 8}}},
- {"11A511", {11, 0, 0}, "macOS", {"Lion", {10, 7, 0}}},
- {"11A511s", {11, 0, 0}, "macOS", {"Lion", {10, 7, 0}}},
- {"11A2061", {11, 0, 2}, "macOS", {"Lion", {10, 7, 0}}},
- {"11A2063", {11, 0, 2}, "macOS", {"Lion", {10, 7, 0}}},
- {"11B26", {11, 1, 0}, "macOS", {"Lion", {10, 7, 1}}},
- {"11B2118", {11, 1, 0}, "macOS", {"Lion", {10, 7, 1}}},
- {"11C74", {11, 2, 0}, "macOS", {"Lion", {10, 7, 2}}},
- {"11D50", {11, 3, 0}, "macOS", {"Lion", {10, 7, 3}}},
- {"11E53", {11, 4, 0}, "macOS", {"Lion", {10, 7, 4}}},
- {"11G56", {11, 4, 2}, "macOS", {"Lion", {10, 7, 5}}},
- {"11G63", {11, 4, 2}, "macOS", {"Lion", {10, 7, 5}}},
- {"12A269", {12, 0, 0}, "macOS", {"Mountain Lion", {10, 8, 0}}},
- {"12B19", {12, 1, 0}, "macOS", {"Mountain Lion", {10, 8, 1}}},
- {"12C54", {12, 2, 0}, "macOS", {"Mountain Lion", {10, 8, 2}}},
- {"12C60", {12, 2, 0}, "macOS", {"Mountain Lion", {10, 8, 2}}},
- {"12C2034", {12, 2, 0}, "macOS", {"Mountain Lion", {10, 8, 2}}},
- {"12C3104", {12, 2, 0}, "macOS", {"Mountain Lion", {10, 8, 2}}},
- {"12D78", {12, 3, 0}, "macOS", {"Mountain Lion", {10, 8, 3}}},
- {"12E55", {12, 4, 0}, "macOS", {"Mountain Lion", {10, 8, 4}}},
- {"12E3067", {12, 4, 0}, "macOS", {"Mountain Lion", {10, 8, 4}}},
- {"12E4022", {12, 4, 0}, "macOS", {"Mountain Lion", {10, 8, 4}}},
- {"12F37", {12, 5, 0}, "macOS", {"Mountain Lion", {10, 8, 5}}},
- {"12F45", {12, 5, 0}, "macOS", {"Mountain Lion", {10, 8, 5}}},
- {"12F2501", {12, 5, 0}, "macOS", {"Mountain Lion", {10, 8, 5}}},
- {"12F2518", {12, 5, 0}, "macOS", {"Mountain Lion", {10, 8, 5}}},
- {"12F2542", {12, 5, 0}, "macOS", {"Mountain Lion", {10, 8, 5}}},
- {"12F2560", {12, 5, 0}, "macOS", {"Mountain Lion", {10, 8, 5}}},
- {"13A603", {13, 0, 0}, "macOS", {"Mavericks", {10, 9, 0}}},
- {"13B42", {13, 0, 0}, "macOS", {"Mavericks", {10, 9, 1}}},
- {"13C64", {13, 1, 0}, "macOS", {"Mavericks", {10, 9, 2}}},
- {"13C1021", {13, 1, 0}, "macOS", {"Mavericks", {10, 9, 2}}},
- {"13D65", {13, 2, 0}, "macOS", {"Mavericks", {10, 9, 3}}},
- {"13E28", {13, 3, 0}, "macOS", {"Mavericks", {10, 9, 4}}},
- {"13F34", {13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}},
- {"13F1066", {13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}},
- {"13F1077", {13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}},
- {"13F1096", {13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}},
- {"13F1112", {13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}},
- {"13F1134", {13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}},
- {"13F1507", {13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}},
- {"13F1603", {13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}},
- {"13F1712", {13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}},
- {"13F1808", {13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}},
- {"13F1911", {13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}},
- {"14A389", {14, 0, 0}, "macOS", {"Yosemite", {10, 10, 0}}},
- {"14B25", {14, 0, 0}, "macOS", {"Yosemite", {10, 10, 1}}},
- {"14C109", {14, 1, 0}, "macOS", {"Yosemite", {10, 10, 2}}},
- {"14C1510", {14, 1, 0}, "macOS", {"Yosemite", {10, 10, 2}}},
- {"14C2043", {14, 1, 0}, "macOS", {"Yosemite", {10, 10, 2}}},
- {"14C1514", {14, 1, 0}, "macOS", {"Yosemite", {10, 10, 2}}},
- {"14C2513", {14, 1, 0}, "macOS", {"Yosemite", {10, 10, 2}}},
- {"14D131", {14, 3, 0}, "macOS", {"Yosemite", {10, 10, 3}}},
- {"14D136", {14, 3, 0}, "macOS", {"Yosemite", {10, 10, 3}}},
- {"14E46", {14, 4, 0}, "macOS", {"Yosemite", {10, 10, 4}}},
- {"14F27", {14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}},
- {"14F1021", {14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}},
- {"14F1505", {14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}},
- {"14F1509", {14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}},
- {"14F1605", {14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}},
- {"14F1713", {14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}},
- {"14F1808", {14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}},
- {"14F1909", {14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}},
- {"14F1912", {14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}},
- {"14F2009", {14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}},
- {"14F2109", {14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}},
- {"14F2315", {14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}},
- {"14F2411", {14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}},
- {"14F2511", {14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}},
- {"15A284", {15, 0, 0}, "macOS", {"El Capitan", {10, 11, 0}}},
- {"15B42", {15, 0, 0}, "macOS", {"El Capitan", {10, 11, 1}}},
- {"15C50", {15, 2, 0}, "macOS", {"El Capitan", {10, 11, 2}}},
- {"15D21", {15, 3, 0}, "macOS", {"El Capitan", {10, 11, 3}}},
- {"15E65", {15, 4, 0}, "macOS", {"El Capitan", {10, 11, 4}}},
- {"15F34", {15, 5, 0}, "macOS", {"El Capitan", {10, 11, 5}}},
- {"15G31", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}},
- {"15G1004", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}},
- {"15G1011", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}},
- {"15G1108", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}},
- {"15G1212", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}},
- {"15G1217", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}},
- {"15G1421", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}},
- {"15G1510", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}},
- {"15G1611", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}},
- {"15G17023", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}},
- {"15G18013", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}},
- {"15G19009", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}},
- {"15G20015", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}},
- {"15G21013", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}},
- {"15G22010", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}},
- {"16A323", {16, 0, 0}, "macOS", {"Sierra", {10, 12, 0}}},
- {"16B2555", {16, 1, 0}, "macOS", {"Sierra", {10, 12, 1}}},
- {"16B2657", {16, 1, 0}, "macOS", {"Sierra", {10, 12, 1}}},
- {"16C67", {16, 3, 0}, "macOS", {"Sierra", {10, 12, 2}}},
- {"16C68", {16, 3, 0}, "macOS", {"Sierra", {10, 12, 2}}},
- {"16D32", {16, 4, 0}, "macOS", {"Sierra", {10, 12, 3}}},
- {"16E195", {16, 5, 0}, "macOS", {"Sierra", {10, 12, 4}}},
- {"16F73", {16, 6, 0}, "macOS", {"Sierra", {10, 12, 5}}},
- {"16F2073", {16, 6, 0}, "macOS", {"Sierra", {10, 12, 5}}},
- {"16G29", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}},
- {"16G1036", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}},
- {"16G1114", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}},
- {"16G1212", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}},
- {"16G1314", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}},
- {"16G1408", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}},
- {"16G1510", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}},
- {"16G1618", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}},
- {"16G1710", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}},
- {"16G1815", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}},
- {"16G1917", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}},
- {"16G1918", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}},
- {"16G2016", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}},
- {"16G2127", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}},
- {"16G2128", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}},
- {"16G2136", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}},
- {"17A365", {17, 0, 0}, "macOS", {"High Sierra", {10, 13, 0}}},
- {"17A405", {17, 0, 0}, "macOS", {"High Sierra", {10, 13, 0}}},
- {"17B48", {17, 2, 0}, "macOS", {"High Sierra", {10, 13, 1}}},
- {"17B1002", {17, 2, 0}, "macOS", {"High Sierra", {10, 13, 1}}},
- {"17B1003", {17, 2, 0}, "macOS", {"High Sierra", {10, 13, 1}}},
- {"17C88", {17, 3, 0}, "macOS", {"High Sierra", {10, 13, 2}}},
- {"17C89", {17, 3, 0}, "macOS", {"High Sierra", {10, 13, 2}}},
- {"17C205", {17, 3, 0}, "macOS", {"High Sierra", {10, 13, 2}}},
- {"17C2205", {17, 3, 0}, "macOS", {"High Sierra", {10, 13, 2}}},
- {"17D47", {17, 4, 0}, "macOS", {"High Sierra", {10, 13, 3}}},
- {"17D2047", {17, 4, 0}, "macOS", {"High Sierra", {10, 13, 3}}},
- {"17D102", {17, 4, 0}, "macOS", {"High Sierra", {10, 13, 3}}},
- {"17D2102", {17, 4, 0}, "macOS", {"High Sierra", {10, 13, 3}}},
- {"17E199", {17, 5, 0}, "macOS", {"High Sierra", {10, 13, 4}}},
- {"17E202", {17, 5, 0}, "macOS", {"High Sierra", {10, 13, 4}}},
- {"17F77", {17, 6, 0}, "macOS", {"High Sierra", {10, 13, 5}}},
- {"17G65", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
- {"17G2208", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
- {"17G2307", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
- {"17G3025", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
- {"17G4015", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
- {"17G5019", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
- {"17G6029", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
- {"17G6030", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
- {"17G7024", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
- {"17G8029", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
- {"17G8030", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
- {"17G8037", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
- {"17G9016", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
- {"17G10021", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
- {"17G11023", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
- {"17G12034", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
- {"17G13033", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
- {"17G13035", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
- {"17G14019", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
- {"17G14033", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
- {"17G14042", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
- {"18A391", {18, 0, 0}, "macOS", {"Mojave", {10, 14, 0}}},
- {"18B75", {18, 2, 0}, "macOS", {"Mojave", {10, 14, 1}}},
- {"18B2107", {18, 2, 0}, "macOS", {"Mojave", {10, 14, 1}}},
- {"18B3094", {18, 2, 0}, "macOS", {"Mojave", {10, 14, 1}}},
- {"18C54", {18, 2, 0}, "macOS", {"Mojave", {10, 14, 2}}},
- {"18D42", {18, 2, 0}, "macOS", {"Mojave", {10, 14, 3}}},
- {"18D43", {18, 2, 0}, "macOS", {"Mojave", {10, 14, 3}}},
- {"18D109", {18, 2, 0}, "macOS", {"Mojave", {10, 14, 3}}},
- {"18E226", {18, 5, 0}, "macOS", {"Mojave", {10, 14, 4}}},
- {"18E227", {18, 5, 0}, "macOS", {"Mojave", {10, 14, 4}}},
- {"18F132", {18, 6, 0}, "macOS", {"Mojave", {10, 14, 5}}},
- {"18G84", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
- {"18G87", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
- {"18G95", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
- {"18G103", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
- {"18G1012", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
- {"18G2022", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
- {"18G3020", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
- {"18G4032", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
- {"18G5033", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
- {"18G6020", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
- {"18G6032", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
- {"18G6042", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
- {"18G7016", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
- {"18G8012", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
- {"18G8022", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
- {"18G9028", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
- {"18G9216", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
- {"18G9323", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
- {"19A583", {19, 0, 0}, "macOS", {"Catalina", {10, 15, 0}}},
- {"19A602", {19, 0, 0}, "macOS", {"Catalina", {10, 15, 0}}},
- {"19A603", {19, 0, 0}, "macOS", {"Catalina", {10, 15, 0}}},
- {"19B88", {19, 0, 0}, "macOS", {"Catalina", {10, 15, 1}}},
- {"19C57", {19, 2, 0}, "macOS", {"Catalina", {10, 15, 2}}},
- {"19C58", {19, 2, 0}, "macOS", {"Catalina", {10, 15, 2}}},
- {"19D76", {19, 3, 0}, "macOS", {"Catalina", {10, 15, 3}}},
- {"19E266", {19, 4, 0}, "macOS", {"Catalina", {10, 15, 4}}},
- {"19E287", {19, 4, 0}, "macOS", {"Catalina", {10, 15, 4}}},
- {"19F96", {19, 5, 0}, "macOS", {"Catalina", {10, 15, 5}}},
- {"19F101", {19, 5, 0}, "macOS", {"Catalina", {10, 15, 5}}},
- {"19G73", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 6}}},
- {"19G2021", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 6}}},
- {"19H2", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
- {"19H4", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
- {"19H15", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
- {"19H114", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
- {"19H512", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
- {"19H524", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
- {"19H1030", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
- {"19H1217", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
- {"19H1323", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
- {"19H1417", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
- {"19H1419", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
- {"19H1519", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
- {"19H1615", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
- {"19H1713", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
- {"19H1715", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
- {"19H1824", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
- {"19H1922", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
- {"19H2026", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
- {"20A2411", {20, 1, 0}, "macOS", {"Big Sur", {11, 0, 0}}},
- {"20B29", {20, 1, 0}, "macOS", {"Big Sur", {11, 0, 1}}},
- {"20B50", {20, 1, 0}, "macOS", {"Big Sur", {11, 0, 1}}},
- {"20C69", {20, 2, 0}, "macOS", {"Big Sur", {11, 1, 0}}},
- {"20D64", {20, 3, 0}, "macOS", {"Big Sur", {11, 2, 0}}},
- {"20D74", {20, 3, 0}, "macOS", {"Big Sur", {11, 2, 1}}},
- {"20D75", {20, 3, 0}, "macOS", {"Big Sur", {11, 2, 1}}},
- {"20D80", {20, 3, 0}, "macOS", {"Big Sur", {11, 2, 2}}},
- {"20D91", {20, 3, 0}, "macOS", {"Big Sur", {11, 2, 3}}},
- {"20E232", {20, 4, 0}, "macOS", {"Big Sur", {11, 3, 0}}},
- {"20E241", {20, 4, 0}, "macOS", {"Big Sur", {11, 3, 1}}},
- {"20F71", {20, 5, 0}, "macOS", {"Big Sur", {11, 4, 0}}},
- {"20G71", {20, 6, 0}, "macOS", {"Big Sur", {11, 5, 0}}},
- {"20G80", {20, 6, 0}, "macOS", {"Big Sur", {11, 5, 1}}},
- {"20G95", {20, 6, 0}, "macOS", {"Big Sur", {11, 5, 2}}},
- {"20G165", {20, 6, 0}, "macOS", {"Big Sur", {11, 6, 0}}},
- {"20G224", {20, 6, 0}, "macOS", {"Big Sur", {11, 6, 1}}},
- {"20G314", {20, 6, 0}, "macOS", {"Big Sur", {11, 6, 2}}},
- {"20G415", {20, 6, 0}, "macOS", {"Big Sur", {11, 6, 3}}},
- {"20G417", {20, 6, 0}, "macOS", {"Big Sur", {11, 6, 4}}},
- {"20G527", {20, 6, 0}, "macOS", {"Big Sur", {11, 6, 5}}},
- {"20G624", {20, 6, 0}, "macOS", {"Big Sur", {11, 6, 6}}},
- {"20G630", {20, 6, 0}, "macOS", {"Big Sur", {11, 6, 7}}},
- {"20G730", {20, 6, 0}, "macOS", {"Big Sur", {11, 6, 8}}},
- {"21A344", {21, 0, 1}, "macOS", {"Monterey", {12, 0, 0}}},
- {"21A559", {21, 1, 0}, "macOS", {"Monterey", {12, 0, 1}}},
- {"21C52", {21, 2, 0}, "macOS", {"Monterey", {12, 1, 0}}},
- {"21D49", {21, 3, 0}, "macOS", {"Monterey", {12, 2, 0}}},
- {"21D62", {21, 3, 0}, "macOS", {"Monterey", {12, 2, 1}}},
- {"21E230", {21, 4, 0}, "macOS", {"Monterey", {12, 3, 0}}},
- {"21E258", {21, 4, 0}, "macOS", {"Monterey", {12, 3, 1}}},
- {"21F79", {21, 5, 0}, "macOS", {"Monterey", {12, 4, 0}}},
- {"21F2081", {21, 5, 0}, "macOS", {"Monterey", {12, 4, 0}}},
- {"21F2092", {21, 5, 0}, "macOS", {"Monterey", {12, 4, 0}}},
- {"21G72", {21, 6, 0}, "macOS", {"Monterey", {12, 5, 0}}},
- {"21G83", {21, 6, 0}, "macOS", {"Monterey", {12, 5, 1}}},
- };
-
-
- b32 build_found = 1;
- b32 darwin_found = 1;
- uint32_t major, minor, patch;
-
- #define MACOS_VERSION_BUFFER_SIZE 100
- char build_buffer[MACOS_VERSION_BUFFER_SIZE];
- char darwin_buffer[MACOS_VERSION_BUFFER_SIZE];
- size_t build_buffer_size = MACOS_VERSION_BUFFER_SIZE - 1;
- size_t darwin_buffer_size = MACOS_VERSION_BUFFER_SIZE - 1;
- #undef MACOS_VERSION_BUFFER_SIZE
-
- int build_mibs[] = { CTL_KERN, KERN_OSVERSION };
- if (sysctl(build_mibs, 2, build_buffer, &build_buffer_size, NULL, 0) == -1) {
- build_found = 0;
- }
-
- int darwin_mibs[] = { CTL_KERN, KERN_OSRELEASE };
- if (sysctl(darwin_mibs, 2, darwin_buffer, &darwin_buffer_size, NULL, 0) == -1) {
- gb_printf("macOS Unknown\n");
- return;
- } else {
- if (sscanf(darwin_buffer, "%u.%u.%u", &major, &minor, &patch) != 3) {
- darwin_found = 0;
- }
- }
-
- // Scan table for match on BUILD
- int macos_release_count = sizeof(macos_release_map) / sizeof(macos_release_map[0]);
- Darwin_To_Release match = {};
-
- for (int build = 0; build < macos_release_count; build++) {
- Darwin_To_Release rel = macos_release_map[build];
-
- // Do we have an exact match on the BUILD?
- if (gb_strcmp(rel.build, (const char *)build_buffer) == 0) {
- match = rel;
- break;
- }
-
- // Do we have an exact Darwin match?
- if (rel.darwin[0] == major && rel.darwin[1] == minor && rel.darwin[2] == patch) {
- match = rel;
- break;
- }
-
- // Major kernel version needs to match exactly,
- if (rel.darwin[0] == major) {
- // No major version match yet.
- if (!match.os_name) {
- match = rel;
- }
- if (minor >= rel.darwin[1]) {
- match = rel;
- if (patch >= rel.darwin[2]) {
- match = rel;
- }
- }
- }
- }
-
- if (match.os_name) {
- gb_printf("%s %s %d", match.os_name, match.release.name, match.release.version[0]);
- if (match.release.version[1] > 0 || match.release.version[2] > 0) {
- gb_printf(".%d", match.release.version[1]);
- }
- if (match.release.version[2] > 0) {
- gb_printf(".%d", match.release.version[2]);
- }
- if (build_found) {
- gb_printf(" (build: %s, kernel: %d.%d.%d)\n", build_buffer, match.darwin[0], match.darwin[1], match.darwin[2]);
- } else {
- gb_printf(" (build: %s?, kernel: %d.%d.%d)\n", match.build, match.darwin[0], match.darwin[1], match.darwin[2]);
- }
- return;
- }
-
- if (build_found && darwin_found) {
- gb_printf("macOS Unknown (build: %s, kernel: %d.%d.%d)\n", build_buffer, major, minor, patch);
- return;
- } else if (build_found) {
- gb_printf("macOS Unknown (build: %s)\n", build_buffer);
- return;
- } else if (darwin_found) {
- gb_printf("macOS Unknown (kernel: %d.%d.%d)\n", major, minor, patch);
- return;
- }
- #elif defined(GB_SYSTEM_OPENBSD)
- struct utsname un;
-
- if (uname(&un) != -1) {
- gb_printf("%s %s %s %s\n", un.sysname, un.release, un.version, un.machine);
- } else {
- gb_printf("OpenBSD: Unknown\n");
- }
- #elif defined(GB_SYSTEM_FREEBSD)
- #define freebsd_version_buffer 129
- char buffer[freebsd_version_buffer];
- size_t buffer_size = freebsd_version_buffer - 1;
- #undef freebsd_version_buffer
-
- int mibs[] = { CTL_KERN, KERN_VERSION };
- if (sysctl(mibs, 2, buffer, &buffer_size, NULL, 0) == -1) {
- gb_printf("FreeBSD: Unknown\n");
- } else {
- // KERN_VERSION can end in a \n, replace it with a \0
- for (int i = 0; i < buffer_size; i += 1) {
- if (buffer[i] == '\n') buffer[i] = 0;
- }
- gb_printf("%s", &buffer[0]);
-
- // Retrieve kernel revision using `sysctl`, e.g. 199506
- mibs[1] = KERN_OSREV;
- uint64_t revision;
- size_t revision_size = sizeof(revision);
-
- if (sysctl(mibs, 2, &revision, &revision_size, NULL, 0) == -1) {
- gb_printf("\n");
- } else {
- gb_printf(", revision %ld\n", revision);
- }
- }
- #else
- gb_printf("Unknown");
- #endif
-}
-
-// NOTE(Jeroen): `odin report` prints some system information for easier bug reporting.
-gb_internal void print_bug_report_help() {
- gb_printf("Where to find more information and get into contact when you encounter a bug:\n\n");
- gb_printf("\tWebsite: https://odin-lang.org\n");
- gb_printf("\tGitHub: https://github.com/odin-lang/Odin/issues\n");
- /*
- Uncomment and update URL once we have a Discord vanity URL. For now people can get here from the site.
- gb_printf("\tDiscord: https://discord.com/invite/sVBPHEv\n");
- */
- gb_printf("\n\n");
-
- gb_printf("Useful information to add to a bug report:\n\n");
-
- gb_printf("\tOdin: %.*s", LIT(ODIN_VERSION));
-
- #ifdef NIGHTLY
- gb_printf("-nightly");
- #endif
-
- #ifdef GIT_SHA
- gb_printf(":%s", GIT_SHA);
- #endif
-
- gb_printf("\n");
-
- /*
- Print OS information.
- */
- report_os_info();
-
- /*
- Now print CPU info.
- */
- report_cpu_info();
-
- /*
- And RAM info.
- */
- report_ram_info();
-} \ No newline at end of file
+/*
+ Gather and print platform and version info to help with reporting Odin bugs.
+*/
+
+#if defined(GB_SYSTEM_LINUX)
+ #include <sys/utsname.h>
+ #include <sys/sysinfo.h>
+#endif
+
+#if defined(GB_SYSTEM_OSX)
+ #include <sys/sysctl.h>
+#endif
+
+#if defined(GB_SYSTEM_OPENBSD) || defined(GB_SYSTEM_NETBSD)
+ #include <sys/sysctl.h>
+ #include <sys/utsname.h>
+#endif
+
+#if defined(GB_SYSTEM_FREEBSD)
+ #include <sys/sysctl.h>
+#endif
+
+/*
+ NOTE(Jeroen): This prints the Windows product edition only, to be called from `print_platform_details`.
+*/
+#if defined(GB_SYSTEM_WINDOWS)
+gb_internal void report_windows_product_type(DWORD ProductType) {
+ switch (ProductType) {
+ case PRODUCT_ULTIMATE:
+ gb_printf("Ultimate");
+ break;
+
+ case PRODUCT_HOME_BASIC:
+ gb_printf("Home Basic");
+ break;
+
+ case PRODUCT_HOME_PREMIUM:
+ gb_printf("Home Premium");
+ break;
+
+ case PRODUCT_ENTERPRISE:
+ gb_printf("Enterprise");
+ break;
+
+ case PRODUCT_CORE:
+ gb_printf("Home Basic");
+ break;
+
+ case PRODUCT_HOME_BASIC_N:
+ gb_printf("Home Basic N");
+ break;
+
+ case PRODUCT_EDUCATION:
+ gb_printf("Education");
+ break;
+
+ case PRODUCT_EDUCATION_N:
+ gb_printf("Education N");
+ break;
+
+ case PRODUCT_BUSINESS:
+ gb_printf("Business");
+ break;
+
+ case PRODUCT_STANDARD_SERVER:
+ gb_printf("Standard Server");
+ break;
+
+ case PRODUCT_DATACENTER_SERVER:
+ gb_printf("Datacenter");
+ break;
+
+ case PRODUCT_SMALLBUSINESS_SERVER:
+ gb_printf("Windows Small Business Server");
+ break;
+
+ case PRODUCT_ENTERPRISE_SERVER:
+ gb_printf("Enterprise Server");
+ break;
+
+ case PRODUCT_STARTER:
+ gb_printf("Starter");
+ break;
+
+ case PRODUCT_DATACENTER_SERVER_CORE:
+ gb_printf("Datacenter Server Core");
+ break;
+
+ case PRODUCT_STANDARD_SERVER_CORE:
+ gb_printf("Server Standard Core");
+ break;
+
+ case PRODUCT_ENTERPRISE_SERVER_CORE:
+ gb_printf("Enterprise Server Core");
+ break;
+
+ case PRODUCT_BUSINESS_N:
+ gb_printf("Business N");
+ break;
+
+ case PRODUCT_HOME_SERVER:
+ gb_printf("Home Server");
+ break;
+
+ case PRODUCT_SERVER_FOR_SMALLBUSINESS:
+ gb_printf("Windows Server 2008 for Windows Essential Server Solutions");
+ break;
+
+ case PRODUCT_SMALLBUSINESS_SERVER_PREMIUM:
+ gb_printf("Small Business Server Premium");
+ break;
+
+ case PRODUCT_HOME_PREMIUM_N:
+ gb_printf("Home Premium N");
+ break;
+
+ case PRODUCT_ENTERPRISE_N:
+ gb_printf("Enterprise N");
+ break;
+
+ case PRODUCT_ULTIMATE_N:
+ gb_printf("Ultimate N");
+ break;
+
+ case PRODUCT_HYPERV:
+ gb_printf("HyperV");
+ break;
+
+ case PRODUCT_STARTER_N:
+ gb_printf("Starter N");
+ break;
+
+ case PRODUCT_PROFESSIONAL:
+ gb_printf("Professional");
+ break;
+
+ case PRODUCT_PROFESSIONAL_N:
+ gb_printf("Professional N");
+ break;
+
+ case PRODUCT_UNLICENSED:
+ gb_printf("Unlicensed");
+ break;
+
+ default:
+ gb_printf("Unknown Edition (%08x)", cast(unsigned)ProductType);
+ }
+}
+#endif
+
+gb_internal void report_cpu_info() {
+ gb_printf("\tCPU: ");
+
+ #if defined(GB_CPU_X86)
+
+ /*
+ Get extended leaf info
+ */
+ int cpu[4];
+
+ odin_cpuid(0x80000000, &cpu[0]);
+ int number_of_extended_ids = cpu[0];
+
+ int brand[0x12] = {};
+
+ /*
+ Read CPU brand if supported.
+ */
+ if (number_of_extended_ids >= 0x80000004) {
+ odin_cpuid(0x80000002, &brand[0]);
+ odin_cpuid(0x80000003, &brand[4]);
+ odin_cpuid(0x80000004, &brand[8]);
+
+ /*
+ Some CPUs like ` Intel(R) Xeon(R) CPU E5-1650 v2 @ 3.50GHz` may include leading spaces. Trim them.
+ */
+ char * brand_name = (char *)&brand[0];
+ for (; brand_name[0] == ' '; brand_name++) {}
+
+ gb_printf("%s\n", brand_name);
+ } else {
+ gb_printf("Unable to retrieve.\n");
+ }
+
+ #elif defined(GB_CPU_ARM)
+ bool generic = true;
+
+ #if defined(GB_SYSTEM_OSX)
+ char cpu_name[128] = {};
+ size_t cpu_name_size = 128;
+ if (sysctlbyname("machdep.cpu.brand_string", &cpu_name, &cpu_name_size, nullptr, 0) == 0) {
+ generic = false;
+ gb_printf("%s\n", (char *)&cpu_name[0]);
+ }
+ #endif
+
+ if (generic) {
+ /*
+ TODO(Jeroen): On *nix, perhaps query `/proc/cpuinfo`.
+ */
+ #if defined(GB_ARCH_64_BIT)
+ gb_printf("ARM64\n");
+ #else
+ gb_printf("ARM\n");
+ #endif
+ }
+ #elif defined(GB_CPU_RISCV)
+ #if defined(GB_ARCH_64_BIT)
+ gb_printf("RISCV64\n");
+ #else
+ gb_printf("RISCV32\n");
+ #endif
+ #else
+ gb_printf("Unknown\n");
+ #endif
+}
+
+/*
+ Report the amount of installed RAM.
+*/
+gb_internal void report_ram_info() {
+ gb_printf("\tRAM: ");
+
+ #if defined(GB_SYSTEM_WINDOWS)
+ MEMORYSTATUSEX statex;
+ statex.dwLength = sizeof(statex);
+ GlobalMemoryStatusEx (&statex);
+
+ gb_printf("%lld MiB\n", statex.ullTotalPhys / gb_megabytes(1));
+
+ #elif defined(GB_SYSTEM_LINUX)
+ /*
+ Retrieve RAM info using `sysinfo()`,
+ */
+ struct sysinfo info;
+ int result = sysinfo(&info);
+
+ if (result == 0x0) {
+ gb_printf("%lu MiB\n", (unsigned long)(info.totalram * info.mem_unit / gb_megabytes(1)));
+ } else {
+ gb_printf("Unknown.\n");
+ }
+ #elif defined(GB_SYSTEM_OSX)
+ uint64_t ram_amount;
+ size_t val_size = sizeof(ram_amount);
+
+ int mibs[] = { CTL_HW, HW_MEMSIZE };
+ if (sysctl(mibs, 2, &ram_amount, &val_size, NULL, 0) != -1) {
+ gb_printf("%lld MiB\n", ram_amount / gb_megabytes(1));
+ }
+ #elif defined(GB_SYSTEM_NETBSD)
+ uint64_t ram_amount;
+ size_t val_size = sizeof(ram_amount);
+
+ int mibs[] = { CTL_HW, HW_PHYSMEM64 };
+ if (sysctl(mibs, 2, &ram_amount, &val_size, NULL, 0) != -1) {
+ gb_printf("%lu MiB\n", ram_amount / gb_megabytes(1));
+ }
+ #elif defined(GB_SYSTEM_OPENBSD)
+ uint64_t ram_amount;
+ size_t val_size = sizeof(ram_amount);
+
+ int mibs[] = { CTL_HW, HW_PHYSMEM64 };
+ if (sysctl(mibs, 2, &ram_amount, &val_size, NULL, 0) != -1) {
+ gb_printf("%lld MiB\n", ram_amount / gb_megabytes(1));
+ }
+ #elif defined(GB_SYSTEM_FREEBSD)
+ uint64_t ram_amount;
+ size_t val_size = sizeof(ram_amount);
+
+ int mibs[] = { CTL_HW, HW_PHYSMEM };
+ if (sysctl(mibs, 2, &ram_amount, &val_size, NULL, 0) != -1) {
+ gb_printf("%lu MiB\n", ram_amount / gb_megabytes(1));
+ }
+ #else
+ gb_printf("Unknown.\n");
+ #endif
+}
+
+gb_internal void report_os_info() {
+ gb_printf("\tOS: ");
+
+ #if defined(GB_SYSTEM_WINDOWS)
+ /*
+ NOTE(Jeroen):
+ `GetVersionEx` will return 6.2 for Windows 10 unless the program is manifested for Windows 10.
+ `RtlGetVersion` will return the true version.
+
+ Rather than include the WinDDK, we ask the kernel directly.
+
+ `HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion` is for the minor build version (Update Build Release)
+
+ */
+ OSVERSIONINFOEXW osvi;
+ ZeroMemory(&osvi, sizeof(OSVERSIONINFOEXW));
+ osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW);
+
+ typedef NTSTATUS (WINAPI* RtlGetVersionPtr)(OSVERSIONINFOW*);
+ typedef BOOL (WINAPI* GetProductInfoPtr)(DWORD dwOSMajorVersion, DWORD dwOSMinorVersion, DWORD dwSpMajorVersion, DWORD dwSpMinorVersion, PDWORD pdwReturnedProductType);
+
+ // https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/nf-wdm-rtlgetversion
+ RtlGetVersionPtr RtlGetVersion = (RtlGetVersionPtr)GetProcAddress(GetModuleHandle(TEXT("ntdll.dll")), "RtlGetVersion");
+ // https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getproductinfo
+ GetProductInfoPtr GetProductInfo = (GetProductInfoPtr)GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "GetProductInfo");
+
+ NTSTATUS status = {};
+ DWORD ProductType = {};
+ if (RtlGetVersion != nullptr) {
+ status = RtlGetVersion((OSVERSIONINFOW*)&osvi);
+ }
+
+ if (RtlGetVersion == nullptr || status != 0x0) {
+ gb_printf("Windows (Unknown Version)");
+ } else {
+ if (GetProductInfo != nullptr) {
+ GetProductInfo(osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.wServicePackMajor, osvi.wServicePackMinor, &ProductType);
+ }
+
+ if (false) {
+ gb_printf("dwMajorVersion: %u\n", cast(unsigned)osvi.dwMajorVersion);
+ gb_printf("dwMinorVersion: %u\n", cast(unsigned)osvi.dwMinorVersion);
+ gb_printf("dwBuildNumber: %u\n", cast(unsigned)osvi.dwBuildNumber);
+ gb_printf("dwPlatformId: %u\n", cast(unsigned)osvi.dwPlatformId);
+ gb_printf("wServicePackMajor: %u\n", cast(unsigned)osvi.wServicePackMajor);
+ gb_printf("wServicePackMinor: %u\n", cast(unsigned)osvi.wServicePackMinor);
+ gb_printf("wSuiteMask: %u\n", cast(unsigned)osvi.wSuiteMask);
+ gb_printf("wProductType: %u\n", cast(unsigned)osvi.wProductType);
+ }
+
+ gb_printf("Windows ");
+
+ switch (osvi.dwMajorVersion) {
+ case 10:
+ /*
+ Windows 10 (Pro), Windows 2016 Server, Windows 2019 Server, Windows 2022 Server
+ */
+ switch (osvi.wProductType) {
+ case VER_NT_WORKSTATION: // Workstation
+ if (osvi.dwBuildNumber < 22000) {
+ gb_printf("10 ");
+ } else {
+ gb_printf("11 ");
+ }
+
+ report_windows_product_type(ProductType);
+
+ break;
+ default: // Server or Domain Controller
+ switch(osvi.dwBuildNumber) {
+ case 14393:
+ gb_printf("2016 Server");
+ break;
+ case 17763:
+ gb_printf("2019 Server");
+ break;
+ case 20348:
+ gb_printf("2022 Server");
+ break;
+ default:
+ gb_printf("Unknown Server");
+ break;
+ }
+ }
+ break;
+ case 6:
+ switch (osvi.dwMinorVersion) {
+ case 0:
+ switch (osvi.wProductType) {
+ case VER_NT_WORKSTATION:
+ gb_printf("Windows Vista ");
+ report_windows_product_type(ProductType);
+ break;
+ case 3:
+ gb_printf("Windows Server 2008");
+ break;
+ }
+ break;
+
+ case 1:
+ switch (osvi.wProductType) {
+ case VER_NT_WORKSTATION:
+ gb_printf("Windows 7 ");
+ report_windows_product_type(ProductType);
+ break;
+ case 3:
+ gb_printf("Windows Server 2008 R2");
+ break;
+ }
+ break;
+ case 2:
+ switch (osvi.wProductType) {
+ case VER_NT_WORKSTATION:
+ gb_printf("Windows 8 ");
+ report_windows_product_type(ProductType);
+ break;
+ case 3:
+ gb_printf("Windows Server 2012");
+ break;
+ }
+ break;
+ case 3:
+ switch (osvi.wProductType) {
+ case VER_NT_WORKSTATION:
+ gb_printf("Windows 8.1 ");
+ report_windows_product_type(ProductType);
+ break;
+ case 3:
+ gb_printf("Windows Server 2012 R2");
+ break;
+ }
+ break;
+ }
+ break;
+ case 5:
+ switch (osvi.dwMinorVersion) {
+ case 0:
+ gb_printf("Windows 2000");
+ break;
+ case 1:
+ gb_printf("Windows XP");
+ break;
+ case 2:
+ gb_printf("Windows Server 2003");
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+
+ /*
+ Grab Windows DisplayVersion (like 20H02)
+ */
+ LPDWORD ValueType = {};
+ DWORD UBR;
+ char DisplayVersion[256];
+ DWORD ValueSize = 256;
+
+ status = RegGetValue(
+ HKEY_LOCAL_MACHINE,
+ TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"),
+ TEXT("DisplayVersion"),
+ RRF_RT_REG_SZ,
+ ValueType,
+ DisplayVersion,
+ &ValueSize
+ );
+
+ if (status == 0x0) {
+ gb_printf(" (version: %s)", DisplayVersion);
+ }
+
+ /*
+ Now print build number.
+ */
+ gb_printf(", build %u", cast(unsigned)osvi.dwBuildNumber);
+
+ ValueSize = sizeof(UBR);
+ status = RegGetValue(
+ HKEY_LOCAL_MACHINE,
+ TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"),
+ TEXT("UBR"),
+ RRF_RT_REG_DWORD,
+ ValueType,
+ &UBR,
+ &ValueSize
+ );
+
+ if (status == 0x0) {
+ gb_printf(".%u", cast(unsigned)UBR);
+ }
+ gb_printf("\n");
+ }
+ #elif defined(GB_SYSTEM_LINUX)
+ /*
+ Try to parse `/etc/os-release` for `PRETTY_NAME="Ubuntu 20.04.3 LTS`
+ */
+ gbAllocator a = heap_allocator();
+
+ gbFileContents release = gb_file_read_contents(a, 1, "/etc/os-release");
+ defer (gb_file_free_contents(&release));
+
+ b32 found = 0;
+ if (release.size) {
+ char *start = (char *)release.data;
+ char *end = (char *)release.data + release.size;
+ const char *needle = "PRETTY_NAME=\"";
+ isize needle_len = gb_strlen((needle));
+
+ char *c = start;
+ for (; c < end; c++) {
+ if (gb_strncmp(c, needle, needle_len) == 0) {
+ found = 1;
+ start = c + needle_len;
+ break;
+ }
+ }
+
+ if (found) {
+ for (c = start; c < end; c++) {
+ if (*c == '"') {
+ // Found the closing quote. Replace it with \0
+ *c = 0;
+ gb_printf("%s", (char *)start);
+ break;
+ } else if (*c == '\n') {
+ found = 0;
+ }
+ }
+ }
+ }
+
+ if (!found) {
+ gb_printf("Unknown Linux Distro");
+ }
+
+ /*
+ Print kernel info using `uname()` syscall, https://linux.die.net/man/2/uname
+ */
+ char buffer[1024];
+ uname((struct utsname *)&buffer[0]);
+
+ struct utsname *info;
+ info = (struct utsname *)&buffer[0];
+
+ gb_printf(", %s %s\n", info->sysname, info->release);
+
+ #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)) {
+ gb_printf("macOS Unknown\n");
+ return;
+ }
+
+ uint32_t major, minor, patch;
+
+ if (sscanf(cast(const char *)sw_vers, "%u.%u.%u", &major, &minor, &patch) != 3) {
+ gb_printf("macOS Unknown\n");
+ return;
+ }
+
+ switch (major) {
+ case 15: gb_printf("macOS Sequoia"); break;
+ case 14: gb_printf("macOS Sonoma"); break;
+ case 13: gb_printf("macOS Ventura"); break;
+ case 12: gb_printf("macOS Monterey"); break;
+ case 11: gb_printf("macOS Big Sur"); break;
+ case 10:
+ {
+ switch (minor) {
+ case 15: gb_printf("macOS Catalina"); break;
+ case 14: gb_printf("macOS Mojave"); break;
+ case 13: gb_printf("macOS High Sierra"); break;
+ case 12: gb_printf("macOS Sierra"); break;
+ case 11: gb_printf("OS X El Capitan"); break;
+ case 10: gb_printf("OS X Yosemite"); break;
+ default: gb_printf("macOS Unknown");
+ };
+ break;
+ }
+ default:
+ gb_printf("macOS Unknown");
+ };
+
+ gb_printf(" %d.%d.%d (build ", major, minor, patch);
+
+ b32 build_found = 1;
+ b32 darwin_found = 1;
+
+ #define MACOS_VERSION_BUFFER_SIZE 100
+ char build_buffer[MACOS_VERSION_BUFFER_SIZE];
+ char darwin_buffer[MACOS_VERSION_BUFFER_SIZE];
+ size_t build_buffer_size = MACOS_VERSION_BUFFER_SIZE - 1;
+ size_t darwin_buffer_size = MACOS_VERSION_BUFFER_SIZE - 1;
+ #undef MACOS_VERSION_BUFFER_SIZE
+
+ int build_mibs[] = { CTL_KERN, KERN_OSVERSION };
+ if (sysctl(build_mibs, 2, build_buffer, &build_buffer_size, NULL, 0) == -1) {
+ build_found = 0;
+ }
+
+ int darwin_mibs[] = { CTL_KERN, KERN_OSRELEASE };
+ if (sysctl(darwin_mibs, 2, darwin_buffer, &darwin_buffer_size, NULL, 0) == -1) {
+ darwin_found = 0;
+ } else {
+ if (sscanf(darwin_buffer, "%u.%u.%u", &major, &minor, &patch) != 3) {
+ darwin_found = 0;
+ }
+ }
+
+ if (build_found) {
+ gb_printf("%s, kernel ", build_buffer);
+ } else {
+ gb_printf("Unknown, kernel ");
+ }
+
+ if (darwin_found) {
+ gb_printf("%s)\n", darwin_buffer);
+ } else {
+ gb_printf("Unknown)\n");
+ }
+
+ #elif defined(GB_SYSTEM_OPENBSD) || defined(GB_SYSTEM_NETBSD)
+ struct utsname un;
+
+ if (uname(&un) != -1) {
+ gb_printf("%s %s %s %s\n", un.sysname, un.release, un.version, un.machine);
+ } else {
+ #if defined(GB_SYSTEM_NETBSD)
+ gb_printf("NetBSD: Unknown\n");
+ #else
+ gb_printf("OpenBSD: Unknown\n");
+ #endif
+ }
+ #elif defined(GB_SYSTEM_FREEBSD)
+ #define freebsd_version_buffer 129
+ char buffer[freebsd_version_buffer];
+ size_t buffer_size = freebsd_version_buffer - 1;
+ #undef freebsd_version_buffer
+
+ int mibs[] = { CTL_KERN, KERN_VERSION };
+ if (sysctl(mibs, 2, buffer, &buffer_size, NULL, 0) == -1) {
+ gb_printf("FreeBSD: Unknown\n");
+ } else {
+ // KERN_VERSION can end in a \n, replace it with a \0
+ for (int i = 0; i < buffer_size; i += 1) {
+ if (buffer[i] == '\n') buffer[i] = 0;
+ }
+ gb_printf("%s", &buffer[0]);
+
+ // Retrieve kernel revision using `sysctl`, e.g. 199506
+ mibs[1] = KERN_OSREV;
+ uint64_t revision;
+ size_t revision_size = sizeof(revision);
+
+ if (sysctl(mibs, 2, &revision, &revision_size, NULL, 0) == -1) {
+ gb_printf("\n");
+ } else {
+ gb_printf(", revision %ld\n", revision);
+ }
+ }
+ #else
+ gb_printf("Unknown");
+ #endif
+}
+
+gb_internal void report_backend_info() {
+ gb_printf("\tBackend: LLVM %s\n", LLVM_VERSION_STRING);
+}
+
+// NOTE(Jeroen): `odin report` prints some system information for easier bug reporting.
+gb_internal void print_bug_report_help() {
+ gb_printf("Where to find more information and get into contact when you encounter a bug:\n\n");
+ gb_printf("\tWebsite: https://odin-lang.org\n");
+ gb_printf("\tGitHub: https://github.com/odin-lang/Odin/issues\n");
+ /*
+ Uncomment and update URL once we have a Discord vanity URL. For now people can get here from the site.
+ gb_printf("\tDiscord: https://discord.com/invite/sVBPHEv\n");
+ */
+ gb_printf("\n\n");
+
+ gb_printf("Useful information to add to a bug report:\n\n");
+
+ gb_printf("\tOdin: %.*s", LIT(ODIN_VERSION));
+
+ #ifdef NIGHTLY
+ gb_printf("-nightly");
+ #endif
+
+ #ifdef GIT_SHA
+ gb_printf(":%s", GIT_SHA);
+ #endif
+
+ gb_printf("\n");
+
+ /*
+ Print OS information.
+ */
+ report_os_info();
+
+ /*
+ Now print CPU info.
+ */
+ report_cpu_info();
+
+ /*
+ And RAM info.
+ */
+ report_ram_info();
+
+ report_backend_info();
+}
diff --git a/src/build_cpuid.cpp b/src/build_cpuid.cpp
new file mode 100644
index 000000000..b7ba5dcdf
--- /dev/null
+++ b/src/build_cpuid.cpp
@@ -0,0 +1,35 @@
+#if !defined(GB_COMPILER_MSVC)
+ #if defined(GB_CPU_X86)
+ #include <cpuid.h>
+ #endif
+#endif
+
+gb_internal void odin_cpuid(int leaf, int result[]) {
+ #if defined(GB_CPU_ARM) || defined(GB_CPU_RISCV)
+ return;
+
+ #elif defined(GB_CPU_X86)
+
+ #if defined(GB_COMPILER_MSVC)
+ __cpuid(result, leaf);
+ #else
+ __get_cpuid(leaf, (unsigned int*)&result[0], (unsigned int*)&result[1], (unsigned int*)&result[2], (unsigned int*)&result[3]);
+ #endif
+
+ #endif
+}
+
+gb_internal bool should_use_march_native() {
+ #if !defined(GB_CPU_X86)
+ return false;
+
+ #else
+
+ int cpu[4];
+ odin_cpuid(0x1, &cpu[0]); // Get feature information in ECX + EDX
+
+ bool have_popcnt = cpu[2] & (1 << 23); // bit 23 in ECX = popcnt
+ return !have_popcnt;
+
+ #endif
+} \ No newline at end of file
diff --git a/src/build_settings.cpp b/src/build_settings.cpp
index ffb276d1e..93168cf77 100644
--- a/src/build_settings.cpp
+++ b/src/build_settings.cpp
@@ -2,6 +2,7 @@
#include <sys/types.h>
#include <sys/sysctl.h>
#endif
+#include "build_cpuid.cpp"
// #if defined(GB_SYSTEM_WINDOWS)
// #define DEFAULT_TO_THREADED_CHECKER
@@ -18,9 +19,12 @@ enum TargetOsKind : u16 {
TargetOs_essence,
TargetOs_freebsd,
TargetOs_openbsd,
+ TargetOs_netbsd,
+ TargetOs_haiku,
TargetOs_wasi,
TargetOs_js,
+ TargetOs_orca,
TargetOs_freestanding,
@@ -36,6 +40,7 @@ enum TargetArchKind : u16 {
TargetArch_arm64,
TargetArch_wasm32,
TargetArch_wasm64p32,
+ TargetArch_riscv64,
TargetArch_COUNT,
};
@@ -56,6 +61,24 @@ enum TargetABIKind : u16 {
TargetABI_COUNT,
};
+enum Windows_Subsystem : u8 {
+ Windows_Subsystem_BOOT_APPLICATION,
+ Windows_Subsystem_CONSOLE, // Default,
+ Windows_Subsystem_EFI_APPLICATION,
+ Windows_Subsystem_EFI_BOOT_SERVICE_DRIVER,
+ Windows_Subsystem_EFI_ROM,
+ Windows_Subsystem_EFI_RUNTIME_DRIVER,
+ Windows_Subsystem_NATIVE,
+ Windows_Subsystem_POSIX,
+ Windows_Subsystem_WINDOWS,
+ Windows_Subsystem_WINDOWSCE,
+ Windows_Subsystem_COUNT,
+};
+
+struct MicroarchFeatureList {
+ String microarch;
+ String features;
+};
gb_global String target_os_names[TargetOs_COUNT] = {
str_lit(""),
@@ -65,9 +88,12 @@ gb_global String target_os_names[TargetOs_COUNT] = {
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"),
};
@@ -80,8 +106,11 @@ gb_global String target_arch_names[TargetArch_COUNT] = {
str_lit("arm64"),
str_lit("wasm32"),
str_lit("wasm64p32"),
+ str_lit("riscv64"),
};
+#include "build_settings_microarch.cpp"
+
gb_global String target_endian_names[TargetEndian_COUNT] = {
str_lit("little"),
str_lit("big"),
@@ -103,14 +132,25 @@ 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
gb_global String const ODIN_VERSION = str_lit(ODIN_VERSION_RAW);
-
-
struct TargetMetrics {
TargetOsKind os;
TargetArchKind arch;
@@ -119,7 +159,6 @@ struct TargetMetrics {
isize max_align;
isize max_simd_align;
String target_triplet;
- String target_data_layout;
TargetABIKind abi;
};
@@ -151,6 +190,7 @@ struct QueryDataSetSettings {
enum BuildModeKind {
BuildMode_Executable,
BuildMode_DynamicLibrary,
+ BuildMode_StaticLibrary,
BuildMode_Object,
BuildMode_Assembly,
BuildMode_LLVM_IR,
@@ -198,6 +238,12 @@ enum TimingsExportFormat : i32 {
TimingsExportCSV = 2,
};
+enum DependenciesExportFormat : i32 {
+ DependenciesExportUnspecified = 0,
+ DependenciesExportMake = 1,
+ DependenciesExportJson = 2,
+};
+
enum ErrorPosStyle {
ErrorPosStyle_Default, // path(line:column) msg
ErrorPosStyle_Unix, // path:line:column: msg
@@ -229,15 +275,22 @@ enum BuildPath : u8 {
};
enum VetFlags : u64 {
- VetFlag_NONE = 0,
- VetFlag_Unused = 1u<<0, // 1
- VetFlag_Shadowing = 1u<<1, // 2
- VetFlag_UsingStmt = 1u<<2, // 4
- VetFlag_UsingParam = 1u<<3, // 8
- VetFlag_Style = 1u<<4, // 16
- VetFlag_Semicolon = 1u<<5, // 32
-
- VetFlag_All = VetFlag_Unused|VetFlag_Shadowing|VetFlag_UsingStmt,
+ VetFlag_NONE = 0,
+ VetFlag_Shadowing = 1u<<0,
+ VetFlag_UsingStmt = 1u<<1,
+ VetFlag_UsingParam = 1u<<2,
+ VetFlag_Style = 1u<<3,
+ VetFlag_Semicolon = 1u<<4,
+ VetFlag_UnusedVariables = 1u<<5,
+ VetFlag_UnusedImports = 1u<<6,
+ VetFlag_Deprecated = 1u<<7,
+ VetFlag_Cast = 1u<<8,
+ VetFlag_Tabs = 1u<<9,
+ VetFlag_UnusedProcedures = 1u<<10,
+
+ VetFlag_Unused = VetFlag_UnusedVariables|VetFlag_UnusedImports,
+
+ VetFlag_All = VetFlag_Unused|VetFlag_Shadowing|VetFlag_UsingStmt|VetFlag_Deprecated|VetFlag_Cast,
VetFlag_Using = VetFlag_UsingStmt|VetFlag_UsingParam,
};
@@ -245,6 +298,10 @@ enum VetFlags : u64 {
u64 get_vet_flag_from_name(String const &name) {
if (name == "unused") {
return VetFlag_Unused;
+ } else if (name == "unused-variables") {
+ return VetFlag_UnusedVariables;
+ } else if (name == "unused-imports") {
+ return VetFlag_UnusedImports;
} else if (name == "shadowing") {
return VetFlag_Shadowing;
} else if (name == "using-stmt") {
@@ -255,10 +312,30 @@ u64 get_vet_flag_from_name(String const &name) {
return VetFlag_Style;
} else if (name == "semicolon") {
return VetFlag_Semicolon;
+ } else if (name == "deprecated") {
+ return VetFlag_Deprecated;
+ } else if (name == "cast") {
+ return VetFlag_Cast;
+ } else if (name == "tabs") {
+ return VetFlag_Tabs;
+ } else if (name == "unused-procedures") {
+ return VetFlag_UnusedProcedures;
}
return VetFlag_NONE;
}
+enum OptInFeatureFlags : u64 {
+ OptInFeatureFlag_NONE = 0,
+ OptInFeatureFlag_DynamicLiterals = 1u<<0,
+};
+
+u64 get_feature_flag_from_name(String const &name) {
+ if (name == "dynamic-literals") {
+ return OptInFeatureFlag_DynamicLiterals;
+ }
+ return OptInFeatureFlag_NONE;
+}
+
enum SanitizerFlags : u32 {
SanitizerFlag_NONE = 0,
@@ -267,20 +344,48 @@ enum SanitizerFlags : u32 {
SanitizerFlag_Thread = 1u<<2,
};
+struct BuildCacheData {
+ u64 crc;
+ String cache_dir;
+
+ // manifests
+ String files_path;
+ String args_path;
+ String env_path;
+
+ bool copy_already_done;
+};
+enum LinkerChoice : i32 {
+ Linker_Invalid = -1,
+ Linker_Default = 0,
+ Linker_lld,
+ Linker_radlink,
+
+ Linker_COUNT,
+};
+
+String linker_choices[Linker_COUNT] = {
+ str_lit("default"),
+ str_lit("lld"),
+ str_lit("radlink"),
+};
+
// This stores the information for the specify architecture of this build
struct BuildContext {
// Constants
- String ODIN_OS; // target operating system
- String ODIN_ARCH; // target architecture
- String ODIN_VENDOR; // compiler vendor
- String ODIN_VERSION; // compiler version
- String ODIN_ROOT; // Odin ROOT
- String ODIN_BUILD_PROJECT_NAME; // Odin main/initial package's directory name
- bool ODIN_DEBUG; // Odin in debug mode
- bool ODIN_DISABLE_ASSERT; // Whether the default 'assert' et al is disabled in code or not
+ String ODIN_OS; // Target operating system
+ String ODIN_ARCH; // Target architecture
+ String ODIN_VENDOR; // Compiler vendor
+ String ODIN_VERSION; // Compiler version
+ String ODIN_ROOT; // Odin ROOT
+ String ODIN_BUILD_PROJECT_NAME; // Odin main/initial package's directory name
+ String ODIN_WINDOWS_SUBSYSTEM; // Empty string for non-Windows targets
+ bool ODIN_DEBUG; // Odin in debug mode
+ bool ODIN_DISABLE_ASSERT; // Whether the default 'assert' et al is disabled in code or not
bool ODIN_DEFAULT_TO_NIL_ALLOCATOR; // Whether the default allocator is a "nil" allocator or not (i.e. it does nothing)
+ bool ODIN_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;
@@ -310,6 +415,7 @@ struct BuildContext {
u64 vet_flags;
u32 sanitizer_flags;
+ StringSet vet_packages;
bool has_resource;
String link_flags;
@@ -323,49 +429,73 @@ struct BuildContext {
bool show_timings;
TimingsExportFormat export_timings_format;
String export_timings_file;
+ DependenciesExportFormat export_dependencies_format;
+ String export_dependencies_file;
bool show_unused;
bool show_unused_with_location;
bool show_more_timings;
+ bool show_defineables;
+ String export_defineables_file;
bool show_system_calls;
bool keep_temp_files;
bool ignore_unknown_attributes;
bool no_bounds_check;
- bool no_dynamic_literals;
+ bool no_type_assert;
bool no_output_files;
bool no_crt;
+ bool no_rpath;
bool no_entry_point;
bool no_thread_local;
- bool use_lld;
bool cross_compiling;
bool different_os;
bool keep_object_files;
bool disallow_do;
+ LinkerChoice linker_choice;
+
+ StringSet custom_attributes;
+
bool strict_style;
bool ignore_warnings;
bool warnings_as_errors;
bool hide_error_line;
+ bool terse_errors;
+ bool json_errors;
bool has_ansi_terminal_colours;
+ bool fast_isel;
bool ignore_lazy;
bool ignore_llvm_build;
+ bool ignore_panic;
- bool use_subsystem_windows;
bool ignore_microsoft_magic;
bool linker_map_file;
bool use_separate_modules;
+ bool module_per_file;
+ bool cached;
+ BuildCacheData build_cache_data;
+
+ bool internal_no_inline;
+ bool internal_by_value;
+
bool no_threaded_checker;
bool show_debug_messages;
-
+
bool copy_file_contents;
bool no_rtti;
bool dynamic_map_calls;
+ bool obfuscate_source_code_locations;
+
+ bool min_link_libs;
+
+ bool print_linker_flags;
+
RelocMode reloc_mode;
bool disable_red_zone;
@@ -377,17 +507,19 @@ struct BuildContext {
u32 cmd_doc_flags;
Array<String> extra_packages;
- StringSet test_names;
+ bool test_all_packages;
gbAffinity affinity;
isize thread_count;
PtrMap<char const *, ExactValue> defined_values;
- BlockingMutex target_features_mutex;
StringSet target_features_set;
String target_features_string;
+ bool strict_target_features;
+
String minimum_os_version_string;
+ bool minimum_os_version_string_given;
};
gb_global BuildContext build_context = {0};
@@ -411,95 +543,136 @@ 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
+#else
+ #define AMD64_MAX_ALIGNMENT 8
+#endif
+
+#if LLVM_VERSION_MAJOR >= 18
+ #define I386_MAX_ALIGNMENT 16
+#else
+ #define I386_MAX_ALIGNMENT 4
+#endif
gb_global TargetMetrics target_windows_i386 = {
TargetOs_windows,
TargetArch_i386,
- 4, 4, 4, 8,
+ 4, 4, I386_MAX_ALIGNMENT, 16,
str_lit("i386-pc-windows-msvc"),
};
gb_global TargetMetrics target_windows_amd64 = {
TargetOs_windows,
TargetArch_amd64,
- 8, 8, 8, 16,
+ 8, 8, AMD64_MAX_ALIGNMENT, 32,
str_lit("x86_64-pc-windows-msvc"),
- str_lit("e-m:w-i64:64-f80:128-n8:16:32:64-S128"),
};
gb_global TargetMetrics target_linux_i386 = {
TargetOs_linux,
TargetArch_i386,
- 4, 4, 4, 8,
+ 4, 4, I386_MAX_ALIGNMENT, 16,
str_lit("i386-pc-linux-gnu"),
-
};
gb_global TargetMetrics target_linux_amd64 = {
TargetOs_linux,
TargetArch_amd64,
- 8, 8, 8, 16,
+ 8, 8, AMD64_MAX_ALIGNMENT, 32,
str_lit("x86_64-pc-linux-gnu"),
- str_lit("e-m:w-i64:64-f80:128-n8:16:32:64-S128"),
};
gb_global TargetMetrics target_linux_arm64 = {
TargetOs_linux,
TargetArch_arm64,
- 8, 8, 8, 16,
+ 8, 8, 16, 32,
str_lit("aarch64-linux-elf"),
- str_lit("e-m:o-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"),
};
-
gb_global TargetMetrics target_linux_arm32 = {
TargetOs_linux,
TargetArch_arm32,
- 4, 4, 4, 8,
- str_lit("arm-linux-gnu"),
- str_lit("e-m:o-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"),
+ 4, 4, 8, 16,
+ str_lit("arm-unknown-linux-gnueabihf"),
+};
+gb_global TargetMetrics target_linux_riscv64 = {
+ TargetOs_linux,
+ TargetArch_riscv64,
+ 8, 8, 16, 32,
+ str_lit("riscv64-linux-gnu"),
};
gb_global TargetMetrics target_darwin_amd64 = {
TargetOs_darwin,
TargetArch_amd64,
- 8, 8, 8, 16,
- str_lit("x86_64-apple-darwin"),
- str_lit("e-m:o-i64:64-f80:128-n8:16:32:64-S128"),
+ 8, 8, AMD64_MAX_ALIGNMENT, 32,
+ str_lit("x86_64-apple-macosx"), // NOTE: Changes during initialization based on build flags.
};
gb_global TargetMetrics target_darwin_arm64 = {
TargetOs_darwin,
TargetArch_arm64,
- 8, 8, 8, 16,
- str_lit("arm64-apple-macosx11.0.0"),
- str_lit("e-m:o-i64:64-i128:128-n32:64-S128"),
+ 8, 8, 16, 32,
+ str_lit("arm64-apple-macosx"), // NOTE: Changes during initialization based on build flags.
};
gb_global TargetMetrics target_freebsd_i386 = {
TargetOs_freebsd,
TargetArch_i386,
- 4, 4, 4, 8,
+ 4, 4, I386_MAX_ALIGNMENT, 16,
str_lit("i386-unknown-freebsd-elf"),
};
gb_global TargetMetrics target_freebsd_amd64 = {
TargetOs_freebsd,
TargetArch_amd64,
- 8, 8, 8, 16,
+ 8, 8, AMD64_MAX_ALIGNMENT, 32,
str_lit("x86_64-unknown-freebsd-elf"),
- str_lit("e-m:w-i64:64-f80:128-n8:16:32:64-S128"),
+};
+
+gb_global TargetMetrics target_freebsd_arm64 = {
+ TargetOs_freebsd,
+ TargetArch_arm64,
+ 8, 8, 16, 32,
+ str_lit("aarch64-unknown-freebsd-elf"),
};
gb_global TargetMetrics target_openbsd_amd64 = {
TargetOs_openbsd,
TargetArch_amd64,
- 8, 8, 8, 16,
+ 8, 8, AMD64_MAX_ALIGNMENT, 32,
str_lit("x86_64-unknown-openbsd-elf"),
- str_lit("e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"),
+};
+
+gb_global TargetMetrics target_netbsd_amd64 = {
+ TargetOs_netbsd,
+ TargetArch_amd64,
+ 8, 8, AMD64_MAX_ALIGNMENT, 32,
+ str_lit("x86_64-unknown-netbsd-elf"),
+};
+
+gb_global TargetMetrics target_netbsd_arm64 = {
+ TargetOs_netbsd,
+ TargetArch_arm64,
+ 8, 8, 16, 32,
+ str_lit("aarch64-unknown-netbsd-elf"),
+};
+
+gb_global TargetMetrics target_haiku_amd64 = {
+ TargetOs_haiku,
+ TargetArch_amd64,
+ 8, 8, AMD64_MAX_ALIGNMENT, 32,
+ str_lit("x86_64-unknown-haiku"),
};
gb_global TargetMetrics target_essence_amd64 = {
TargetOs_essence,
TargetArch_amd64,
- 8, 8, 8, 16,
+ 8, 8, AMD64_MAX_ALIGNMENT, 32,
str_lit("x86_64-pc-none-elf"),
};
@@ -509,7 +682,6 @@ gb_global TargetMetrics target_freestanding_wasm32 = {
TargetArch_wasm32,
4, 4, 8, 16,
str_lit("wasm32-freestanding-js"),
- str_lit("e-m:e-p:32:32-i64:64-n32:64-S128"),
};
gb_global TargetMetrics target_js_wasm32 = {
@@ -517,7 +689,6 @@ gb_global TargetMetrics target_js_wasm32 = {
TargetArch_wasm32,
4, 4, 8, 16,
str_lit("wasm32-js-js"),
- str_lit("e-m:e-p:32:32-i64:64-n32:64-S128"),
};
gb_global TargetMetrics target_wasi_wasm32 = {
@@ -525,7 +696,14 @@ gb_global TargetMetrics target_wasi_wasm32 = {
TargetArch_wasm32,
4, 4, 8, 16,
str_lit("wasm32-wasi-js"),
- str_lit("e-m:e-p:32:32-i64:64-n32:64-S128"),
+};
+
+
+gb_global TargetMetrics target_orca_wasm32 = {
+ TargetOs_orca,
+ TargetArch_wasm32,
+ 4, 4, 8, 16,
+ str_lit("wasm32-wasi-js"),
};
@@ -534,7 +712,6 @@ gb_global TargetMetrics target_freestanding_wasm64p32 = {
TargetArch_wasm64p32,
4, 8, 8, 16,
str_lit("wasm32-freestanding-js"),
- str_lit("e-m:e-p:32:32-i64:64-n32:64-S128"),
};
gb_global TargetMetrics target_js_wasm64p32 = {
@@ -542,7 +719,6 @@ gb_global TargetMetrics target_js_wasm64p32 = {
TargetArch_wasm64p32,
4, 8, 8, 16,
str_lit("wasm32-js-js"),
- str_lit("e-m:e-p:32:32-i64:64-n32:64-S128"),
};
gb_global TargetMetrics target_wasi_wasm64p32 = {
@@ -550,7 +726,6 @@ gb_global TargetMetrics target_wasi_wasm64p32 = {
TargetArch_wasm32,
4, 8, 8, 16,
str_lit("wasm32-wasi-js"),
- str_lit("e-m:e-p:32:32-i64:64-n32:64-S128"),
};
@@ -558,12 +733,38 @@ gb_global TargetMetrics target_wasi_wasm64p32 = {
gb_global TargetMetrics target_freestanding_amd64_sysv = {
TargetOs_freestanding,
TargetArch_amd64,
- 8, 8, 8, 16,
+ 8, 8, AMD64_MAX_ALIGNMENT, 32,
str_lit("x86_64-pc-none-gnu"),
- str_lit("e-m:w-i64:64-f80:128-n8:16:32:64-S128"),
TargetABI_SysV,
};
+gb_global TargetMetrics target_freestanding_amd64_win64 = {
+ TargetOs_freestanding,
+ TargetArch_amd64,
+ 8, 8, AMD64_MAX_ALIGNMENT, 32,
+ str_lit("x86_64-pc-none-msvc"),
+ TargetABI_Win64,
+};
+
+gb_global TargetMetrics target_freestanding_arm64 = {
+ TargetOs_freestanding,
+ TargetArch_arm64,
+ 8, 8, 16, 32,
+ str_lit("aarch64-none-elf"),
+};
+
+gb_global TargetMetrics target_freestanding_arm32 = {
+ TargetOs_freestanding,
+ TargetArch_arm32,
+ 4, 4, 8, 16,
+ str_lit("arm-unknown-unknown-gnueabihf"),
+};
+gb_global TargetMetrics target_freestanding_riscv64 = {
+ TargetOs_freestanding,
+ TargetArch_riscv64,
+ 8, 8, 16, 32,
+ str_lit("riscv64-unknown-gnu"),
+};
struct NamedTargetMetrics {
@@ -581,24 +782,37 @@ gb_global NamedTargetMetrics named_targets[] = {
{ str_lit("linux_amd64"), &target_linux_amd64 },
{ str_lit("linux_arm64"), &target_linux_arm64 },
{ str_lit("linux_arm32"), &target_linux_arm32 },
+ { str_lit("linux_riscv64"), &target_linux_riscv64 },
{ str_lit("windows_i386"), &target_windows_i386 },
{ str_lit("windows_amd64"), &target_windows_amd64 },
{ str_lit("freebsd_i386"), &target_freebsd_i386 },
{ str_lit("freebsd_amd64"), &target_freebsd_amd64 },
+ { str_lit("freebsd_arm64"), &target_freebsd_arm64 },
+
+ { str_lit("netbsd_amd64"), &target_netbsd_amd64 },
+ { str_lit("netbsd_arm64"), &target_netbsd_arm64 },
{ str_lit("openbsd_amd64"), &target_openbsd_amd64 },
+ { str_lit("haiku_amd64"), &target_haiku_amd64 },
{ str_lit("freestanding_wasm32"), &target_freestanding_wasm32 },
{ str_lit("wasi_wasm32"), &target_wasi_wasm32 },
{ str_lit("js_wasm32"), &target_js_wasm32 },
+ { str_lit("orca_wasm32"), &target_orca_wasm32 },
{ str_lit("freestanding_wasm64p32"), &target_freestanding_wasm64p32 },
{ str_lit("js_wasm64p32"), &target_js_wasm64p32 },
{ str_lit("wasi_wasm64p32"), &target_wasi_wasm64p32 },
- { str_lit("freestanding_amd64_sysv"), &target_freestanding_amd64_sysv },
+ { str_lit("freestanding_amd64_sysv"), &target_freestanding_amd64_sysv },
+ { str_lit("freestanding_amd64_win64"), &target_freestanding_amd64_win64 },
+
+ { str_lit("freestanding_arm64"), &target_freestanding_arm64 },
+ { str_lit("freestanding_arm32"), &target_freestanding_arm32 },
+
+ { str_lit("freestanding_riscv64"), &target_freestanding_riscv64 },
};
gb_global NamedTargetMetrics *selected_target_metrics;
@@ -623,7 +837,6 @@ gb_internal TargetArchKind get_target_arch_from_string(String str) {
return TargetArch_Invalid;
}
-
gb_internal bool is_excluded_target_filename(String name) {
String original_name = name;
name = remove_extension_from_path(name);
@@ -722,15 +935,6 @@ gb_internal bool is_arch_x86(void) {
return false;
}
-gb_internal bool allow_check_foreign_filepath(void) {
- switch (build_context.metrics.arch) {
- case TargetArch_wasm32:
- case TargetArch_wasm64p32:
- return false;
- }
- return true;
-}
-
// TODO(bill): OS dependent versions for the BuildContext
// join_path
// is_dir
@@ -754,13 +958,11 @@ gb_internal String odin_root_dir(void) {
char const *found = gb_get_env("ODIN_ROOT", a);
if (found) {
String path = path_to_full_path(a, make_string_c(found));
- if (path[path.len-1] != '/' && path[path.len-1] != '\\') {
#if defined(GB_SYSTEM_WINDOWS)
- path = concatenate_strings(a, path, WIN32_SEPARATOR_STRING);
+ path = normalize_path(a, path, WIN32_SEPARATOR_STRING);
#else
- path = concatenate_strings(a, path, NIX_SEPARATOR_STRING);
+ path = normalize_path(a, path, NIX_SEPARATOR_STRING);
#endif
- }
global_module_path = path;
global_module_path_set = true;
@@ -820,11 +1022,63 @@ gb_internal String internal_odin_root_dir(void) {
return path;
}
+#elif defined(GB_SYSTEM_HAIKU)
+
+#include <FindDirectory.h>
+
+gb_internal String path_to_fullpath(gbAllocator a, String s, bool *ok_);
+
+gb_internal String internal_odin_root_dir(void) {
+ String path = global_module_path;
+ isize len, i;
+ u8 *text;
+
+ if (global_module_path_set) {
+ return global_module_path;
+ }
+
+ auto path_buf = array_make<char>(heap_allocator(), 300);
+ defer (array_free(&path_buf));
+
+ len = 0;
+ for (;;) {
+ u32 sz = path_buf.count;
+ int res = find_path(B_APP_IMAGE_SYMBOL, B_FIND_PATH_IMAGE_PATH, nullptr, &path_buf[0], sz);
+ if(res == B_OK) {
+ len = sz;
+ break;
+ } else {
+ array_resize(&path_buf, sz + 1);
+ }
+ }
+
+ mutex_lock(&string_buffer_mutex);
+ defer (mutex_unlock(&string_buffer_mutex));
+
+ text = gb_alloc_array(permanent_allocator(), u8, len + 1);
+ gb_memmove(text, &path_buf[0], len);
+
+ path = path_to_fullpath(heap_allocator(), make_string(text, len), nullptr);
+
+ for (i = path.len-1; i >= 0; i--) {
+ u8 c = path[i];
+ if (c == '/' || c == '\\') {
+ break;
+ }
+ path.len--;
+ }
+
+ global_module_path = path;
+ global_module_path_set = true;
+
+ return path;
+}
+
#elif defined(GB_SYSTEM_OSX)
#include <mach-o/dyld.h>
-gb_internal String path_to_fullpath(gbAllocator a, String s);
+gb_internal String path_to_fullpath(gbAllocator a, String s, bool *ok_);
gb_internal String internal_odin_root_dir(void) {
String path = global_module_path;
@@ -836,6 +1090,7 @@ gb_internal String internal_odin_root_dir(void) {
}
auto path_buf = array_make<char>(heap_allocator(), 300);
+ defer (array_free(&path_buf));
len = 0;
for (;;) {
@@ -855,7 +1110,7 @@ gb_internal String internal_odin_root_dir(void) {
text = gb_alloc_array(permanent_allocator(), u8, len + 1);
gb_memmove(text, &path_buf[0], len);
- path = path_to_fullpath(heap_allocator(), make_string(text, len));
+ path = path_to_fullpath(heap_allocator(), make_string(text, len), nullptr);
for (i = path.len-1; i >= 0; i--) {
u8 c = path[i];
@@ -868,9 +1123,6 @@ gb_internal String internal_odin_root_dir(void) {
global_module_path = path;
global_module_path_set = true;
-
- // array_free(&path_buf);
-
return path;
}
#else
@@ -878,7 +1130,7 @@ gb_internal String internal_odin_root_dir(void) {
// NOTE: Linux / Unix is unfinished and not tested very well.
#include <sys/stat.h>
-gb_internal String path_to_fullpath(gbAllocator a, String s);
+gb_internal String path_to_fullpath(gbAllocator a, String s, bool *ok_);
gb_internal String internal_odin_root_dir(void) {
String path = global_module_path;
@@ -1020,7 +1272,7 @@ gb_internal String internal_odin_root_dir(void) {
gb_memmove(text, &path_buf[0], len);
- path = path_to_fullpath(heap_allocator(), make_string(text, len));
+ path = path_to_fullpath(heap_allocator(), make_string(text, len), nullptr);
for (i = path.len-1; i >= 0; i--) {
u8 c = path[i];
if (c == '/' || c == '\\') {
@@ -1039,7 +1291,7 @@ gb_internal String internal_odin_root_dir(void) {
gb_global BlockingMutex fullpath_mutex;
#if defined(GB_SYSTEM_WINDOWS)
-gb_internal String path_to_fullpath(gbAllocator a, String s) {
+gb_internal String path_to_fullpath(gbAllocator a, String s, bool *ok_) {
String result = {};
String16 string16 = string_to_string16(heap_allocator(), s);
@@ -1065,27 +1317,70 @@ gb_internal String path_to_fullpath(gbAllocator a, String s) {
result.text[i] = '/';
}
}
+ if (ok_) *ok_ = true;
} else {
+ if (ok_) *ok_ = false;
mutex_unlock(&fullpath_mutex);
}
return result;
}
#elif defined(GB_SYSTEM_OSX) || defined(GB_SYSTEM_UNIX)
-gb_internal String path_to_fullpath(gbAllocator a, String s) {
+
+struct PathToFullpathResult {
+ String result;
+ bool ok;
+};
+
+gb_internal String path_to_fullpath(gbAllocator a, String s, bool *ok_) {
+ static gb_thread_local StringMap<PathToFullpathResult> cache;
+
+ PathToFullpathResult *cached = string_map_get(&cache, s);
+ if (cached != nullptr) {
+ if (ok_) *ok_ = cached->ok;
+ return copy_string(a, cached->result);
+ }
+
char *p;
- mutex_lock(&fullpath_mutex);
p = realpath(cast(char *)s.text, 0);
- mutex_unlock(&fullpath_mutex);
- if(p == nullptr) return String{};
- return make_string_c(p);
+ defer (free(p));
+ if(p == nullptr) {
+ if (ok_) *ok_ = false;
+
+ // Path doesn't exist or is malformed, Windows's `GetFullPathNameW` does not check for
+ // existence of the file where `realpath` does, which causes different behaviour between platforms.
+ // Two things could be done here:
+ // 1. clean the path and resolve it manually, just like the Windows function does,
+ // probably requires porting `filepath.clean` from Odin and doing some more processing.
+ // 2. just return a copy of the original path.
+ //
+ // I have opted for 2 because it is much simpler + we already return `ok = false` + further
+ // checks and processes will use the path and cause errors (which we want).
+ String result = copy_string(a, s);
+
+ PathToFullpathResult cached_result = {};
+ cached_result.result = copy_string(permanent_allocator(), result);
+ cached_result.ok = false;
+ string_map_set(&cache, copy_string(permanent_allocator(), s), cached_result);
+
+ return result;
+ }
+ if (ok_) *ok_ = true;
+ String result = copy_string(a, make_string_c(p));
+
+ PathToFullpathResult cached_result = {};
+ cached_result.result = copy_string(permanent_allocator(), result);
+ cached_result.ok = true;
+ string_map_set(&cache, copy_string(permanent_allocator(), s), cached_result);
+
+ return result;
}
#else
#error Implement system
#endif
-gb_internal String get_fullpath_relative(gbAllocator a, String base_dir, String path) {
+gb_internal String get_fullpath_relative(gbAllocator a, String base_dir, String path, bool *ok_) {
u8 *str = gb_alloc_array(heap_allocator(), u8, base_dir.len+1+path.len+1);
defer (gb_free(heap_allocator(), str));
@@ -1107,11 +1402,31 @@ gb_internal String get_fullpath_relative(gbAllocator a, String base_dir, String
String res = make_string(str, i);
res = string_trim_whitespace(res);
- return path_to_fullpath(a, res);
+ return path_to_fullpath(a, res, ok_);
}
-gb_internal String get_fullpath_core(gbAllocator a, String path) {
+gb_internal String get_fullpath_base_collection(gbAllocator a, String path, bool *ok_) {
+ String module_dir = odin_root_dir();
+
+ String base = str_lit("base/");
+
+ isize str_len = module_dir.len + base.len + path.len;
+ u8 *str = gb_alloc_array(heap_allocator(), u8, str_len+1);
+ defer (gb_free(heap_allocator(), str));
+
+ isize i = 0;
+ gb_memmove(str+i, module_dir.text, module_dir.len); i += module_dir.len;
+ gb_memmove(str+i, base.text, base.len); i += base.len;
+ gb_memmove(str+i, path.text, path.len); i += path.len;
+ str[i] = 0;
+
+ String res = make_string(str, i);
+ res = string_trim_whitespace(res);
+ return path_to_fullpath(a, res, ok_);
+}
+
+gb_internal String get_fullpath_core_collection(gbAllocator a, String path, bool *ok_) {
String module_dir = odin_root_dir();
String core = str_lit("core/");
@@ -1128,14 +1443,21 @@ gb_internal String get_fullpath_core(gbAllocator a, String path) {
String res = make_string(str, i);
res = string_trim_whitespace(res);
- return path_to_fullpath(a, res);
+ return path_to_fullpath(a, res, ok_);
}
gb_internal bool show_error_line(void) {
- return !build_context.hide_error_line;
+ return !build_context.hide_error_line && !build_context.json_errors;
+}
+
+gb_internal bool terse_errors(void) {
+ return build_context.terse_errors;
+}
+gb_internal bool json_errors(void) {
+ return build_context.json_errors;
}
gb_internal bool has_ansi_terminal_colours(void) {
- return build_context.has_ansi_terminal_colours;
+ return build_context.has_ansi_terminal_colours && !json_errors();
}
gb_internal bool has_asm_extension(String const &path) {
@@ -1220,14 +1542,38 @@ gb_internal void init_build_context(TargetMetrics *cross_target, Subtarget subta
metrics = &target_darwin_amd64;
#endif
#elif defined(GB_SYSTEM_FREEBSD)
- metrics = &target_freebsd_amd64;
+ #if defined(GB_CPU_ARM)
+ metrics = &target_freebsd_arm64;
+ #else
+ metrics = &target_freebsd_amd64;
+ #endif
#elif defined(GB_SYSTEM_OPENBSD)
metrics = &target_openbsd_amd64;
+ #elif defined(GB_SYSTEM_NETBSD)
+ #if defined(GB_CPU_ARM)
+ metrics = &target_netbsd_arm64;
+ #else
+ metrics = &target_netbsd_amd64;
+ #endif
+ #elif defined(GB_SYSTEM_HAIKU)
+ metrics = &target_haiku_amd64;
#elif defined(GB_CPU_ARM)
metrics = &target_linux_arm64;
+ #elif defined(GB_CPU_RISCV)
+ metrics = &target_linux_riscv64;
#else
metrics = &target_linux_amd64;
#endif
+ #elif defined(GB_CPU_ARM)
+ #if defined(GB_SYSTEM_WINDOWS)
+ #error "Build Error: Unsupported architecture"
+ #elif defined(GB_SYSTEM_OSX)
+ #error "Build Error: Unsupported architecture"
+ #elif defined(GB_SYSTEM_FREEBSD)
+ #error "Build Error: Unsupported architecture"
+ #else
+ metrics = &target_linux_arm32;
+ #endif
#else
#if defined(GB_SYSTEM_WINDOWS)
metrics = &target_windows_i386;
@@ -1258,32 +1604,16 @@ gb_internal void init_build_context(TargetMetrics *cross_target, Subtarget subta
GB_ASSERT(metrics->int_size == 2*metrics->ptr_size);
}
-
-
bc->metrics = *metrics;
- switch (subtarget) {
- case Subtarget_Default:
- break;
- case Subtarget_iOS:
- GB_ASSERT(metrics->os == TargetOs_darwin);
- if (metrics->arch == TargetArch_arm64) {
- bc->metrics.target_triplet = str_lit("arm64-apple-ios");
- } else if (metrics->arch == TargetArch_amd64) {
- bc->metrics.target_triplet = str_lit("x86_64-apple-ios");
- } else {
- GB_PANIC("Unknown architecture for darwin");
- }
- break;
- }
- bc->ODIN_OS = target_os_names[metrics->os];
- bc->ODIN_ARCH = target_arch_names[metrics->arch];
- bc->endian_kind = target_endians[metrics->arch];
- bc->ptr_size = metrics->ptr_size;
- bc->int_size = metrics->int_size;
- bc->max_align = metrics->max_align;
- bc->max_simd_align = metrics->max_simd_align;
- bc->link_flags = str_lit(" ");
+ bc->ODIN_OS = target_os_names[metrics->os];
+ bc->ODIN_ARCH = target_arch_names[metrics->arch];
+ bc->endian_kind = target_endians[metrics->arch];
+ bc->ptr_size = metrics->ptr_size;
+ bc->int_size = metrics->int_size;
+ bc->max_align = metrics->max_align;
+ bc->max_simd_align = metrics->max_simd_align;
+ bc->link_flags = str_lit(" ");
#if defined(DEFAULT_TO_THREADED_CHECKER)
bc->threaded_checker = true;
@@ -1305,90 +1635,101 @@ gb_internal void init_build_context(TargetMetrics *cross_target, Subtarget subta
}
}
- // NOTE(zangent): The linker flags to set the build architecture are different
- // across OSs. It doesn't make sense to allocate extra data on the heap
- // here, so I just #defined the linker flags to keep things concise.
- if (bc->metrics.arch == TargetArch_amd64) {
- switch (bc->metrics.os) {
- case TargetOs_windows:
- bc->link_flags = str_lit("/machine:x64 ");
- break;
- case TargetOs_darwin:
- break;
- case TargetOs_linux:
- bc->link_flags = str_lit("-arch x86-64 ");
- break;
- case TargetOs_freebsd:
- bc->link_flags = str_lit("-arch x86-64 ");
- break;
- case TargetOs_openbsd:
- bc->link_flags = str_lit("-arch x86-64 ");
- break;
- }
- } else if (bc->metrics.arch == TargetArch_i386) {
- switch (bc->metrics.os) {
- case TargetOs_windows:
- bc->link_flags = str_lit("/machine:x86 ");
- break;
- case TargetOs_darwin:
- gb_printf_err("Unsupported architecture\n");
- gb_exit(1);
- break;
- case TargetOs_linux:
- bc->link_flags = str_lit("-arch x86 ");
- break;
- case TargetOs_freebsd:
- bc->link_flags = str_lit("-arch x86 ");
+ // 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 (metrics->os == TargetOs_darwin && subtarget == Subtarget_iOS) {
+ switch (metrics->arch) {
+ case TargetArch_arm64:
+ bc->metrics.target_triplet = str_lit("arm64-apple-ios");
break;
- }
- } else if (bc->metrics.arch == TargetArch_arm32) {
- switch (bc->metrics.os) {
- case TargetOs_linux:
- bc->link_flags = str_lit("-arch arm ");
+ case TargetArch_amd64:
+ bc->metrics.target_triplet = str_lit("x86_64-apple-ios");
break;
default:
- gb_printf_err("Compiler Error: Unsupported architecture\n");
- gb_exit(1);
+ GB_PANIC("Unknown architecture for darwin");
}
- } else if (bc->metrics.arch == TargetArch_arm64) {
- switch (bc->metrics.os) {
- case TargetOs_darwin:
- bc->link_flags = str_lit("-arch arm64 ");
+ }
+
+ if (bc->metrics.os == TargetOs_windows) {
+ switch (bc->metrics.arch) {
+ case TargetArch_amd64:
+ bc->link_flags = str_lit("/machine:x64 ");
break;
- case TargetOs_linux:
- bc->link_flags = str_lit("-arch aarch64 ");
+ case TargetArch_i386:
+ bc->link_flags = str_lit("/machine:x86 ");
break;
}
+ } else if (bc->metrics.os == TargetOs_darwin) {
+ bc->link_flags = concatenate3_strings(permanent_allocator(),
+ str_lit("-target "), bc->metrics.target_triplet, str_lit(" "));
} else if (is_arch_wasm()) {
gbString link_flags = gb_string_make(heap_allocator(), " ");
// link_flags = gb_string_appendc(link_flags, "--export-all ");
// link_flags = gb_string_appendc(link_flags, "--export-table ");
- link_flags = gb_string_appendc(link_flags, "--allow-undefined ");
// if (bc->metrics.arch == TargetArch_wasm64) {
// link_flags = gb_string_appendc(link_flags, "-mwasm64 ");
// }
- if (bc->no_entry_point) {
+ if (bc->metrics.os != TargetOs_orca) {
+ link_flags = gb_string_appendc(link_flags, "--allow-undefined ");
+ }
+ if (bc->no_entry_point || bc->metrics.os == TargetOs_orca) {
link_flags = gb_string_appendc(link_flags, "--no-entry ");
}
-
+
bc->link_flags = make_string_c(link_flags);
-
+
// Disallow on wasm
bc->use_separate_modules = false;
+ } if(bc->metrics.arch == TargetArch_riscv64 && bc->cross_compiling) {
+ bc->link_flags = str_lit("-target riscv64 ");
} else {
- gb_printf_err("Compiler Error: Unsupported architecture\n");
- gb_exit(1);
+ // NOTE: for targets other than darwin, we don't specify a `-target` link flag.
+ // This is because we don't support cross-linking and clang is better at figuring
+ // out what the actual target for linking is,
+ // for example, on x86/alpine/musl it HAS to be `x86_64-alpine-linux-musl` to link correctly.
+ //
+ // Note that codegen will still target the triplet we specify, but the intricate details of
+ // a target shouldn't matter as much to codegen (if it does at all) as it does to linking.
}
- if (bc->ODIN_DEBUG && !bc->custom_optimization_level) {
+ // NOTE: needs to be done after adding the -target flag to the linker flags so the linker
+ // 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->metrics.target_triplet = concatenate_strings(permanent_allocator(), bc->metrics.target_triplet, bc->minimum_os_version_string);
+ }
+ }
+
+ if (!bc->custom_optimization_level) {
// NOTE(bill): when building with `-debug` but not specifying an optimization level
// default to `-o:none` to improve the debug symbol generation by default
- bc->optimization_level = -1; // -o:none
+ if (bc->ODIN_DEBUG) {
+ bc->optimization_level = -1; // -o:none
+ } else {
+ bc->optimization_level = 0; // -o:minimal
+ }
}
bc->optimization_level = gb_clamp(bc->optimization_level, -1, 3);
- if (bc->metrics.os != TargetOs_windows) {
+#if defined(GB_SYSTEM_WINDOWS)
+ if (bc->optimization_level <= 0) {
+ if (!is_arch_wasm()) {
+ bc->use_separate_modules = true;
+ }
+ }
+#endif
+
+
+ // TODO: Static map calls are bugged on `amd64sysv` abi.
+ if (bc->metrics.os != TargetOs_windows && bc->metrics.arch == TargetArch_amd64) {
// ENFORCE DYNAMIC MAP CALLS
bc->dynamic_map_calls = true;
}
@@ -1401,6 +1742,10 @@ gb_internal void init_build_context(TargetMetrics *cross_target, Subtarget subta
break;
}
}
+
+ if (bc->metrics.os == TargetOs_freestanding) {
+ bc->ODIN_DEFAULT_TO_NIL_ALLOCATOR = !bc->ODIN_DEFAULT_TO_PANIC_ALLOCATOR;
+ }
}
#if defined(GB_SYSTEM_WINDOWS)
@@ -1409,48 +1754,53 @@ gb_internal void init_build_context(TargetMetrics *cross_target, Subtarget subta
#include "microsoft_craziness.h"
#endif
+// NOTE: the target feature and microarch lists are all sorted, so if it turns out to be slow (I don't think it will)
+// a binary search is possible.
-gb_internal Array<String> split_by_comma(String const &list) {
- isize n = 1;
- for (isize i = 0; i < list.len; i++) {
- if (list.text[i] == ',') {
- n++;
+gb_internal bool check_single_target_feature_is_valid(String const &feature_list, String const &feature) {
+ String_Iterator it = {feature_list, 0};
+ for (;;) {
+ String str = string_split_iterator(&it, ',');
+ if (str == "") break;
+ if (str == feature) {
+ return true;
}
}
- auto res = array_make<String>(heap_allocator(), n);
- String s = list;
- for (isize i = 0; i < n; i++) {
- isize m = string_index_byte(s, ',');
- if (m < 0) {
- res[i] = s;
- break;
+ return false;
+}
+
+gb_internal bool check_target_feature_is_valid(String const &feature, TargetArchKind arch, String *invalid) {
+ String feature_list = target_features_list[arch];
+ String_Iterator it = {feature, 0};
+ for (;;) {
+ String str = string_split_iterator(&it, ',');
+ if (str == "") break;
+ if (!check_single_target_feature_is_valid(feature_list, str)) {
+ if (invalid) *invalid = str;
+ return false;
}
- res[i] = substring(s, 0, m);
- s = substring(s, m+1, s.len);
}
- return res;
-}
-gb_internal bool check_target_feature_is_valid(TokenPos pos, String const &feature) {
- // TODO(bill): check_target_feature_is_valid
return true;
}
-gb_internal bool check_target_feature_is_enabled(TokenPos pos, String const &target_feature_list) {
- BuildContext *bc = &build_context;
- mutex_lock(&bc->target_features_mutex);
- defer (mutex_unlock(&bc->target_features_mutex));
-
- auto items = split_by_comma(target_feature_list);
- array_free(&items);
- for (String const &item : items) {
- if (!check_target_feature_is_valid(pos, item)) {
- error(pos, "Target feature '%.*s' is not valid", LIT(item));
- return false;
+gb_internal bool check_target_feature_is_valid_globally(String const &feature, String *invalid) {
+ String_Iterator it = {feature, 0};
+ for (;;) {
+ String str = string_split_iterator(&it, ',');
+ if (str == "") break;
+
+ bool valid = false;
+ for (int arch = TargetArch_Invalid; arch < TargetArch_COUNT; arch += 1) {
+ if (check_target_feature_is_valid(str, cast(TargetArchKind)arch, invalid)) {
+ valid = true;
+ break;
+ }
}
- if (!string_set_exists(&bc->target_features_set, item)) {
- error(pos, "Target feature '%.*s' is not enabled", LIT(item));
+
+ if (!valid) {
+ if (invalid) *invalid = str;
return false;
}
}
@@ -1458,52 +1808,35 @@ gb_internal bool check_target_feature_is_enabled(TokenPos pos, String const &tar
return true;
}
-gb_internal void enable_target_feature(TokenPos pos, String const &target_feature_list) {
- BuildContext *bc = &build_context;
- mutex_lock(&bc->target_features_mutex);
- defer (mutex_unlock(&bc->target_features_mutex));
-
- auto items = split_by_comma(target_feature_list);
- for (String const &item : items) {
- if (!check_target_feature_is_valid(pos, item)) {
- error(pos, "Target feature '%.*s' is not valid", LIT(item));
- continue;
- }
-
- string_set_add(&bc->target_features_set, item);
- }
- array_free(&items);
+gb_internal bool check_target_feature_is_valid_for_target_arch(String const &feature, String *invalid) {
+ return check_target_feature_is_valid(feature, build_context.metrics.arch, invalid);
}
-
-gb_internal char const *target_features_set_to_cstring(gbAllocator allocator, bool with_quotes) {
- isize len = 0;
- isize i = 0;
- for (String const &feature : build_context.target_features_set) {
- if (i != 0) {
- len += 1;
+gb_internal bool check_target_feature_is_enabled(String const &feature, String *not_enabled) {
+ String_Iterator it = {feature, 0};
+ for (;;) {
+ String str = string_split_iterator(&it, ',');
+ if (str == "") break;
+ if (!string_set_exists(&build_context.target_features_set, str)) {
+ if (not_enabled) *not_enabled = str;
+ return false;
}
- len += feature.len;
- if (with_quotes) len += 2;
- i += 1;
}
- char *features = gb_alloc_array(allocator, char, len+1);
- len = 0;
- i = 0;
- for (String const &feature : build_context.target_features_set) {
- if (i != 0) {
- features[len++] = ',';
- }
- if (with_quotes) features[len++] = '"';
- gb_memmove(features + len, feature.text, feature.len);
- len += feature.len;
- if (with_quotes) features[len++] = '"';
- i += 1;
- }
- features[len++] = 0;
+ return true;
+}
- return features;
+gb_internal bool check_target_feature_is_superset_of(String const &superset, String const &of, String *missing) {
+ String_Iterator it = {of, 0};
+ for (;;) {
+ String str = string_split_iterator(&it, ',');
+ if (str == "") break;
+ if (!check_single_target_feature_is_valid(superset, str)) {
+ if (missing) *missing = str;
+ return false;
+ }
+ }
+ return true;
}
// NOTE(Jeroen): Set/create the output and other paths and report an error as appropriate.
@@ -1533,11 +1866,6 @@ gb_internal bool init_build_paths(String init_filename) {
produces_output_file = true;
}
-
- if (build_context.ODIN_DEFAULT_TO_NIL_ALLOCATOR) {
- bc->no_dynamic_literals = true;
- }
-
if (!produces_output_file) {
// Command doesn't produce output files. We're done.
return true;
@@ -1546,10 +1874,12 @@ gb_internal bool init_build_paths(String init_filename) {
#if defined(GB_SYSTEM_WINDOWS)
if (bc->metrics.os == TargetOs_windows) {
if (bc->resource_filepath.len > 0) {
- bc->build_paths[BuildPath_RC] = path_from_string(ha, bc->resource_filepath);
- bc->build_paths[BuildPath_RES] = path_from_string(ha, bc->resource_filepath);
- bc->build_paths[BuildPath_RC].ext = copy_string(ha, STR_LIT("rc"));
- bc->build_paths[BuildPath_RES].ext = copy_string(ha, STR_LIT("res"));
+ bc->build_paths[BuildPath_RES] = path_from_string(ha, bc->resource_filepath);
+ if (!string_ends_with(bc->resource_filepath, str_lit(".res"))) {
+ bc->build_paths[BuildPath_RES].ext = copy_string(ha, STR_LIT("res"));
+ bc->build_paths[BuildPath_RC] = path_from_string(ha, bc->resource_filepath);
+ bc->build_paths[BuildPath_RC].ext = copy_string(ha, STR_LIT("rc"));
+ }
}
if (bc->pdb_filepath.len > 0) {
@@ -1565,7 +1895,7 @@ gb_internal bool init_build_paths(String init_filename) {
return false;
}
- if (!build_context.use_lld && find_result.vs_exe_path.len == 0) {
+ if (build_context.linker_choice == Linker_Default && find_result.vs_exe_path.len == 0) {
gb_printf_err("link.exe not found.\n");
return false;
}
@@ -1637,7 +1967,12 @@ gb_internal bool init_build_paths(String init_filename) {
} else if (build_context.metrics.os == TargetOs_darwin) {
output_extension = STR_LIT("dylib");
}
- } else if (build_context.build_mode == BuildMode_Object) {
+ } else if (build_context.build_mode == BuildMode_StaticLibrary) {
+ output_extension = STR_LIT("a");
+ 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");
@@ -1725,25 +2060,33 @@ gb_internal bool init_build_paths(String init_filename) {
// 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 || build_context.build_mode != BuildMode_Executable) {
+ if (build_context.metrics.os == TargetOs_windows || is_arch_wasm() || build_context.build_mode != BuildMode_Executable) {
bc->build_paths[BuildPath_Output].ext = copy_string(ha, output_extension);
}
}
+ String output_file = path_to_string(ha, bc->build_paths[BuildPath_Output]);
+ defer (gb_free(ha, output_file.text));
+
// Check if output path is a directory.
if (path_is_directory(bc->build_paths[BuildPath_Output])) {
- String output_file = path_to_string(ha, bc->build_paths[BuildPath_Output]);
- defer (gb_free(ha, output_file.text));
gb_printf_err("Output path %.*s is a directory.\n", LIT(output_file));
return false;
}
- if (!write_directory(bc->build_paths[BuildPath_Output].basename)) {
- String output_file = path_to_string(ha, bc->build_paths[BuildPath_Output]);
- defer (gb_free(ha, output_file.text));
- gb_printf_err("No write permissions for output path: %.*s\n", LIT(output_file));
- return false;
- }
+ // gbFile output_file_test;
+ // const char* output_file_name = (const char*)output_file.text;
+ // gbFileError output_test_err = gb_file_open_mode(&output_file_test, gbFileMode_Append | gbFileMode_Rw, output_file_name);
+
+ // if (output_test_err == 0) {
+ // gb_file_close(&output_file_test);
+ // gb_file_remove(output_file_name);
+ // } else {
+ // String output_file = path_to_string(ha, bc->build_paths[BuildPath_Output]);
+ // defer (gb_free(ha, output_file.text));
+ // gb_printf_err("No write permissions for output path: %.*s\n", LIT(output_file));
+ // return false;
+ // }
if (build_context.sanitizer_flags & SanitizerFlag_Address) {
switch (build_context.metrics.os) {
@@ -1781,9 +2124,18 @@ gb_internal bool init_build_paths(String init_filename) {
}
}
-
- if (bc->target_features_string.len != 0) {
- enable_target_feature({}, bc->target_features_string);
+ if (build_context.no_crt && !build_context.ODIN_DEFAULT_TO_NIL_ALLOCATOR && !build_context.ODIN_DEFAULT_TO_PANIC_ALLOCATOR) {
+ switch (build_context.metrics.os) {
+ case TargetOs_linux:
+ case TargetOs_darwin:
+ case TargetOs_essence:
+ case TargetOs_freebsd:
+ case TargetOs_openbsd:
+ case TargetOs_netbsd:
+ case TargetOs_haiku:
+ gb_printf_err("-no-crt on unix systems requires either -default-to-nil-allocator or -default-to-panic-allocator to also be present because the default allocator requires crt\n");
+ return false;
+ }
}
return true;
diff --git a/src/build_settings_microarch.cpp b/src/build_settings_microarch.cpp
new file mode 100644
index 000000000..8f64d4026
--- /dev/null
+++ b/src/build_settings_microarch.cpp
@@ -0,0 +1,516 @@
+// 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,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"),
+ // 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,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"),
+ // 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-a53,cortex-a55,cortex-a57,cortex-a7,cortex-a710,cortex-a72,cortex-a73,cortex-a75,cortex-a76,cortex-a76ae,cortex-a77,cortex-a78,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-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,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-latest,apple-m1,apple-m2,apple-m3,apple-s4,apple-s5,carmel,cortex-a34,cortex-a35,cortex-a510,cortex-a520,cortex-a53,cortex-a55,cortex-a57,cortex-a65,cortex-a65ae,cortex-a710,cortex-a715,cortex-a72,cortex-a720,cortex-a73,cortex-a75,cortex-a76,cortex-a76ae,cortex-a77,cortex-a78,cortex-a78c,cortex-r82,cortex-x1,cortex-x1c,cortex-x2,cortex-x3,cortex-x4,cyclone,exynos-m3,exynos-m4,exynos-m5,falkor,generic,kryo,neoverse-512tvb,neoverse-e1,neoverse-n1,neoverse-n2,neoverse-v1,neoverse-v2,saphira,thunderx,thunderx2t99,thunderx3t110,thunderxt81,thunderxt83,thunderxt88,tsv110"),
+ // TargetArch_wasm32:
+ str_lit("bleeding-edge,generic,mvp"),
+ // TargetArch_wasm64p32:
+ str_lit("bleeding-edge,generic,mvp"),
+ // TargetArch_riscv64:
+ str_lit("generic,generic-rv32,generic-rv64,rocket,rocket-rv32,rocket-rv64,sifive-7-series,sifive-e20,sifive-e21,sifive-e24,sifive-e31,sifive-e34,sifive-e76,sifive-p450,sifive-p670,sifive-s21,sifive-s51,sifive-s54,sifive-s76,sifive-u54,sifive-u74,sifive-x280,syntacore-scr1-base,syntacore-scr1-max,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,3dnow,3dnowa,64bit,64bit-mode,adx,aes,allow-light-256-bit,amx-bf16,amx-complex,amx-fp16,amx-int8,amx-tile,avx,avx10.1-256,avx10.1-512,avx2,avx512bf16,avx512bitalg,avx512bw,avx512cd,avx512dq,avx512er,avx512f,avx512fp16,avx512ifma,avx512pf,avx512vbmi,avx512vbmi2,avx512vl,avx512vnni,avx512vp2intersect,avx512vpopcntdq,avxifma,avxneconvert,avxvnni,avxvnniint16,avxvnniint8,bmi,bmi2,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-gather,fast-hops,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,invpcid,kl,lea-sp,lea-uses-ag,lvi-cfi,lvi-load-hardening,lwp,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,mwaitx,ndd,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,prefetchwt1,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"),
+ // TargetArch_i386:
+ str_lit("16bit-mode,32bit-mode,3dnow,3dnowa,64bit,64bit-mode,adx,aes,allow-light-256-bit,amx-bf16,amx-complex,amx-fp16,amx-int8,amx-tile,avx,avx10.1-256,avx10.1-512,avx2,avx512bf16,avx512bitalg,avx512bw,avx512cd,avx512dq,avx512er,avx512f,avx512fp16,avx512ifma,avx512pf,avx512vbmi,avx512vbmi2,avx512vl,avx512vnni,avx512vp2intersect,avx512vpopcntdq,avxifma,avxneconvert,avxvnni,avxvnniint16,avxvnniint8,bmi,bmi2,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-gather,fast-hops,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,invpcid,kl,lea-sp,lea-uses-ag,lvi-cfi,lvi-load-hardening,lwp,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,mwaitx,ndd,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,prefetchwt1,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"),
+ // 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,aapcs-frame-chain-leaf,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,atomics-32,avoid-movs-shop,avoid-partial-cpsr,bf16,big-endian-instructions,cde,cdecp0,cdecp1,cdecp2,cdecp3,cdecp4,cdecp5,cdecp6,cdecp7,cheap-predicable-cpsr,clrbhb,cortex-a710,cortex-a78,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,m7,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,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,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,a53,a55,a57,a64fx,a65,a710,a715,a72,a720,a73,a75,a76,a77,a78,a78c,addr-lsl-fast,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-a7-sysreg,arith-bcc-fusion,arith-cbz-fusion,ascend-store-address,b16b16,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,complxnum,cortex-r82,cortex-x1,cortex-x2,cortex-x3,cortex-x4,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,falkor,faminmax,fgt,fix-cortex-a53-835769,flagm,fmv,force-32bit-jump-tables,fp-armv8,fp16fml,fp8,fp8dot2,fp8dot4,fp8fma,fpmr,fptoint,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,lut,mec,mops,mpam,mte,neon,neoverse512tvb,neoversee1,neoversen1,neoversen2,neoversev1,neoversev2,nmi,no-bti-at-return-twice,no-neg-immediates,no-sve-fp-ld1r,no-zcz-fp,nv,outline-atomics,pan,pan-rwv,pauth,pauth-lr,perfmon,predictable-select-expensive,predres,prfm-slc-target,rand,ras,rasv2,rcpc,rcpc-immo,rcpc3,rdm,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-x30,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-f16f16,sme-f64f64,sme-f8f16,sme-f8f32,sme-fa64,sme-i16i64,sme-lutv2,sme2,sme2p1,spe,spe-eef,specres2,specrestrict,ssbs,ssve-fp8dot2,ssve-fp8dot4,ssve-fp8fma,store-pair-suppress,stp-aligned-only,strict-align,sve,sve2,sve2-aes,sve2-bitperm,sve2-sha3,sve2-sm4,sve2p1,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-postra-scheduler,use-reciprocal-square-root,use-scalar-inc-vl,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,v9a,vh,wfxt,xs,zcm,zcz,zcz-fp-workaround,zcz-gp"),
+ // TargetArch_wasm32:
+ str_lit("atomics,bulk-memory,exception-handling,extended-const,multimemory,multivalue,mutable-globals,nontrapping-fptoint,reference-types,relaxed-simd,sign-ext,simd128,tail-call"),
+ // TargetArch_wasm64p32:
+ str_lit("atomics,bulk-memory,exception-handling,extended-const,multimemory,multivalue,mutable-globals,nontrapping-fptoint,reference-types,relaxed-simd,sign-ext,simd128,tail-call"),
+ // TargetArch_riscv64:
+ str_lit("32bit,64bit,a,auipc-addi-fusion,c,conditional-cmv-fusion,d,dlen-factor-2,e,experimental,experimental-zacas,experimental-zcmop,experimental-zfbfmin,experimental-zicfilp,experimental-zicfiss,experimental-zimop,experimental-ztso,experimental-zvfbfmin,experimental-zvfbfwma,f,fast-unaligned-access,forced-atomics,h,i,ld-add-fusion,lui-addi-fusion,m,no-default-unroll,no-optimized-zero-stride-load,no-rvc-hints,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,save-restore,seq-cst-trailing-fence,shifted-zextw-fusion,short-forward-branch-opt,sifive7,smaia,smepmp,ssaia,svinval,svnapot,svpbmt,tagged-globals,unaligned-scalar-mem,use-postra-scheduler,v,ventana-veyron,xcvalu,xcvbi,xcvbitmanip,xcvelw,xcvmac,xcvmem,xcvsimd,xsfvcp,xsfvfnrclipxfqf,xsfvfwmaccqqq,xsfvqmaccdod,xsfvqmaccqoq,xtheadba,xtheadbb,xtheadbs,xtheadcmo,xtheadcondmov,xtheadfmemidx,xtheadmac,xtheadmemidx,xtheadmempair,xtheadsync,xtheadvdot,xventanacondops,za128rs,za64rs,zawrs,zba,zbb,zbc,zbkb,zbkc,zbkx,zbs,zca,zcb,zcd,zce,zcf,zcmp,zcmt,zdinx,zexth-fusion,zextw-fusion,zfa,zfh,zfhmin,zfinx,zhinx,zhinxmin,zic64b,zicbom,zicbop,zicboz,ziccamoa,ziccif,zicclsm,ziccrse,zicntr,zicond,zicsr,zifencei,zihintntl,zihintpause,zihpm,zk,zkn,zknd,zkne,zknh,zkr,zks,zksed,zksh,zkt,zmmul,zvbb,zvbc,zve32f,zve32x,zve64d,zve64f,zve64x,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:
+ 127,
+ // TargetArch_i386:
+ 127,
+ // TargetArch_arm32:
+ 91,
+ // TargetArch_arm64:
+ 69,
+ // TargetArch_wasm32:
+ 3,
+ // TargetArch_wasm64p32:
+ 3,
+ // TargetArch_riscv64:
+ 26,
+};
+
+// 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("3dnow,3dnowa,64bit,64bit-mode,cmov,cx16,cx8,fast-scalar-shift-masks,fxsr,lzcnt,mmx,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("3dnow,3dnowa,64bit-mode,cmov,cx8,mmx,nopl,slow-shld,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("athlon-4"), str_lit("3dnow,3dnowa,64bit-mode,cmov,cx8,fxsr,mmx,nopl,slow-shld,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("athlon-fx"), str_lit("3dnow,3dnowa,64bit,64bit-mode,cmov,cx8,fast-scalar-shift-masks,fxsr,mmx,nopl,sbb-dep-breaking,slow-shld,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("athlon-mp"), str_lit("3dnow,3dnowa,64bit-mode,cmov,cx8,fxsr,mmx,nopl,slow-shld,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("athlon-tbird"), str_lit("3dnow,3dnowa,64bit-mode,cmov,cx8,mmx,nopl,slow-shld,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("athlon-xp"), str_lit("3dnow,3dnowa,64bit-mode,cmov,cx8,fxsr,mmx,nopl,slow-shld,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("athlon64"), str_lit("3dnow,3dnowa,64bit,64bit-mode,cmov,cx8,fast-scalar-shift-masks,fxsr,mmx,nopl,sbb-dep-breaking,slow-shld,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("athlon64-sse3"), str_lit("3dnow,3dnowa,64bit,64bit-mode,cmov,cx16,cx8,fast-scalar-shift-masks,fxsr,mmx,nopl,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,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-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-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("3dnow,3dnowa,64bit,64bit-mode,cmov,cx16,cx8,fast-scalar-shift-masks,fxsr,lzcnt,mmx,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,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,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,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,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,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-scalar-shift-masks,fast-vector-shift-masks,fxsr,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-lzcnt,fast-movbe,fast-scalar-shift-masks,fast-vector-shift-masks,fxsr,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("3dnow,64bit-mode,mmx,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,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,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,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("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("3dnow,3dnowa,64bit-mode,cx8,mmx,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-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-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-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,idivl-to-divb,idivq-to-divl,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,fast-movbe,fma,fsgsbase,fxsr,gfni,hreset,invpcid,kl,lzcnt,mmx,movbe,movdir64b,movdiri,no-bypass-delay,nopl,pclmul,pconfig,pku,popcnt,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,serialize,sha,shstk,slow-incdec,slow-lea,slow-two-mem-ops,sse,sse2,sse3,sse4.1,sse4.2,ssse3,uintr,use-glm-div-sqrt-costs,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,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,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,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("3dnow,64bit-mode,cx8,mmx,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("k6-3"), str_lit("3dnow,64bit-mode,cx8,mmx,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("k8"), str_lit("3dnow,3dnowa,64bit,64bit-mode,cmov,cx8,fast-scalar-shift-masks,fxsr,mmx,nopl,sbb-dep-breaking,slow-shld,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("k8-sse3"), str_lit("3dnow,3dnowa,64bit,64bit-mode,cmov,cx16,cx8,fast-scalar-shift-masks,fxsr,mmx,nopl,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,avx512er,avx512f,avx512pf,bmi,bmi2,cmov,crc32,cx16,cx8,evex512,f16c,fast-gather,fast-movbe,fma,fsgsbase,fxsr,idivq-to-divl,lzcnt,mmx,movbe,nopl,pclmul,popcnt,prefer-mask-registers,prefetchwt1,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,avx512er,avx512f,avx512pf,avx512vpopcntdq,bmi,bmi2,cmov,crc32,cx16,cx8,evex512,f16c,fast-gather,fast-movbe,fma,fsgsbase,fxsr,idivq-to-divl,lzcnt,mmx,movbe,nopl,pclmul,popcnt,prefer-mask-registers,prefetchwt1,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,avx512er,avx512f,avx512pf,bmi,bmi2,cmov,crc32,cx16,cx8,evex512,f16c,fast-gather,fast-movbe,fma,fsgsbase,fxsr,idivq-to-divl,lzcnt,mmx,movbe,nopl,pclmul,popcnt,prefer-mask-registers,prefetchwt1,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,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("3dnow,3dnowa,64bit,64bit-mode,cmov,cx8,fast-scalar-shift-masks,fxsr,mmx,nopl,sbb-dep-breaking,slow-shld,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("opteron-sse3"), str_lit("3dnow,3dnowa,64bit,64bit-mode,cmov,cx16,cx8,fast-scalar-shift-masks,fxsr,mmx,nopl,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,fast-movbe,fma,fsgsbase,fxsr,gfni,hreset,invpcid,kl,lzcnt,mmx,movbe,movdir64b,movdiri,no-bypass-delay,nopl,pclmul,pconfig,pku,popcnt,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,serialize,sha,shstk,slow-incdec,slow-lea,slow-two-mem-ops,sse,sse2,sse3,sse4.1,sse4.2,ssse3,uintr,use-glm-div-sqrt-costs,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-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-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-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,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("3dnow,64bit-mode,mmx,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-lzcnt,fast-movbe,fast-scalar-fsqrt,fast-scalar-shift-masks,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fxsr,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-lzcnt,fast-movbe,fast-scalar-fsqrt,fast-scalar-shift-masks,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fxsr,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-lzcnt,fast-movbe,fast-scalar-fsqrt,fast-scalar-shift-masks,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fsrm,fxsr,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-lzcnt,fast-movbe,fast-scalar-fsqrt,fast-scalar-shift-masks,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fsrm,fxsr,gfni,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") },
+ // 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,3dnow,3dnowa,64bit,cmov,cx16,cx8,fast-scalar-shift-masks,fxsr,lzcnt,mmx,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,3dnow,3dnowa,cmov,cx8,mmx,nopl,slow-shld,slow-unaligned-mem-16,vzeroupper,x87") },
+ { str_lit("athlon-4"), str_lit("32bit-mode,3dnow,3dnowa,cmov,cx8,fxsr,mmx,nopl,slow-shld,slow-unaligned-mem-16,sse,vzeroupper,x87") },
+ { str_lit("athlon-fx"), str_lit("32bit-mode,3dnow,3dnowa,64bit,cmov,cx8,fast-scalar-shift-masks,fxsr,mmx,nopl,sbb-dep-breaking,slow-shld,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("athlon-mp"), str_lit("32bit-mode,3dnow,3dnowa,cmov,cx8,fxsr,mmx,nopl,slow-shld,slow-unaligned-mem-16,sse,vzeroupper,x87") },
+ { str_lit("athlon-tbird"), str_lit("32bit-mode,3dnow,3dnowa,cmov,cx8,mmx,nopl,slow-shld,slow-unaligned-mem-16,vzeroupper,x87") },
+ { str_lit("athlon-xp"), str_lit("32bit-mode,3dnow,3dnowa,cmov,cx8,fxsr,mmx,nopl,slow-shld,slow-unaligned-mem-16,sse,vzeroupper,x87") },
+ { str_lit("athlon64"), str_lit("32bit-mode,3dnow,3dnowa,64bit,cmov,cx8,fast-scalar-shift-masks,fxsr,mmx,nopl,sbb-dep-breaking,slow-shld,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("athlon64-sse3"), str_lit("32bit-mode,3dnow,3dnowa,64bit,cmov,cx16,cx8,fast-scalar-shift-masks,fxsr,mmx,nopl,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,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-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-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,3dnow,3dnowa,64bit,cmov,cx16,cx8,fast-scalar-shift-masks,fxsr,lzcnt,mmx,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,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,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,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,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,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-scalar-shift-masks,fast-vector-shift-masks,fxsr,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-lzcnt,fast-movbe,fast-scalar-shift-masks,fast-vector-shift-masks,fxsr,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,3dnow,mmx,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,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,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,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("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,3dnow,3dnowa,cx8,mmx,slow-unaligned-mem-16,vzeroupper,x87") },
+ { str_lit("goldmont"), str_lit("32bit-mode,64bit,aes,clflushopt,cmov,crc32,cx16,cx8,false-deps-popcnt,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-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-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,idivl-to-divb,idivq-to-divl,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,fast-movbe,fma,fsgsbase,fxsr,gfni,hreset,invpcid,kl,lzcnt,mmx,movbe,movdir64b,movdiri,no-bypass-delay,nopl,pclmul,pconfig,pku,popcnt,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,serialize,sha,shstk,slow-incdec,slow-lea,slow-two-mem-ops,sse,sse2,sse3,sse4.1,sse4.2,ssse3,uintr,use-glm-div-sqrt-costs,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,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,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,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,3dnow,cx8,mmx,slow-unaligned-mem-16,vzeroupper,x87") },
+ { str_lit("k6-3"), str_lit("32bit-mode,3dnow,cx8,mmx,slow-unaligned-mem-16,vzeroupper,x87") },
+ { str_lit("k8"), str_lit("32bit-mode,3dnow,3dnowa,64bit,cmov,cx8,fast-scalar-shift-masks,fxsr,mmx,nopl,sbb-dep-breaking,slow-shld,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("k8-sse3"), str_lit("32bit-mode,3dnow,3dnowa,64bit,cmov,cx16,cx8,fast-scalar-shift-masks,fxsr,mmx,nopl,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,avx512er,avx512f,avx512pf,bmi,bmi2,cmov,crc32,cx16,cx8,evex512,f16c,fast-gather,fast-movbe,fma,fsgsbase,fxsr,idivq-to-divl,lzcnt,mmx,movbe,nopl,pclmul,popcnt,prefer-mask-registers,prefetchwt1,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,avx512er,avx512f,avx512pf,avx512vpopcntdq,bmi,bmi2,cmov,crc32,cx16,cx8,evex512,f16c,fast-gather,fast-movbe,fma,fsgsbase,fxsr,idivq-to-divl,lzcnt,mmx,movbe,nopl,pclmul,popcnt,prefer-mask-registers,prefetchwt1,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,avx512er,avx512f,avx512pf,bmi,bmi2,cmov,crc32,cx16,cx8,evex512,f16c,fast-gather,fast-movbe,fma,fsgsbase,fxsr,idivq-to-divl,lzcnt,mmx,movbe,nopl,pclmul,popcnt,prefer-mask-registers,prefetchwt1,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,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,3dnow,3dnowa,64bit,cmov,cx8,fast-scalar-shift-masks,fxsr,mmx,nopl,sbb-dep-breaking,slow-shld,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("opteron-sse3"), str_lit("32bit-mode,3dnow,3dnowa,64bit,cmov,cx16,cx8,fast-scalar-shift-masks,fxsr,mmx,nopl,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,fast-movbe,fma,fsgsbase,fxsr,gfni,hreset,invpcid,kl,lzcnt,mmx,movbe,movdir64b,movdiri,no-bypass-delay,nopl,pclmul,pconfig,pku,popcnt,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,serialize,sha,shstk,slow-incdec,slow-lea,slow-two-mem-ops,sse,sse2,sse3,sse4.1,sse4.2,ssse3,uintr,use-glm-div-sqrt-costs,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-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-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-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,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,3dnow,mmx,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-lzcnt,fast-movbe,fast-scalar-fsqrt,fast-scalar-shift-masks,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fxsr,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-lzcnt,fast-movbe,fast-scalar-fsqrt,fast-scalar-shift-masks,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fxsr,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-lzcnt,fast-movbe,fast-scalar-fsqrt,fast-scalar-shift-masks,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fsrm,fxsr,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-lzcnt,fast-movbe,fast-scalar-fsqrt,fast-scalar-shift-masks,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fsrm,fxsr,gfni,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") },
+ // 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,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,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,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,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-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,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-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,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-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,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,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,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,db,dsp,fp-armv8d16,fp-armv8d16sp,fp16,fp64,fpregs,fpregs16,fpregs64,fullfp16,hwdiv,lob,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-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,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,fp64,fpregs,fpregs64,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("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,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,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,addr-lsl-fast,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,fuse-address,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,addr-lsl-fast,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,fuse-address,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,addr-lsl-fast,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,crypto,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,crypto,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,ccidx,ccpp,complxnum,crc,crypto,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,ccidx,ccpp,complxnum,crc,crypto,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,ccidx,ccpp,complxnum,crc,crypto,disable-latency-sched-heuristic,dit,dotprod,el2vmsa,el3,flagm,fp-armv8,fp16fml,fptoint,fullfp16,fuse-address,fuse-adrp-add,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,ccidx,ccpp,complxnum,crc,crypto,disable-latency-sched-heuristic,dit,dotprod,ecv,el2vmsa,el3,fgt,flagm,fp-armv8,fp16fml,fptoint,fullfp16,fuse-address,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,ccidx,ccpp,complxnum,crc,crypto,disable-latency-sched-heuristic,dit,dotprod,ecv,el2vmsa,el3,fgt,flagm,fp-armv8,fp16fml,fptoint,fullfp16,fuse-address,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,ccidx,ccpp,complxnum,crc,crypto,disable-latency-sched-heuristic,dit,dotprod,ecv,el2vmsa,el3,fgt,flagm,fp-armv8,fp16fml,fptoint,fullfp16,fuse-address,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,apple-a7-sysreg,arith-bcc-fusion,arith-cbz-fusion,crypto,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,apple-a7-sysreg,arith-bcc-fusion,arith-cbz-fusion,crypto,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,apple-a7-sysreg,arith-bcc-fusion,arith-cbz-fusion,crypto,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-latest"), str_lit("CONTEXTIDREL2,aes,alternate-sextload-cvt-f32-pattern,altnzcv,am,amvs,apple-a16,arith-bcc-fusion,arith-cbz-fusion,bf16,bti,ccdp,ccidx,ccpp,complxnum,crc,crypto,disable-latency-sched-heuristic,dit,dotprod,ecv,el2vmsa,el3,fgt,flagm,fp-armv8,fp16fml,fptoint,fullfp16,fuse-address,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-m1"), str_lit("CONTEXTIDREL2,aes,aggressive-fma,alternate-sextload-cvt-f32-pattern,altnzcv,am,apple-a14,arith-bcc-fusion,arith-cbz-fusion,ccdp,ccidx,ccpp,complxnum,crc,crypto,disable-latency-sched-heuristic,dit,dotprod,el2vmsa,el3,flagm,fp-armv8,fp16fml,fptoint,fullfp16,fuse-address,fuse-adrp-add,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,ccidx,ccpp,complxnum,crc,crypto,disable-latency-sched-heuristic,dit,dotprod,ecv,el2vmsa,el3,fgt,flagm,fp-armv8,fp16fml,fptoint,fullfp16,fuse-address,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,ccidx,ccpp,complxnum,crc,crypto,disable-latency-sched-heuristic,dit,dotprod,ecv,el2vmsa,el3,fgt,flagm,fp-armv8,fp16fml,fptoint,fullfp16,fuse-address,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-s4"), str_lit("CONTEXTIDREL2,aes,alternate-sextload-cvt-f32-pattern,apple-a12,arith-bcc-fusion,arith-cbz-fusion,ccidx,ccpp,complxnum,crc,crypto,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,ccidx,ccpp,complxnum,crc,crypto,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,crypto,el2vmsa,el3,fp-armv8,fullfp16,lor,lse,neon,pan,pan-rwv,ras,rdm,sha2,uaops,v8.1a,v8.2a,v8a,vh") },
+ { str_lit("cortex-a34"), str_lit("a35,aes,crc,crypto,el2vmsa,el3,fp-armv8,neon,perfmon,sha2,v8a") },
+ { str_lit("cortex-a35"), str_lit("a35,aes,crc,crypto,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,mec,mpam,mte,neon,nv,pan,pan-rwv,pauth,perfmon,predres,ras,rcpc,rcpc-immo,rdm,rme,sb,sel2,specrestrict,ssbs,sve,sve2,sve2-bitperm,tlb-rmi,tracev8.4,trbe,uaops,use-postra-scheduler,use-scalar-inc-vl,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,mec,mpam,mte,neon,nv,pan,pan-rwv,pauth,perfmon,predres,ras,rcpc,rcpc-immo,rdm,rme,sb,sel2,specrestrict,ssbs,sve,sve2,sve2-bitperm,tlb-rmi,tracev8.4,trbe,uaops,use-postra-scheduler,use-scalar-inc-vl,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,crypto,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,crypto,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,aes,balance-fp-ops,crc,crypto,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,crypto,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,crypto,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,addr-lsl-fast,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,mec,mpam,mte,neon,nv,pan,pan-rwv,pauth,perfmon,predictable-select-expensive,predres,ras,rcpc,rcpc-immo,rdm,rme,sb,sel2,specrestrict,ssbs,sve,sve2,sve2-bitperm,tlb-rmi,tracev8.4,trbe,uaops,use-postra-scheduler,use-scalar-inc-vl,v8.1a,v8.2a,v8.3a,v8.4a,v8.5a,v8a,v9a,vh") },
+ { str_lit("cortex-a715"), str_lit("CONTEXTIDREL2,a715,addr-lsl-fast,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,mec,mpam,mte,neon,nv,pan,pan-rwv,pauth,perfmon,predictable-select-expensive,predres,ras,rcpc,rcpc-immo,rdm,rme,sb,sel2,spe,specrestrict,ssbs,sve,sve2,sve2-bitperm,tlb-rmi,tracev8.4,trbe,uaops,use-postra-scheduler,use-scalar-inc-vl,v8.1a,v8.2a,v8.3a,v8.4a,v8.5a,v8a,v9a,vh") },
+ { str_lit("cortex-a72"), str_lit("a72,aes,crc,crypto,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,addr-lsl-fast,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,mec,mpam,mte,neon,nv,pan,pan-rwv,pauth,perfmon,predictable-select-expensive,predres,ras,rcpc,rcpc-immo,rdm,rme,sb,sel2,spe,spe-eef,specrestrict,ssbs,sve,sve2,sve2-bitperm,tlb-rmi,tracev8.4,trbe,uaops,use-postra-scheduler,use-scalar-inc-vl,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,aes,crc,crypto,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,aes,ccpp,crc,crypto,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-fast,aes,alu-lsl-fast,ccpp,crc,crypto,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-fast,aes,alu-lsl-fast,ccpp,crc,crypto,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-fast,aes,alu-lsl-fast,ccpp,cmp-bcc-fusion,crc,crypto,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-fast,aes,alu-lsl-fast,ccpp,cmp-bcc-fusion,crc,crypto,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-fast,aes,alu-lsl-fast,ccpp,cmp-bcc-fusion,crc,crypto,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,ccidx,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-x1"), str_lit("CONTEXTIDREL2,addr-lsl-fast,aes,alu-lsl-fast,ccpp,cmp-bcc-fusion,cortex-x1,crc,crypto,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-fast,aes,alu-lsl-fast,ccpp,cmp-bcc-fusion,cortex-x1,crc,crypto,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,addr-lsl-fast,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,mec,mpam,mte,neon,nv,pan,pan-rwv,pauth,perfmon,predictable-select-expensive,predres,ras,rcpc,rcpc-immo,rdm,rme,sb,sel2,specrestrict,ssbs,sve,sve2,sve2-bitperm,tlb-rmi,tracev8.4,trbe,uaops,use-postra-scheduler,use-scalar-inc-vl,v8.1a,v8.2a,v8.3a,v8.4a,v8.5a,v8a,v9a,vh") },
+ { str_lit("cortex-x3"), str_lit("CONTEXTIDREL2,addr-lsl-fast,altnzcv,alu-lsl-fast,am,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,mec,mpam,mte,neon,nv,pan,pan-rwv,pauth,perfmon,predictable-select-expensive,predres,ras,rcpc,rcpc-immo,rdm,rme,sb,sel2,spe,specrestrict,ssbs,sve,sve2,sve2-bitperm,tlb-rmi,tracev8.4,trbe,uaops,use-postra-scheduler,use-scalar-inc-vl,v8.1a,v8.2a,v8.3a,v8.4a,v8.5a,v8a,v9a,vh") },
+ { str_lit("cortex-x4"), str_lit("CONTEXTIDREL2,addr-lsl-fast,altnzcv,alu-lsl-fast,am,amvs,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,mec,mpam,mte,neon,nv,pan,pan-rwv,pauth,perfmon,predictable-select-expensive,predres,ras,rcpc,rcpc-immo,rdm,rme,sb,sel2,spe,spe-eef,specrestrict,ssbs,sve,sve2,sve2-bitperm,tlb-rmi,tracev8.4,trbe,uaops,use-postra-scheduler,use-scalar-inc-vl,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,apple-a7-sysreg,arith-bcc-fusion,arith-cbz-fusion,crypto,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("addr-lsl-fast,aes,alu-lsl-fast,crc,crypto,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,addr-lsl-fast,aes,alu-lsl-fast,arith-bcc-fusion,arith-cbz-fusion,ccpp,crc,crypto,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,addr-lsl-fast,aes,alu-lsl-fast,arith-bcc-fusion,arith-cbz-fusion,ccpp,crc,crypto,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("addr-lsl-fast,aes,alu-lsl-fast,crc,crypto,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("generic"), str_lit("enable-select-opt,ete,fp-armv8,fuse-adrp-add,fuse-aes,neon,trbe,use-postra-scheduler") },
+ { str_lit("kryo"), str_lit("addr-lsl-fast,aes,alu-lsl-fast,crc,crypto,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,addr-lsl-fast,aes,alu-lsl-fast,am,bf16,ccdp,ccidx,ccpp,complxnum,crc,crypto,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,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,crypto,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-fast,aes,alu-lsl-fast,ccpp,crc,crypto,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,addr-lsl-fast,altnzcv,alu-lsl-fast,am,bf16,bti,ccdp,ccidx,ccpp,complxnum,crc,dit,dotprod,el2vmsa,el3,enable-select-opt,ete,flagm,fp-armv8,fptoint,fullfp16,fuse-adrp-add,fuse-aes,i8mm,jsconv,lor,lse,lse2,mec,mpam,mte,neon,neoversen2,nv,pan,pan-rwv,pauth,perfmon,predictable-select-expensive,predres,ras,rcpc,rcpc-immo,rdm,rme,sb,sel2,specrestrict,ssbs,sve,sve2,sve2-bitperm,tlb-rmi,tracev8.4,trbe,uaops,use-postra-scheduler,use-scalar-inc-vl,v8.1a,v8.2a,v8.3a,v8.4a,v8.5a,v8a,v9a,vh") },
+ { str_lit("neoverse-v1"), str_lit("CONTEXTIDREL2,addr-lsl-fast,aes,alu-lsl-fast,am,bf16,ccdp,ccidx,ccpp,complxnum,crc,crypto,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,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,addr-lsl-fast,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,mec,mpam,mte,neon,neoversev2,nv,pan,pan-rwv,pauth,perfmon,predictable-select-expensive,predres,rand,ras,rcpc,rcpc-immo,rdm,rme,sb,sel2,spe,specrestrict,ssbs,sve,sve2,sve2-bitperm,tlb-rmi,tracev8.4,trbe,uaops,use-postra-scheduler,use-scalar-inc-vl,v8.1a,v8.2a,v8.3a,v8.4a,v8.5a,v8a,v9a,vh") },
+ { str_lit("saphira"), str_lit("CONTEXTIDREL2,addr-lsl-fast,aes,alu-lsl-fast,am,ccidx,ccpp,complxnum,crc,crypto,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,crypto,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,crypto,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,crypto,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,crypto,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,crypto,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,crypto,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,crypto,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,mutable-globals,nontrapping-fptoint,sign-ext,simd128,tail-call") },
+ { str_lit("generic"), str_lit("mutable-globals,sign-ext") },
+ { str_lit("mvp"), str_lit("") },
+ // TargetArch_wasm64p32:
+ { str_lit("bleeding-edge"), str_lit("atomics,bulk-memory,mutable-globals,nontrapping-fptoint,sign-ext,simd128,tail-call") },
+ { str_lit("generic"), str_lit("mutable-globals,sign-ext") },
+ { str_lit("mvp"), str_lit("") },
+ // TargetArch_riscv64:
+ { str_lit("generic"), str_lit("64bit") },
+ { str_lit("generic-rv32"), str_lit("32bit") },
+ { str_lit("generic-rv64"), str_lit("64bit") },
+ { str_lit("rocket"), str_lit("") },
+ { str_lit("rocket-rv32"), str_lit("32bit,zicsr,zifencei") },
+ { str_lit("rocket-rv64"), str_lit("64bit,zicsr,zifencei") },
+ { str_lit("sifive-7-series"), str_lit("no-default-unroll,short-forward-branch-opt,sifive7") },
+ { str_lit("sifive-e20"), str_lit("32bit,c,m,zicsr,zifencei") },
+ { str_lit("sifive-e21"), str_lit("32bit,a,c,m,zicsr,zifencei") },
+ { str_lit("sifive-e24"), str_lit("32bit,a,c,f,m,zicsr,zifencei") },
+ { str_lit("sifive-e31"), str_lit("32bit,a,c,m,zicsr,zifencei") },
+ { str_lit("sifive-e34"), str_lit("32bit,a,c,f,m,zicsr,zifencei") },
+ { str_lit("sifive-e76"), str_lit("32bit,a,c,f,m,no-default-unroll,short-forward-branch-opt,sifive7,zicsr,zifencei") },
+ { str_lit("sifive-p450"), str_lit("64bit,a,auipc-addi-fusion,c,conditional-cmv-fusion,d,f,fast-unaligned-access,lui-addi-fusion,m,no-default-unroll,za64rs,zba,zbb,zbs,zfhmin,zic64b,zicbom,zicbop,zicboz,ziccamoa,ziccif,zicclsm,ziccrse,zicsr,zifencei,zihintntl,zihintpause,zihpm") },
+ { str_lit("sifive-p670"), str_lit("64bit,a,auipc-addi-fusion,c,conditional-cmv-fusion,d,f,fast-unaligned-access,lui-addi-fusion,m,no-default-unroll,v,za64rs,zba,zbb,zbs,zfhmin,zic64b,zicbom,zicbop,zicboz,ziccamoa,ziccif,zicclsm,ziccrse,zicsr,zifencei,zihintntl,zihintpause,zihpm,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,m,zicsr,zifencei") },
+ { str_lit("sifive-s51"), str_lit("64bit,a,c,m,zicsr,zifencei") },
+ { str_lit("sifive-s54"), str_lit("64bit,a,c,d,f,m,zicsr,zifencei") },
+ { str_lit("sifive-s76"), str_lit("64bit,a,c,d,f,m,no-default-unroll,short-forward-branch-opt,sifive7,zicsr,zifencei,zihintpause") },
+ { str_lit("sifive-u54"), str_lit("64bit,a,c,d,f,m,zicsr,zifencei") },
+ { str_lit("sifive-u74"), str_lit("64bit,a,c,d,f,m,no-default-unroll,short-forward-branch-opt,sifive7,zicsr,zifencei") },
+ { str_lit("sifive-x280"), str_lit("64bit,a,c,d,dlen-factor-2,f,m,no-default-unroll,short-forward-branch-opt,sifive7,v,zba,zbb,zfh,zfhmin,zicsr,zifencei,zve32f,zve32x,zve64d,zve64f,zve64x,zvfh,zvfhmin,zvl128b,zvl256b,zvl32b,zvl512b,zvl64b") },
+ { str_lit("syntacore-scr1-base"), str_lit("32bit,c,no-default-unroll,zicsr,zifencei") },
+ { str_lit("syntacore-scr1-max"), str_lit("32bit,c,m,no-default-unroll,zicsr,zifencei") },
+ { 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") },
+};
diff --git a/src/cached.cpp b/src/cached.cpp
new file mode 100644
index 000000000..efdadce7b
--- /dev/null
+++ b/src/cached.cpp
@@ -0,0 +1,474 @@
+gb_internal GB_COMPARE_PROC(string_cmp) {
+ String const &x = *(String *)a;
+ String const &y = *(String *)b;
+ return string_compare(x, y);
+}
+
+gb_internal bool recursively_delete_directory(wchar_t *wpath_c) {
+#if defined(GB_SYSTEM_WINDOWS)
+ auto const is_dots_w = [](wchar_t const *str) -> bool {
+ if (!str) {
+ return false;
+ }
+ return wcscmp(str, L".") == 0 || wcscmp(str, L"..") == 0;
+ };
+
+ TEMPORARY_ALLOCATOR_GUARD();
+
+ wchar_t dir_path[MAX_PATH] = {};
+ wchar_t filename[MAX_PATH] = {};
+ wcscpy_s(dir_path, wpath_c);
+ wcscat_s(dir_path, L"\\*");
+
+ wcscpy_s(filename, wpath_c);
+ wcscat_s(filename, L"\\");
+
+
+ WIN32_FIND_DATAW find_file_data = {};
+ HANDLE hfind = FindFirstFileW(dir_path, &find_file_data);
+ if (hfind == INVALID_HANDLE_VALUE) {
+ return false;
+ }
+ defer (FindClose(hfind));
+
+ wcscpy_s(dir_path, filename);
+
+ for (;;) {
+ if (FindNextFileW(hfind, &find_file_data)) {
+ if (is_dots_w(find_file_data.cFileName)) {
+ continue;
+ }
+ wcscat_s(filename, find_file_data.cFileName);
+
+ if (find_file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
+ if (!recursively_delete_directory(filename)) {
+ return false;
+ }
+ RemoveDirectoryW(filename);
+ wcscpy_s(filename, dir_path);
+ } else {
+ if (find_file_data.dwFileAttributes & FILE_ATTRIBUTE_READONLY) {
+ _wchmod(filename, _S_IWRITE);
+ }
+ if (!DeleteFileW(filename)) {
+ return false;
+ }
+ wcscpy_s(filename, dir_path);
+ }
+ } else {
+ if (GetLastError() == ERROR_NO_MORE_FILES) {
+ break;
+ }
+ return false;
+ }
+ }
+
+
+ return RemoveDirectoryW(wpath_c);
+#else
+ return false;
+#endif
+}
+
+gb_internal bool recursively_delete_directory(String const &path) {
+#if defined(GB_SYSTEM_WINDOWS)
+ String16 wpath = string_to_string16(permanent_allocator(), path);
+ wchar_t *wpath_c = alloc_wstring(permanent_allocator(), wpath);
+ return recursively_delete_directory(wpath_c);
+#else
+ return false;
+#endif
+}
+
+gb_internal bool try_clear_cache(void) {
+ return recursively_delete_directory(str_lit(".odin-cache"));
+}
+
+
+gb_internal u64 crc64_with_seed(void const *data, isize len, u64 seed) {
+ isize remaining;
+ u64 result = ~seed;
+ u8 const *c = cast(u8 const *)data;
+ for (remaining = len; remaining--; c++) {
+ result = (result >> 8) ^ (GB__CRC64_TABLE[(result ^ *c) & 0xff]);
+ }
+ return ~result;
+}
+
+gb_internal bool check_if_exists_file_otherwise_create(String const &str) {
+ char const *str_c = alloc_cstring(permanent_allocator(), str);
+ if (!gb_file_exists(str_c)) {
+ gbFile f = {};
+ gb_file_create(&f, str_c);
+ gb_file_close(&f);
+ return true;
+ }
+ return false;
+}
+
+
+gb_internal bool check_if_exists_directory_otherwise_create(String const &str) {
+#if defined(GB_SYSTEM_WINDOWS)
+ String16 wstr = string_to_string16(permanent_allocator(), str);
+ wchar_t *wstr_c = alloc_wstring(permanent_allocator(), wstr);
+ return CreateDirectoryW(wstr_c, nullptr);
+#else
+ char const *str_c = alloc_cstring(permanent_allocator(), str);
+ if (!gb_file_exists(str_c)) {
+ int status = mkdir(str_c, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
+ return status == 0;
+ }
+ return false;
+#endif
+}
+gb_internal bool try_copy_executable_cache_internal(bool to_cache) {
+ String exe_name = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_Output]);
+ defer (gb_free(heap_allocator(), exe_name.text));
+
+ gbString cache_name = gb_string_make(heap_allocator(), "");
+ defer (gb_string_free(cache_name));
+
+ String cache_dir = build_context.build_cache_data.cache_dir;
+
+ cache_name = gb_string_append_length(cache_name, cache_dir.text, cache_dir.len);
+ cache_name = gb_string_appendc(cache_name, "/");
+
+ cache_name = gb_string_appendc(cache_name, "cached-exe");
+ if (selected_target_metrics) {
+ cache_name = gb_string_appendc(cache_name, "-");
+ cache_name = gb_string_append_length(cache_name, selected_target_metrics->name.text, selected_target_metrics->name.len);
+ }
+ if (selected_subtarget) {
+ String st = subtarget_strings[selected_subtarget];
+ cache_name = gb_string_appendc(cache_name, "-");
+ cache_name = gb_string_append_length(cache_name, st.text, st.len);
+ }
+ cache_name = gb_string_appendc(cache_name, ".bin");
+
+ if (to_cache) {
+ return gb_file_copy(
+ alloc_cstring(temporary_allocator(), exe_name),
+ cache_name,
+ false
+ );
+ } else {
+ return gb_file_copy(
+ cache_name,
+ alloc_cstring(temporary_allocator(), exe_name),
+ false
+ );
+ }
+}
+
+
+
+gb_internal bool try_copy_executable_to_cache(void) {
+ debugf("Cache: try_copy_executable_to_cache\n");
+
+ if (try_copy_executable_cache_internal(true)) {
+ build_context.build_cache_data.copy_already_done = true;
+ return true;
+ }
+ return false;
+}
+
+gb_internal bool try_copy_executable_from_cache(void) {
+ debugf("Cache: try_copy_executable_from_cache\n");
+
+ if (try_copy_executable_cache_internal(false)) {
+ build_context.build_cache_data.copy_already_done = true;
+ return true;
+ }
+ return false;
+}
+
+
+#if !defined(GB_SYSTEM_WINDOWS)
+extern char **environ;
+#endif
+
+Array<String> cache_gather_files(Checker *c) {
+ Parser *p = c->parser;
+
+ auto files = array_make<String>(heap_allocator());
+ for (AstPackage *pkg : p->packages) {
+ for (AstFile *f : pkg->files) {
+ array_add(&files, f->fullpath);
+ }
+ }
+
+ #if defined(GB_SYSTEM_WINDOWS)
+ if (build_context.has_resource) {
+ String res_path = {};
+ if (build_context.build_paths[BuildPath_RC].basename == "") {
+ res_path = path_to_string(permanent_allocator(), build_context.build_paths[BuildPath_RES]);
+ } else {
+ res_path = path_to_string(permanent_allocator(), build_context.build_paths[BuildPath_RC]);
+ }
+ array_add(&files, res_path);
+ }
+ #endif
+
+ for (auto const &entry : c->info.load_file_cache) {
+ auto *cache = entry.value;
+ if (!cache || !cache->exists) {
+ continue;
+ }
+ array_add(&files, cache->path);
+ }
+
+ array_sort(files, string_cmp);
+
+ return files;
+}
+
+Array<String> cache_gather_envs() {
+ auto envs = array_make<String>(heap_allocator());
+ {
+ #if defined(GB_SYSTEM_WINDOWS)
+ wchar_t *strings = GetEnvironmentStringsW();
+ defer (FreeEnvironmentStringsW(strings));
+
+ wchar_t *curr_string = strings;
+ while (curr_string && *curr_string) {
+ String16 wstr = make_string16_c(curr_string);
+ curr_string += wstr.len+1;
+ String str = string16_to_string(temporary_allocator(), wstr);
+ if (string_starts_with(str, str_lit("CURR_DATE_TIME="))) {
+ continue;
+ }
+ array_add(&envs, str);
+ }
+ #else
+ char **curr_env = environ;
+ while (curr_env && *curr_env) {
+ String str = make_string_c(*curr_env++);
+ if (string_starts_with(str, str_lit("PROMPT="))) {
+ continue;
+ }
+ if (string_starts_with(str, str_lit("RPROMPT="))) {
+ continue;
+ }
+ array_add(&envs, str);
+ }
+ #endif
+ }
+ array_sort(envs, string_cmp);
+ return envs;
+}
+
+// returns false if different, true if it is the same
+gb_internal bool try_cached_build(Checker *c, Array<String> const &args) {
+ TEMPORARY_ALLOCATOR_GUARD();
+
+ auto files = cache_gather_files(c);
+ auto envs = cache_gather_envs();
+ defer (array_free(&envs));
+
+ u64 crc = 0;
+ for (String const &path : files) {
+ crc = crc64_with_seed(path.text, path.len, crc);
+ }
+
+ String base_cache_dir = build_context.build_paths[BuildPath_Output].basename;
+ base_cache_dir = concatenate_strings(permanent_allocator(), base_cache_dir, str_lit("/.odin-cache"));
+ (void)check_if_exists_directory_otherwise_create(base_cache_dir);
+
+ gbString crc_str = gb_string_make_reserve(permanent_allocator(), 16);
+ crc_str = gb_string_append_fmt(crc_str, "%016llx", crc);
+ String cache_dir = concatenate3_strings(permanent_allocator(), base_cache_dir, str_lit("/"), make_string_c(crc_str));
+ String files_path = concatenate3_strings(permanent_allocator(), cache_dir, str_lit("/"), str_lit("files.manifest"));
+ String args_path = concatenate3_strings(permanent_allocator(), cache_dir, str_lit("/"), str_lit("args.manifest"));
+ String env_path = concatenate3_strings(permanent_allocator(), cache_dir, str_lit("/"), str_lit("env.manifest"));
+
+ build_context.build_cache_data.cache_dir = cache_dir;
+ build_context.build_cache_data.files_path = files_path;
+ build_context.build_cache_data.args_path = args_path;
+ build_context.build_cache_data.env_path = env_path;
+
+ if (check_if_exists_directory_otherwise_create(cache_dir)) {
+ return false;
+ }
+
+ if (check_if_exists_file_otherwise_create(files_path)) {
+ return false;
+ }
+ if (check_if_exists_file_otherwise_create(args_path)) {
+ return false;
+ }
+ if (check_if_exists_file_otherwise_create(env_path)) {
+ return false;
+ }
+
+ {
+ // exists already
+ LoadedFile loaded_file = {};
+
+ LoadedFileError file_err = load_file_32(
+ alloc_cstring(temporary_allocator(), files_path),
+ &loaded_file,
+ true
+ );
+ if (file_err > LoadedFile_Empty) {
+ return false;
+ }
+
+ String data = {cast(u8 *)loaded_file.data, loaded_file.size};
+ String_Iterator it = {data, 0};
+
+ isize file_count = 0;
+
+ for (; it.pos < data.len; file_count++) {
+ String line = string_split_iterator(&it, '\n');
+ if (line.len == 0) {
+ break;
+ }
+ isize sep = string_index_byte(line, ' ');
+ if (sep < 0) {
+ return false;
+ }
+
+ String timestamp_str = substring(line, 0, sep);
+ String path_str = substring(line, sep+1, line.len);
+
+ timestamp_str = string_trim_whitespace(timestamp_str);
+ path_str = string_trim_whitespace(path_str);
+
+ if (file_count >= files.count) {
+ return false;
+ }
+ if (files[file_count] != path_str) {
+ return false;
+ }
+
+ u64 timestamp = exact_value_to_u64(exact_value_integer_from_string(timestamp_str));
+ gbFileTime last_write_time = gb_file_last_write_time(alloc_cstring(temporary_allocator(), path_str));
+ if (last_write_time != timestamp) {
+ return false;
+ }
+ }
+
+ if (file_count != files.count) {
+ return false;
+ }
+ }
+ {
+ LoadedFile loaded_file = {};
+
+ LoadedFileError file_err = load_file_32(
+ alloc_cstring(temporary_allocator(), args_path),
+ &loaded_file,
+ true
+ );
+ if (file_err > LoadedFile_Empty) {
+ return false;
+ }
+
+ String data = {cast(u8 *)loaded_file.data, loaded_file.size};
+ String_Iterator it = {data, 0};
+
+ isize args_count = 0;
+
+ for (; it.pos < data.len; args_count++) {
+ String line = string_split_iterator(&it, '\n');
+ line = string_trim_whitespace(line);
+ if (line.len == 0) {
+ break;
+ }
+ if (args_count >= args.count) {
+ return false;
+ }
+
+ if (line != args[args_count]) {
+ return false;
+ }
+ }
+ }
+ {
+ LoadedFile loaded_file = {};
+
+ LoadedFileError file_err = load_file_32(
+ alloc_cstring(temporary_allocator(), env_path),
+ &loaded_file,
+ true
+ );
+ if (file_err > LoadedFile_Empty) {
+ return false;
+ }
+
+ String data = {cast(u8 *)loaded_file.data, loaded_file.size};
+ String_Iterator it = {data, 0};
+
+ isize env_count = 0;
+
+ for (; it.pos < data.len; env_count++) {
+ String line = string_split_iterator(&it, '\n');
+ line = string_trim_whitespace(line);
+ if (line.len == 0) {
+ break;
+ }
+ if (env_count >= envs.count) {
+ return false;
+ }
+
+ if (line != envs[env_count]) {
+ return false;
+ }
+ }
+ }
+
+ return try_copy_executable_from_cache();
+}
+
+void write_cached_build(Checker *c, Array<String> const &args) {
+ auto files = cache_gather_files(c);
+ defer (array_free(&files));
+ auto envs = cache_gather_envs();
+ defer (array_free(&envs));
+
+ {
+ char const *path_c = alloc_cstring(temporary_allocator(), build_context.build_cache_data.files_path);
+ gb_file_remove(path_c);
+
+ debugf("Cache: updating %s\n", path_c);
+
+ gbFile f = {};
+ defer (gb_file_close(&f));
+ gb_file_open_mode(&f, gbFileMode_Write, path_c);
+
+ for (String const &path : files) {
+ gbFileTime ft = gb_file_last_write_time(alloc_cstring(temporary_allocator(), path));
+ gb_fprintf(&f, "%llu %.*s\n", cast(unsigned long long)ft, LIT(path));
+ }
+ }
+ {
+ char const *path_c = alloc_cstring(temporary_allocator(), build_context.build_cache_data.args_path);
+ gb_file_remove(path_c);
+
+ debugf("Cache: updating %s\n", path_c);
+
+ gbFile f = {};
+ defer (gb_file_close(&f));
+ gb_file_open_mode(&f, gbFileMode_Write, path_c);
+
+ for (String const &arg : args) {
+ String targ = string_trim_whitespace(arg);
+ gb_fprintf(&f, "%.*s\n", LIT(targ));
+ }
+ }
+ {
+ char const *path_c = alloc_cstring(temporary_allocator(), build_context.build_cache_data.env_path);
+ gb_file_remove(path_c);
+
+ debugf("Cache: updating %s\n", path_c);
+
+ gbFile f = {};
+ defer (gb_file_close(&f));
+ gb_file_open_mode(&f, gbFileMode_Write, path_c);
+
+ for (String const &env : envs) {
+ gb_fprintf(&f, "%.*s\n", LIT(env));
+ }
+ }
+}
+
diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp
index 77ba6b435..ea902387b 100644
--- a/src/check_builtin.cpp
+++ b/src/check_builtin.cpp
@@ -89,6 +89,7 @@ gb_internal void check_or_else_split_types(CheckerContext *c, Operand *x, String
gb_internal void check_or_else_expr_no_value_error(CheckerContext *c, String const &name, Operand const &x, Type *type_hint) {
+ ERROR_BLOCK();
gbString t = type_to_string(x.type);
error(x.expr, "'%.*s' does not return a value, value is of type %s", LIT(name), t);
if (is_type_union(type_deref(x.type))) {
@@ -154,6 +155,11 @@ gb_internal bool does_require_msgSend_stret(Type *return_type) {
return false;
}
+ // No objc here so this doesn't matter, right?
+ if (build_context.metrics.arch == TargetArch_riscv64) {
+ return false;
+ }
+
// if (build_context.metrics.arch == TargetArch_arm32) {
// i64 struct_limit = type_size_of(t_uintptr);
// // NOTE(bill): This is technically wrong
@@ -469,8 +475,8 @@ gb_internal bool check_builtin_simd_operation(CheckerContext *c, Operand *operan
}
// Integer only
- case BuiltinProc_simd_add_sat:
- case BuiltinProc_simd_sub_sat:
+ case BuiltinProc_simd_saturating_add:
+ case BuiltinProc_simd_saturating_sub:
case BuiltinProc_simd_bit_and:
case BuiltinProc_simd_bit_or:
case BuiltinProc_simd_bit_xor:
@@ -500,8 +506,8 @@ gb_internal bool check_builtin_simd_operation(CheckerContext *c, Operand *operan
Type *elem = base_array_type(x.type);
switch (id) {
- case BuiltinProc_simd_add_sat:
- case BuiltinProc_simd_sub_sat:
+ case BuiltinProc_simd_saturating_add:
+ case BuiltinProc_simd_saturating_sub:
if (!is_type_integer(elem)) {
gbString xs = type_to_string(x.type);
error(x.expr, "'%.*s' expected a #simd type with an integer element, got '%s'", LIT(builtin_name), xs);
@@ -662,6 +668,91 @@ gb_internal bool check_builtin_simd_operation(CheckerContext *c, Operand *operan
return true;
}
+ case BuiltinProc_simd_gather:
+ case BuiltinProc_simd_scatter:
+ case BuiltinProc_simd_masked_load:
+ case BuiltinProc_simd_masked_store:
+ case BuiltinProc_simd_masked_expand_load:
+ case BuiltinProc_simd_masked_compress_store:
+ {
+ // gather (ptr: #simd[N]rawptr, values: #simd[N]T, mask: #simd[N]int_or_bool) -> #simd[N]T
+ // scatter(ptr: #simd[N]rawptr, values: #simd[N]T, mask: #simd[N]int_or_bool)
+
+ // masked_load (ptr: rawptr, values: #simd[N]T, mask: #simd[N]int_or_bool) -> #simd[N]T
+ // masked_store(ptr: rawptr, values: #simd[N]T, mask: #simd[N]int_or_bool)
+ // masked_expand_load (ptr: rawptr, values: #simd[N]T, mask: #simd[N]int_or_bool) -> #simd[N]T
+ // masked_compress_store(ptr: rawptr, values: #simd[N]T, mask: #simd[N]int_or_bool)
+
+ Operand ptr = {};
+ Operand values = {};
+ Operand mask = {};
+ check_expr(c, &ptr, ce->args[0]); if (ptr.mode == Addressing_Invalid) return false;
+ check_expr(c, &values, ce->args[1]); if (values.mode == Addressing_Invalid) return false;
+ check_expr(c, &mask, ce->args[2]); if (mask.mode == Addressing_Invalid) return false;
+ if (!is_type_simd_vector(values.type)) { error(values.expr, "'%.*s' expected a simd vector type", LIT(builtin_name)); return false; }
+ if (!is_type_simd_vector(mask.type)) { error(mask.expr, "'%.*s' expected a simd vector type", LIT(builtin_name)); return false; }
+
+ if (id == BuiltinProc_simd_gather || id == BuiltinProc_simd_scatter) {
+ if (!is_type_simd_vector(ptr.type)) { error(ptr.expr, "'%.*s' expected a simd vector type", LIT(builtin_name)); return false; }
+ Type *ptr_elem = base_array_type(ptr.type);
+ if (!is_type_rawptr(ptr_elem)) {
+ gbString s = type_to_string(ptr.type);
+ error(ptr.expr, "Expected a simd vector of 'rawptr' for the addresses, got %s", s);
+ gb_string_free(s);
+ return false;
+ }
+ } else {
+ if (!is_type_pointer(ptr.type)) {
+ gbString s = type_to_string(ptr.type);
+ error(ptr.expr, "Expected a pointer type for the address, got %s", s);
+ gb_string_free(s);
+ return false;
+ }
+ }
+ Type *mask_elem = base_array_type(mask.type);
+
+ if (!is_type_integer(mask_elem) && !is_type_boolean(mask_elem)) {
+ gbString s = type_to_string(mask.type);
+ error(mask.expr, "Expected a simd vector of integers or booleans for the mask, got %s", s);
+ gb_string_free(s);
+ return false;
+ }
+
+ if (id == BuiltinProc_simd_gather || id == BuiltinProc_simd_scatter) {
+ i64 ptr_count = get_array_type_count(ptr.type);
+ i64 values_count = get_array_type_count(values.type);
+ i64 mask_count = get_array_type_count(mask.type);
+ if (ptr_count != values_count ||
+ values_count != mask_count ||
+ mask_count != ptr_count) {
+ gbString s = type_to_string(mask.type);
+ error(mask.expr, "All simd vectors must be of the same length, got %lld vs %lld vs %lld", cast(long long)ptr_count, cast(long long)values_count, cast(long long)mask_count);
+ gb_string_free(s);
+ return false;
+ }
+ } else {
+ i64 values_count = get_array_type_count(values.type);
+ i64 mask_count = get_array_type_count(mask.type);
+ if (values_count != mask_count) {
+ gbString s = type_to_string(mask.type);
+ error(mask.expr, "All simd vectors must be of the same length, got %lld vs %lld", cast(long long)values_count, cast(long long)mask_count);
+ gb_string_free(s);
+ return false;
+ }
+ }
+
+ if (id == BuiltinProc_simd_gather ||
+ id == BuiltinProc_simd_masked_load ||
+ id == BuiltinProc_simd_masked_expand_load) {
+ operand->mode = Addressing_Value;
+ operand->type = values.type;
+ } else {
+ operand->mode = Addressing_NoValue;
+ operand->type = nullptr;
+ }
+ return true;
+ }
+
case BuiltinProc_simd_extract:
{
Operand x = {};
@@ -774,6 +865,29 @@ gb_internal bool check_builtin_simd_operation(CheckerContext *c, Operand *operan
return true;
}
+ case BuiltinProc_simd_reduce_any:
+ case BuiltinProc_simd_reduce_all:
+ {
+ Operand x = {};
+ check_expr(c, &x, ce->args[0]); if (x.mode == Addressing_Invalid) return false;
+
+ if (!is_type_simd_vector(x.type)) {
+ error(x.expr, "'%.*s' expected a simd vector type", LIT(builtin_name));
+ return false;
+ }
+ Type *elem = base_array_type(x.type);
+ if (!is_type_boolean(elem)) {
+ gbString xs = type_to_string(x.type);
+ error(x.expr, "'%.*s' expected a #simd type with a boolean element, got '%s'", LIT(builtin_name), xs);
+ gb_string_free(xs);
+ return false;
+ }
+
+ operand->mode = Addressing_Value;
+ operand->type = t_untyped_bool;
+ return true;
+ }
+
case BuiltinProc_simd_shuffle:
{
@@ -1078,29 +1192,43 @@ gb_internal bool check_builtin_simd_operation(CheckerContext *c, Operand *operan
return false;
}
-gb_internal bool cache_load_file_directive(CheckerContext *c, Ast *call, String const &original_string, bool err_on_not_found, LoadFileCache **cache_) {
+gb_internal bool cache_load_file_directive(CheckerContext *c, Ast *call, String const &original_string, bool err_on_not_found, LoadFileCache **cache_, LoadFileTier tier, bool use_mutex=true) {
ast_node(ce, CallExpr, call);
ast_node(bd, BasicDirective, ce->proc);
String builtin_name = bd->name.string;
- String base_dir = dir_from_path(get_file_path_string(call->file_id));
-
- BlockingMutex *ignore_mutex = nullptr;
- String path = {};
- bool ok = determine_path_from_string(ignore_mutex, call, base_dir, original_string, &path);
- gb_unused(ok);
+ String path;
+ if (gb_path_is_absolute((char*)original_string.text)) {
+ path = original_string;
+ } else {
+ String base_dir = dir_from_path(get_file_path_string(call->file_id));
+ BlockingMutex *ignore_mutex = nullptr;
+ bool ok = determine_path_from_string(ignore_mutex, call, base_dir, original_string, &path);
+ if (!ok) {
+ if (err_on_not_found) {
+ error(ce->proc, "Failed to `#%.*s` file: %.*s; invalid file or cannot be found", LIT(builtin_name), LIT(original_string));
+ }
+ call->state_flags |= StateFlag_DirectiveWasFalse;
+ return false;
+ }
+ }
- MUTEX_GUARD(&c->info->load_file_mutex);
+ if (use_mutex) mutex_lock(&c->info->load_file_mutex);
+ defer (if (use_mutex) mutex_unlock(&c->info->load_file_mutex));
gbFileError file_error = gbFileError_None;
String data = {};
+ bool exists = false;
+ LoadFileTier cache_tier = LoadFileTier_Invalid;
LoadFileCache **cache_ptr = string_map_get(&c->info->load_file_cache, path);
LoadFileCache *cache = cache_ptr ? *cache_ptr : nullptr;
if (cache) {
file_error = cache->file_error;
data = cache->data;
+ exists = cache->exists;
+ cache_tier = cache->tier;
}
defer ({
if (cache == nullptr) {
@@ -1108,60 +1236,78 @@ gb_internal bool cache_load_file_directive(CheckerContext *c, Ast *call, String
new_cache->path = path;
new_cache->data = data;
new_cache->file_error = file_error;
+ new_cache->exists = exists;
+ new_cache->tier = cache_tier;
string_map_init(&new_cache->hashes, 32);
string_map_set(&c->info->load_file_cache, path, new_cache);
if (cache_) *cache_ = new_cache;
} else {
cache->data = data;
cache->file_error = file_error;
+ cache->exists = exists;
+ cache->tier = cache_tier;
if (cache_) *cache_ = cache;
}
});
- TEMPORARY_ALLOCATOR_GUARD();
- char *c_str = alloc_cstring(temporary_allocator(), path);
+ if (tier > cache_tier) {
+ cache_tier = tier;
+
+ TEMPORARY_ALLOCATOR_GUARD();
+ char *c_str = alloc_cstring(temporary_allocator(), path);
- gbFile f = {};
- if (cache == nullptr) {
+ gbFile f = {};
file_error = gb_file_open(&f, c_str);
+ defer (gb_file_close(&f));
+
+ if (file_error == gbFileError_None) {
+ exists = true;
+
+ switch(tier) {
+ case LoadFileTier_Exists:
+ // Nothing to do.
+ break;
+ case LoadFileTier_Contents: {
+ isize file_size = cast(isize)gb_file_size(&f);
+ if (file_size > 0) {
+ u8 *ptr = cast(u8 *)gb_alloc(permanent_allocator(), file_size+1);
+ gb_file_read_at(&f, ptr, file_size, 0);
+ ptr[file_size] = '\0';
+ data.text = ptr;
+ data.len = file_size;
+ }
+ break;
+ }
+ default:
+ GB_PANIC("Unhandled LoadFileTier");
+ };
+ }
}
- defer (gb_file_close(&f));
switch (file_error) {
default:
case gbFileError_Invalid:
if (err_on_not_found) {
- error(ce->proc, "Failed to `#%.*s` file: %s; invalid file or cannot be found", LIT(builtin_name), c_str);
+ error(ce->proc, "Failed to `#%.*s` file: %.*s; invalid file or cannot be found", LIT(builtin_name), LIT(path));
}
call->state_flags |= StateFlag_DirectiveWasFalse;
return false;
case gbFileError_NotExists:
if (err_on_not_found) {
- error(ce->proc, "Failed to `#%.*s` file: %s; file cannot be found", LIT(builtin_name), c_str);
+ error(ce->proc, "Failed to `#%.*s` file: %.*s; file cannot be found", LIT(builtin_name), LIT(path));
}
call->state_flags |= StateFlag_DirectiveWasFalse;
return false;
case gbFileError_Permission:
if (err_on_not_found) {
- error(ce->proc, "Failed to `#%.*s` file: %s; file permissions problem", LIT(builtin_name), c_str);
+ error(ce->proc, "Failed to `#%.*s` file: %.*s; file permissions problem", LIT(builtin_name), LIT(path));
}
call->state_flags |= StateFlag_DirectiveWasFalse;
return false;
case gbFileError_None:
// Okay
break;
- }
-
- if (cache == nullptr) {
- isize file_size = cast(isize)gb_file_size(&f);
- if (file_size > 0) {
- u8 *ptr = cast(u8 *)gb_alloc(permanent_allocator(), file_size+1);
- gb_file_read_at(&f, ptr, file_size, 0);
- ptr[file_size] = '\0';
- data.text = ptr;
- data.len = file_size;
- }
- }
+ };
return true;
}
@@ -1253,7 +1399,7 @@ gb_internal LoadDirectiveResult check_load_directive(CheckerContext *c, Operand
operand->mode = Addressing_Constant;
LoadFileCache *cache = nullptr;
- if (cache_load_file_directive(c, call, o.value.value_string, err_on_not_found, &cache)) {
+ if (cache_load_file_directive(c, call, o.value.value_string, err_on_not_found, &cache, LoadFileTier_Contents)) {
operand->value = exact_value_string(cache->data);
return LoadDirective_Success;
}
@@ -1261,6 +1407,210 @@ gb_internal LoadDirectiveResult check_load_directive(CheckerContext *c, Operand
}
+gb_internal int file_cache_sort_cmp(void const *x, void const *y) {
+ LoadFileCache const *a = *(LoadFileCache const **)(x);
+ LoadFileCache const *b = *(LoadFileCache const **)(y);
+ if (a == b) {
+ return 0;
+ }
+ return string_compare(a->path, b->path);
+}
+
+gb_internal LoadDirectiveResult check_load_directory_directive(CheckerContext *c, Operand *operand, Ast *call, Type *type_hint, bool err_on_not_found) {
+ ast_node(ce, CallExpr, call);
+ ast_node(bd, BasicDirective, ce->proc);
+ String name = bd->name.string;
+ GB_ASSERT(name == "load_directory");
+
+ if (ce->args.count != 1) {
+ error(ce->args[0], "'#%.*s' expects 1 argument, got %td", LIT(name), ce->args.count);
+ return LoadDirective_Error;
+ }
+
+ Ast *arg = ce->args[0];
+ Operand o = {};
+ check_expr(c, &o, arg);
+ if (o.mode != Addressing_Constant) {
+ error(arg, "'#%.*s' expected a constant string argument", LIT(name));
+ return LoadDirective_Error;
+ }
+
+ if (!is_type_string(o.type)) {
+ gbString str = type_to_string(o.type);
+ error(arg, "'#%.*s' expected a constant string, got %s", LIT(name), str);
+ gb_string_free(str);
+ return LoadDirective_Error;
+ }
+
+ GB_ASSERT(o.value.kind == ExactValue_String);
+
+ init_core_load_directory_file(c->checker);
+
+ operand->type = t_load_directory_file_slice;
+ operand->mode = Addressing_Value;
+
+
+ String original_string = o.value.value_string;
+ String path;
+ if (gb_path_is_absolute((char*)original_string.text)) {
+ path = original_string;
+ } else {
+ String base_dir = dir_from_path(get_file_path_string(call->file_id));
+
+ BlockingMutex *ignore_mutex = nullptr;
+ bool ok = determine_path_from_string(ignore_mutex, call, base_dir, original_string, &path);
+ gb_unused(ok);
+ }
+ MUTEX_GUARD(&c->info->load_directory_mutex);
+
+
+ gbFileError file_error = gbFileError_None;
+
+ Array<LoadFileCache *> file_caches = {};
+
+ LoadDirectoryCache **cache_ptr = string_map_get(&c->info->load_directory_cache, path);
+ LoadDirectoryCache *cache = cache_ptr ? *cache_ptr : nullptr;
+ if (cache) {
+ file_error = cache->file_error;
+ }
+ defer ({
+ if (cache == nullptr) {
+ LoadDirectoryCache *new_cache = gb_alloc_item(permanent_allocator(), LoadDirectoryCache);
+ new_cache->path = path;
+ new_cache->files = file_caches;
+ new_cache->file_error = file_error;
+ string_map_set(&c->info->load_directory_cache, path, new_cache);
+
+ map_set(&c->info->load_directory_map, call, new_cache);
+ } else {
+ cache->file_error = file_error;
+
+ map_set(&c->info->load_directory_map, call, cache);
+ }
+ });
+
+
+ LoadDirectiveResult result = LoadDirective_Success;
+
+
+ if (cache == nullptr) {
+ Array<FileInfo> list = {};
+ ReadDirectoryError rd_err = read_directory(path, &list);
+ defer (array_free(&list));
+
+ if (list.count == 1) {
+ GB_ASSERT(path != list[0].fullpath);
+ }
+
+
+ switch (rd_err) {
+ case ReadDirectory_InvalidPath:
+ error(call, "%.*s error - invalid path: %.*s", LIT(name), LIT(original_string));
+ return LoadDirective_NotFound;
+ case ReadDirectory_NotExists:
+ error(call, "%.*s error - path does not exist: %.*s", LIT(name), LIT(original_string));
+ return LoadDirective_NotFound;
+ case ReadDirectory_Permission:
+ error(call, "%.*s error - unknown error whilst reading path, %.*s", LIT(name), LIT(original_string));
+ return LoadDirective_Error;
+ case ReadDirectory_NotDir:
+ error(call, "%.*s error - expected a directory, got a file: %.*s", LIT(name), LIT(original_string));
+ return LoadDirective_Error;
+ case ReadDirectory_Empty:
+ error(call, "%.*s error - empty directory: %.*s", LIT(name), LIT(original_string));
+ return LoadDirective_NotFound;
+ case ReadDirectory_Unknown:
+ error(call, "%.*s error - unknown error whilst reading path %.*s", LIT(name), LIT(original_string));
+ return LoadDirective_Error;
+ }
+
+ isize files_to_reserve = list.count+1; // always reserve 1
+
+ file_caches = array_make<LoadFileCache *>(heap_allocator(), 0, files_to_reserve);
+
+ mutex_lock(&c->info->load_file_mutex);
+ defer (mutex_unlock(&c->info->load_file_mutex));
+
+ for (FileInfo fi : list) {
+ LoadFileCache *cache = nullptr;
+ if (fi.is_dir) {
+ continue;
+ }
+
+ if (cache_load_file_directive(c, call, fi.fullpath, err_on_not_found, &cache, LoadFileTier_Contents, /*use_mutex*/false)) {
+ array_add(&file_caches, cache);
+ } else {
+ result = LoadDirective_Error;
+ }
+ }
+
+ array_sort(file_caches, file_cache_sort_cmp);
+
+ }
+
+ return result;
+}
+
+gb_internal bool check_hash_kind(CheckerContext *c, Ast *call, String const &hash_kind, u8 const *data, isize data_size, u64 *hash_value) {
+ ast_node(ce, CallExpr, call);
+ ast_node(bd, BasicDirective, ce->proc);
+ String name = bd->name.string;
+ GB_ASSERT(name == "load_hash" || name == "hash");
+
+ String supported_hashes[] = {
+ str_lit("adler32"),
+ str_lit("crc32"),
+ str_lit("crc64"),
+ str_lit("fnv32"),
+ str_lit("fnv64"),
+ str_lit("fnv32a"),
+ str_lit("fnv64a"),
+ str_lit("murmur32"),
+ str_lit("murmur64"),
+ };
+
+ bool hash_found = false;
+ for (isize i = 0; i < gb_count_of(supported_hashes); i++) {
+ if (supported_hashes[i] == hash_kind) {
+ hash_found = true;
+ break;
+ }
+ }
+ if (!hash_found) {
+ ERROR_BLOCK();
+ error(ce->proc, "Invalid hash kind passed to `#%.*s`, got: %.*s", LIT(name), LIT(hash_kind));
+ error_line("\tAvailable hash kinds:\n");
+ for (isize i = 0; i < gb_count_of(supported_hashes); i++) {
+ error_line("\t%.*s\n", LIT(supported_hashes[i]));
+ }
+ return false;
+ }
+
+ if (hash_kind == "adler32") {
+ *hash_value = gb_adler32(data, data_size);
+ } else if (hash_kind == "crc32") {
+ *hash_value = gb_crc32(data, data_size);
+ } else if (hash_kind == "crc64") {
+ *hash_value = gb_crc64(data, data_size);
+ } else if (hash_kind == "fnv32") {
+ *hash_value = gb_fnv32(data, data_size);
+ } else if (hash_kind == "fnv64") {
+ *hash_value = gb_fnv64(data, data_size);
+ } else if (hash_kind == "fnv32a") {
+ *hash_value = fnv32a(data, data_size);
+ } else if (hash_kind == "fnv64a") {
+ *hash_value = fnv64a(data, data_size);
+ } else if (hash_kind == "murmur32") {
+ *hash_value = gb_murmur32(data, data_size);
+ } else if (hash_kind == "murmur64") {
+ *hash_value = gb_murmur64(data, data_size);
+ } else {
+ compiler_error("unhandled hash kind: %.*s", LIT(hash_kind));
+ }
+ return true;
+}
+
+
gb_internal bool check_builtin_procedure_directive(CheckerContext *c, Operand *operand, Ast *call, Type *type_hint) {
ast_node(ce, CallExpr, call);
@@ -1286,8 +1636,50 @@ gb_internal bool check_builtin_procedure_directive(CheckerContext *c, Operand *o
operand->type = t_source_code_location;
operand->mode = Addressing_Value;
+ } else if (name == "caller_expression") {
+ if (ce->args.count > 1) {
+ error(ce->args[0], "'#caller_expression' expects either 0 or 1 arguments, got %td", ce->args.count);
+ }
+ 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");
+ }
+ arg->Ident.entity = e;
+ }
+
+ operand->type = t_string;
+ operand->mode = Addressing_Value;
+ } else if (name == "exists") {
+ if (ce->args.count != 1) {
+ error(ce->close, "'#exists' expects 1 argument, got %td", ce->args.count);
+ return false;
+ }
+
+ Operand o = {};
+ check_expr(c, &o, ce->args[0]);
+ if (o.mode != Addressing_Constant || !is_type_string(o.type)) {
+ error(ce->args[0], "'#exists' expected a constant string argument");
+ return false;
+ }
+
+ operand->type = t_untyped_bool;
+ operand->mode = Addressing_Constant;
+
+ String original_string = o.value.value_string;
+ LoadFileCache *cache = nullptr;
+ if (cache_load_file_directive(c, call, original_string, /* err_on_not_found=*/ false, &cache, LoadFileTier_Exists)) {
+ operand->value = exact_value_bool(cache->exists);
+ } else {
+ operand->value = exact_value_bool(false);
+ }
+
} else if (name == "load") {
return check_load_directive(c, operand, call, type_hint, true) == LoadDirective_Success;
+ } else if (name == "load_directory") {
+ return check_load_directory_directive(c, operand, call, type_hint, true) == LoadDirective_Success;
} else if (name == "load_hash") {
if (ce->args.count != 2) {
if (ce->args.count == 0) {
@@ -1335,37 +1727,8 @@ gb_internal bool check_builtin_procedure_directive(CheckerContext *c, Operand *o
String original_string = o.value.value_string;
String hash_kind = o_hash.value.value_string;
- String supported_hashes[] = {
- str_lit("adler32"),
- str_lit("crc32"),
- str_lit("crc64"),
- str_lit("fnv32"),
- str_lit("fnv64"),
- str_lit("fnv32a"),
- str_lit("fnv64a"),
- str_lit("murmur32"),
- str_lit("murmur64"),
- };
-
- bool hash_found = false;
- for (isize i = 0; i < gb_count_of(supported_hashes); i++) {
- if (supported_hashes[i] == hash_kind) {
- hash_found = true;
- break;
- }
- }
- if (!hash_found) {
- ERROR_BLOCK();
- error(ce->proc, "Invalid hash kind passed to `#load_hash`, got: %.*s", LIT(hash_kind));
- error_line("\tAvailable hash kinds:\n");
- for (isize i = 0; i < gb_count_of(supported_hashes); i++) {
- error_line("\t%.*s\n", LIT(supported_hashes[i]));
- }
- return false;
- }
-
LoadFileCache *cache = nullptr;
- if (cache_load_file_directive(c, call, original_string, true, &cache)) {
+ if (cache_load_file_directive(c, call, original_string, true, &cache, LoadFileTier_Contents)) {
MUTEX_GUARD(&c->info->load_file_mutex);
// TODO(bill): make these procedures fast :P
u64 hash_value = 0;
@@ -1375,26 +1738,9 @@ gb_internal bool check_builtin_procedure_directive(CheckerContext *c, Operand *o
} else {
u8 *data = cache->data.text;
isize file_size = cache->data.len;
- if (hash_kind == "adler32") {
- hash_value = gb_adler32(data, file_size);
- } else if (hash_kind == "crc32") {
- hash_value = gb_crc32(data, file_size);
- } else if (hash_kind == "crc64") {
- hash_value = gb_crc64(data, file_size);
- } else if (hash_kind == "fnv32") {
- hash_value = gb_fnv32(data, file_size);
- } else if (hash_kind == "fnv64") {
- hash_value = gb_fnv64(data, file_size);
- } else if (hash_kind == "fnv32a") {
- hash_value = fnv32a(data, file_size);
- } else if (hash_kind == "fnv64a") {
- hash_value = fnv64a(data, file_size);
- } else if (hash_kind == "murmur32") {
- hash_value = gb_murmur32(data, file_size);
- } else if (hash_kind == "murmur64") {
- hash_value = gb_murmur64(data, file_size);
- } else {
- compiler_error("unhandled hash kind: %.*s", LIT(hash_kind));
+
+ if (!check_hash_kind(c, call, hash_kind, data, file_size, &hash_value)) {
+ return false;
}
string_map_set(&cache->hashes, hash_kind, hash_value);
}
@@ -1405,64 +1751,71 @@ gb_internal bool check_builtin_procedure_directive(CheckerContext *c, Operand *o
return true;
}
return false;
- } else if (name == "load_or") {
- error(call, "'#load_or' has now been removed in favour of '#load(path) or_else default'");
-
- if (ce->args.count != 2) {
+ } else if (name == "hash") {
+ if (ce->args.count != 2) {
if (ce->args.count == 0) {
- error(ce->close, "'#load_or' expects 2 arguments, got 0");
+ error(ce->close, "'#hash' expects 2 argument, got 0");
} else {
- error(ce->args[0], "'#load_or' expects 2 arguments, got %td", ce->args.count);
+ error(ce->args[0], "'#hash' expects 2 argument, got %td", ce->args.count);
}
return false;
}
- Ast *arg = ce->args[0];
+ Ast *arg0 = ce->args[0];
+ Ast *arg1 = ce->args[1];
Operand o = {};
- check_expr(c, &o, arg);
+ check_expr(c, &o, arg0);
if (o.mode != Addressing_Constant) {
- error(arg, "'#load_or' expected a constant string argument");
+ error(arg0, "'#hash' expected a constant string argument");
return false;
}
if (!is_type_string(o.type)) {
gbString str = type_to_string(o.type);
- error(arg, "'#load_or' expected a constant string, got %s", str);
+ error(arg0, "'#hash' expected a constant string, got %s", str);
gb_string_free(str);
return false;
}
- Ast *default_arg = ce->args[1];
- Operand default_op = {};
- check_expr_with_type_hint(c, &default_op, default_arg, t_u8_slice);
- if (default_op.mode != Addressing_Constant) {
- error(arg, "'#load_or' expected a constant '[]byte' argument");
+ Operand o_hash = {};
+ check_expr(c, &o_hash, arg1);
+ if (o_hash.mode != Addressing_Constant) {
+ error(arg1, "'#hash' expected a constant string argument");
return false;
}
- if (!are_types_identical(base_type(default_op.type), t_u8_slice)) {
- gbString str = type_to_string(default_op.type);
- error(arg, "'#load_or' expected a constant '[]byte', got %s", str);
+ if (!is_type_string(o_hash.type)) {
+ gbString str = type_to_string(o.type);
+ error(arg1, "'#hash' expected a constant string, got %s", str);
gb_string_free(str);
return false;
}
+ gbAllocator a = heap_allocator();
+
GB_ASSERT(o.value.kind == ExactValue_String);
+ GB_ASSERT(o_hash.value.kind == ExactValue_String);
+
String original_string = o.value.value_string;
+ String hash_kind = o_hash.value.value_string;
- operand->type = t_u8_slice;
- operand->mode = Addressing_Constant;
- LoadFileCache *cache = nullptr;
- if (cache_load_file_directive(c, call, original_string, false, &cache)) {
- operand->value = exact_value_string(cache->data);
- } else {
- operand->value = default_op.value;
+ // TODO: Cache hash values based off of string constant and hash kind?
+ u64 hash_value = 0;
+ if (check_hash_kind(c, call, hash_kind, original_string.text, original_string.len, &hash_value)) {
+ operand->type = t_untyped_integer;
+ operand->mode = Addressing_Constant;
+ operand->value = exact_value_u64(hash_value);
+ return true;
}
+ return false;
} else if (name == "assert") {
if (ce->args.count != 1 && ce->args.count != 2) {
error(call, "'#assert' expects either 1 or 2 arguments, got %td", ce->args.count);
return false;
}
- if (!is_type_boolean(operand->type) || operand->mode != Addressing_Constant) {
+
+ // operand->type can be nil if the condition is a procedure, for example: #assert(assert())
+ // So let's check it before we use it, so we get the same error as if we wrote `#exists(assert())
+ if (operand->type == nullptr || !is_type_boolean(operand->type) || operand->mode != Addressing_Constant) {
gbString str = expr_to_string(ce->args[0]);
error(call, "'%s' is not a constant boolean", str);
gb_string_free(str);
@@ -1479,6 +1832,7 @@ gb_internal bool check_builtin_procedure_directive(CheckerContext *c, Operand *o
}
if (!operand->value.value_bool) {
+ ERROR_BLOCK();
gbString arg1 = expr_to_string(ce->args[0]);
gbString arg2 = {};
@@ -1504,6 +1858,7 @@ gb_internal bool check_builtin_procedure_directive(CheckerContext *c, Operand *o
operand->type = t_untyped_bool;
operand->mode = Addressing_Constant;
} else if (name == "panic") {
+ ERROR_BLOCK();
if (ce->args.count != 1) {
error(call, "'#panic' expects 1 argument, got %td", ce->args.count);
return false;
@@ -1514,11 +1869,13 @@ gb_internal bool check_builtin_procedure_directive(CheckerContext *c, Operand *o
gb_string_free(str);
return false;
}
- error(call, "Compile time panic: %.*s", LIT(operand->value.value_string));
- if (c->proc_name != "") {
- gbString str = type_to_string(c->curr_proc_sig);
- error_line("\tCalled within '%.*s' :: %s\n", LIT(c->proc_name), str);
- gb_string_free(str);
+ if (!build_context.ignore_panic) {
+ error(call, "Compile time panic: %.*s", LIT(operand->value.value_string));
+ if (c->proc_name != "") {
+ gbString str = type_to_string(c->curr_proc_sig);
+ error_line("\tCalled within '%.*s' :: %s\n", LIT(c->proc_name), str);
+ gb_string_free(str);
+ }
}
operand->type = t_invalid;
operand->mode = Addressing_NoValue;
@@ -1544,9 +1901,21 @@ gb_internal bool check_builtin_procedure_directive(CheckerContext *c, Operand *o
operand->mode = Addressing_Constant;
operand->value = exact_value_bool(is_defined);
+ // If the arg is a selector expression we don't add it, `-define` only allows identifiers.
+ if (arg->kind == Ast_Ident) {
+ Defineable defineable = {};
+ defineable.docs = nullptr;
+ defineable.name = arg->Ident.token.string;
+ defineable.default_value = exact_value_bool(false);
+ defineable.pos = arg->Ident.token.pos;
+
+ MUTEX_GUARD(&c->info->defineables_mutex);
+ array_add(&c->info->defineables, defineable);
+ }
+
} else if (name == "config") {
if (ce->args.count != 2) {
- error(call, "'#config' expects 2 argument, got %td", ce->args.count);
+ error(call, "'#config' expects 2 arguments, got %td", ce->args.count);
return false;
}
Ast *arg = unparen_expr(ce->args[0]);
@@ -1581,6 +1950,20 @@ gb_internal bool check_builtin_procedure_directive(CheckerContext *c, Operand *o
operand->value = found->Constant.value;
}
}
+
+ Defineable defineable = {};
+ defineable.docs = nullptr;
+ defineable.name = name;
+ defineable.default_value = def.value;
+ defineable.pos = arg->Ident.token.pos;
+
+ if (c->decl) {
+ defineable.docs = c->decl->docs;
+ }
+
+ MUTEX_GUARD(&c->info->defineables_mutex);
+ array_add(&c->info->defineables, defineable);
+
} else {
error(call, "Unknown directive call: #%.*s", LIT(name));
}
@@ -1630,6 +2013,8 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
case BuiltinProc_objc_register_selector:
case BuiltinProc_objc_register_class:
case BuiltinProc_atomic_type_is_lock_free:
+ case BuiltinProc_has_target_feature:
+ case BuiltinProc_procedure_of:
// NOTE(bill): The first arg may be a Type, this will be checked case by case
break;
@@ -1663,7 +2048,12 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
if (ce->args.count > 0) {
if (ce->args[0]->kind == Ast_FieldValue) {
- if (id != BuiltinProc_soa_zip) {
+ switch (id) {
+ case BuiltinProc_soa_zip:
+ case BuiltinProc_quaternion:
+ // okay
+ break;
+ default:
error(call, "'field = value' calling is not allowed on built-in procedures");
return false;
}
@@ -1674,13 +2064,21 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
bool ok = check_builtin_simd_operation(c, operand, call, id, type_hint);
if (!ok) {
operand->type = t_invalid;
+ operand->mode = Addressing_Value;
}
- operand->mode = Addressing_Value;
operand->value = {};
operand->expr = call;
return ok;
}
+ if (BuiltinProc__atomic_begin < id && id < BuiltinProc__atomic_end) {
+ if (build_context.metrics.arch == TargetArch_riscv64) {
+ if (!check_target_feature_is_enabled(str_lit("a"), nullptr)) {
+ error(call, "missing required target feature \"a\" for atomics, enable it by setting a different -microarch or explicitly adding it through -target-features");
+ }
+ }
+ }
+
switch (id) {
default:
GB_PANIC("Implement built-in procedure: %.*s", LIT(builtin_name));
@@ -1928,6 +2326,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
Selection sel = lookup_field(type, field_name, false);
if (sel.entity == nullptr) {
+ ERROR_BLOCK();
gbString type_str = type_to_string_shorthand(type);
error(ce->args[0],
"'%s' has no field named '%.*s'", type_str, LIT(field_name));
@@ -2001,6 +2400,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
Selection sel = lookup_field(type, field_name, false);
if (sel.entity == nullptr) {
+ ERROR_BLOCK();
gbString type_str = type_to_string_shorthand(type);
error(ce->args[0],
"'%s' has no field named '%.*s'", type_str, LIT(field_name));
@@ -2041,6 +2441,14 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
error(o.expr, "Invalid argument to 'type_of'");
return false;
}
+
+ if (is_type_untyped(o.type)) {
+ gbString t = type_to_string(o.type);
+ error(o.expr, "'type_of' of %s cannot be determined", t);
+ gb_string_free(t);
+ return false;
+ }
+
// NOTE(bill): Prevent type cycles for procedure declarations
if (c->curr_proc_sig == o.type) {
gbString s = expr_to_string(o.expr);
@@ -2088,6 +2496,8 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
t = default_type(t);
add_type_info_type(c, t);
+ GB_ASSERT(t_type_info_ptr != nullptr);
+ add_type_info_type(c, t_type_info_ptr);
if (is_operand_value(o) && is_type_typeid(t)) {
add_package_dependency(c, "runtime", "__type_info_of");
@@ -2141,6 +2551,10 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
case BuiltinProc_swizzle: {
// swizzle :: proc(v: [N]T, ..int) -> [M]T
+ if (!operand->type) {
+ return false;
+ }
+
Type *original_type = operand->type;
Type *type = base_type(original_type);
i64 max_count = 0;
@@ -2194,9 +2608,12 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
arg_count++;
}
- if (arg_count > max_count) {
+ if (false && arg_count > max_count) {
error(call, "Too many 'swizzle' indices, %td > %td", arg_count, max_count);
return false;
+ } else if (arg_count < 2) {
+ error(call, "Not enough 'swizzle' indices, %td < 2", arg_count);
+ return false;
}
if (type->kind == Type_Array) {
@@ -2294,61 +2711,150 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
}
case BuiltinProc_quaternion: {
- // quaternion :: proc(real, imag, jmag, kmag: float_type) -> complex_type
- Operand x = *operand;
- Operand y = {};
- Operand z = {};
- Operand w = {};
+ bool first_is_field_value = (ce->args[0]->kind == Ast_FieldValue);
+
+ bool fail = false;
+ for (Ast *arg : ce->args) {
+ bool mix = false;
+ if (first_is_field_value) {
+ mix = arg->kind != Ast_FieldValue;
+ } else {
+ mix = arg->kind == Ast_FieldValue;
+ }
+ if (mix) {
+ error(arg, "Mixture of 'field = value' and value elements in the procedure call '%.*s' is not allowed", LIT(builtin_name));
+ fail = true;
+ break;
+ }
+ }
+
+ if (fail) {
+ operand->type = t_untyped_quaternion;
+ operand->mode = Addressing_Constant;
+ operand->value = exact_value_quaternion(0.0, 0.0, 0.0, 0.0);
+ break;
+ }
+
+ // quaternion :: proc(imag, jmag, kmag, real: float_type) -> complex_type
+ Operand xyzw[4] = {};
+
+ u32 first_index = 0;
// NOTE(bill): Invalid will be the default till fixed
operand->type = t_invalid;
operand->mode = Addressing_Invalid;
- check_expr(c, &y, ce->args[1]);
- if (y.mode == Addressing_Invalid) {
- return false;
- }
- check_expr(c, &z, ce->args[2]);
- if (y.mode == Addressing_Invalid) {
- return false;
- }
- check_expr(c, &w, ce->args[3]);
- if (y.mode == Addressing_Invalid) {
- return false;
- }
+ if (first_is_field_value) {
+ u32 fields_set[4] = {}; // 0 unset, 1 xyzw, 2 real/etc
- convert_to_typed(c, &x, y.type); if (x.mode == Addressing_Invalid) return false;
- convert_to_typed(c, &y, x.type); if (y.mode == Addressing_Invalid) return false;
- convert_to_typed(c, &z, x.type); if (z.mode == Addressing_Invalid) return false;
- convert_to_typed(c, &w, x.type); if (w.mode == Addressing_Invalid) return false;
- if (x.mode == Addressing_Constant &&
- y.mode == Addressing_Constant &&
- z.mode == Addressing_Constant &&
- w.mode == Addressing_Constant) {
- x.value = exact_value_to_float(x.value);
- y.value = exact_value_to_float(y.value);
- z.value = exact_value_to_float(z.value);
- w.value = exact_value_to_float(w.value);
- if (is_type_numeric(x.type) && x.value.kind == ExactValue_Float) {
- x.type = t_untyped_float;
+ auto const check_field = [&fields_set, &builtin_name](CheckerContext *c, Operand *o, Ast *arg, i32 *index) -> bool {
+ *index = -1;
+
+ ast_node(field, FieldValue, arg);
+ String name = {};
+ if (field->field->kind == Ast_Ident) {
+ name = field->field->Ident.token.string;
+ } else {
+ error(field->field, "Expected an identifier for field argument");
+ return false;
+ }
+
+ u32 style = 0;
+
+ if (name == "x") {
+ *index = 0; style = 1;
+ } else if (name == "y") {
+ *index = 1; style = 1;
+ } else if (name == "z") {
+ *index = 2; style = 1;
+ } else if (name == "w") {
+ *index = 3; style = 1;
+ } else if (name == "imag") {
+ *index = 0; style = 2;
+ } else if (name == "jmag") {
+ *index = 1; style = 2;
+ } else if (name == "kmag") {
+ *index = 2; style = 2;
+ } else if (name == "real") {
+ *index = 3; style = 2;
+ } else {
+ error(field->field, "Unknown name for '%.*s', expected (w, x, y, z; or real, imag, jmag, kmag), got '%.*s'", LIT(builtin_name), LIT(name));
+ return false;
+ }
+
+ if (fields_set[*index]) {
+ error(field->field, "Previously assigned field: '%.*s'", LIT(name));
+ }
+ fields_set[*index] = style;
+
+ check_expr(c, o, field->value);
+ return o->mode != Addressing_Invalid;
+ };
+
+ Operand *refs[4] = {&xyzw[0], &xyzw[1], &xyzw[2], &xyzw[3]};
+
+ for (i32 i = 0; i < 4; i++) {
+ i32 index = -1;
+ Operand o = {};
+ bool ok = check_field(c, &o, ce->args[i], &index);
+ if (!ok || index < 0) {
+ return false;
+ }
+ first_index = cast(u32)index;
+ *refs[index] = o;
}
- if (is_type_numeric(y.type) && y.value.kind == ExactValue_Float) {
- y.type = t_untyped_float;
+
+ for (i32 i = 0; i < 4; i++) {
+ GB_ASSERT(fields_set[i]);
+ }
+ for (i32 i = 1; i < 4; i++) {
+ if (fields_set[i] != fields_set[i-1]) {
+ error(call, "Mixture of xyzw and real/etc is not allowed with '%.*s'", LIT(builtin_name));
+ break;
+ }
+ }
+ } else {
+ error(call, "'%.*s' requires that all arguments are named (w, x, y, z; or real, imag, jmag, kmag)", LIT(builtin_name));
+
+ for (i32 i = 0; i < 4; i++) {
+ check_expr(c, &xyzw[i], ce->args[i]);
+ if (xyzw[i].mode == Addressing_Invalid) {
+ return false;
+ }
}
- if (is_type_numeric(z.type) && z.value.kind == ExactValue_Float) {
- z.type = t_untyped_float;
+ }
+
+
+ for (u32 i = 0; i < 4; i++ ){
+ u32 j = (i + first_index) % 4;
+ if (j == first_index) {
+ convert_to_typed(c, &xyzw[j], xyzw[(first_index+1)%4].type); if (xyzw[j].mode == Addressing_Invalid) return false;
+ } else {
+ convert_to_typed(c, &xyzw[j], xyzw[first_index].type); if (xyzw[j].mode == Addressing_Invalid) return false;
}
- if (is_type_numeric(w.type) && w.value.kind == ExactValue_Float) {
- w.type = t_untyped_float;
+ }
+ if (xyzw[0].mode == Addressing_Constant &&
+ xyzw[1].mode == Addressing_Constant &&
+ xyzw[2].mode == Addressing_Constant &&
+ xyzw[3].mode == Addressing_Constant) {
+ for (i32 i = 0; i < 4; i++) {
+ xyzw[i].value = exact_value_to_float(xyzw[i].value);
+ }
+ for (i32 i = 0; i < 4; i++) {
+ if (is_type_numeric(xyzw[i].type) && xyzw[i].value.kind == ExactValue_Float) {
+ xyzw[i].type = t_untyped_float;
+ }
}
}
- if (!(are_types_identical(x.type, y.type) && are_types_identical(x.type, z.type) && are_types_identical(x.type, w.type))) {
- gbString tx = type_to_string(x.type);
- gbString ty = type_to_string(y.type);
- gbString tz = type_to_string(z.type);
- gbString tw = type_to_string(w.type);
- error(call, "Mismatched types to 'quaternion', '%s' vs '%s' vs '%s' vs '%s'", tx, ty, tz, tw);
+ if (!(are_types_identical(xyzw[0].type, xyzw[1].type) &&
+ are_types_identical(xyzw[0].type, xyzw[2].type) &&
+ are_types_identical(xyzw[0].type, xyzw[3].type))) {
+ gbString tx = type_to_string(xyzw[0].type);
+ gbString ty = type_to_string(xyzw[1].type);
+ gbString tz = type_to_string(xyzw[2].type);
+ gbString tw = type_to_string(xyzw[3].type);
+ error(call, "Mismatched types to 'quaternion', 'x=%s' vs 'y=%s' vs 'z=%s' vs 'w=%s'", tx, ty, tz, tw);
gb_string_free(tw);
gb_string_free(tz);
gb_string_free(ty);
@@ -2356,31 +2862,35 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
return false;
}
- if (!is_type_float(x.type)) {
- gbString s = type_to_string(x.type);
+ if (!is_type_float(xyzw[0].type)) {
+ gbString s = type_to_string(xyzw[0].type);
error(call, "Arguments have type '%s', expected a floating point", s);
gb_string_free(s);
return false;
}
- if (is_type_endian_specific(x.type)) {
- gbString s = type_to_string(x.type);
+ if (is_type_endian_specific(xyzw[0].type)) {
+ gbString s = type_to_string(xyzw[0].type);
error(call, "Arguments with a specified endian are not allow, expected a normal floating point, got '%s'", s);
gb_string_free(s);
return false;
}
- if (x.mode == Addressing_Constant && y.mode == Addressing_Constant && z.mode == Addressing_Constant && w.mode == Addressing_Constant) {
- f64 r = exact_value_to_float(x.value).value_float;
- f64 i = exact_value_to_float(y.value).value_float;
- f64 j = exact_value_to_float(z.value).value_float;
- f64 k = exact_value_to_float(w.value).value_float;
+
+ operand->mode = Addressing_Value;
+
+ if (xyzw[0].mode == Addressing_Constant &&
+ xyzw[1].mode == Addressing_Constant &&
+ xyzw[2].mode == Addressing_Constant &&
+ xyzw[3].mode == Addressing_Constant) {
+ f64 r = exact_value_to_float(xyzw[3].value).value_float;
+ f64 i = exact_value_to_float(xyzw[0].value).value_float;
+ f64 j = exact_value_to_float(xyzw[1].value).value_float;
+ f64 k = exact_value_to_float(xyzw[2].value).value_float;
operand->value = exact_value_quaternion(r, i, j, k);
operand->mode = Addressing_Constant;
- } else {
- operand->mode = Addressing_Value;
}
- BasicKind kind = core_type(x.type)->Basic.kind;
+ BasicKind kind = core_type(xyzw[first_index].type)->Basic.kind;
switch (kind) {
case Basic_f16: operand->type = t_quaternion64; break;
case Basic_f32: operand->type = t_quaternion128; break;
@@ -2402,6 +2912,10 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
// imag :: proc(x: type) -> float_type
Operand *x = operand;
+ if (!x->type) {
+ return false;
+ }
+
if (is_type_untyped(x->type)) {
if (x->mode == Addressing_Constant) {
if (is_type_numeric(x->type)) {
@@ -2462,6 +2976,10 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
// kmag :: proc(x: type) -> float_type
Operand *x = operand;
+ if (!x->type) {
+ return false;
+ }
+
if (is_type_untyped(x->type)) {
if (x->mode == Addressing_Constant) {
if (is_type_numeric(x->type)) {
@@ -2511,6 +3029,10 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
case BuiltinProc_conj: {
// conj :: proc(x: type) -> type
Operand *x = operand;
+ if (!x->type) {
+ return false;
+ }
+
Type *t = x->type;
Type *elem = core_array_type(t);
@@ -2551,10 +3073,14 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
}
case BuiltinProc_expand_values: {
+ if (!operand->type) {
+ return false;
+ }
+
Type *type = base_type(operand->type);
if (!is_type_struct(type) && !is_type_array(type)) {
gbString type_str = type_to_string(operand->type);
- error(call, "Expected a struct or array type, got '%s'", type_str);
+ error(call, "Expected a struct or array type to 'expand_values', got '%s'", type_str);
gb_string_free(type_str);
return false;
}
@@ -2590,13 +3116,18 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
check_multi_expr_or_type(c, operand, ce->args[0]);
+ if (!operand->type) {
+ return false;
+ }
+
Type *original_type = operand->type;
Type *type = base_type(operand->type);
+
if (operand->mode == Addressing_Type && is_type_enumerated_array(type)) {
// Okay
} else if (!is_type_ordered(type) || !(is_type_numeric(type) || is_type_string(type))) {
gbString type_str = type_to_string(original_type);
- error(call, "Expected a ordered numeric type to 'min', got '%s'", type_str);
+ error(call, "Expected an ordered numeric type to 'min', got '%s'", type_str);
gb_string_free(type_str);
return false;
}
@@ -2664,6 +3195,10 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
return false;
}
+ if (ce->args.count <= 1) {
+ error(call, "Too few arguments for 'min', two or more are required");
+ return false;
+ }
bool all_constant = operand->mode == Addressing_Constant;
@@ -2682,7 +3217,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
if (!is_type_ordered(b.type) || !(is_type_numeric(b.type) || is_type_string(b.type))) {
gbString type_str = type_to_string(b.type);
error(call,
- "Expected a ordered numeric type to 'min', got '%s'",
+ "Expected an ordered numeric type to 'min', got '%s'",
type_str);
gb_string_free(type_str);
return false;
@@ -2758,6 +3293,10 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
check_multi_expr_or_type(c, operand, ce->args[0]);
+ if (!operand->type) {
+ return false;
+ }
+
Type *original_type = operand->type;
Type *type = base_type(operand->type);
@@ -2765,7 +3304,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
// Okay
} else if (!is_type_ordered(type) || !(is_type_numeric(type) || is_type_string(type))) {
gbString type_str = type_to_string(original_type);
- error(call, "Expected a ordered numeric type to 'max', got '%s'", type_str);
+ error(call, "Expected an ordered numeric type to 'max', got '%s'", type_str);
gb_string_free(type_str);
return false;
}
@@ -2837,6 +3376,11 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
gb_string_free(type_str);
return false;
}
+
+ if (ce->args.count <= 1) {
+ error(call, "Too few arguments for 'max', two or more are required");
+ return false;
+ }
bool all_constant = operand->mode == Addressing_Constant;
@@ -2856,7 +3400,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
if (!is_type_ordered(b.type) || !(is_type_numeric(b.type) || is_type_string(b.type))) {
gbString type_str = type_to_string(b.type);
error(arg,
- "Expected a ordered numeric type to 'max', got '%s'",
+ "Expected an ordered numeric type to 'max', got '%s'",
type_str);
gb_string_free(type_str);
return false;
@@ -2928,6 +3472,10 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
case BuiltinProc_abs: {
// abs :: proc(n: numeric) -> numeric
+ if (!operand->type) {
+ return false;
+ }
+
if (!(is_type_numeric(operand->type) && !is_type_array(operand->type))) {
gbString type_str = type_to_string(operand->type);
error(call, "Expected a numeric type to 'abs', got '%s'", type_str);
@@ -2983,10 +3531,14 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
case BuiltinProc_clamp: {
// clamp :: proc(a, min, max: ordered) -> ordered
+ if (!operand->type) {
+ return false;
+ }
+
Type *type = operand->type;
if (!is_type_ordered(type) || !(is_type_numeric(type) || is_type_string(type))) {
gbString type_str = type_to_string(operand->type);
- error(call, "Expected a ordered numeric or string type to 'clamp', got '%s'", type_str);
+ error(call, "Expected an ordered numeric or string type to 'clamp', got '%s'", type_str);
gb_string_free(type_str);
return false;
}
@@ -3003,7 +3555,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
}
if (!is_type_ordered(y.type) || !(is_type_numeric(y.type) || is_type_string(y.type))) {
gbString type_str = type_to_string(y.type);
- error(call, "Expected a ordered numeric or string type to 'clamp', got '%s'", type_str);
+ error(call, "Expected an ordered numeric or string type to 'clamp', got '%s'", type_str);
gb_string_free(type_str);
return false;
}
@@ -3014,7 +3566,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
}
if (!is_type_ordered(z.type) || !(is_type_numeric(z.type) || is_type_string(z.type))) {
gbString type_str = type_to_string(z.type);
- error(call, "Expected a ordered numeric or string type to 'clamp', got '%s'", type_str);
+ error(call, "Expected an ordered numeric or string type to 'clamp', got '%s'", type_str);
gb_string_free(type_str);
return false;
}
@@ -3087,7 +3639,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
mix = arg->kind == Ast_FieldValue;
}
if (mix) {
- error(arg, "Mixture of 'field = value' and value elements in the procedure call 'soa_zip' is not allowed");
+ error(arg, "Mixture of 'field = value' and value elements in the procedure call '%.*s' is not allowed", LIT(builtin_name));
fail = true;
break;
}
@@ -3204,6 +3756,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
elem->Struct.tags = gb_alloc_array(permanent_allocator(), String, fields.count);
elem->Struct.node = dummy_node_struct;
type_set_offsets(elem);
+ wait_signal_set(&elem->Struct.fields_wait_signal);
}
Type *soa_type = make_soa_struct_slice(c, dummy_node_soa, nullptr, elem);
@@ -3235,8 +3788,8 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
auto types = slice_make<Type *>(permanent_allocator(), t->Struct.fields.count-1);
for_array(i, types) {
Entity *f = t->Struct.fields[i];
- GB_ASSERT(f->type->kind == Type_Pointer);
- types[i] = alloc_type_slice(f->type->Pointer.elem);
+ GB_ASSERT(f->type->kind == Type_MultiPointer);
+ types[i] = alloc_type_slice(f->type->MultiPointer.elem);
}
operand->type = alloc_type_tuple_from_field_types(types.data, types.count, false, false);
@@ -3302,7 +3855,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
}
} else {
GB_ASSERT(t->kind == Type_Matrix);
- operand->type = alloc_type_matrix(t->Matrix.elem, t->Matrix.column_count, t->Matrix.row_count);
+ operand->type = alloc_type_matrix(t->Matrix.elem, t->Matrix.column_count, t->Matrix.row_count, nullptr, nullptr, t->Matrix.is_row_major);
}
operand->type = check_matrix_type_hint(operand->type, type_hint);
break;
@@ -3370,7 +3923,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
}
operand->mode = Addressing_Value;
- operand->type = alloc_type_matrix(elem, xt->Array.count, yt->Array.count);
+ operand->type = alloc_type_matrix(elem, xt->Array.count, yt->Array.count, nullptr, nullptr, false);
operand->type = check_matrix_type_hint(operand->type, type_hint);
break;
}
@@ -3473,6 +4026,58 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
break;
}
+ case BuiltinProc_has_target_feature: {
+ String features = str_lit("");
+
+ check_expr_or_type(c, operand, ce->args[0]);
+
+ if (is_type_string(operand->type) && operand->mode == Addressing_Constant) {
+ GB_ASSERT(operand->value.kind == ExactValue_String);
+ features = operand->value.value_string;
+ } else {
+ Type *pt = base_type(operand->type);
+ if (pt->kind == Type_Proc) {
+ if (pt->Proc.require_target_feature.len != 0) {
+ GB_ASSERT(pt->Proc.enable_target_feature.len == 0);
+ features = pt->Proc.require_target_feature;
+ } else if (pt->Proc.enable_target_feature.len != 0) {
+ features = pt->Proc.enable_target_feature;
+ } else {
+ error(ce->args[0], "Expected the procedure type given to '%.*s' to have @(require_target_feature=\"...\") or @(enable_target_feature=\"...\")", LIT(builtin_name));
+ }
+ } else {
+ error(ce->args[0], "Expected a constant string or procedure type for '%.*s'", LIT(builtin_name));
+ }
+ }
+
+ String invalid;
+ if (!check_target_feature_is_valid_globally(features, &invalid)) {
+ error(ce->args[0], "Target feature '%.*s' is not a valid target feature", LIT(invalid));
+ }
+
+ operand->value = exact_value_bool(check_target_feature_is_enabled(features, nullptr));
+ operand->mode = Addressing_Constant;
+ operand->type = t_untyped_bool;
+ break;
+ }
+
+ case BuiltinProc_constant_log2: {
+ Operand o = {};
+ check_expr(c, &o, ce->args[0]);
+
+ if (!is_type_integer(o.type) && (o.mode != Addressing_Constant)) {
+ error(ce->args[0], "Expected a constant integer for '%.*s'", LIT(builtin_name));
+ return false;
+ }
+
+ int log2 = big_int_log2(&o.value.value_integer);
+
+ operand->mode = Addressing_Constant;
+ operand->value = exact_value_i64(cast(i64)log2);
+ operand->type = t_untyped_integer;
+ break;
+ }
+
case BuiltinProc_soa_struct: {
Operand x = {};
Operand y = {};
@@ -3577,6 +4182,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
soa_struct->Struct.tags[i] = old_struct->Struct.tags[i];
}
}
+ wait_signal_set(&soa_struct->Struct.fields_wait_signal);
Token token = {};
token.string = str_lit("Base_Type");
@@ -3898,7 +4504,50 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
}
}
- operand->mode = Addressing_OptionalOk;
+ operand->mode = Addressing_Value;
+ operand->type = make_optional_ok_type(default_type(x.type));
+ }
+ break;
+
+ case BuiltinProc_saturating_add:
+ case BuiltinProc_saturating_sub:
+ {
+ Operand x = {};
+ Operand y = {};
+ check_expr(c, &x, ce->args[0]);
+ check_expr(c, &y, ce->args[1]);
+ if (x.mode == Addressing_Invalid) {
+ return false;
+ }
+ if (y.mode == Addressing_Invalid) {
+ return false;
+ }
+ convert_to_typed(c, &y, x.type); if (y.mode == Addressing_Invalid) return false;
+ convert_to_typed(c, &x, y.type);
+ if (is_type_untyped(x.type)) {
+ gbString xts = type_to_string(x.type);
+ error(x.expr, "Expected a typed integer for '%.*s', got %s", LIT(builtin_name), xts);
+ gb_string_free(xts);
+ return false;
+ }
+ if (!is_type_integer(x.type)) {
+ gbString xts = type_to_string(x.type);
+ error(x.expr, "Expected an integer for '%.*s', got %s", LIT(builtin_name), xts);
+ gb_string_free(xts);
+ return false;
+ }
+ Type *ct = core_type(x.type);
+ if (is_type_different_to_arch_endianness(ct)) {
+ GB_ASSERT(ct->kind == Type_Basic);
+ if (ct->Basic.flags & (BasicFlag_EndianLittle|BasicFlag_EndianBig)) {
+ gbString xts = type_to_string(x.type);
+ error(x.expr, "Expected an integer which does not specify the explicit endianness for '%.*s', got %s", LIT(builtin_name), xts);
+ gb_string_free(xts);
+ return false;
+ }
+ }
+
+ operand->mode = Addressing_Value;
operand->type = default_type(x.type);
}
break;
@@ -4370,16 +5019,14 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
check_assignment(c, &x, elem, builtin_name);
Type *t = type_deref(operand->type);
- switch (id) {
- case BuiltinProc_atomic_add:
- case BuiltinProc_atomic_sub:
- if (!is_type_numeric(t)) {
+ if (id != BuiltinProc_atomic_exchange) {
+ if (!is_type_integer_like(t)) {
gbString str = type_to_string(t);
- error(operand->expr, "Expected a numeric type for '%.*s', got %s", LIT(builtin_name), str);
+ error(operand->expr, "Expected an integer type for '%.*s', got %s", LIT(builtin_name), str);
gb_string_free(str);
} else if (is_type_different_to_arch_endianness(t)) {
gbString str = type_to_string(t);
- error(operand->expr, "Expected a numeric type of the same platform endianness for '%.*s', got %s", LIT(builtin_name), str);
+ error(operand->expr, "Expected an integer type of the same platform endianness for '%.*s', got %s", LIT(builtin_name), str);
gb_string_free(str);
}
}
@@ -4415,19 +5062,16 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
}
Type *t = type_deref(operand->type);
- switch (id) {
- case BuiltinProc_atomic_add_explicit:
- case BuiltinProc_atomic_sub_explicit:
- if (!is_type_numeric(t)) {
+ if (id != BuiltinProc_atomic_exchange_explicit) {
+ if (!is_type_integer_like(t)) {
gbString str = type_to_string(t);
- error(operand->expr, "Expected a numeric type for '%.*s', got %s", LIT(builtin_name), str);
+ error(operand->expr, "Expected an integer type for '%.*s', got %s", LIT(builtin_name), str);
gb_string_free(str);
} else if (is_type_different_to_arch_endianness(t)) {
gbString str = type_to_string(t);
- error(operand->expr, "Expected a numeric type of the same platform endianness for '%.*s', got %s", LIT(builtin_name), str);
+ error(operand->expr, "Expected an integer type of the same platform endianness for '%.*s', got %s", LIT(builtin_name), str);
gb_string_free(str);
}
- break;
}
operand->type = elem;
@@ -4620,6 +5264,16 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
return false;
}
+ if (sz >= 64) {
+ if (is_type_unsigned(x.type)) {
+ add_package_dependency(c, "runtime", "umodti3", true);
+ add_package_dependency(c, "runtime", "udivti3", true);
+ } else {
+ add_package_dependency(c, "runtime", "modti3", true);
+ add_package_dependency(c, "runtime", "divti3", true);
+ }
+ }
+
operand->type = x.type;
operand->mode = Addressing_Value;
}
@@ -4733,15 +5387,10 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
isize max_arg_count = 32;
switch (build_context.metrics.os) {
- case TargetOs_windows:
- case TargetOs_freestanding:
- error(call, "'%.*s' is not supported on this platform (%.*s)", LIT(builtin_name), LIT(target_os_names[build_context.metrics.os]));
- break;
case TargetOs_darwin:
case TargetOs_linux:
case TargetOs_essence:
- case TargetOs_freebsd:
- case TargetOs_openbsd:
+ case TargetOs_haiku:
switch (build_context.metrics.arch) {
case TargetArch_i386:
case TargetArch_amd64:
@@ -4750,6 +5399,9 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
break;
}
break;
+ default:
+ error(call, "'%.*s' is not supported on this platform (%.*s)", LIT(builtin_name), LIT(target_os_names[build_context.metrics.os]));
+ break;
}
if (ce->args.count > max_arg_count) {
@@ -4763,6 +5415,55 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
return true;
}
break;
+ case BuiltinProc_syscall_bsd:
+ {
+ convert_to_typed(c, operand, t_uintptr);
+ if (!is_type_uintptr(operand->type)) {
+ gbString t = type_to_string(operand->type);
+ error(operand->expr, "Argument 0 must be of type 'uintptr', got %s", t);
+ gb_string_free(t);
+ }
+ for (isize i = 1; i < ce->args.count; i++) {
+ Operand x = {};
+ check_expr(c, &x, ce->args[i]);
+ if (x.mode != Addressing_Invalid) {
+ convert_to_typed(c, &x, t_uintptr);
+ }
+ convert_to_typed(c, &x, t_uintptr);
+ if (!is_type_uintptr(x.type)) {
+ gbString t = type_to_string(x.type);
+ error(x.expr, "Argument %td must be of type 'uintptr', got %s", i, t);
+ gb_string_free(t);
+ }
+ }
+
+ isize max_arg_count = 32;
+
+ switch (build_context.metrics.os) {
+ case TargetOs_freebsd:
+ case TargetOs_netbsd:
+ case TargetOs_openbsd:
+ switch (build_context.metrics.arch) {
+ case TargetArch_amd64:
+ case TargetArch_arm64:
+ max_arg_count = 7;
+ break;
+ }
+ break;
+ default:
+ error(call, "'%.*s' is not supported on this platform (%.*s)", LIT(builtin_name), LIT(target_os_names[build_context.metrics.os]));
+ break;
+ }
+
+ if (ce->args.count > max_arg_count) {
+ error(ast_end_token(call), "'%.*s' has a maximum of %td arguments on this platform (%.*s), got %td", LIT(builtin_name), max_arg_count, LIT(target_os_names[build_context.metrics.os]), ce->args.count);
+ }
+
+ operand->mode = Addressing_Value;
+ operand->type = make_optional_ok_type(t_uintptr);
+ return true;
+ }
+ break;
case BuiltinProc_type_base_type:
@@ -4789,8 +5490,10 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
switch (bt->kind) {
case Type_Basic:
switch (bt->Basic.kind) {
+ case Basic_complex32: operand->type = t_f16; break;
case Basic_complex64: operand->type = t_f32; break;
case Basic_complex128: operand->type = t_f64; break;
+ case Basic_quaternion64: operand->type = t_f16; break;
case Basic_quaternion128: operand->type = t_f32; break;
case Basic_quaternion256: operand->type = t_f64; break;
}
@@ -4989,6 +5692,34 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
operand->type = t_untyped_bool;
break;
+
+ case BuiltinProc_type_is_matrix_row_major:
+ case BuiltinProc_type_is_matrix_column_major:
+ {
+ 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 (type->kind != Type_Matrix) {
+ gbString s = type_to_string(bt);
+ error(ce->args[0], "Expected a matrix type for '%.*s', got '%s'", LIT(builtin_name), s);
+ gb_string_free(s);
+ return false;
+ }
+
+ if (id == BuiltinProc_type_is_matrix_row_major) {
+ operand->value = exact_value_bool(bt->Matrix.is_row_major == true);
+ } else {
+ operand->value = exact_value_bool(bt->Matrix.is_row_major == false);
+ }
+ operand->mode = Addressing_Constant;
+ operand->type = t_untyped_bool;
+ break;
+ }
+
case BuiltinProc_type_has_field:
{
Operand op = {};
@@ -5016,6 +5747,59 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
break;
}
break;
+
+ case BuiltinProc_type_has_shared_fields:
+ {
+ Type *u = check_type(c, ce->args[0]);
+ Type *ut = base_type(u);
+ if (ut == nullptr || ut == t_invalid) {
+ error(ce->args[0], "Expected a type for '%.*s'", LIT(builtin_name));
+ return false;
+ }
+ if (ut->kind != Type_Struct || ut->Struct.soa_kind != StructSoa_None) {
+ gbString t = type_to_string(ut);
+ error(ce->args[0], "Expected a struct type for '%.*s', got %s", LIT(builtin_name), t);
+ gb_string_free(t);
+ return false;
+ }
+
+ Type *v = check_type(c, ce->args[1]);
+ Type *vt = base_type(v);
+ if (vt == nullptr || vt == t_invalid) {
+ error(ce->args[1], "Expected a type for '%.*s'", LIT(builtin_name));
+ return false;
+ }
+ if (vt->kind != Type_Struct || vt->Struct.soa_kind != StructSoa_None) {
+ gbString t = type_to_string(vt);
+ error(ce->args[1], "Expected a struct type for '%.*s', got %s", LIT(builtin_name), t);
+ gb_string_free(t);
+ return false;
+ }
+
+ bool is_shared = true;
+
+ for (Entity *v_field : vt->Struct.fields) {
+ bool found = false;
+ for (Entity *u_field : ut->Struct.fields) {
+ if (v_field->token.string == u_field->token.string &&
+ are_types_identical(v_field->type, u_field->type)) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ is_shared = false;
+ break;
+ }
+ }
+
+ operand->mode = Addressing_Constant;
+ operand->value = exact_value_bool(is_shared);
+ operand->type = t_untyped_bool;
+ break;
+ }
+
case BuiltinProc_type_field_type:
{
Operand op = {};
@@ -5117,6 +5901,254 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
}
break;
+ case BuiltinProc_type_union_tag_type:
+ {
+ if (operand->mode != Addressing_Type) {
+ error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name));
+ operand->mode = Addressing_Invalid;
+ operand->type = t_invalid;
+ return false;
+ }
+
+ Type *u = operand->type;
+
+ if (!is_type_union(u)) {
+ error(operand->expr, "Expected a union type for '%.*s'", LIT(builtin_name));
+ operand->mode = Addressing_Invalid;
+ operand->type = t_invalid;
+ return false;
+ }
+
+ u = base_type(u);
+ GB_ASSERT(u->kind == Type_Union);
+
+ operand->mode = Addressing_Type;
+ operand->type = union_tag_type(u);
+ }
+ break;
+
+ case BuiltinProc_type_union_tag_offset:
+ {
+ if (operand->mode != Addressing_Type) {
+ error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name));
+ operand->mode = Addressing_Invalid;
+ operand->type = t_invalid;
+ return false;
+ }
+
+ Type *u = operand->type;
+
+ if (!is_type_union(u)) {
+ error(operand->expr, "Expected a union type for '%.*s'", LIT(builtin_name));
+ operand->mode = Addressing_Invalid;
+ operand->type = t_invalid;
+ return false;
+ }
+
+ u = base_type(u);
+ GB_ASSERT(u->kind == Type_Union);
+
+ // 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);
+
+ operand->mode = Addressing_Constant;
+ operand->type = t_untyped_integer;
+ operand->value = exact_value_i64(tag_offset);
+ }
+ break;
+
+ case BuiltinProc_type_union_base_tag_value:
+ {
+ if (operand->mode != Addressing_Type) {
+ error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name));
+ operand->mode = Addressing_Invalid;
+ operand->type = t_invalid;
+ return false;
+ }
+
+ Type *u = operand->type;
+
+ if (!is_type_union(u)) {
+ error(operand->expr, "Expected a union type for '%.*s'", LIT(builtin_name));
+ operand->mode = Addressing_Invalid;
+ operand->type = t_invalid;
+ return false;
+ }
+
+ u = base_type(u);
+ GB_ASSERT(u->kind == Type_Union);
+
+ operand->mode = Addressing_Constant;
+ operand->type = t_untyped_integer;
+ operand->value = exact_value_i64(u->Union.kind == UnionType_no_nil ? 0 : 1);
+ } break;
+
+ case BuiltinProc_type_bit_set_elem_type:
+ {
+
+ if (operand->mode != Addressing_Type) {
+ error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name));
+ operand->mode = Addressing_Invalid;
+ operand->type = t_invalid;
+ return false;
+ }
+
+ Type *bs = operand->type;
+
+ if (!is_type_bit_set(bs)) {
+ error(operand->expr, "Expected a bit_set type for '%.*s'", LIT(builtin_name));
+ operand->mode = Addressing_Invalid;
+ operand->type = t_invalid;
+ return false;
+ }
+
+ bs = base_type(bs);
+ GB_ASSERT(bs->kind == Type_BitSet);
+
+ operand->mode = Addressing_Type;
+ operand->type = bs->BitSet.elem;
+ } break;
+
+ case BuiltinProc_type_bit_set_underlying_type:
+ {
+
+ if (operand->mode != Addressing_Type) {
+ error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name));
+ operand->mode = Addressing_Invalid;
+ operand->type = t_invalid;
+ return false;
+ }
+
+ Type *bs = operand->type;
+
+ if (!is_type_bit_set(bs)) {
+ error(operand->expr, "Expected a bit_set type for '%.*s'", LIT(builtin_name));
+ operand->mode = Addressing_Invalid;
+ operand->type = t_invalid;
+ return false;
+ }
+
+ bs = base_type(bs);
+ GB_ASSERT(bs->kind == Type_BitSet);
+
+ operand->mode = Addressing_Type;
+ operand->type = bit_set_to_int(bs);
+ } break;
+
+ case BuiltinProc_type_union_variant_count:
+ {
+ if (operand->mode != Addressing_Type) {
+ error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name));
+ operand->mode = Addressing_Invalid;
+ operand->type = t_invalid;
+ return false;
+ }
+
+ Type *u = operand->type;
+
+ if (!is_type_union(u)) {
+ error(operand->expr, "Expected a union type for '%.*s'", LIT(builtin_name));
+ operand->mode = Addressing_Invalid;
+ operand->type = t_invalid;
+ return false;
+ }
+
+ u = base_type(u);
+ GB_ASSERT(u->kind == Type_Union);
+
+ operand->mode = Addressing_Constant;
+ operand->type = t_untyped_integer;
+ operand->value = exact_value_i64(u->Union.variants.count);
+ } break;
+
+ case BuiltinProc_type_variant_type_of:
+ {
+ if (operand->mode != Addressing_Type) {
+ error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name));
+ operand->mode = Addressing_Invalid;
+ operand->type = t_invalid;
+ return false;
+ }
+
+ Type *u = operand->type;
+
+ if (!is_type_union(u)) {
+ error(operand->expr, "Expected a union type for '%.*s'", LIT(builtin_name));
+ operand->mode = Addressing_Invalid;
+ operand->type = t_invalid;
+ return false;
+ }
+
+ u = base_type(u);
+ GB_ASSERT(u->kind == Type_Union);
+ Operand x = {};
+ check_expr_or_type(c, &x, ce->args[1]);
+ if (!is_type_integer(x.type) || x.mode != Addressing_Constant) {
+ error(call, "Expected a constant integer for '%.*s", LIT(builtin_name));
+ operand->mode = Addressing_Type;
+ operand->type = t_invalid;
+ return false;
+ }
+
+ i64 index = big_int_to_i64(&x.value.value_integer);
+ if (index < 0 || index >= u->Union.variants.count) {
+ error(call, "Variant tag out of bounds index for '%.*s", LIT(builtin_name));
+ operand->mode = Addressing_Type;
+ operand->type = t_invalid;
+ return false;
+ }
+
+ operand->mode = Addressing_Type;
+ operand->type = u->Union.variants[index];
+ }
+ break;
+
+ case BuiltinProc_type_variant_index_of:
+ {
+ if (operand->mode != Addressing_Type) {
+ error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name));
+ operand->mode = Addressing_Invalid;
+ operand->type = t_invalid;
+ return false;
+ }
+
+ Type *u = operand->type;
+
+ if (!is_type_union(u)) {
+ error(operand->expr, "Expected a union type for '%.*s'", LIT(builtin_name));
+ operand->mode = Addressing_Invalid;
+ operand->type = t_invalid;
+ return false;
+ }
+
+ Type *v = check_type(c, ce->args[1]);
+ u = base_type(u);
+ GB_ASSERT(u->kind == Type_Union);
+
+ i64 index = -1;
+ for_array(i, u->Union.variants) {
+ Type *vt = u->Union.variants[i];
+ if (union_variant_index_types_equal(v, vt)) {
+ index = i64(i);
+ break;
+ }
+ }
+
+ if (index < 0) {
+ error(operand->expr, "Expected a variant type for '%.*s'", LIT(builtin_name));
+ operand->mode = Addressing_Invalid;
+ operand->type = t_invalid;
+ return false;
+ }
+
+ operand->mode = Addressing_Constant;
+ operand->type = t_untyped_integer;
+ operand->value = exact_value_i64(index);
+ }
+ break;
+
case BuiltinProc_type_struct_field_count:
operand->value = exact_value_i64(0);
if (operand->mode != Addressing_Type) {
@@ -5130,6 +6162,31 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
operand->mode = Addressing_Constant;
operand->type = t_untyped_integer;
break;
+ case BuiltinProc_type_struct_has_implicit_padding:
+ operand->value = exact_value_bool(false);
+ if (operand->mode != Addressing_Type) {
+ error(operand->expr, "Expected a struct type for '%.*s'", LIT(builtin_name));
+ } else if (!is_type_struct(operand->type) && !is_type_soa_struct(operand->type)) {
+ error(operand->expr, "Expected a struct type for '%.*s'", LIT(builtin_name));
+ } else {
+ Type *bt = base_type(operand->type);
+ if (bt->Struct.is_packed) {
+ operand->value = exact_value_bool(false);
+ } else if (bt->Struct.fields.count != 0) {
+ i64 size = type_size_of(bt);
+ Type *field_type = nullptr;
+ i64 last_offset = type_offset_of(bt, bt->Struct.fields.count-1, &field_type);
+ if (last_offset+type_size_of(field_type) < size) {
+ operand->value = exact_value_bool(true);
+ } else {
+ i64 packed_size = type_size_of_struct_pretend_is_packed(bt);
+ operand->value = exact_value_bool(packed_size < size);
+ }
+ }
+ }
+ operand->mode = Addressing_Constant;
+ operand->type = t_untyped_bool;
+ break;
case BuiltinProc_type_proc_parameter_count:
operand->value = exact_value_i64(0);
@@ -5281,15 +6338,9 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
if (operand->mode != Addressing_Type) {
error(operand->expr, "Expected a record type for '%.*s'", LIT(builtin_name));
} else {
- Type *bt = base_type(operand->type);
- if (bt->kind == Type_Struct) {
- if (bt->Struct.polymorphic_params != nullptr) {
- operand->value = exact_value_i64(bt->Struct.polymorphic_params->Tuple.variables.count);
- }
- } else if (bt->kind == Type_Union) {
- if (bt->Union.polymorphic_params != nullptr) {
- operand->value = exact_value_i64(bt->Union.polymorphic_params->Tuple.variables.count);
- }
+ TypeTuple *tuple = get_record_polymorphic_params(operand->type);
+ if (tuple) {
+ operand->value = exact_value_i64(tuple->variables.count);
} else {
error(operand->expr, "Expected a record type for '%.*s'", LIT(builtin_name));
}
@@ -5321,20 +6372,11 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
Entity *param = nullptr;
i64 count = 0;
- Type *bt = base_type(operand->type);
- if (bt->kind == Type_Struct) {
- if (bt->Struct.polymorphic_params != nullptr) {
- count = bt->Struct.polymorphic_params->Tuple.variables.count;
- if (index < count) {
- param = bt->Struct.polymorphic_params->Tuple.variables[cast(isize)index];
- }
- }
- } else if (bt->kind == Type_Union) {
- if (bt->Union.polymorphic_params != nullptr) {
- count = bt->Union.polymorphic_params->Tuple.variables.count;
- if (index < count) {
- param = bt->Union.polymorphic_params->Tuple.variables[cast(isize)index];
- }
+ TypeTuple *tuple = get_record_polymorphic_params(operand->type);
+ if (tuple) {
+ count = tuple->variables.count;
+ if (index < count) {
+ param = tuple->variables[cast(isize)index];
}
} else {
error(operand->expr, "Expected a specialized polymorphic record type for '%.*s'", LIT(builtin_name));
@@ -5385,7 +6427,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
return false;
}
- operand->value = exact_value_bool(is_type_subtype_of(op_src.type, op_dst.type));
+ operand->value = exact_value_bool(is_type_subtype_of_and_allow_polymorphic(op_src.type, op_dst.type));
operand->mode = Addressing_Constant;
operand->type = t_untyped_bool;
} break;
@@ -5411,6 +6453,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
Selection sel = lookup_field(type, field_name, false);
if (sel.entity == nullptr) {
+ ERROR_BLOCK();
gbString type_str = type_to_string(bt);
error(ce->args[0],
"'%s' has no field named '%.*s'", type_str, LIT(field_name));
@@ -5436,6 +6479,26 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
}
break;
+ case BuiltinProc_type_bit_set_backing_type:
+ {
+ 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;
+ }
+ if (bt->kind != Type_BitSet) {
+ gbString s = type_to_string(type);
+ error(ce->args[0], "Expected a bit_set type for '%.*s', got %s", LIT(builtin_name), s);
+ return false;
+ }
+
+ operand->mode = Addressing_Type;
+ operand->type = bit_set_to_int(bt);
+ break;
+ }
+
case BuiltinProc_type_equal_proc:
{
Operand op = {};
@@ -5517,6 +6580,51 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
break;
}
+ case BuiltinProc_procedure_of:
+ {
+ Ast *call_expr = unparen_expr(ce->args[0]);
+ Operand op = {};
+ check_expr_base(c, &op, ce->args[0], nullptr);
+ if (op.mode != Addressing_Value && !(call_expr && call_expr->kind == Ast_CallExpr)) {
+ error(ce->args[0], "Expected a call expression for '%.*s'", LIT(builtin_name));
+ return false;
+ }
+
+ Ast *proc = call_expr->CallExpr.proc;
+ Entity *e = entity_of_node(proc);
+
+ if (e == nullptr) {
+ error(ce->args[0], "Invalid procedure value, expected a regular/specialized procedure");
+ return false;
+ }
+
+ TypeAndValue tav = proc->tav;
+
+
+ operand->type = e->type;
+ operand->mode = Addressing_Value;
+ operand->value = tav.value;
+ operand->builtin_id = BuiltinProc_Invalid;
+ operand->proc_group = nullptr;
+
+ if (tav.mode == Addressing_Builtin) {
+ operand->mode = tav.mode;
+ operand->builtin_id = cast(BuiltinProcId)e->Builtin.id;
+ break;
+ }
+
+ if (!is_type_proc(e->type)) {
+ gbString s = type_to_string(e->type);
+ error(ce->args[0], "Expected a procedure value, got '%s'", s);
+ gb_string_free(s);
+ return false;
+ }
+
+
+ ce->entity_procedure_of = e;
+ break;
+ }
+
case BuiltinProc_constant_utf16_cstring:
{
String value = {};
@@ -5604,6 +6712,11 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
return false;
}
+ if (!check_target_feature_is_enabled(str_lit("atomics"), nullptr)) {
+ error(call, "'%.*s' requires target feature 'atomics' to be enabled, enable it with -target-features:\"atomics\" or choose a different -microarch", LIT(builtin_name));
+ return false;
+ }
+
Operand ptr = {};
Operand expected = {};
Operand timeout = {};
@@ -5656,6 +6769,11 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
return false;
}
+ if (!check_target_feature_is_enabled(str_lit("atomics"), nullptr)) {
+ error(call, "'%.*s' requires target feature 'atomics' to be enabled, enable it with -target-features:\"atomics\" or choose a different -microarch", LIT(builtin_name));
+ return false;
+ }
+
Operand ptr = {};
Operand waiters = {};
check_expr(c, &ptr, ce->args[0]); if (ptr.mode == Addressing_Invalid) return false;
diff --git a/src/check_decl.cpp b/src/check_decl.cpp
index 71b897a84..1d792dad8 100644
--- a/src/check_decl.cpp
+++ b/src/check_decl.cpp
@@ -88,9 +88,18 @@ gb_internal Type *check_init_variable(CheckerContext *ctx, Entity *e, Operand *o
e->type = t_invalid;
return nullptr;
} else if (is_type_polymorphic(t)) {
+ Entity *e2 = entity_of_node(operand->expr);
+ if (e2 == nullptr) {
+ e->type = t_invalid;
+ return nullptr;
+ }
+ if (e2->state.load() != EntityState_Resolved) {
+ e->type = t;
+ return nullptr;
+ }
gbString str = type_to_string(t);
defer (gb_string_free(str));
- error(e->token, "Invalid use of a polymorphic type '%s' in %.*s", str, LIT(context_name));
+ error(operand->expr, "Invalid use of a non-specialized polymorphic type '%s' in %.*s", str, LIT(context_name));
e->type = t_invalid;
return nullptr;
} else if (is_type_empty_union(t)) {
@@ -138,11 +147,10 @@ gb_internal void check_init_variables(CheckerContext *ctx, Entity **lhs, isize l
}
if (o->type && is_type_no_copy(o->type)) {
- begin_error_block();
+ ERROR_BLOCK();
if (check_no_copy_assignment(*o, str_lit("initialization"))) {
error_line("\tInitialization of a #no_copy type must be either implicitly zero, a constant literal, or a return value from a call expression");
}
- end_error_block();
}
}
if (rhs_count > 0 && lhs_count != rhs_count) {
@@ -150,6 +158,160 @@ gb_internal void check_init_variables(CheckerContext *ctx, Entity **lhs, isize l
}
}
+
+gb_internal void override_entity_in_scope(Entity *original_entity, Entity *new_entity) {
+ // NOTE(bill): The original_entity's scope may not be same scope that it was inserted into
+ // e.g. file entity inserted into its package scope
+ String original_name = original_entity->token.string;
+ Scope *found_scope = nullptr;
+ Entity *found_entity = nullptr;
+ scope_lookup_parent(original_entity->scope, original_name, &found_scope, &found_entity);
+ if (found_scope == nullptr) {
+ return;
+ }
+ rw_mutex_lock(&found_scope->mutex);
+ defer (rw_mutex_unlock(&found_scope->mutex));
+
+ // IMPORTANT NOTE(bill, 2021-04-10): Overriding behaviour was flawed in that the
+ // original entity was still used check checked, but the checking was only
+ // relying on "constant" data such as the Entity.type and Entity.Constant.value
+ //
+ // Therefore two things can be done: the type can be assigned to state that it
+ // has been "evaluated" and the variant data can be copied across
+
+ string_map_set(&found_scope->elements, original_name, new_entity);
+
+ original_entity->flags |= EntityFlag_Overridden;
+ original_entity->type = new_entity->type;
+ original_entity->kind = new_entity->kind;
+ original_entity->decl_info = new_entity->decl_info;
+ original_entity->aliased_of = new_entity;
+
+ original_entity->identifier.store(new_entity->identifier);
+
+ if (original_entity->identifier.load() != nullptr &&
+ original_entity->identifier.load()->kind == Ast_Ident) {
+ original_entity->identifier.load()->Ident.entity = new_entity;
+ }
+
+ // IMPORTANT NOTE(bill, 2021-04-10): copy only the variants
+ // This is most likely NEVER required, but it does not at all hurt to keep
+ isize offset = cast(u8 *)&original_entity->Dummy.start - cast(u8 *)original_entity;
+ isize size = gb_size_of(*original_entity) - offset;
+ gb_memmove(cast(u8 *)original_entity + offset, cast(u8 *)new_entity + offset, size);
+}
+
+gb_internal bool check_override_as_type_due_to_aliasing(CheckerContext *ctx, Entity *e, Entity *entity, Ast *init, Type *named_type) {
+ if (entity != nullptr && entity->kind == Entity_TypeName) {
+ // @TypeAliasingProblem
+ // NOTE(bill, 2022-02-03): This is used to solve the problem caused by type aliases
+ // being "confused" as constants
+ //
+ // A :: B
+ // C :: proc "c" (^A)
+ // B :: struct {x: C}
+ //
+ // A gets evaluated first, and then checks B.
+ // B then checks C.
+ // C then tries to check A which is unresolved but thought to be a constant.
+ // Therefore within C's check, A errs as "not a type".
+ //
+ // This is because a const declaration may or may not be a type and this cannot
+ // be determined from a syntactical standpoint.
+ // This check allows the compiler to override the entity to be checked as a type.
+ //
+ // There is no problem if B is prefixed with the `#type` helper enforcing at
+ // both a syntax and semantic level that B must be a type.
+ //
+ // A :: #type B
+ //
+ // This approach is not fool proof and can fail in case such as:
+ //
+ // X :: type_of(x)
+ // X :: Foo(int).Type
+ //
+ // Since even these kind of declarations may cause weird checking cycles.
+ // For the time being, these are going to be treated as an unfortunate error
+ // until there is a proper delaying system to try declaration again if they
+ // have failed.
+
+ if (e->type != nullptr && is_type_typed(e->type)) {
+ return false;
+ }
+
+ e->kind = Entity_TypeName;
+ check_type_decl(ctx, e, init, named_type);
+ return true;
+ }
+ return false;
+}
+
+gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d);
+
+gb_internal bool check_try_override_const_decl(CheckerContext *ctx, Entity *e, Entity *entity, Ast *init, Type *named_type) {
+ if (entity == nullptr) {
+ retry_proc_lit:;
+ init = unparen_expr(init);
+ if (init == nullptr) {
+ return false;
+ }
+ if (init->kind == Ast_TernaryWhenExpr) {
+ ast_node(we, TernaryWhenExpr, init);
+ if (we->cond == nullptr) {
+ return false;
+ }
+ if (we->cond->tav.value.kind != ExactValue_Bool) {
+ return false;
+ }
+ init = we->cond->tav.value.value_bool ? we->x : we->y;
+ goto retry_proc_lit;
+ } if (init->kind == Ast_ProcLit) {
+ // NOTE(bill, 2024-07-04): Override as a procedure entity because this could be within a `when` statement
+ e->kind = Entity_Procedure;
+ e->type = nullptr;
+ DeclInfo *d = decl_info_of_entity(e);
+ d->proc_lit = init;
+ check_proc_decl(ctx, e, d);
+ return true;
+ }
+
+ return false;
+ }
+ switch (entity->kind) {
+ case Entity_TypeName:
+ if (check_override_as_type_due_to_aliasing(ctx, e, entity, init, named_type)) {
+ return true;
+ }
+ break;
+ case Entity_Builtin:
+ if (e->type != nullptr) {
+ return false;
+ }
+ e->kind = Entity_Builtin;
+ e->Builtin.id = entity->Builtin.id;
+ e->type = t_invalid;
+ return true;
+ }
+
+ if (e->type != nullptr && entity->type != nullptr) {
+ Operand x = {};
+ x.type = entity->type;
+ x.mode = Addressing_Variable;
+ if (!check_is_assignable_to(ctx, &x, e->type)) {
+ return false;
+ }
+ }
+
+ // NOTE(bill): Override aliased entity
+ switch (entity->kind) {
+ case Entity_ProcGroup:
+ case Entity_Procedure:
+ override_entity_in_scope(e, entity);
+ return true;
+ }
+ return false;
+}
+
gb_internal void check_init_constant(CheckerContext *ctx, Entity *e, Operand *operand) {
if (operand->mode == Addressing_Invalid ||
operand->type == t_invalid ||
@@ -161,6 +323,13 @@ gb_internal void check_init_constant(CheckerContext *ctx, Entity *e, Operand *op
}
if (operand->mode != Addressing_Constant) {
+ Entity *entity = entity_of_node(operand->expr);
+ if (check_try_override_const_decl(ctx, e, entity, operand->expr, nullptr)) {
+ return;
+ }
+ }
+
+ if (operand->mode != Addressing_Constant) {
gbString str = expr_to_string(operand->expr);
error(operand->expr, "'%s' is not a compile-time known constant", str);
gb_string_free(str);
@@ -211,6 +380,7 @@ gb_internal bool is_type_distinct(Ast *node) {
case Ast_UnionType:
case Ast_EnumType:
case Ast_ProcType:
+ case Ast_BitFieldType:
return true;
case Ast_PointerType:
@@ -367,49 +537,6 @@ gb_internal void check_type_decl(CheckerContext *ctx, Entity *e, Ast *init_expr,
}
-gb_internal void override_entity_in_scope(Entity *original_entity, Entity *new_entity) {
- // NOTE(bill): The original_entity's scope may not be same scope that it was inserted into
- // e.g. file entity inserted into its package scope
- String original_name = original_entity->token.string;
- Scope *found_scope = nullptr;
- Entity *found_entity = nullptr;
- scope_lookup_parent(original_entity->scope, original_name, &found_scope, &found_entity);
- if (found_scope == nullptr) {
- return;
- }
- rw_mutex_lock(&found_scope->mutex);
- defer (rw_mutex_unlock(&found_scope->mutex));
-
- // IMPORTANT NOTE(bill, 2021-04-10): Overriding behaviour was flawed in that the
- // original entity was still used check checked, but the checking was only
- // relying on "constant" data such as the Entity.type and Entity.Constant.value
- //
- // Therefore two things can be done: the type can be assigned to state that it
- // has been "evaluated" and the variant data can be copied across
-
- string_map_set(&found_scope->elements, original_name, new_entity);
-
- original_entity->flags |= EntityFlag_Overridden;
- original_entity->type = new_entity->type;
- original_entity->aliased_of = new_entity;
-
- Ast *empty_ident = nullptr;
- original_entity->identifier.compare_exchange_strong(empty_ident, new_entity->identifier);
-
- if (original_entity->identifier.load() != nullptr &&
- original_entity->identifier.load()->kind == Ast_Ident) {
- original_entity->identifier.load()->Ident.entity = new_entity;
- }
-
- // IMPORTANT NOTE(bill, 2021-04-10): copy only the variants
- // This is most likely NEVER required, but it does not at all hurt to keep
- isize offset = cast(u8 *)&original_entity->Dummy.start - cast(u8 *)original_entity;
- isize size = gb_size_of(*original_entity) - offset;
- gb_memmove(cast(u8 *)original_entity, cast(u8 *)new_entity, size);
-}
-
-
-
gb_internal void check_const_decl(CheckerContext *ctx, Entity *e, Ast *type_expr, Ast *init, Type *named_type) {
GB_ASSERT(e->type == nullptr);
GB_ASSERT(e->kind == Entity_Constant);
@@ -435,41 +562,7 @@ gb_internal void check_const_decl(CheckerContext *ctx, Entity *e, Ast *type_expr
if (init != nullptr) {
Entity *entity = check_entity_from_ident_or_selector(ctx, init, false);
- if (entity != nullptr && entity->kind == Entity_TypeName) {
- // @TypeAliasingProblem
- // NOTE(bill, 2022-02-03): This is used to solve the problem caused by type aliases
- // being "confused" as constants
- //
- // A :: B
- // C :: proc "c" (^A)
- // B :: struct {x: C}
- //
- // A gets evaluated first, and then checks B.
- // B then checks C.
- // C then tries to check A which is unresolved but thought to be a constant.
- // Therefore within C's check, A errs as "not a type".
- //
- // This is because a const declaration may or may not be a type and this cannot
- // be determined from a syntactical standpoint.
- // This check allows the compiler to override the entity to be checked as a type.
- //
- // There is no problem if B is prefixed with the `#type` helper enforcing at
- // both a syntax and semantic level that B must be a type.
- //
- // A :: #type B
- //
- // This approach is not fool proof and can fail in case such as:
- //
- // X :: type_of(x)
- // X :: Foo(int).Type
- //
- // Since even these kind of declarations may cause weird checking cycles.
- // For the time being, these are going to be treated as an unfortunate error
- // until there is a proper delaying system to try declaration again if they
- // have failed.
-
- e->kind = Entity_TypeName;
- check_type_decl(ctx, e, init, named_type);
+ if (check_override_as_type_due_to_aliasing(ctx, e, entity, init, named_type)) {
return;
}
entity = nullptr;
@@ -479,6 +572,9 @@ gb_internal void check_const_decl(CheckerContext *ctx, Entity *e, Ast *type_expr
entity = check_selector(ctx, &operand, init, e->type);
} else {
check_expr_or_type(ctx, &operand, init, e->type);
+ if (init->kind == Ast_CallExpr) {
+ entity = init->CallExpr.entity_procedure_of;
+ }
}
switch (operand.mode) {
@@ -526,6 +622,7 @@ gb_internal void check_const_decl(CheckerContext *ctx, Entity *e, Ast *type_expr
return;
}
+
if (entity != nullptr) {
if (e->type != nullptr) {
Operand x = {};
@@ -601,6 +698,13 @@ gb_internal bool sig_compare(TypeCheckSig *a, TypeCheckSig *b, Type *x, Type *y)
}
gb_internal bool signature_parameter_similar_enough(Type *x, Type *y) {
+ if (is_type_bit_set(x)) {
+ x = bit_set_to_int(x);
+ }
+ if (is_type_bit_set(y)) {
+ y = bit_set_to_int(y);
+ }
+
if (sig_compare(is_type_pointer, x, y)) {
return true;
}
@@ -647,6 +751,14 @@ gb_internal bool signature_parameter_similar_enough(Type *x, Type *y) {
return true;
}
+ if (sig_compare(is_type_slice, x, y)) {
+ Type *s1 = core_type(x);
+ Type *s2 = core_type(y);
+ if (signature_parameter_similar_enough(s1->Slice.elem, s2->Slice.elem)) {
+ return true;
+ }
+ }
+
return are_types_identical(x, y);
}
@@ -663,16 +775,56 @@ gb_internal bool are_signatures_similar_enough(Type *a_, Type *b_) {
if (a->result_count != b->result_count) {
return false;
}
+
+ if (a->c_vararg != b->c_vararg) {
+ return false;
+ }
+
+ if (a->variadic != b->variadic) {
+ return false;
+ }
+
+ if (a->variadic && a->variadic_index != b->variadic_index) {
+ return false;
+ }
+
for (isize i = 0; i < a->param_count; i++) {
Type *x = core_type(a->params->Tuple.variables[i]->type);
Type *y = core_type(b->params->Tuple.variables[i]->type);
+
+ if (x->kind == Type_BitSet && x->BitSet.underlying) {
+ x = core_type(x->BitSet.underlying);
+ }
+ if (y->kind == Type_BitSet && y->BitSet.underlying) {
+ y = core_type(y->BitSet.underlying);
+ }
+
+ // Allow a `#c_vararg args: ..any` with `#c_vararg args: ..foo`.
+ if (a->variadic && i == a->variadic_index) {
+ GB_ASSERT(x->kind == Type_Slice);
+ GB_ASSERT(y->kind == Type_Slice);
+ Type *x_elem = core_type(x->Slice.elem);
+ Type *y_elem = core_type(y->Slice.elem);
+ if (is_type_any(x_elem) || is_type_any(y_elem)) {
+ continue;
+ }
+ }
+
if (!signature_parameter_similar_enough(x, y)) {
return false;
}
}
for (isize i = 0; i < a->result_count; i++) {
- Type *x = base_type(a->results->Tuple.variables[i]->type);
- Type *y = base_type(b->results->Tuple.variables[i]->type);
+ Type *x = core_type(a->results->Tuple.variables[i]->type);
+ Type *y = core_type(b->results->Tuple.variables[i]->type);
+
+ if (x->kind == Type_BitSet && x->BitSet.underlying) {
+ x = core_type(x->BitSet.underlying);
+ }
+ if (y->kind == Type_BitSet && y->BitSet.underlying) {
+ y = core_type(y->BitSet.underlying);
+ }
+
if (!signature_parameter_similar_enough(x, y)) {
return false;
}
@@ -699,7 +851,7 @@ gb_internal Entity *init_entity_foreign_library(CheckerContext *ctx, Entity *e)
}
if (ident == nullptr) {
- error(e->token, "foreign entiies must declare which library they are from");
+ error(e->token, "foreign entities must declare which library they are from");
} else if (ident->kind != Ast_Ident) {
error(ident, "foreign library names must be an identifier");
} else {
@@ -724,9 +876,10 @@ gb_internal Entity *init_entity_foreign_library(CheckerContext *ctx, Entity *e)
return nullptr;
}
-gb_internal String handle_link_name(CheckerContext *ctx, Token token, String link_name, String link_prefix) {
+gb_internal String handle_link_name(CheckerContext *ctx, Token token, String link_name, String link_prefix, String link_suffix) {
+ String original_link_name = link_name;
if (link_prefix.len > 0) {
- if (link_name.len > 0) {
+ if (original_link_name.len > 0) {
error(token, "'link_name' and 'link_prefix' cannot be used together");
} else {
isize len = link_prefix.len + token.string.len;
@@ -738,9 +891,28 @@ gb_internal String handle_link_name(CheckerContext *ctx, Token token, String lin
link_name = make_string(name, len);
}
}
+
+ if (link_suffix.len > 0) {
+ if (original_link_name.len > 0) {
+ error(token, "'link_name' and 'link_suffix' cannot be used together");
+ } else {
+ String new_name = token.string;
+ if (link_name != original_link_name) {
+ new_name = link_name;
+ }
+
+ isize len = new_name.len + link_suffix.len;
+ u8 *name = gb_alloc_array(permanent_allocator(), u8, len+1);
+ gb_memmove(name, &new_name[0], new_name.len);
+ gb_memmove(name+new_name.len, &link_suffix[0], link_suffix.len);
+ name[len] = 0;
+ link_name = make_string(name, len);
+ }
+ }
return link_name;
}
+
gb_internal void check_objc_methods(CheckerContext *ctx, Entity *e, AttributeContext const &ac) {
if (!(ac.objc_name.len || ac.objc_is_class_method || ac.objc_type)) {
return;
@@ -801,6 +973,43 @@ gb_internal void check_objc_methods(CheckerContext *ctx, Entity *e, AttributeCon
}
}
+gb_internal void check_foreign_procedure(CheckerContext *ctx, Entity *e, DeclInfo *d) {
+ GB_ASSERT(e != nullptr);
+ GB_ASSERT(e->kind == Entity_Procedure);
+ String name = e->Procedure.link_name;
+
+ mutex_lock(&ctx->info->foreign_mutex);
+
+ auto *fp = &ctx->info->foreigns;
+ StringHashKey key = string_hash_string(name);
+ Entity **found = string_map_get(fp, key);
+ if (found && e != *found) {
+ Entity *f = *found;
+ TokenPos pos = f->token.pos;
+ Type *this_type = base_type(e->type);
+ Type *other_type = base_type(f->type);
+ if (is_type_proc(this_type) && is_type_proc(other_type)) {
+ if (!are_signatures_similar_enough(this_type, other_type)) {
+ error(d->proc_lit,
+ "Redeclaration of foreign procedure '%.*s' with different type signatures\n"
+ "\tat %s",
+ LIT(name), token_pos_to_string(pos));
+ }
+ } else if (!signature_parameter_similar_enough(this_type, other_type)) {
+ error(d->proc_lit,
+ "Foreign entity '%.*s' previously declared elsewhere with a different type\n"
+ "\tat %s",
+ LIT(name), token_pos_to_string(pos));
+ }
+ } else if (name == "main") {
+ error(d->proc_lit, "The link name 'main' is reserved for internal use");
+ } else {
+ string_map_set(fp, key, e);
+ }
+
+ mutex_unlock(&ctx->info->foreign_mutex);
+}
+
gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
GB_ASSERT(e->type == nullptr);
if (d->proc_lit->kind != Ast_ProcLit) {
@@ -862,7 +1071,7 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
}
TypeProc *pt = &proc_type->Proc;
- AttributeContext ac = make_attribute_context(e->Procedure.link_prefix);
+ AttributeContext ac = make_attribute_context(e->Procedure.link_prefix, e->Procedure.link_suffix);
if (d != nullptr) {
check_decl_attributes(ctx, d->attributes, proc_decl_attribute, &ac);
@@ -886,32 +1095,135 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
check_objc_methods(ctx, e, ac);
- if (ac.require_target_feature.len != 0 && ac.enable_target_feature.len != 0) {
- error(e->token, "Attributes @(require_target_feature=...) and @(enable_target_feature=...) cannot be used together");
- } else if (ac.require_target_feature.len != 0) {
- if (check_target_feature_is_enabled(e->token.pos, ac.require_target_feature)) {
- e->Procedure.target_feature = ac.require_target_feature;
- } else {
- e->Procedure.target_feature_disabled = true;
+ {
+ if (ac.require_target_feature.len != 0 && ac.enable_target_feature.len != 0) {
+ error(e->token, "A procedure cannot have both @(require_target_feature=\"...\") and @(enable_target_feature=\"...\")");
+ }
+
+ if (build_context.strict_target_features && ac.enable_target_feature.len != 0) {
+ ac.require_target_feature = ac.enable_target_feature;
+ ac.enable_target_feature.len = 0;
+ }
+
+ if (ac.require_target_feature.len != 0) {
+ pt->require_target_feature = ac.require_target_feature;
+ String invalid;
+ if (!check_target_feature_is_valid_globally(ac.require_target_feature, &invalid)) {
+ error(e->token, "Required target feature '%.*s' is not a valid target feature", LIT(invalid));
+ } else if (!check_target_feature_is_enabled(ac.require_target_feature, nullptr)) {
+ e->flags |= EntityFlag_Disabled;
+ }
+ } else if (ac.enable_target_feature.len != 0) {
+
+ // NOTE: disallow wasm, features on that arch are always global to the module.
+ if (is_arch_wasm()) {
+ error(e->token, "@(enable_target_feature=\"...\") is not allowed on wasm, features for wasm must be declared globally");
+ }
+
+ pt->enable_target_feature = ac.enable_target_feature;
+ String invalid;
+ if (!check_target_feature_is_valid_globally(ac.enable_target_feature, &invalid)) {
+ error(e->token, "Procedure enabled target feature '%.*s' is not a valid target feature", LIT(invalid));
+ }
}
- } else if (ac.enable_target_feature.len != 0) {
- enable_target_feature(e->token.pos, ac.enable_target_feature);
- e->Procedure.target_feature = ac.enable_target_feature;
}
switch (e->Procedure.optimization_mode) {
case ProcedureOptimizationMode_None:
- case ProcedureOptimizationMode_Minimal:
if (pl->inlining == ProcInlining_inline) {
error(e->token, "#force_inline cannot be used in conjunction with the attribute 'optimization_mode' with neither \"none\" nor \"minimal\"");
}
break;
}
+ e->Procedure.entry_point_only = ac.entry_point_only;
e->Procedure.is_export = ac.is_export;
+
+ bool has_instrumentation = false;
+ if (pl->body == nullptr) {
+ has_instrumentation = false;
+ if (ac.no_instrumentation != Instrumentation_Default) {
+ error(e->token, "@(no_instrumentation) is not allowed on foreign procedures");
+ }
+ } else {
+ AstFile *file = e->token.pos.file_id ? global_files[e->token.pos.file_id] : nullptr;
+ if (file) {
+ has_instrumentation = (file->flags & AstFile_NoInstrumentation) == 0;
+ }
+
+ switch (ac.no_instrumentation) {
+ case Instrumentation_Enabled: has_instrumentation = true; break;
+ case Instrumentation_Default: break;
+ case Instrumentation_Disabled: has_instrumentation = false; break;
+ }
+ }
+
+ auto const is_valid_instrumentation_call = [](Type *type) -> bool {
+ if (type == nullptr || type->kind != Type_Proc) {
+ return false;
+ }
+ if (type->Proc.calling_convention != ProcCC_Contextless) {
+ return false;
+ }
+ if (type->Proc.result_count != 0) {
+ return false;
+ }
+ if (type->Proc.param_count != 3) {
+ return false;
+ }
+ Type *p0 = type->Proc.params->Tuple.variables[0]->type;
+ Type *p1 = type->Proc.params->Tuple.variables[1]->type;
+ Type *p3 = type->Proc.params->Tuple.variables[2]->type;
+ return is_type_rawptr(p0) && is_type_rawptr(p1) && are_types_identical(p3, t_source_code_location);
+ };
+
+ static char const *instrumentation_proc_type_str = "proc \"contextless\" (proc_address: rawptr, call_site_return_address: rawptr, loc: runtime.Source_Code_Location)";
+
+ if (ac.instrumentation_enter && ac.instrumentation_exit) {
+ error(e->token, "A procedure cannot be marked with both @(instrumentation_enter) and @(instrumentation_exit)");
+
+ has_instrumentation = false;
+ e->flags |= EntityFlag_Require;
+ } else if (ac.instrumentation_enter) {
+ if (!is_valid_instrumentation_call(e->type)) {
+ init_core_source_code_location(ctx->checker);
+ gbString s = type_to_string(e->type);
+ error(e->token, "@(instrumentation_enter) procedures must have the type '%s', got %s", instrumentation_proc_type_str, s);
+ gb_string_free(s);
+ }
+ MUTEX_GUARD(&ctx->info->instrumentation_mutex);
+ if (ctx->info->instrumentation_enter_entity != nullptr) {
+ error(e->token, "@(instrumentation_enter) has already been set");
+ } else {
+ ctx->info->instrumentation_enter_entity = e;
+ }
+
+ has_instrumentation = false;
+ e->flags |= EntityFlag_Require;
+ } else if (ac.instrumentation_exit) {
+ init_core_source_code_location(ctx->checker);
+ if (!is_valid_instrumentation_call(e->type)) {
+ gbString s = type_to_string(e->type);
+ error(e->token, "@(instrumentation_exit) procedures must have the type '%s', got %s", instrumentation_proc_type_str, s);
+ gb_string_free(s);
+ }
+ MUTEX_GUARD(&ctx->info->instrumentation_mutex);
+ if (ctx->info->instrumentation_exit_entity != nullptr) {
+ error(e->token, "@(instrumentation_exit) has already been set");
+ } else {
+ ctx->info->instrumentation_exit_entity = e;
+ }
+
+ has_instrumentation = false;
+ e->flags |= EntityFlag_Require;
+ }
+
+ e->Procedure.has_instrumentation = has_instrumentation;
+
+
e->deprecated_message = ac.deprecated_message;
e->warning_message = ac.warning_message;
- ac.link_name = handle_link_name(ctx, e->token, ac.link_name, ac.link_prefix);
+ ac.link_name = handle_link_name(ctx, e->token, ac.link_name, ac.link_prefix, ac.link_suffix);
if (ac.has_disabled_proc) {
if (ac.disabled_proc) {
e->flags |= EntityFlag_Disabled;
@@ -943,7 +1255,7 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
}
- if (e->pkg != nullptr && e->token.string == "main") {
+ if (e->pkg != nullptr && e->token.string == "main" && !build_context.no_entry_point) {
if (e->pkg->kind != Package_Runtime) {
if (pt->param_count != 0 ||
pt->result_count != 0) {
@@ -1008,10 +1320,19 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
} else {
pt->require_results = true;
}
+ } else if (d->foreign_require_results && pt->result_count != 0) {
+ pt->require_results = true;
}
if (ac.link_name.len > 0) {
- e->Procedure.link_name = ac.link_name;
+ String ln = ac.link_name;
+ e->Procedure.link_name = ln;
+ if (ln == "memcpy" ||
+ ln == "memmove" ||
+ ln == "mem_copy" ||
+ ln == "mem_copy_non_overlapping") {
+ e->Procedure.is_memcpy_like = true;
+ }
}
if (ac.deferred_procedure.entity != nullptr) {
@@ -1025,54 +1346,16 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
name = e->Procedure.link_name;
}
Entity *foreign_library = init_entity_foreign_library(ctx, e);
-
- if (is_arch_wasm() && foreign_library != nullptr) {
- String module_name = str_lit("env");
- GB_ASSERT (foreign_library->kind == Entity_LibraryName);
- if (foreign_library->LibraryName.paths.count != 1) {
- error(foreign_library->token, "'foreign import' for '%.*s' architecture may only have one path, got %td",
- LIT(target_arch_names[build_context.metrics.arch]), foreign_library->LibraryName.paths.count);
- }
-
- if (foreign_library->LibraryName.paths.count >= 1) {
- module_name = foreign_library->LibraryName.paths[0];
- }
- name = concatenate3_strings(permanent_allocator(), module_name, WASM_MODULE_NAME_SEPARATOR, name);
- }
-
e->Procedure.is_foreign = true;
e->Procedure.link_name = name;
+ e->Procedure.foreign_library = foreign_library;
- mutex_lock(&ctx->info->foreign_mutex);
-
- auto *fp = &ctx->info->foreigns;
- StringHashKey key = string_hash_string(name);
- Entity **found = string_map_get(fp, key);
- if (found && e != *found) {
- Entity *f = *found;
- TokenPos pos = f->token.pos;
- Type *this_type = base_type(e->type);
- Type *other_type = base_type(f->type);
- if (is_type_proc(this_type) && is_type_proc(other_type)) {
- if (!are_signatures_similar_enough(this_type, other_type)) {
- error(d->proc_lit,
- "Redeclaration of foreign procedure '%.*s' with different type signatures\n"
- "\tat %s",
- LIT(name), token_pos_to_string(pos));
- }
- } else if (!are_types_identical(this_type, other_type)) {
- error(d->proc_lit,
- "Foreign entity '%.*s' previously declared elsewhere with a different type\n"
- "\tat %s",
- LIT(name), token_pos_to_string(pos));
- }
- } else if (name == "main") {
- error(d->proc_lit, "The link name 'main' is reserved for internal use");
+ if (is_arch_wasm() && foreign_library != nullptr) {
+ // 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 {
- string_map_set(fp, key, e);
+ check_foreign_procedure(ctx, e, d);
}
-
- mutex_unlock(&ctx->info->foreign_mutex);
} else {
String name = e->token.string;
if (e->Procedure.link_name.len > 0) {
@@ -1119,7 +1402,7 @@ gb_internal void check_global_variable_decl(CheckerContext *ctx, Entity *&e, Ast
}
e->flags |= EntityFlag_Visited;
- AttributeContext ac = make_attribute_context(e->Variable.link_prefix);
+ AttributeContext ac = make_attribute_context(e->Variable.link_prefix, e->Variable.link_suffix);
ac.init_expr_list_count = init_expr != nullptr ? 1 : 0;
DeclInfo *decl = decl_info_of_entity(e);
@@ -1140,7 +1423,10 @@ gb_internal void check_global_variable_decl(CheckerContext *ctx, Entity *&e, Ast
if (ac.is_static) {
error(e->token, "@(static) is not supported for global variables, nor required");
}
- ac.link_name = handle_link_name(ctx, e->token, ac.link_name, ac.link_prefix);
+ if (ac.rodata) {
+ e->Variable.is_rodata = true;
+ }
+ ac.link_name = handle_link_name(ctx, e->token, ac.link_name, ac.link_prefix, ac.link_suffix);
if (is_arch_wasm() && e->Variable.thread_local_model.len != 0) {
e->Variable.thread_local_model.len = 0;
@@ -1176,8 +1462,8 @@ gb_internal void check_global_variable_decl(CheckerContext *ctx, Entity *&e, Ast
error(e->token, "A foreign variable declaration cannot have a default value");
}
init_entity_foreign_library(ctx, e);
- if (is_arch_wasm()) {
- error(e->token, "A foreign variable declaration are not allowed for the '%.*s' architecture", LIT(target_arch_names[build_context.metrics.arch]));
+ if (is_arch_wasm() && e->Variable.foreign_library != nullptr) {
+ error(e->token, "A foreign variable declaration can not be scoped to a module and must be declared in a 'foreign {' (without a library) block");
}
}
if (ac.link_name.len > 0) {
@@ -1201,7 +1487,7 @@ gb_internal void check_global_variable_decl(CheckerContext *ctx, Entity *&e, Ast
TokenPos pos = f->token.pos;
Type *this_type = base_type(e->type);
Type *other_type = base_type(f->type);
- if (!are_types_identical(this_type, other_type)) {
+ if (!signature_parameter_similar_enough(this_type, other_type)) {
error(e->token,
"Foreign entity '%.*s' previously declared elsewhere with a different type\n"
"\tat %s",
@@ -1226,6 +1512,9 @@ gb_internal void check_global_variable_decl(CheckerContext *ctx, Entity *&e, Ast
Operand o = {};
check_expr_with_type_hint(ctx, &o, init_expr, e->type);
check_init_variable(ctx, e, &o, str_lit("variable declaration"));
+ if (e->Variable.is_rodata && o.mode != Addressing_Constant) {
+ error(o.expr, "Variables declared with @(rodata) must have constant initialization");
+ }
check_rtti_type_disallowed(e->token, e->type, "A variable declaration is using a type, %s, which has been disallowed");
}
@@ -1286,6 +1575,10 @@ gb_internal void check_proc_group_decl(CheckerContext *ctx, Entity *pg_entity, D
continue;
}
+ if (p->flags & EntityFlag_Disabled) {
+ continue;
+ }
+
String name = p->token.string;
for (isize k = j+1; k < pge->entities.count; k++) {
@@ -1300,8 +1593,12 @@ gb_internal void check_proc_group_decl(CheckerContext *ctx, Entity *pg_entity, D
continue;
}
- begin_error_block();
- defer (end_error_block());
+
+ ERROR_BLOCK();
+
+ if (q->flags & EntityFlag_Disabled) {
+ continue;
+ }
ProcTypeOverloadKind kind = are_proc_types_overload_safe(p->type, q->type);
bool both_have_where_clauses = false;
@@ -1339,6 +1636,7 @@ gb_internal void check_proc_group_decl(CheckerContext *ctx, Entity *pg_entity, D
break;
case ProcOverload_ParamCount:
case ProcOverload_ParamTypes:
+ case ProcOverload_TargetFeatures:
// This is okay :)
break;
@@ -1506,6 +1804,17 @@ gb_internal bool check_proc_body(CheckerContext *ctx_, Token token, DeclInfo *de
if (e->kind != Entity_Variable) {
continue;
}
+ if (is_type_polymorphic(e->type) && is_type_polymorphic_record_unspecialized(e->type)) {
+ gbString s = type_to_string(e->type);
+ char const *msg = "Unspecialized polymorphic types are not allowed in procedure parameters, got %s";
+ if (e->Variable.type_expr) {
+ error(e->Variable.type_expr, msg, s);
+ } else {
+ error(e->token, msg, s);
+ }
+ gb_string_free(s);
+ }
+
if (!(e->flags & EntityFlag_Using)) {
continue;
}
@@ -1546,6 +1855,7 @@ gb_internal bool check_proc_body(CheckerContext *ctx_, Token token, DeclInfo *de
Entity *uvar = entry.uvar;
Entity *prev = scope_insert_no_mutex(ctx->scope, uvar);
if (prev != nullptr) {
+ ERROR_BLOCK();
error(e->token, "Namespace collision while 'using' procedure argument '%.*s' of: %.*s", LIT(e->token.string), LIT(prev->token.string));
error_line("%.*s != %.*s\n", LIT(uvar->token.string), LIT(prev->token.string));
break;
@@ -1620,5 +1930,14 @@ gb_internal bool check_proc_body(CheckerContext *ctx_, Token token, DeclInfo *de
add_deps_from_child_to_parent(decl);
+ for (VariadicReuseData const &vr : decl->variadic_reuses) {
+ GB_ASSERT(vr.slice_type->kind == Type_Slice);
+ Type *elem = vr.slice_type->Slice.elem;
+ i64 size = type_size_of(elem);
+ i64 align = type_align_of(elem);
+ decl->variadic_reuse_max_bytes = gb_max(decl->variadic_reuse_max_bytes, size*vr.max_count);
+ decl->variadic_reuse_max_align = gb_max(decl->variadic_reuse_max_align, align);
+ }
+
return true;
}
diff --git a/src/check_expr.cpp b/src/check_expr.cpp
index 5cc548739..231ece2f4 100644
--- a/src/check_expr.cpp
+++ b/src/check_expr.cpp
@@ -79,7 +79,6 @@ gb_internal Type * check_type_expr (CheckerContext *c, Ast *exp
gb_internal Type * make_optional_ok_type (Type *value, bool typed=true);
gb_internal Entity * check_selector (CheckerContext *c, Operand *operand, Ast *node, Type *type_hint);
gb_internal Entity * check_ident (CheckerContext *c, Operand *o, Ast *n, Type *named_type, Type *type_hint, bool allow_import_name);
-gb_internal Entity * find_polymorphic_record_entity (CheckerContext *c, Type *original_type, isize param_count, Array<Operand> const &ordered_operands, bool *failure);
gb_internal void check_not_tuple (CheckerContext *c, Operand *operand);
gb_internal void convert_to_typed (CheckerContext *c, Operand *operand, Type *target_type);
gb_internal gbString expr_to_string (Ast *expression);
@@ -100,15 +99,16 @@ gb_internal void check_union_type (CheckerContext *c, Type *un
gb_internal Type * check_init_variable (CheckerContext *c, Entity *e, Operand *operand, String context_name);
-gb_internal void check_assignment_error_suggestion(CheckerContext *c, Operand *o, Type *type);
+gb_internal void check_assignment_error_suggestion(CheckerContext *c, Operand *o, Type *type, i64 max_bit_size=0);
gb_internal void add_map_key_type_dependencies(CheckerContext *ctx, Type *key);
+gb_internal Type *make_soa_struct_fixed(CheckerContext *ctx, Ast *array_typ_expr, Ast *elem_expr, Type *elem, i64 count, Type *generic_type);
gb_internal Type *make_soa_struct_slice(CheckerContext *ctx, Ast *array_typ_expr, Ast *elem_expr, Type *elem);
gb_internal Type *make_soa_struct_dynamic_array(CheckerContext *ctx, Ast *array_typ_expr, Ast *elem_expr, Type *elem);
gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 id, Type *type_hint);
-gb_internal void check_promote_optional_ok(CheckerContext *c, Operand *x, Type **val_type_, Type **ok_type_);
+gb_internal void check_promote_optional_ok(CheckerContext *c, Operand *x, Type **val_type_, Type **ok_type_, bool change_operand=true);
gb_internal void check_or_else_right_type(CheckerContext *c, Ast *expr, String const &name, Type *right_type);
gb_internal void check_or_else_split_types(CheckerContext *c, Operand *x, String const &name, Type **left_type_, Type **right_type_);
@@ -119,6 +119,16 @@ gb_internal bool is_diverging_expr(Ast *expr);
gb_internal isize get_procedure_param_count_excluding_defaults(Type *pt, isize *param_count_);
+gb_internal bool is_expr_inferred_fixed_array(Ast *type_expr);
+
+gb_internal Entity *find_polymorphic_record_entity(GenTypesData *found_gen_types, isize param_count, Array<Operand> const &ordered_operands);
+
+gb_internal bool complete_soa_type(Checker *checker, Type *t, bool wait_to_finish);
+
+gb_internal bool check_is_castable_to(CheckerContext *c, Operand *operand, Type *y);
+
+gb_internal bool is_exact_value_zero(ExactValue const &v);
+
enum LoadDirectiveResult {
LoadDirective_Success = 0,
LoadDirective_Error = 1,
@@ -184,6 +194,8 @@ gb_internal void populate_check_did_you_mean_objc_entity(StringSet *set, Entity
gb_internal void check_did_you_mean_objc_entity(String const &name, Entity *e, bool is_type, char const *prefix = "") {
+ if (build_context.terse_errors) { return; }
+
ERROR_BLOCK();
GB_ASSERT(e->kind == Entity_TypeName);
GB_ASSERT(e->TypeName.objc_metadata != nullptr);
@@ -204,6 +216,8 @@ gb_internal void check_did_you_mean_objc_entity(String const &name, Entity *e, b
}
gb_internal void check_did_you_mean_type(String const &name, Array<Entity *> const &fields, char const *prefix = "") {
+ if (build_context.terse_errors) { return; }
+
ERROR_BLOCK();
DidYouMeanAnswers d = did_you_mean_make(heap_allocator(), fields.count, name);
@@ -217,6 +231,8 @@ gb_internal void check_did_you_mean_type(String const &name, Array<Entity *> con
gb_internal void check_did_you_mean_type(String const &name, Slice<Entity *> const &fields, char const *prefix = "") {
+ if (build_context.terse_errors) { return; }
+
ERROR_BLOCK();
DidYouMeanAnswers d = did_you_mean_make(heap_allocator(), fields.count, name);
@@ -229,6 +245,8 @@ gb_internal void check_did_you_mean_type(String const &name, Slice<Entity *> con
}
gb_internal void check_did_you_mean_scope(String const &name, Scope *scope, char const *prefix = "") {
+ if (build_context.terse_errors) { return; }
+
ERROR_BLOCK();
DidYouMeanAnswers d = did_you_mean_make(heap_allocator(), scope->elements.count, name);
@@ -265,9 +283,21 @@ gb_internal void error_operand_not_expression(Operand *o) {
gb_internal void error_operand_no_value(Operand *o) {
if (o->mode == Addressing_NoValue) {
- gbString err = expr_to_string(o->expr);
Ast *x = unparen_expr(o->expr);
- if (x->kind == Ast_CallExpr) {
+
+ if (x != nullptr && x->kind == Ast_CallExpr) {
+ Ast *p = unparen_expr(x->CallExpr.proc);
+ if (p->kind == Ast_BasicDirective) {
+ String tag = p->BasicDirective.name.string;
+ if (tag == "panic" ||
+ tag == "assert") {
+ return;
+ }
+ }
+ }
+
+ gbString err = expr_to_string(o->expr);
+ if (x != nullptr && x->kind == Ast_CallExpr) {
error(o->expr, "'%s' call does not return a value and cannot be used as a value", err);
} else {
error(o->expr, "'%s' used as a value", err);
@@ -472,7 +502,9 @@ gb_internal bool find_or_generate_polymorphic_procedure(CheckerContext *old_c, E
nctx.no_polymorphic_errors = false;
// NOTE(bill): Reset scope from the failed procedure type
- scope_reset(scope);
+ scope->head_child.store(nullptr, std::memory_order_relaxed);
+ string_map_clear(&scope->elements);
+ ptr_set_clear(&scope->imported);
// LEAK NOTE(bill): Cloning this AST may be leaky but this is not really an issue due to arena-based allocation
Ast *cloned_proc_type_node = clone_ast(pt->node);
@@ -520,13 +552,15 @@ gb_internal bool find_or_generate_polymorphic_procedure(CheckerContext *old_c, E
final_proc_type->Proc.is_poly_specialized = true;
final_proc_type->Proc.is_polymorphic = true;
- final_proc_type->Proc.variadic = src->Proc.variadic;
- final_proc_type->Proc.require_results = src->Proc.require_results;
- final_proc_type->Proc.c_vararg = src->Proc.c_vararg;
- final_proc_type->Proc.has_named_results = src->Proc.has_named_results;
- final_proc_type->Proc.diverging = src->Proc.diverging;
- final_proc_type->Proc.return_by_pointer = src->Proc.return_by_pointer;
- final_proc_type->Proc.optional_ok = src->Proc.optional_ok;
+ final_proc_type->Proc.variadic = src->Proc.variadic;
+ final_proc_type->Proc.require_results = src->Proc.require_results;
+ final_proc_type->Proc.c_vararg = src->Proc.c_vararg;
+ final_proc_type->Proc.has_named_results = src->Proc.has_named_results;
+ final_proc_type->Proc.diverging = src->Proc.diverging;
+ final_proc_type->Proc.return_by_pointer = src->Proc.return_by_pointer;
+ final_proc_type->Proc.optional_ok = src->Proc.optional_ok;
+ final_proc_type->Proc.enable_target_feature = src->Proc.enable_target_feature;
+ final_proc_type->Proc.require_target_feature = src->Proc.require_target_feature;
for (isize i = 0; i < operands.count; i++) {
@@ -550,6 +584,7 @@ gb_internal bool find_or_generate_polymorphic_procedure(CheckerContext *old_c, E
d->defer_use_checked = false;
Entity *entity = alloc_entity_procedure(nullptr, token, final_proc_type, tags);
+ entity->state.store(EntityState_Resolved);
entity->identifier = ident;
add_entity_and_decl_info(&nctx, ident, entity, d);
@@ -558,6 +593,16 @@ gb_internal bool find_or_generate_polymorphic_procedure(CheckerContext *old_c, E
entity->file = base_entity->file;
entity->pkg = base_entity->pkg;
entity->flags = 0;
+
+ entity->Procedure.optimization_mode = base_entity->Procedure.optimization_mode;
+
+ if (base_entity->flags & EntityFlag_Cold) {
+ entity->flags |= EntityFlag_Cold;
+ }
+ if (base_entity->flags & EntityFlag_Disabled) {
+ entity->flags |= EntityFlag_Disabled;
+ }
+
d->entity = entity;
AstFile *file = nullptr;
@@ -613,7 +658,7 @@ gb_internal bool check_cast_internal(CheckerContext *c, Operand *x, Type *type);
#define MAXIMUM_TYPE_DISTANCE 10
-gb_internal i64 check_distance_between_types(CheckerContext *c, Operand *operand, Type *type) {
+gb_internal i64 check_distance_between_types(CheckerContext *c, Operand *operand, Type *type, bool allow_array_programming) {
if (c == nullptr) {
GB_ASSERT(operand->mode == Addressing_Value);
GB_ASSERT(is_type_typed(operand->type));
@@ -822,7 +867,7 @@ gb_internal i64 check_distance_between_types(CheckerContext *c, Operand *operand
if (dst->Union.variants.count == 1) {
Type *vt = dst->Union.variants[0];
- i64 score = check_distance_between_types(c, operand, vt);
+ i64 score = check_distance_between_types(c, operand, vt, allow_array_programming);
if (score >= 0) {
return score+2;
}
@@ -830,7 +875,7 @@ gb_internal i64 check_distance_between_types(CheckerContext *c, Operand *operand
i64 prev_lowest_score = -1;
i64 lowest_score = -1;
for (Type *vt : dst->Union.variants) {
- i64 score = check_distance_between_types(c, operand, vt);
+ i64 score = check_distance_between_types(c, operand, vt, allow_array_programming);
if (score >= 0) {
if (lowest_score < 0) {
lowest_score = score;
@@ -852,20 +897,6 @@ gb_internal i64 check_distance_between_types(CheckerContext *c, Operand *operand
}
}
- if (is_type_relative_pointer(dst)) {
- i64 score = check_distance_between_types(c, operand, dst->RelativePointer.pointer_type);
- if (score >= 0) {
- return score+2;
- }
- }
-
- if (is_type_relative_multi_pointer(dst)) {
- i64 score = check_distance_between_types(c, operand, dst->RelativeMultiPointer.pointer_type);
- if (score >= 0) {
- return score+2;
- }
- }
-
if (is_type_proc(dst)) {
if (are_types_identical(src, dst)) {
return 3;
@@ -886,19 +917,21 @@ gb_internal i64 check_distance_between_types(CheckerContext *c, Operand *operand
}
}
- if (is_type_array(dst)) {
- Type *elem = base_array_type(dst);
- i64 distance = check_distance_between_types(c, operand, elem);
- if (distance >= 0) {
- return distance + 6;
+ if (allow_array_programming) {
+ if (is_type_array(dst)) {
+ Type *elem = base_array_type(dst);
+ i64 distance = check_distance_between_types(c, operand, elem, allow_array_programming);
+ if (distance >= 0) {
+ return distance + 6;
+ }
}
- }
- if (is_type_simd_vector(dst)) {
- Type *dst_elem = base_array_type(dst);
- i64 distance = check_distance_between_types(c, operand, dst_elem);
- if (distance >= 0) {
- return distance + 6;
+ if (is_type_simd_vector(dst)) {
+ Type *dst_elem = base_array_type(dst);
+ i64 distance = check_distance_between_types(c, operand, dst_elem, allow_array_programming);
+ if (distance >= 0) {
+ return distance + 6;
+ }
}
}
@@ -908,7 +941,7 @@ gb_internal i64 check_distance_between_types(CheckerContext *c, Operand *operand
}
if (dst->Matrix.row_count == dst->Matrix.column_count) {
Type *dst_elem = base_array_type(dst);
- i64 distance = check_distance_between_types(c, operand, dst_elem);
+ i64 distance = check_distance_between_types(c, operand, dst_elem, allow_array_programming);
if (distance >= 0) {
return distance + 7;
}
@@ -956,9 +989,9 @@ 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) {
+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);
+ 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);
@@ -968,9 +1001,9 @@ gb_internal bool check_is_assignable_to_with_score(CheckerContext *c, Operand *o
}
-gb_internal bool check_is_assignable_to(CheckerContext *c, Operand *operand, Type *type) {
+gb_internal bool check_is_assignable_to(CheckerContext *c, Operand *operand, Type *type, bool allow_array_programming=true) {
i64 score = 0;
- return check_is_assignable_to_with_score(c, operand, type, &score);
+ return check_is_assignable_to_with_score(c, operand, type, &score, /*is_variadic*/false, allow_array_programming);
}
gb_internal bool internal_check_is_assignable_to(Type *src, Type *dst) {
@@ -1005,12 +1038,6 @@ gb_internal AstPackage *get_package_of_type(Type *type) {
case Type_DynamicArray:
type = type->DynamicArray.elem;
continue;
- case Type_RelativePointer:
- type = type->RelativePointer.pointer_type;
- continue;
- case Type_RelativeMultiPointer:
- type = type->RelativeMultiPointer.pointer_type;
- continue;
}
return nullptr;
}
@@ -1024,16 +1051,19 @@ gb_internal void check_assignment(CheckerContext *c, Operand *operand, Type *typ
return;
}
+ // Grab definite or indefinite article matching `context_name`, or "" if not found.
+ String article = error_article(context_name);
+
if (is_type_untyped(operand->type)) {
Type *target_type = type;
if (type == nullptr || is_type_any(type)) {
if (type == nullptr && is_type_untyped_uninit(operand->type)) {
- error(operand->expr, "Use of --- in %.*s", LIT(context_name));
+ error(operand->expr, "Use of --- in %.*s%.*s", LIT(article), LIT(context_name));
operand->mode = Addressing_Invalid;
return;
}
if (type == nullptr && is_type_untyped_nil(operand->type)) {
- error(operand->expr, "Use of untyped nil in %.*s", LIT(context_name));
+ error(operand->expr, "Use of untyped nil in %.*s%.*s", LIT(article), LIT(context_name));
operand->mode = Addressing_Invalid;
return;
}
@@ -1088,9 +1118,10 @@ gb_internal void check_assignment(CheckerContext *c, Operand *operand, Type *typ
// TODO(bill): is this a good enough error message?
error(operand->expr,
- "Cannot assign overloaded procedure group '%s' to '%s' in %.*s",
+ "Cannot assign overloaded procedure group '%s' to '%s' in %.*s%.*s",
expr_str,
op_type_str,
+ LIT(article),
LIT(context_name));
operand->mode = Addressing_Invalid;
}
@@ -1116,21 +1147,28 @@ gb_internal void check_assignment(CheckerContext *c, Operand *operand, Type *typ
switch (operand->mode) {
case Addressing_Builtin:
error(operand->expr,
- "Cannot assign built-in procedure '%s' in %.*s",
+ "Cannot assign built-in procedure '%s' to %.*s%.*s",
expr_str,
+ LIT(article),
LIT(context_name));
break;
case Addressing_Type:
if (is_type_polymorphic(operand->type)) {
error(operand->expr,
- "Cannot assign '%s' which is a polymorphic type in %.*s",
+ "Cannot assign '%s', a polymorphic type, to %.*s%.*s",
op_type_str,
+ LIT(article),
LIT(context_name));
} else {
+ ERROR_BLOCK();
error(operand->expr,
- "Cannot assign '%s' which is a type in %.*s",
+ "Cannot assign '%s', a type, to %.*s%.*s",
op_type_str,
+ LIT(article),
LIT(context_name));
+ if (type && are_types_identical(type, t_any)) {
+ error_line("\tSuggestion: 'typeid_of(%s)'", expr_str);
+ }
}
break;
default:
@@ -1156,12 +1194,85 @@ gb_internal void check_assignment(CheckerContext *c, Operand *operand, Type *typ
ERROR_BLOCK();
error(operand->expr,
- "Cannot assign value '%s' of type '%s%s' to '%s%s' in %.*s",
+ "Cannot assign value '%s' of type '%s%s' to '%s%s' in %.*s%.*s",
expr_str,
op_type_str, op_type_extra,
type_str, type_extra,
+ LIT(article),
LIT(context_name));
check_assignment_error_suggestion(c, operand, type);
+
+ Type *src = base_type(operand->type);
+ Type *dst = base_type(type);
+ if (context_name == "procedure argument") {
+ if (is_type_slice(src) && are_types_identical(src->Slice.elem, dst)) {
+ gbString a = expr_to_string(operand->expr);
+ error_line("\tSuggestion: Did you mean to pass the slice into the variadic parameter with ..%s?\n\n", a);
+ gb_string_free(a);
+ }
+ }
+ if (src->kind == dst->kind && src->kind == Type_Proc) {
+ Type *x = src;
+ Type *y = dst;
+ bool same_inputs = are_types_identical_internal(x->Proc.params, y->Proc.params, false);
+ bool same_outputs = are_types_identical_internal(x->Proc.results, y->Proc.results, false);
+ if (same_inputs && same_outputs &&
+ x->Proc.calling_convention != y->Proc.calling_convention) {
+ gbString s_expected = type_to_string(y);
+ gbString s_got = type_to_string(x);
+
+ error_line("\tNote: The calling conventions differ between the procedure signature types\n");
+ error_line("\t Expected \"%s\", got \"%s\"\n",
+ proc_calling_convention_strings[y->Proc.calling_convention],
+ proc_calling_convention_strings[x->Proc.calling_convention]);
+ error_line("\t Expected: %s\n", s_expected);
+ error_line("\t Got: %s\n", s_got);
+ gb_string_free(s_got);
+ gb_string_free(s_expected);
+ } else if (same_inputs && same_outputs &&
+ x->Proc.diverging != y->Proc.diverging) {
+
+ gbString s_expected = type_to_string(y);
+ if (y->Proc.diverging) {
+ s_expected = gb_string_appendc(s_expected, " -> !");
+ }
+
+ gbString s_got = type_to_string(x);
+ if (x->Proc.diverging) {
+ s_got = gb_string_appendc(s_got, " -> !");
+ }
+
+ error_line("\tNote: One of the procedures is diverging while the other isn't\n");
+ error_line("\t Expected: %s\n", s_expected);
+ error_line("\t Got: %s\n", s_got);
+ gb_string_free(s_got);
+ gb_string_free(s_expected);
+ } else if (same_inputs && !same_outputs) {
+ gbString s_expected = type_to_string(y->Proc.results);
+ gbString s_got = type_to_string(x->Proc.results);
+ error_line("\tNote: The return types differ between the procedure signature types\n");
+ error_line("\t Expected: %s\n", s_expected);
+ error_line("\t Got: %s\n", s_got);
+ gb_string_free(s_got);
+ gb_string_free(s_expected);
+ } else if (!same_inputs && same_outputs) {
+ gbString s_expected = type_to_string(y->Proc.params);
+ gbString s_got = type_to_string(x->Proc.params);
+ error_line("\tNote: The input parameter types differ between the procedure signature types\n");
+ error_line("\t Expected: %s\n", s_expected);
+ error_line("\t Got: %s\n", s_got);
+ gb_string_free(s_got);
+ gb_string_free(s_expected);
+ } else {
+ gbString s_expected = type_to_string(y);
+ gbString s_got = type_to_string(x);
+ error_line("\tNote: The signature type do not match whatsoever\n");
+ error_line("\t Expected: %s\n", s_expected);
+ error_line("\t Got: %s\n", s_got);
+ gb_string_free(s_got);
+ gb_string_free(s_expected);
+ }
+ }
}
break;
}
@@ -1233,7 +1344,7 @@ gb_internal bool is_polymorphic_type_assignable(CheckerContext *c, Type *poly, T
}
case Type_Pointer:
if (source->kind == Type_Pointer) {
- isize level = check_is_assignable_to_using_subtype(source->Pointer.elem, poly->Pointer.elem);
+ isize level = check_is_assignable_to_using_subtype(source->Pointer.elem, poly->Pointer.elem, /*level*/0, /*src_is_ptr*/false, /*allow_polymorphic*/true);
if (level > 0) {
return true;
}
@@ -1350,6 +1461,16 @@ gb_internal bool is_polymorphic_type_assignable(CheckerContext *c, Type *poly, T
if (!is_polymorphic_type_assignable(c, poly->BitSet.elem, source->BitSet.elem, true, modify_type)) {
return false;
}
+
+ // For generic types like bit_set[$T] the upper and lower of the poly type will be zeroes since
+ // it could not figure that stuff out when the poly type was created.
+ if (poly->BitSet.upper == 0 && modify_type) {
+ poly->BitSet.upper = source->BitSet.upper;
+ }
+ if (poly->BitSet.lower == 0 && modify_type) {
+ poly->BitSet.lower = source->BitSet.lower;
+ }
+
if (poly->BitSet.underlying == nullptr) {
if (modify_type) {
poly->BitSet.underlying = source->BitSet.underlying;
@@ -1384,11 +1505,16 @@ gb_internal bool is_polymorphic_type_assignable(CheckerContext *c, Type *poly, T
poly->Struct.soa_kind != StructSoa_None) {
bool ok = is_polymorphic_type_assignable(c, poly->Struct.soa_elem, source->Struct.soa_elem, true, modify_type);
if (ok) switch (source->Struct.soa_kind) {
- case StructSoa_Fixed:
+ case StructSoa_None:
default:
GB_PANIC("Unhandled SOA Kind");
break;
-
+ case StructSoa_Fixed:
+ if (modify_type) {
+ Type *type = make_soa_struct_fixed(c, nullptr, poly->Struct.node, poly->Struct.soa_elem, poly->Struct.soa_count, nullptr);
+ gb_memmove(poly, type, gb_size_of(*type));
+ }
+ break;
case StructSoa_Slice:
if (modify_type) {
Type *type = make_soa_struct_slice(c, nullptr, poly->Struct.node, poly->Struct.soa_elem);
@@ -1405,9 +1531,18 @@ gb_internal bool is_polymorphic_type_assignable(CheckerContext *c, Type *poly, T
return ok;
}
- // return check_is_assignable_to(c, &o, poly);
+
+ // NOTE(bill): Check for subtypes of
+ // return check_is_assignable_to(c, &o, poly); // && is_type_subtype_of_and_allow_polymorphic(o.type, poly);
+ }
+ return false;
+
+ case Type_BitField:
+ if (source->kind == Type_BitField) {
+ return is_polymorphic_type_assignable(c, poly->BitField.backing_type, source->BitField.backing_type, true, modify_type);
}
return false;
+
case Type_Tuple:
GB_PANIC("This should never happen");
return false;
@@ -1518,6 +1653,55 @@ gb_internal bool check_cycle(CheckerContext *c, Entity *curr, bool report) {
return false;
}
+struct CIdentSuggestion {
+ String name;
+ String msg;
+};
+
+// NOTE(bill): this linear look-up table might be slow but because it's an error case, it should be fine
+gb_internal CIdentSuggestion const c_ident_suggestions[] = {
+ {str_lit("while"), str_lit("'for'? Odin only has one loop construct: 'for'")},
+
+ {str_lit("sizeof"), str_lit("'size_of'?")},
+ {str_lit("alignof"), str_lit("'align_of'?")},
+ {str_lit("offsetof"), str_lit("'offset_of'?")},
+
+ {str_lit("_Bool"), str_lit("'bool'?")},
+
+ {str_lit("char"), str_lit("'u8', 'i8', or 'c.char' (which is part of 'core:c')?")},
+ {str_lit("short"), str_lit("'i16' or 'c.short' (which is part of 'core:c')?")},
+ {str_lit("long"), str_lit("'c.long' (which is part of 'core:c')?")},
+ {str_lit("float"), str_lit("'f32'?")},
+ {str_lit("double"), str_lit("'f64'?")},
+ {str_lit("unsigned"), str_lit("'c.uint' (which is part of 'core:c')?")},
+ {str_lit("signed"), str_lit("'c.int' (which is part of 'core:c')?")},
+
+ {str_lit("size_t"), str_lit("'uint', or 'c.size_t' (which is part of 'core:c')?")},
+ {str_lit("ssize_t"), str_lit("'int', or 'c.ssize_t' (which is part of 'core:c')?")},
+
+ {str_lit("uintptr_t"), str_lit("'uintptr'?")},
+ {str_lit("intptr_t"), str_lit("'uintptr' or `int` or something else?")},
+ {str_lit("ptrdiff_t"), str_lit("'int' or 'c.ptrdiff_t' (which is part of 'core:c')?")},
+ {str_lit("intmax_t"), str_lit("'c.intmax_t' (which is part of 'core:c')?")},
+ {str_lit("uintmax_t"), str_lit("'c.uintmax_t' (which is part of 'core:c')?")},
+
+ {str_lit("uint8_t"), str_lit("'u8'?")},
+ {str_lit("int8_t"), str_lit("'i8'?")},
+ {str_lit("uint16_t"), str_lit("'u16'?")},
+ {str_lit("int16_t"), str_lit("'i16'?")},
+ {str_lit("uint32_t"), str_lit("'u32'?")},
+ {str_lit("int32_t"), str_lit("'i32'?")},
+ {str_lit("uint64_t"), str_lit("'u64'?")},
+ {str_lit("int64_t"), str_lit("'i64'?")},
+ {str_lit("uint128_t"), str_lit("'u128'?")},
+ {str_lit("int128_t"), str_lit("'i128'?")},
+
+ {str_lit("float32"), str_lit("'f32'?")},
+ {str_lit("float64"), str_lit("'f64'?")},
+ {str_lit("float32_t"), str_lit("'f32'?")},
+ {str_lit("float64_t"), str_lit("'f64'?")},
+};
+
gb_internal Entity *check_ident(CheckerContext *c, Operand *o, Ast *n, Type *named_type, Type *type_hint, bool allow_import_name) {
GB_ASSERT(n->kind == Ast_Ident);
o->mode = Addressing_Invalid;
@@ -1529,7 +1713,16 @@ gb_internal Entity *check_ident(CheckerContext *c, Operand *o, Ast *n, Type *nam
if (is_blank_ident(name)) {
error(n, "'_' cannot be used as a value");
} else {
+ ERROR_BLOCK();
error(n, "Undeclared name: %.*s", LIT(name));
+
+ // NOTE(bill): Loads of checks for C programmers
+
+ for (CIdentSuggestion const &suggestion : c_ident_suggestions) {
+ if (name == suggestion.name) {
+ error_line("\tSuggestion: Did you mean %.*s\n", LIT(suggestion.msg));
+ }
+ }
}
o->type = t_invalid;
o->mode = Addressing_Invalid;
@@ -1655,7 +1848,7 @@ gb_internal Entity *check_ident(CheckerContext *c, Operand *o, Ast *n, Type *nam
if (check_cycle(c, e, true)) {
o->type = t_invalid;
}
- if (o->type != nullptr && type->kind == Type_Named && o->type->Named.type_name->TypeName.is_type_alias) {
+ if (o->type != nullptr && o->type->kind == Type_Named && o->type->Named.type_name->TypeName.is_type_alias) {
o->type = base_type(o->type);
}
@@ -1663,7 +1856,7 @@ gb_internal Entity *check_ident(CheckerContext *c, Operand *o, Ast *n, Type *nam
case Entity_ImportName:
if (!allow_import_name) {
- error(n, "Use of import '%.*s' not in selector", LIT(name));
+ error(n, "Use of import name '%.*s' not in the form of 'x.y'", LIT(name));
}
return e;
case Entity_LibraryName:
@@ -1696,6 +1889,13 @@ gb_internal bool check_unary_op(CheckerContext *c, Operand *o, Token op) {
gb_string_free(str);
return false;
}
+ if (o->mode == Addressing_Type) {
+ gbString str = type_to_string(o->type);
+ error(o->expr, "Expected an expression for operator '%.*s', got type '%s'", LIT(op.string), str);
+ gb_string_free(str);
+ return false;
+ }
+
Type *type = base_type(core_array_type(o->type));
gbString str = nullptr;
switch (op.kind) {
@@ -1717,17 +1917,34 @@ gb_internal bool check_unary_op(CheckerContext *c, Operand *o, Token op) {
case Token_Not:
if (!is_type_boolean(type) || is_type_array_like(o->type)) {
ERROR_BLOCK();
- str = expr_to_string(o->expr);
error(op, "Operator '%.*s' is only allowed on boolean expressions", LIT(op.string));
- gb_string_free(str);
if (is_type_integer(type)) {
- error_line("\tSuggestion: Did you mean to use the bitwise not operator '~'?\n");
+ str = expr_to_string(o->expr);
+ error_line("\tSuggestion: Did you mean to do one of the following?\n");
+ error_line("\t\t'%s == 0'?\n", str);
+ error_line("\t\tUse of the bitwise not operator '~'?\n");
+ gb_string_free(str);
}
} else {
o->type = t_untyped_bool;
}
break;
+ case Token_Mul:
+ {
+ ERROR_BLOCK();
+ error(op, "Operator '%.*s' is not a valid unary operator in Odin", LIT(op.string));
+ if (is_type_pointer(o->type)) {
+ str = expr_to_string(o->expr);
+ error_line("\tSuggestion: Did you mean '%s^'?\n", str);
+ o->type = type_deref(o->type);
+ } else if (is_type_multi_pointer(o->type)) {
+ str = expr_to_string(o->expr);
+ error_line("\tSuggestion: The value is a multi-pointer, did you mean '%s[0]'?\n", str);
+ o->type = type_deref(o->type, true);
+ }
+ }
+ break;
default:
error(op, "Unknown operator '%.*s'", LIT(op.string));
return false;
@@ -1876,33 +2093,55 @@ gb_internal bool check_representable_as_constant(CheckerContext *c, ExactValue i
BigInt i = v.value_integer;
- i64 bit_size = type_size_of(type);
+ i64 byte_size = type_size_of(type);
BigInt umax = {};
BigInt imin = {};
BigInt imax = {};
- if (bit_size < 16) {
- big_int_from_u64(&umax, unsigned_integer_maxs[bit_size]);
- big_int_from_i64(&imin, signed_integer_mins[bit_size]);
- big_int_from_i64(&imax, signed_integer_maxs[bit_size]);
- } else {
+ if (c->bit_field_bit_size > 0) {
+ i64 bit_size = gb_min(cast(i64)(8*byte_size), cast(i64)c->bit_field_bit_size);
+
big_int_from_u64(&umax, 1);
big_int_from_i64(&imin, 1);
big_int_from_i64(&imax, 1);
- BigInt bi128 = {};
- BigInt bi127 = {};
- big_int_from_i64(&bi128, 128);
- big_int_from_i64(&bi127, 127);
+ BigInt bu = {};
+ BigInt bi = {};
+ big_int_from_i64(&bu, bit_size);
+ big_int_from_i64(&bi, bit_size-1);
- big_int_shl_eq(&umax, &bi128);
+ big_int_shl_eq(&umax, &bu);
mp_decr(&umax);
- big_int_shl_eq(&imin, &bi127);
+ big_int_shl_eq(&imin, &bi);
big_int_neg(&imin, &imin);
- big_int_shl_eq(&imax, &bi127);
+ big_int_shl_eq(&imax, &bi);
mp_decr(&imax);
+ } else {
+ if (byte_size < 16) {
+ big_int_from_u64(&umax, unsigned_integer_maxs[byte_size]);
+ big_int_from_i64(&imin, signed_integer_mins[byte_size]);
+ big_int_from_i64(&imax, signed_integer_maxs[byte_size]);
+ } else {
+ big_int_from_u64(&umax, 1);
+ big_int_from_i64(&imin, 1);
+ big_int_from_i64(&imax, 1);
+
+ BigInt bi128 = {};
+ BigInt bi127 = {};
+ big_int_from_i64(&bi128, 128);
+ big_int_from_i64(&bi127, 127);
+
+ big_int_shl_eq(&umax, &bi128);
+ mp_decr(&umax);
+
+ big_int_shl_eq(&imin, &bi127);
+ big_int_neg(&imin, &imin);
+
+ big_int_shl_eq(&imax, &bi127);
+ mp_decr(&imax);
+ }
}
switch (type->Basic.kind) {
@@ -2061,48 +2300,85 @@ gb_internal bool check_representable_as_constant(CheckerContext *c, ExactValue i
}
-gb_internal bool check_integer_exceed_suggestion(CheckerContext *c, Operand *o, Type *type) {
+gb_internal bool check_integer_exceed_suggestion(CheckerContext *c, Operand *o, Type *type, i64 max_bit_size=0) {
if (is_type_integer(type) && o->value.kind == ExactValue_Integer) {
gbString b = type_to_string(type);
+ defer (gb_string_free(b));
+
+ if (is_type_enum(o->type)) {
+ if (check_is_castable_to(c, o, type)) {
+ gbString ot = type_to_string(o->type);
+ error_line("\tSuggestion: Try casting the '%s' expression to '%s'", ot, b);
+ gb_string_free(ot);
+ }
+ return true;
+ }
+
i64 sz = type_size_of(type);
+ i64 bit_size = 8*sz;
+ bool size_changed = false;
+ if (max_bit_size > 0) {
+ size_changed = (bit_size != max_bit_size);
+ bit_size = gb_min(bit_size, max_bit_size);
+ }
BigInt *bi = &o->value.value_integer;
if (is_type_unsigned(type)) {
+ BigInt one = big_int_make_u64(1);
+ BigInt max_size = big_int_make_u64(1);
+ BigInt bits = big_int_make_i64(bit_size);
+ big_int_shl_eq(&max_size, &bits);
+ big_int_sub_eq(&max_size, &one);
+
if (big_int_is_neg(bi)) {
error_line("\tA negative value cannot be represented by the unsigned integer type '%s'\n", b);
+ BigInt dst = {};
+ big_int_neg(&dst, bi);
+ if (big_int_cmp(&dst, &max_size) < 0) {
+ big_int_sub_eq(&dst, &one);
+ String dst_str = big_int_to_string(temporary_allocator(), &dst);
+ gbString t = type_to_string(type);
+ error_line("\tSuggestion: ~%s(%.*s)\n", t, LIT(dst_str));
+ gb_string_free(t);
+ }
} else {
- BigInt one = big_int_make_u64(1);
- BigInt max_size = big_int_make_u64(1);
- BigInt bits = big_int_make_i64(8*sz);
- big_int_shl_eq(&max_size, &bits);
- big_int_sub_eq(&max_size, &one);
String max_size_str = big_int_to_string(temporary_allocator(), &max_size);
- error_line("\tThe maximum value that can be represented by '%s' is '%.*s'\n", b, LIT(max_size_str));
+
+ if (size_changed) {
+ error_line("\tThe maximum value that can be represented with that bit_field's field of '%s | %u' is '%.*s'\n", b, bit_size, LIT(max_size_str));
+ } else {
+ error_line("\tThe maximum value that can be represented by '%s' is '%.*s'\n", b, LIT(max_size_str));
+ }
}
} else {
BigInt zero = big_int_make_u64(0);
BigInt one = big_int_make_u64(1);
BigInt max_size = big_int_make_u64(1);
- BigInt bits = big_int_make_i64(8*sz - 1);
+ BigInt bits = big_int_make_i64(bit_size - 1);
big_int_shl_eq(&max_size, &bits);
+
+ String max_size_str = {};
if (big_int_is_neg(bi)) {
big_int_neg(&max_size, &max_size);
- String max_size_str = big_int_to_string(temporary_allocator(), &max_size);
- error_line("\tThe minimum value that can be represented by '%s' is '%.*s'\n", b, LIT(max_size_str));
+ max_size_str = big_int_to_string(temporary_allocator(), &max_size);
} else {
big_int_sub_eq(&max_size, &one);
- String max_size_str = big_int_to_string(temporary_allocator(), &max_size);
+ max_size_str = big_int_to_string(temporary_allocator(), &max_size);
+ }
+
+ if (size_changed) {
+ error_line("\tThe maximum value that can be represented with that bit_field's field of '%s | %u' is '%.*s'\n", b, bit_size, LIT(max_size_str));
+ } else {
error_line("\tThe maximum value that can be represented by '%s' is '%.*s'\n", b, LIT(max_size_str));
}
}
- gb_string_free(b);
return true;
}
return false;
}
-gb_internal void check_assignment_error_suggestion(CheckerContext *c, Operand *o, Type *type) {
+gb_internal void check_assignment_error_suggestion(CheckerContext *c, Operand *o, Type *type, i64 max_bit_size) {
gbString a = expr_to_string(o->expr);
gbString b = type_to_string(type);
defer(
@@ -2133,8 +2409,23 @@ gb_internal void check_assignment_error_suggestion(CheckerContext *c, Operand *o
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);
- } else if (check_integer_exceed_suggestion(c, o, type)) {
+ } 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);
+ gb_string_free(s);
+ } else if (is_type_pointer(type) &&
+ o->mode == Addressing_Variable &&
+ are_types_identical(type_deref(type), o->type)) {
+ gbString s = expr_to_string(o->expr);
+ error_line("\tSuggestion: Did you mean `&%s`\n", s);
+ gb_string_free(s);
+ } 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);
+ gb_string_free(s);
}
}
@@ -2203,18 +2494,22 @@ gb_internal bool check_is_expressible(CheckerContext *ctx, Operand *o, Type *typ
ERROR_BLOCK();
-
if (is_type_numeric(o->type) && is_type_numeric(type)) {
if (!is_type_integer(o->type) && is_type_integer(type)) {
error(o->expr, "'%s' truncated to '%s', got %s", a, b, s);
} else {
+ i64 max_bit_size = 0;
+ if (ctx->bit_field_bit_size) {
+ max_bit_size = ctx->bit_field_bit_size;
+ }
+
if (are_types_identical(o->type, type)) {
error(o->expr, "Numeric value '%s' from '%s' cannot be represented by '%s'", s, a, b);
} else {
error(o->expr, "Cannot convert numeric value '%s' from '%s' to '%s' from '%s'", s, a, b, c);
}
- check_assignment_error_suggestion(ctx, o, type);
+ check_assignment_error_suggestion(ctx, o, type, max_bit_size);
}
} else {
error(o->expr, "Cannot convert '%s' to '%s' from '%s', got %s", a, b, c, s);
@@ -2225,6 +2520,11 @@ gb_internal bool check_is_expressible(CheckerContext *ctx, Operand *o, Type *typ
}
gb_internal bool check_is_not_addressable(CheckerContext *c, Operand *o) {
+ if (o->expr && o->expr->kind == Ast_SelectorExpr) {
+ if (o->expr->SelectorExpr.is_bit_field) {
+ return true;
+ }
+ }
if (o->mode == Addressing_OptionalOk) {
Ast *expr = unselector_expr(o->expr);
if (expr->kind != Ast_TypeAssertion) {
@@ -2255,35 +2555,82 @@ gb_internal bool check_is_not_addressable(CheckerContext *c, Operand *o) {
return o->mode != Addressing_Variable && o->mode != Addressing_SoaVariable;
}
-gb_internal void check_old_for_or_switch_value_usage(Ast *expr) {
- if (!(build_context.strict_style || (check_vet_flags(expr) & VetFlag_Style))) {
- return;
- }
+gb_internal ExactValue exact_bit_set_all_set_mask(Type *type) {
+ type = base_type(type);
+ GB_ASSERT(type->kind == Type_BitSet);
- Entity *e = entity_of_node(expr);
- if (e != nullptr && (e->flags & EntityFlag_OldForOrSwitchValue) != 0) {
- GB_ASSERT(e->kind == Entity_Variable);
+ i64 lower = type->BitSet.lower;
+ i64 upper = type->BitSet.upper;
+ Type *elem = type->BitSet.elem;
+ Type *underlying = type->BitSet.underlying;
+ bool is_backed = underlying != nullptr;
+ gb_unused(is_backed);
- begin_error_block();
- defer (end_error_block());
+ BigInt b_lower = {};
+ BigInt b_upper = {};
+ big_int_from_i64(&b_lower, lower);
+ big_int_from_i64(&b_upper, upper);
- if ((e->flags & EntityFlag_ForValue) != 0) {
- Type *parent_type = type_deref(e->Variable.for_loop_parent_type);
- error(expr, "Assuming a for-in defined value is addressable as the iterable is passed by value has been disallowed with '-strict-style'.");
+ BigInt one = {};
+ big_int_from_u64(&one, 1);
- if (is_type_map(parent_type)) {
- error_line("\tSuggestion: Prefer doing 'for key, &%.*s in ...'\n", LIT(e->token.string));
- } else {
- error_line("\tSuggestion: Prefer doing 'for &%.*s in ...'\n", LIT(e->token.string));
+ BigInt mask = {};
+
+ if (elem == nullptr) {
+ big_int_from_i64(&mask, -1);
+ } else if (is_type_enum(elem)) {
+ Type *e = base_type(elem);
+ GB_ASSERT(e->kind == Type_Enum);
+ gb_unused(e);
+
+ if ((big_int_cmp(&e->Enum.min_value->value_integer, &b_lower) == 0 || is_backed) &&
+ big_int_cmp(&e->Enum.max_value->value_integer, &b_upper) == 0) {
+
+ i64 lower_base = is_backed ? gb_min(0, lower) : lower;
+ BigInt b_lower_base = {};
+ big_int_from_i64(&b_lower_base, lower_base);
+
+ for (Entity *f : e->Enum.fields) {
+ if (f->kind != Entity_Constant) {
+ continue;
+ }
+ if (f->Constant.value.kind != ExactValue_Integer) {
+ continue;
+ }
+
+ BigInt shift_amount = f->Constant.value.value_integer;
+ big_int_sub_eq(&shift_amount, &b_lower_base);
+
+
+ BigInt value = {};
+ big_int_shl(&value, &one, &shift_amount);
+
+ big_int_or(&mask, &mask, &value);
}
+
} else {
- GB_ASSERT((e->flags & EntityFlag_SwitchValue) != 0);
+ // TODO(bill): enum range based");
+ big_int_from_i64(&mask, -1);
+ }
+ } else {
+ i64 lower_base = lower;
+ for (i64 x = lower; x <= upper; x++) {
+ BigInt shift_amount = {};
+ big_int_from_i64(&shift_amount, x - lower_base);
- error(expr, "Assuming a switch-in defined value is addressable as the iterable is passed by value has been disallowed with '-strict-style'.");
- error_line("\tSuggestion: Prefer doing 'switch &%.*s in ...'\n", LIT(e->token.string));
+ BigInt value = {};
+ big_int_shl(&value, &one, &shift_amount);
+
+ big_int_or(&mask, &mask, &value);
}
}
+
+
+ ExactValue res = {};
+ res.kind = ExactValue_Integer;
+ res.value_integer = mask;
+ return res;
}
gb_internal void check_unary_expr(CheckerContext *c, Operand *o, Token op, Ast *node) {
@@ -2298,6 +2645,8 @@ gb_internal void check_unary_expr(CheckerContext *c, Operand *o, Token op, Ast *
Entity *e = entity_of_node(ue->expr);
if (e != nullptr && (e->flags & EntityFlag_Param) != 0) {
error(op, "Cannot take the pointer address of '%s' which is a procedure parameter", str);
+ } else if (e != nullptr && (e->flags & EntityFlag_BitFieldField) != 0) {
+ error(op, "Cannot take the pointer address of '%s' which is a bit_field's field", str);
} else {
switch (o->mode) {
case Addressing_Constant:
@@ -2309,10 +2658,12 @@ gb_internal void check_unary_expr(CheckerContext *c, Operand *o, Token op, Ast *
break;
default:
{
- begin_error_block();
- defer (end_error_block());
+ ERROR_BLOCK();
error(op, "Cannot take the pointer address of '%s'", str);
- if (e != nullptr && (e->flags & EntityFlag_ForValue) != 0) {
+ if (e == nullptr) {
+ break;
+ }
+ if ((e->flags & EntityFlag_ForValue) != 0) {
Type *parent_type = type_deref(e->Variable.for_loop_parent_type);
if (parent_type != nullptr && is_type_string(parent_type)) {
@@ -2322,9 +2673,17 @@ gb_internal void check_unary_expr(CheckerContext *c, Operand *o, Token op, Ast *
} else {
error_line("\tSuggestion: Did you want to pass the iterable value to the for statement by pointer to get addressable semantics?\n");
}
+
+ if (parent_type != nullptr && is_type_map(parent_type)) {
+ error_line("\t Prefer doing 'for key, &%.*s in ...'\n", LIT(e->token.string));
+ } else {
+ error_line("\t Prefer doing 'for &%.*s in ...'\n", LIT(e->token.string));
+ }
}
- if (e != nullptr && (e->flags & EntityFlag_SwitchValue) != 0) {
+ if ((e->flags & EntityFlag_SwitchValue) != 0) {
error_line("\tSuggestion: Did you want to pass the value to the switch statement by pointer to get addressable semantics?\n");
+
+ error_line("\t Prefer doing 'switch &%.*s in ...'\n", LIT(e->token.string));
}
}
break;
@@ -2339,18 +2698,13 @@ gb_internal void check_unary_expr(CheckerContext *c, Operand *o, Token op, Ast *
ast_node(ue, UnaryExpr, node);
if (ast_node_expect(ue->expr, Ast_IndexExpr)) {
ast_node(ie, IndexExpr, ue->expr);
- Type *soa_type = type_of_expr(ie->expr);
+ Type *soa_type = type_deref(type_of_expr(ie->expr));
GB_ASSERT(is_type_soa_struct(soa_type));
o->type = alloc_type_soa_pointer(soa_type);
} else {
o->type = alloc_type_pointer(o->type);
}
} else {
- if (ast_node_expect(node, Ast_UnaryExpr)) {
- ast_node(ue, UnaryExpr, node);
- check_old_for_or_switch_value_usage(ue->expr);
- }
-
o->type = alloc_type_pointer(o->type);
}
@@ -2376,6 +2730,11 @@ gb_internal void check_unary_expr(CheckerContext *c, Operand *o, Token op, Ast *
if (o->mode == Addressing_Constant) {
Type *type = base_type(o->type);
if (!is_type_constant_type(o->type)) {
+ if (is_type_array_like(o->type)) {
+ o->mode = Addressing_Value;
+ return;
+ }
+
gbString xt = type_to_string(o->type);
gbString err_str = expr_to_string(node);
error(op, "Invalid type, '%s', for constant unary expression '%s'", xt, err_str);
@@ -2413,6 +2772,10 @@ gb_internal void check_unary_expr(CheckerContext *c, Operand *o, Token op, Ast *
}
o->value = exact_unary_operator_value(op.kind, o->value, precision, is_unsigned);
+ if (op.kind == Token_Xor && is_type_bit_set(type)) {
+ ExactValue mask = exact_bit_set_all_set_mask(type);
+ o->value = exact_binary_operator_value(Token_And, o->value, mask);
+ }
if (is_type_typed(type)) {
if (node != nullptr) {
@@ -2759,7 +3122,7 @@ gb_internal void check_shift(CheckerContext *c, Operand *x, Operand *y, Ast *nod
x->mode = Addressing_Value;
if (type_hint) {
if (is_type_integer(type_hint)) {
- x->type = type_hint;
+ convert_to_typed(c, x, type_hint);
} else {
gbString x_str = expr_to_string(x->expr);
gbString to_type = type_to_string(type_hint);
@@ -2872,6 +3235,13 @@ gb_internal bool check_is_castable_to(CheckerContext *c, Operand *operand, Type
}
}
+ if (is_type_bit_field(src)) {
+ return are_types_identical(core_type(src->BitField.backing_type), dst);
+ }
+ if (is_type_bit_field(dst)) {
+ return are_types_identical(src, core_type(dst->BitField.backing_type));
+ }
+
if (is_type_integer(src) && is_type_rune(dst)) {
return true;
}
@@ -2983,6 +3353,13 @@ gb_internal bool check_is_castable_to(CheckerContext *c, Operand *operand, Type
}
// proc <-> proc
if (is_type_proc(src) && is_type_proc(dst)) {
+ if (is_type_polymorphic(dst)) {
+ if (is_type_polymorphic(src) &&
+ operand->mode == Addressing_Variable) {
+ return true;
+ }
+ return false;
+ }
return true;
}
@@ -2995,6 +3372,14 @@ gb_internal bool check_is_castable_to(CheckerContext *c, Operand *operand, Type
return true;
}
+
+ if (is_type_array(dst)) {
+ Type *elem = base_array_type(dst);
+ if (check_is_castable_to(c, operand, elem)) {
+ return true;
+ }
+ }
+
if (is_type_simd_vector(src) && is_type_simd_vector(dst)) {
if (src->SimdVector.count != dst->SimdVector.count) {
return false;
@@ -3053,7 +3438,7 @@ gb_internal bool check_cast_internal(CheckerContext *c, Operand *x, Type *type)
}
-gb_internal void check_cast(CheckerContext *c, Operand *x, Type *type) {
+gb_internal void check_cast(CheckerContext *c, Operand *x, Type *type, bool forbid_identical = false) {
if (!is_operand_value(*x)) {
error(x->expr, "Only values can be casted");
x->mode = Addressing_Invalid;
@@ -3062,7 +3447,6 @@ gb_internal void check_cast(CheckerContext *c, Operand *x, Type *type) {
bool is_const_expr = x->mode == Addressing_Constant;
bool can_convert = check_cast_internal(c, x, type);
-
if (!can_convert) {
TEMPORARY_ALLOCATOR_GUARD();
gbString expr_str = expr_to_string(x->expr, temporary_allocator());
@@ -3071,7 +3455,7 @@ gb_internal void check_cast(CheckerContext *c, Operand *x, Type *type) {
x->mode = Addressing_Invalid;
- begin_error_block();
+ ERROR_BLOCK();
error(x->expr, "Cannot cast '%s' as '%s' from '%s'", expr_str, to_type, from_type);
if (is_const_expr) {
gbString val_str = exact_value_to_string(x->value);
@@ -3094,37 +3478,70 @@ gb_internal void check_cast(CheckerContext *c, Operand *x, Type *type) {
}
check_cast_error_suggestion(c, x, type);
- end_error_block();
-
return;
}
if (is_type_untyped(x->type)) {
Type *final_type = type;
if (is_const_expr && !is_type_constant_type(type)) {
+ if (is_type_union(type)) {
+ convert_to_typed(c, x, type);
+ }
final_type = default_type(x->type);
}
update_untyped_expr_type(c, x->expr, final_type, true);
+ } else {
+ Type *src = core_type(x->type);
+ Type *dst = core_type(type);
+ if (src != dst) {
+ bool const REQUIRE = true;
+ if (is_type_integer_128bit(src) && is_type_float(dst)) {
+ add_package_dependency(c, "runtime", "floattidf_unsigned", REQUIRE);
+ add_package_dependency(c, "runtime", "floattidf", REQUIRE);
+ } else if (is_type_integer_128bit(dst) && is_type_float(src)) {
+ add_package_dependency(c, "runtime", "fixunsdfti", REQUIRE);
+ add_package_dependency(c, "runtime", "fixunsdfdi", REQUIRE);
+ } else if (src == t_f16 && is_type_float(dst)) {
+ add_package_dependency(c, "runtime", "gnu_h2f_ieee", REQUIRE);
+ add_package_dependency(c, "runtime", "extendhfsf2", REQUIRE);
+ } else if (is_type_float(dst) && dst == t_f16) {
+ add_package_dependency(c, "runtime", "truncsfhf2", REQUIRE);
+ add_package_dependency(c, "runtime", "truncdfhf2", REQUIRE);
+ add_package_dependency(c, "runtime", "gnu_f2h_ieee", REQUIRE);
+ }
+ }
+ // If we check polymorphic procedures, we risk erring on
+ // identical casts that cannot be foreseen or otherwise
+ // forbidden, so just skip them.
+ if (forbid_identical && check_vet_flags(c) & VetFlag_Cast &&
+ (c->curr_proc_sig == nullptr || !is_type_polymorphic(c->curr_proc_sig))) {
+ Type *src_exact = x->type;
+ Type *dst_exact = type;
+
+ if (src_exact != nullptr &&
+ dst_exact != nullptr &&
+ are_types_identical(src_exact, dst_exact)
+ ) {
+ gbString oper_str = expr_to_string(x->expr);
+ gbString to_type = type_to_string(dst_exact);
+ error(x->expr, "Unneeded cast of '%s' to identical type '%s'", oper_str, to_type);
+ gb_string_free(oper_str);
+ gb_string_free(to_type);
+ }
+ }
}
x->type = type;
}
-gb_internal bool check_transmute(CheckerContext *c, Ast *node, Operand *o, Type *t) {
+gb_internal bool check_transmute(CheckerContext *c, Ast *node, Operand *o, Type *t, bool forbid_identical = false) {
if (!is_operand_value(*o)) {
error(o->expr, "'transmute' can only be applied to values");
o->mode = Addressing_Invalid;
return false;
}
- // if (o->mode == Addressing_Constant) {
- // gbString expr_str = expr_to_string(o->expr);
- // error(o->expr, "Cannot transmute a constant expression: '%s'", expr_str);
- // gb_string_free(expr_str);
- // o->mode = Addressing_Invalid;
- // o->expr = node;
- // return false;
- // }
+ Operand src = *o;
Type *src_t = o->type;
Type *dst_t = t;
@@ -3177,10 +3594,11 @@ gb_internal bool check_transmute(CheckerContext *c, Ast *node, Operand *o, Type
if (are_types_identical(src_bt, dst_bt)) {
return true;
}
- if (is_type_integer(src_t) && is_type_integer(dst_t)) {
+ if ((is_type_integer(src_t) && is_type_integer(dst_t)) ||
+ is_type_integer(src_t) && is_type_bit_set(dst_t)) {
if (types_have_same_internal_endian(src_t, dst_t)) {
ExactValue src_v = exact_value_to_integer(o->value);
- GB_ASSERT(src_v.kind == ExactValue_Integer);
+ GB_ASSERT(src_v.kind == ExactValue_Integer || src_v.kind == ExactValue_Invalid);
BigInt v = src_v.value_integer;
BigInt smax = {};
@@ -3208,6 +3626,37 @@ gb_internal bool check_transmute(CheckerContext *c, Ast *node, Operand *o, Type
return true;
}
}
+ } else {
+ // If we check polymorphic procedures, we risk erring on
+ // identical casts that cannot be foreseen or otherwise
+ // forbidden, so just skip them.
+ if (forbid_identical && check_vet_flags(c) & VetFlag_Cast &&
+ (c->curr_proc_sig == nullptr || !is_type_polymorphic(c->curr_proc_sig)) &&
+ check_is_castable_to(c, &src, dst_t)) {
+ if (are_types_identical(src_t, dst_t)) {
+ gbString oper_str = expr_to_string(o->expr);
+ gbString to_type = type_to_string(dst_t);
+ error(o->expr, "Unneeded transmute of '%s' to identical type '%s'", oper_str, to_type);
+ gb_string_free(oper_str);
+ gb_string_free(to_type);
+ } else if (is_type_internally_pointer_like(src_t) &&
+ is_type_internally_pointer_like(dst_t)) {
+ error(o->expr, "Use of 'transmute' where 'cast' would be preferred since the types are pointer-like");
+ } else if (are_types_identical(src_bt, dst_bt)) {
+ gbString oper_str = expr_to_string(o->expr);
+ gbString to_type = type_to_string(dst_t);
+ error(o->expr, "Unneeded transmute of '%s' to identical type '%s'", oper_str, to_type);
+ gb_string_free(oper_str);
+ gb_string_free(to_type);
+ } else if (is_type_integer(src_t) && is_type_integer(dst_t) &&
+ types_have_same_internal_endian(src_t, dst_t)) {
+ gbString oper_type = type_to_string(src_t);
+ gbString to_type = type_to_string(dst_t);
+ error(o->expr, "Use of 'transmute' where 'cast' would be preferred since both are integers of the same endianness, from '%s' to '%s'", oper_type, to_type);
+ gb_string_free(to_type);
+ gb_string_free(oper_type);
+ }
+ }
}
o->mode = Addressing_Value;
@@ -3223,6 +3672,13 @@ gb_internal bool check_binary_array_expr(CheckerContext *c, Token op, Operand *x
}
}
}
+ if (is_type_simd_vector(x->type) && !is_type_simd_vector(y->type)) {
+ if (check_is_assignable_to(c, y, x->type)) {
+ if (check_binary_op(c, x, op)) {
+ return true;
+ }
+ }
+ }
return false;
}
@@ -3244,6 +3700,13 @@ gb_internal Type *check_matrix_type_hint(Type *matrix, Type *type_hint) {
Type *th = base_type(type_hint);
if (are_types_identical(th, xt)) {
return type_hint;
+ } else if (xt->kind == Type_Matrix && th->kind == Type_Matrix) {
+ if (!are_types_identical(xt->Matrix.elem, th->Matrix.elem)) {
+ // ignore
+ } if (xt->Matrix.row_count == th->Matrix.row_count &&
+ xt->Matrix.column_count == th->Matrix.column_count) {
+ return type_hint;
+ }
} else if (xt->kind == Type_Matrix && th->kind == Type_Array) {
if (!are_types_identical(xt->Matrix.elem, th->Array.elem)) {
// ignore
@@ -3278,14 +3741,23 @@ gb_internal void check_binary_matrix(CheckerContext *c, Token const &op, Operand
if (xt->Matrix.column_count != yt->Matrix.row_count) {
goto matrix_error;
}
+
+ if (xt->Matrix.is_row_major != yt->Matrix.is_row_major) {
+ goto matrix_error;
+ }
+
x->mode = Addressing_Value;
if (are_types_identical(xt, yt)) {
+ if (are_types_identical(x->type, y->type)) {
+ return;
+ }
if (!is_type_named(x->type) && is_type_named(y->type)) {
// prefer the named type
x->type = y->type;
}
} else {
- x->type = alloc_type_matrix(xt->Matrix.elem, xt->Matrix.row_count, yt->Matrix.column_count);
+ bool is_row_major = xt->Matrix.is_row_major && yt->Matrix.is_row_major;
+ x->type = alloc_type_matrix(xt->Matrix.elem, xt->Matrix.row_count, yt->Matrix.column_count, nullptr, nullptr, is_row_major);
}
goto matrix_success;
} else if (yt->kind == Type_Array) {
@@ -3299,10 +3771,10 @@ gb_internal void check_binary_matrix(CheckerContext *c, Token const &op, Operand
// Treat arrays as column vectors
x->mode = Addressing_Value;
- if (type_hint == nullptr && xt->Matrix.row_count == yt->Array.count) {
+ if (xt->Matrix.row_count == yt->Array.count) {
x->type = y->type;
} else {
- x->type = alloc_type_matrix(xt->Matrix.elem, xt->Matrix.row_count, 1);
+ x->type = alloc_type_matrix(xt->Matrix.elem, xt->Matrix.row_count, 1, nullptr, nullptr, xt->Matrix.is_row_major);
}
goto matrix_success;
}
@@ -3330,10 +3802,10 @@ gb_internal void check_binary_matrix(CheckerContext *c, Token const &op, Operand
// Treat arrays as row vectors
x->mode = Addressing_Value;
- if (type_hint == nullptr && yt->Matrix.column_count == xt->Array.count) {
+ if (yt->Matrix.column_count == xt->Array.count) {
x->type = x->type;
} else {
- x->type = alloc_type_matrix(yt->Matrix.elem, 1, yt->Matrix.column_count);
+ x->type = alloc_type_matrix(yt->Matrix.elem, 1, yt->Matrix.column_count, nullptr, nullptr, yt->Matrix.is_row_major);
}
goto matrix_success;
} else if (are_types_identical(yt->Matrix.elem, xt)) {
@@ -3387,10 +3859,10 @@ gb_internal void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, Typ
// NOTE(bill): Allow comparisons between types
if (is_ise_expr(be->left)) {
// Evalute the right before the left for an '.X' expression
- check_expr_or_type(c, y, be->right, type_hint);
+ check_expr_or_type(c, y, be->right, nullptr /* ignore type hint */);
check_expr_or_type(c, x, be->left, y->type);
} else {
- check_expr_or_type(c, x, be->left, type_hint);
+ check_expr_or_type(c, x, be->left, nullptr /* ignore type hint */);
check_expr_or_type(c, y, be->right, x->type);
}
bool xt = x->mode == Addressing_Type;
@@ -3419,6 +3891,12 @@ gb_internal void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, Typ
// IMPORTANT NOTE(bill): This uses right-left evaluation in type checking only no in
check_expr(c, y, be->right);
Type *rhs_type = type_deref(y->type);
+ if (rhs_type == nullptr) {
+ error(y->expr, "Cannot use '%.*s' on an expression with no value", LIT(op.string));
+ x->mode = Addressing_Invalid;
+ x->expr = node;
+ return;
+ }
if (is_type_bit_set(rhs_type)) {
Type *elem = base_type(rhs_type)->BitSet.elem;
@@ -3558,6 +4036,60 @@ gb_internal void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, Typ
return;
}
+ 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;
+ }
+ }
+
if (token_is_shift(op.kind)) {
check_shift(c, x, y, node, type_hint);
return;
@@ -3726,25 +4258,6 @@ gb_internal void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, Typ
return;
}
- if (op.kind == Token_Quo || op.kind == Token_QuoEq) {
- Type *bt = base_type(x->type);
- 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;
- }
- } else if (op.kind == Token_Mul || op.kind == Token_MulEq) {
- Type *bt = base_type(x->type);
- 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;
- }
- }
-
x->mode = Addressing_Value;
}
@@ -3891,7 +4404,7 @@ gb_internal void update_untyped_expr_value(CheckerContext *c, Ast *e, ExactValue
}
}
-gb_internal void convert_untyped_error(CheckerContext *c, Operand *operand, Type *target_type) {
+gb_internal void convert_untyped_error(CheckerContext *c, Operand *operand, Type *target_type, bool ignore_error_block=false) {
gbString expr_str = expr_to_string(operand->expr);
gbString type_str = type_to_string(target_type);
gbString from_type_str = type_to_string(operand->type);
@@ -3905,7 +4418,9 @@ gb_internal void convert_untyped_error(CheckerContext *c, Operand *operand, Type
}
}
}
- ERROR_BLOCK();
+ if (!ignore_error_block) {
+ begin_error_block();
+ }
error(operand->expr, "Cannot convert untyped value '%s' to '%s' from '%s'%s", expr_str, type_str, from_type_str, extra_text);
if (operand->value.kind == ExactValue_String) {
@@ -3920,6 +4435,11 @@ gb_internal void convert_untyped_error(CheckerContext *c, Operand *operand, Type
gb_string_free(type_str);
gb_string_free(expr_str);
operand->mode = Addressing_Invalid;
+
+ if (!ignore_error_block) {
+ end_error_block();
+ }
+
}
gb_internal ExactValue convert_exact_value_for_type(ExactValue v, Type *type) {
@@ -3941,8 +4461,8 @@ 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 (operand->mode == Addressing_Invalid ||
+ // GB_ASSERT_NOT_NULL(target_type);
+ if (target_type == nullptr || operand->mode == Addressing_Invalid ||
operand->mode == Addressing_Type ||
is_type_typed(operand->type) ||
target_type == t_invalid) {
@@ -3983,7 +4503,8 @@ gb_internal void convert_to_typed(CheckerContext *c, Operand *operand, Type *tar
} else {
switch (operand->type->Basic.kind) {
case Basic_UntypedBool:
- if (!is_type_boolean(target_type)) {
+ if (!is_type_boolean(target_type) &&
+ !is_type_integer(target_type)) {
operand->mode = Addressing_Invalid;
convert_untyped_error(c, operand, target_type);
return;
@@ -4042,15 +4563,27 @@ gb_internal void convert_to_typed(CheckerContext *c, Operand *operand, Type *tar
break;
}
+ case Type_SimdVector: {
+ Type *elem = base_array_type(t);
+ if (check_is_assignable_to(c, operand, elem)) {
+ operand->mode = Addressing_Value;
+ } else {
+ operand->mode = Addressing_Invalid;
+ convert_untyped_error(c, operand, target_type);
+ return;
+ }
+
+ break;
+ }
+
case Type_Matrix: {
Type *elem = base_array_type(t);
if (check_is_assignable_to(c, operand, elem)) {
if (t->Matrix.row_count != t->Matrix.column_count) {
operand->mode = Addressing_Invalid;
- begin_error_block();
- defer (end_error_block());
+ ERROR_BLOCK();
- convert_untyped_error(c, operand, target_type);
+ convert_untyped_error(c, operand, target_type, true);
error_line("\tNote: Only a square matrix types can be initialized with a scalar value\n");
return;
} else {
@@ -4066,6 +4599,27 @@ gb_internal void convert_to_typed(CheckerContext *c, Operand *operand, Type *tar
case Type_Union:
+ // IMPORTANT NOTE HACK(bill): This is just to allow for comparisons against `0` with the `os.Error` type
+ // as a kind of transition period
+ if (!build_context.strict_style &&
+ operand->mode == Addressing_Constant &&
+ target_type->kind == Type_Named &&
+ (c->pkg == nullptr || c->pkg->name != "os") &&
+ target_type->Named.name == "Error") {
+ Entity *e = target_type->Named.type_name;
+ if (e->pkg && e->pkg->name == "os") {
+ if (is_exact_value_zero(operand->value) &&
+ (operand->value.kind == ExactValue_Integer ||
+ operand->value.kind == ExactValue_Float)) {
+ operand->mode = Addressing_Value;
+ // target_type = t_untyped_nil;
+ operand->value = empty_exact_value;
+ update_untyped_expr_value(c, operand->expr, operand->value);
+ break;
+ }
+ }
+ }
+ // "fallthrough"
if (!is_operand_nil(*operand) && !is_operand_uninit(*operand)) {
TEMPORARY_ALLOCATOR_GUARD();
@@ -4104,17 +4658,21 @@ gb_internal void convert_to_typed(CheckerContext *c, Operand *operand, Type *tar
defer (gb_string_free(type_str));
if (valid_count == 1) {
+ Type *new_type = t->Union.variants[first_success_index];
+ target_type = new_type;
+ if (is_type_union(new_type)) {
+ convert_to_typed(c, operand, new_type);
+ break;
+ }
+ operand->type = new_type;
operand->mode = Addressing_Value;
- operand->type = t->Union.variants[first_success_index];
- target_type = t->Union.variants[first_success_index];
break;
} else if (valid_count > 1) {
- begin_error_block();
- defer (end_error_block());
+ ERROR_BLOCK();
GB_ASSERT(first_success_index >= 0);
operand->mode = Addressing_Invalid;
- convert_untyped_error(c, operand, target_type);
+ convert_untyped_error(c, operand, target_type, true);
error_line("Ambiguous type conversion to '%s', which variant did you mean:\n\t", type_str);
i32 j = 0;
@@ -4136,13 +4694,13 @@ gb_internal void convert_to_typed(CheckerContext *c, Operand *operand, Type *tar
} else if (is_type_untyped_uninit(operand->type)) {
target_type = t_untyped_uninit;
} else if (!is_type_untyped_nil(operand->type) || !type_has_nil(target_type)) {
- begin_error_block();
- defer (end_error_block());
+ ERROR_BLOCK();
operand->mode = Addressing_Invalid;
- convert_untyped_error(c, operand, target_type);
+ convert_untyped_error(c, operand, target_type, true);
if (count > 0) {
error_line("'%s' is a union which only excepts the following types:\n", type_str);
+
error_line("\t");
for (i32 i = 0; i < count; i++) {
Type *v = t->Union.variants[i];
@@ -4196,7 +4754,8 @@ gb_internal bool check_index_value(CheckerContext *c, Type *main_type, bool open
check_expr_with_type_hint(c, &operand, index_value, type_hint);
if (operand.mode == Addressing_Invalid) {
if (value) *value = 0;
- return false;
+ // NOTE(bill): return true here to propagate the errors better
+ return true;
}
Type *index_type = t_int;
@@ -4352,7 +4911,8 @@ gb_internal ExactValue get_constant_field_single(CheckerContext *c, ExactValue v
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[0] == index) {
+ if (sub_sel.index.count > 0 &&
+ sub_sel.index[0] == index) {
value = fv->value->tav.value;
found = true;
break;
@@ -4416,7 +4976,7 @@ gb_internal ExactValue get_constant_field_single(CheckerContext *c, ExactValue v
TypeAndValue tav = fv->value->tav;
if (success_) *success_ = true;
if (finish_) *finish_ = false;
- return tav.value;;
+ return tav.value;
}
}
@@ -4440,8 +5000,12 @@ gb_internal ExactValue get_constant_field_single(CheckerContext *c, ExactValue v
if (success_) *success_ = true;
if (finish_) *finish_ = false;
return tav.value;
+ } else if (is_type_proc(tav.type)) {
+ if (success_) *success_ = true;
+ if (finish_) *finish_ = false;
+ return tav.value;
} else {
- GB_ASSERT(is_type_untyped_nil(tav.type));
+ GB_ASSERT_MSG(is_type_untyped_nil(tav.type), "%s", type_to_string(tav.type));
if (success_) *success_ = true;
if (finish_) *finish_ = false;
return tav.value;
@@ -4491,7 +5055,6 @@ gb_internal ExactValue get_constant_field(CheckerContext *c, Operand const *oper
return value;
}
}
-
if (success_) *success_ = true;
return value;
} else if (value.kind == ExactValue_Quaternion) {
@@ -4575,7 +5138,8 @@ gb_internal bool is_entity_declared_for_selector(Entity *entity, Scope *import_s
if (entity->kind == Entity_Builtin) {
// NOTE(bill): Builtin's are in the universal scope which is part of every scopes hierarchy
// This means that we should just ignore the found result through it
- *allow_builtin = entity->scope == import_scope || entity->scope != builtin_pkg->scope;
+ *allow_builtin = entity->scope == import_scope ||
+ (entity->scope != builtin_pkg->scope && entity->scope != intrinsics_pkg->scope);
} else if ((entity->scope->flags&ScopeFlag_Global) == ScopeFlag_Global && (import_scope->flags&ScopeFlag_Global) == 0) {
is_declared = false;
}
@@ -4585,7 +5149,27 @@ gb_internal bool is_entity_declared_for_selector(Entity *entity, Scope *import_s
// NOTE(bill, 2022-02-03): see `check_const_decl` for why it exists reasoning
gb_internal Entity *check_entity_from_ident_or_selector(CheckerContext *c, Ast *node, bool ident_only) {
- if (node->kind == Ast_Ident) {
+ if (node == nullptr) {
+ return nullptr;
+ }
+ /*if (node->kind == Ast_TernaryWhenExpr) {
+ ast_node(we, TernaryWhenExpr, node);
+ if (we->cond == nullptr) {
+ return nullptr;
+ }
+ if (we->cond->tav.mode != Addressing_Constant) {
+ return nullptr;
+ }
+ if (we->cond->tav.value.kind != ExactValue_Bool) {
+ return nullptr;
+ }
+ if (we->cond->tav.value.value_bool) {
+ return check_entity_from_ident_or_selector(c, we->x, ident_only);
+ } else {
+ Entity *e = check_entity_from_ident_or_selector(c, we->y, ident_only);
+ return e;
+ }
+ } else */if (node->kind == Ast_Ident) {
String name = node->Ident.token.string;
return scope_lookup(c->scope, name);
} else if (!ident_only) if (node->kind == Ast_SelectorExpr) {
@@ -4673,10 +5257,18 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod
Selection sel = {}; // NOTE(bill): Not used if it's an import name
if (!c->allow_arrow_right_selector_expr && se->token.kind == Token_ArrowRight) {
+ ERROR_BLOCK();
error(node, "Illegal use of -> selector shorthand outside of a call");
- operand->mode = Addressing_Invalid;
- operand->expr = node;
- return nullptr;
+ gbString x = expr_to_string(se->expr);
+ gbString y = expr_to_string(se->selector);
+ error_line("\tSuggestion: Did you mean '%s.%s'?\n", x, y);
+ gb_string_free(y);
+ gb_string_free(x);
+
+ // TODO(bill): Should this terminate here or propagate onwards?
+ // operand->mode = Addressing_Invalid;
+ // operand->expr = node;
+ // return nullptr;
}
operand->expr = node;
@@ -4702,7 +5294,14 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod
add_entity_use(c, op_expr, e);
expr_entity = e;
- if (e != nullptr && e->kind == Entity_ImportName && selector->kind == Ast_Ident) {
+ if (e != nullptr && (e->kind == Entity_Procedure || e->kind == Entity_ProcGroup) && selector->kind == Ast_Ident) {
+ gbString sel_str = expr_to_string(selector);
+ error(node, "'%s' is not declared by by '%.*s'", sel_str, LIT(e->token.string));
+ gb_string_free(sel_str);
+ operand->mode = Addressing_Invalid;
+ operand->expr = node;
+ return nullptr;
+ } else if (e != nullptr && e->kind == Entity_ImportName && selector->kind == Ast_Ident) {
// IMPORTANT NOTE(bill): This is very sloppy code but it's also very fragile
// It pretty much needs to be in this order and this way
// If you can clean this up, please do but be really careful
@@ -4710,10 +5309,19 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod
Scope *import_scope = e->ImportName.scope;
String entity_name = selector->Ident.token.string;
+ if (import_scope == nullptr) {
+ ERROR_BLOCK();
+ error(node, "'%.*s' is not imported in this file, '%.*s' is unavailable", LIT(import_name), LIT(entity_name));
+ operand->mode = Addressing_Invalid;
+ operand->expr = node;
+ return nullptr;
+ }
+
check_op_expr = false;
entity = scope_lookup_current(import_scope, entity_name);
bool allow_builtin = false;
if (!is_entity_declared_for_selector(entity, import_scope, &allow_builtin)) {
+ ERROR_BLOCK();
error(node, "'%.*s' is not declared by '%.*s'", LIT(entity_name), LIT(import_name));
operand->mode = Addressing_Invalid;
operand->expr = node;
@@ -4784,28 +5392,34 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod
}
}
+ if (operand->type && is_type_soa_struct(type_deref(operand->type))) {
+ complete_soa_type(c->checker, type_deref(operand->type), false);
+ }
if (entity == nullptr && selector->kind == Ast_Ident) {
String field_name = selector->Ident.token.string;
Type *t = type_deref(operand->type);
if (t == nullptr) {
error(operand->expr, "Cannot use a selector expression on 0-value expression");
- } else if (is_type_dynamic_array(t)) {
- init_mem_allocator(c->checker);
- }
- sel = lookup_field(operand->type, field_name, operand->mode == Addressing_Type);
- entity = sel.entity;
+ } else {
+ if (is_type_dynamic_array(t)) {
+ init_mem_allocator(c->checker);
+ }
+ sel = lookup_field(operand->type, field_name, operand->mode == Addressing_Type);
+ entity = sel.entity;
- // NOTE(bill): Add type info needed for fields like 'names'
- if (entity != nullptr && (entity->flags&EntityFlag_TypeField)) {
- add_type_info_type(c, operand->type);
- }
- if (is_type_enum(operand->type)) {
- add_type_info_type(c, operand->type);
+ // NOTE(bill): Add type info needed for fields like 'names'
+ if (entity != nullptr && (entity->flags&EntityFlag_TypeField)) {
+ add_type_info_type(c, operand->type);
+ }
+ if (is_type_enum(operand->type)) {
+ add_type_info_type(c, operand->type);
+ }
}
}
- if (entity == nullptr && selector->kind == Ast_Ident && is_type_array(type_deref(operand->type))) {
+ 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)))) {
String field_name = selector->Ident.token.string;
if (1 < field_name.len && field_name.len <= 4) {
u8 swizzles_xyzw[4] = {'x', 'y', 'z', 'w'};
@@ -4860,8 +5474,10 @@ 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);
- i64 array_count = array_type->Array.count;
+ GB_ASSERT(array_type->kind == Type_Array || array_type->kind == Type_SimdVector);
+
+ i64 array_count = get_array_type_count(array_type);
+
for (u8 i = 0; i < index_count; i++) {
u8 idx = indices>>(i*2) & 3;
if (idx >= array_count) {
@@ -4881,7 +5497,6 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod
se->swizzle_count = index_count;
se->swizzle_indices = indices;
-
AddressingMode prev_mode = operand->mode;
operand->mode = Addressing_SwizzleValue;
operand->type = determine_swizzle_array_type(original_type, type_hint, index_count);
@@ -4895,6 +5510,10 @@ 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;
@@ -4914,6 +5533,8 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod
error(op_expr, "Type '%s' has no field '%s'", op_str, sel_str);
}
} else {
+ ERROR_BLOCK();
+
error(op_expr, "'%s' of type '%s' has no field '%s'", op_str, type_str, sel_str);
if (operand->type != nullptr && selector->kind == Ast_Ident) {
@@ -5008,6 +5629,11 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod
operand->type = entity->type;
operand->expr = node;
+ if (entity->flags & EntityFlag_BitFieldField) {
+ add_package_dependency(c, "runtime", "__write_bits");
+ add_package_dependency(c, "runtime", "__read_bits");
+ }
+
switch (entity->kind) {
case Entity_Constant:
operand->value = entity->Constant.value;
@@ -5021,6 +5647,9 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod
}
break;
case Entity_Variable:
+ if (sel.is_bit_field) {
+ se->is_bit_field = true;
+ }
if (sel.indirect) {
operand->mode = Addressing_Variable;
} else if (operand->mode == Addressing_Context) {
@@ -5126,7 +5755,7 @@ gb_internal bool check_identifier_exists(Scope *s, Ast *node, bool nested = fals
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) {
+ if (expr && o.mode != Addressing_Constant && o.mode != Addressing_Type) {
if (expr->kind == Ast_CallExpr) {
// Okay
} else {
@@ -5569,10 +6198,14 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A
Operand *variadic_operand = &ordered_operands[pt->variadic_index];
if (vari_expand) {
- GB_ASSERT(variadic_operands.count != 0);
- *variadic_operand = variadic_operands[0];
- variadic_operand->type = default_type(variadic_operand->type);
- actually_variadic = true;
+ if (variadic_operands.count == 0) {
+ error(call, "'..' in the wrong position");
+ } else {
+ GB_ASSERT(variadic_operands.count != 0);
+ *variadic_operand = variadic_operands[0];
+ variadic_operand->type = default_type(variadic_operand->type);
+ actually_variadic = true;
+ }
} else {
AstFile *f = call->file();
@@ -5606,9 +6239,6 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A
for (isize i = 0; i < pt->param_count; i++) {
if (!visited[i]) {
Entity *e = pt->params->Tuple.variables[i];
- if (is_blank_ident(e->token)) {
- continue;
- }
if (e->kind == Entity_Variable) {
if (e->Variable.param_value.kind != ParameterValue_Invalid) {
ordered_operands[i].mode = Addressing_Value;
@@ -5639,14 +6269,20 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A
}
auto eval_param_and_score = [](CheckerContext *c, Operand *o, Type *param_type, CallArgumentError &err, bool param_is_variadic, Entity *e, bool show_error) -> i64 {
+ bool allow_array_programming = !(e && (e->flags & EntityFlag_NoBroadcast));
i64 s = 0;
- if (!check_is_assignable_to_with_score(c, o, param_type, &s, param_is_variadic)) {
+ if (!check_is_assignable_to_with_score(c, o, param_type, &s, param_is_variadic, allow_array_programming)) {
bool ok = false;
- if (e && e->flags & EntityFlag_AnyInt) {
+ if (e && (e->flags & EntityFlag_AnyInt)) {
if (is_type_integer(param_type)) {
ok = check_is_castable_to(c, o, param_type);
}
}
+ if (!allow_array_programming && check_is_assignable_to_with_score(c, o, param_type, nullptr, param_is_variadic, !allow_array_programming)) {
+ if (show_error) {
+ error(o->expr, "'#no_broadcast' disallows automatic broadcasting a value across all elements of an array-like type in a procedure argument");
+ }
+ }
if (ok) {
s = assign_score_function(MAXIMUM_TYPE_DISTANCE);
} else {
@@ -5655,7 +6291,6 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A
}
err = CallArgumentError_WrongTypes;
}
-
} else if (show_error) {
check_assignment(c, o, param_type, str_lit("procedure argument"));
}
@@ -5738,20 +6373,25 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A
}
if (param_is_variadic) {
- continue;
+ if (!named_variadic_param) {
+ continue;
+ }
}
- score += eval_param_and_score(c, o, e->type, err, param_is_variadic, e, show_error);
+ score += eval_param_and_score(c, o, e->type, err, false, e, show_error);
}
}
if (variadic) {
- Type *slice = pt->params->Tuple.variables[pt->variadic_index]->type;
+ Entity *var_entity = pt->params->Tuple.variables[pt->variadic_index];
+ Type *slice = var_entity->type;
GB_ASSERT(is_type_slice(slice));
Type *elem = base_type(slice)->Slice.elem;
Type *t = elem;
if (is_type_polymorphic(t)) {
- error(call, "Ambiguous call to a polymorphic variadic procedure with no variadic input %s", type_to_string(final_proc_type));
+ if (show_error) {
+ error(call, "Ambiguous call to a polymorphic variadic procedure with no variadic input %s", type_to_string(final_proc_type));
+ }
err = CallArgumentError_AmbiguousPolymorphicVariadic;
}
@@ -5771,7 +6411,24 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A
return CallArgumentError_MultipleVariadicExpand;
}
}
- score += eval_param_and_score(c, o, t, err, true, nullptr, show_error);
+ score += eval_param_and_score(c, o, t, err, true, var_entity, show_error);
+ }
+
+ if (!vari_expand && variadic_operands.count != 0) {
+ // NOTE(bill, 2024-07-14): minimize the stack usage for variadic parameters with the backing array
+ if (c->decl) {
+ bool found = false;
+ for (auto &vr : c->decl->variadic_reuses) {
+ if (are_types_identical(slice, vr.slice_type)) {
+ vr.max_count = gb_max(vr.max_count, variadic_operands.count);
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ array_add(&c->decl->variadic_reuses, VariadicReuseData{slice, variadic_operands.count});
+ }
+ }
}
}
@@ -5899,10 +6556,27 @@ gb_internal bool evaluate_where_clauses(CheckerContext *ctx, Ast *call_expr, Sco
}
}
- if (call_expr) error(call_expr, "at caller location");
+ if (call_expr) {
+ TokenPos pos = ast_token(call_expr).pos;
+ error_line("%s at caller location\n", token_pos_to_string(pos));
+ }
}
return false;
}
+
+ if (ast_file_vet_style(ctx->file)) {
+ Ast *c = unparen_expr(clause);
+ if (c->kind == Ast_BinaryExpr && c->BinaryExpr.op.kind == Token_CmpAnd) {
+ ERROR_BLOCK();
+ error(c, "Prefer to separate 'where' clauses with a comma rather than '&&'");
+ gbString x = expr_to_string(c->BinaryExpr.left);
+ gbString y = expr_to_string(c->BinaryExpr.right);
+ error_line("\tSuggestion: '%s, %s'\n", x, y);
+ gb_string_free(y);
+ gb_string_free(x);
+ }
+ }
+
}
}
@@ -6244,12 +6918,17 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c,
array_add(&proc_entities, proc);
}
+ int max_matched_features = 0;
gbString expr_name = expr_to_string(operand->expr);
defer (gb_string_free(expr_name));
for_array(i, procs) {
Entity *p = procs[i];
+ if (p->flags & EntityFlag_Disabled) {
+ continue;
+ }
+
Type *pt = base_type(p->type);
if (pt != nullptr && is_type_proc(pt)) {
CallArgumentData data = {};
@@ -6280,13 +6959,26 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c,
item.score += assign_score_function(1);
}
+ max_matched_features = gb_max(max_matched_features, matched_target_features(&pt->Proc));
+
item.index = index;
array_add(&valids, item);
}
}
+ if (max_matched_features > 0) {
+ for_array(i, valids) {
+ Entity *p = procs[valids[i].index];
+ Type *t = base_type(p->type);
+ GB_ASSERT(t->kind == Type_Proc);
+
+ int matched = matched_target_features(&t->Proc);
+ valids[i].score += assign_score_function(max_matched_features-matched);
+ }
+ }
+
if (valids.count > 1) {
- gb_sort_array(valids.data, valids.count, valid_index_and_score_cmp);
+ array_sort(valids, valid_index_and_score_cmp);
i64 best_score = valids[0].score;
Entity *best_entity = proc_entities[valids[0].index];
GB_ASSERT(best_entity != nullptr);
@@ -6333,8 +7025,7 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c,
};
if (valids.count == 0) {
- begin_error_block();
- defer (end_error_block());
+ ERROR_BLOCK();
error(operand->expr, "No procedures or ambiguous call for procedure group '%s' that match with the given arguments", expr_name);
if (positional_operands.count == 0 && named_operands.count == 0) {
@@ -6349,9 +7040,73 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c,
if (procs.count > 0) {
error_line("Did you mean to use one of the following:\n");
}
+
+ // Try to reduce the list further for `$T: typeid` like parameters
+ bool *possibly_ignore = gb_alloc_array(temporary_allocator(), bool, procs.count);
+ isize possibly_ignore_set = 0;
+
+ if (true) {
+ // NOTE(bill): This currently only checks for #soa types
+ for_array(i, procs) {
+ Entity *proc = procs[i];
+ Type *t = base_type(proc->type);
+ if (t == nullptr || t->kind != Type_Proc) {
+ continue;
+ }
+
+ TypeProc *pt = &t->Proc;
+ if (pt->param_count == 0) {
+ continue;
+ }
+
+ for_array(j, pt->params->Tuple.variables) {
+ Entity *v = pt->params->Tuple.variables[j];
+ if (v->kind != Entity_TypeName) {
+ continue;
+ }
+
+ Type *dst_t = base_type(v->type);
+ while (dst_t->kind == Type_Generic && dst_t->Generic.specialized) {
+ dst_t = dst_t->Generic.specialized;
+ }
+
+ if (j >= positional_operands.count) {
+ continue;
+ }
+ Operand const &o = positional_operands[j];
+ if (o.mode != Addressing_Type) {
+ continue;
+ }
+ Type *t = base_type(o.type);
+ if (t->kind == dst_t->kind) {
+ continue;
+ }
+ Type *st = base_type(type_deref(o.type));
+ Type *dt = base_type(type_deref(dst_t));
+ if (st->kind == dt->kind) {
+ continue;
+ }
+ if (is_type_soa_struct(st)) {
+ possibly_ignore[i] = true;
+ possibly_ignore_set += 1;
+ continue;
+ }
+ }
+ }
+ }
+
+ if (possibly_ignore_set == procs.count) {
+ possibly_ignore_set = 0;
+ }
+
+
isize max_name_length = 0;
isize max_type_length = 0;
- for (Entity *proc : procs) {
+ for_array(i, procs) {
+ if (possibly_ignore_set != 0 && possibly_ignore[i]) {
+ continue;
+ }
+ Entity *proc = procs[i];
Type *t = base_type(proc->type);
if (t == t_invalid) continue;
String prefix = {};
@@ -6381,7 +7136,11 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c,
}
spaces[max_spaces] = 0;
- for (Entity *proc : procs) {
+ for_array(i, procs) {
+ if (possibly_ignore_set != 0 && possibly_ignore[i]) {
+ continue;
+ }
+ Entity *proc = procs[i];
TokenPos pos = proc->token.pos;
Type *t = base_type(proc->type);
if (t == t_invalid) continue;
@@ -6424,11 +7183,14 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c,
data.result_type = t_invalid;
} else if (valids.count > 1) {
- begin_error_block();
- defer (end_error_block());
+ ERROR_BLOCK();
error(operand->expr, "Ambiguous procedure group call '%s' that match with the given arguments", expr_name);
- print_argument_types();
+ if (positional_operands.count == 0 && named_operands.count == 0) {
+ error_line("\tNo given arguments\n");
+ } else {
+ print_argument_types();
+ }
for (auto const &valid : valids) {
Entity *proc = proc_entities[valid.index];
@@ -6641,6 +7403,8 @@ gb_internal CallArgumentError check_polymorphic_record_type(CheckerContext *c, O
Array<Operand> operands = {};
defer (array_free(&operands));
+ CallArgumentError err = CallArgumentError_None;
+
bool named_fields = false;
{
// NOTE(bill, 2019-10-26): Allow a cycle in the parameters but not in the fields themselves
@@ -6658,12 +7422,17 @@ gb_internal CallArgumentError check_polymorphic_record_type(CheckerContext *c, O
Ast *arg = ce->args[i];
ast_node(fv, FieldValue, arg);
+ if (fv->value == nullptr) {
+ error(fv->eq, "Expected a value");
+ err = CallArgumentError_InvalidFieldValue;
+ continue;
+ }
if (fv->field->kind == Ast_Ident) {
String name = fv->field->Ident.token.string;
isize index = lookup_polymorphic_record_parameter(original_type, name);
if (index >= 0) {
TypeTuple *params = get_record_polymorphic_params(original_type);
- Entity *e = params->variables[i];
+ Entity *e = params->variables[index];
if (e->kind == Entity_Constant) {
check_expr_with_type_hint(c, &operands[i], fv->value, e->type);
continue;
@@ -6696,7 +7465,10 @@ gb_internal CallArgumentError check_polymorphic_record_type(CheckerContext *c, O
}
- CallArgumentError err = CallArgumentError_None;
+ if (err != 0) {
+ operand->mode = Addressing_Invalid;
+ return err;
+ }
TypeTuple *tuple = get_record_polymorphic_params(original_type);
isize param_count = tuple->variables.count;
@@ -6714,7 +7486,7 @@ gb_internal CallArgumentError check_polymorphic_record_type(CheckerContext *c, O
Array<Operand> ordered_operands = operands;
if (!named_fields) {
- ordered_operands = array_make<Operand>(permanent_allocator(), param_count);
+ ordered_operands = array_make<Operand>(permanent_allocator(), operands.count);
array_copy(&ordered_operands, operands, 0);
} else {
TEMPORARY_ALLOCATOR_GUARD();
@@ -6902,8 +7674,12 @@ gb_internal CallArgumentError check_polymorphic_record_type(CheckerContext *c, O
}
{
- bool failure = false;
- Entity *found_entity = find_polymorphic_record_entity(c, original_type, param_count, ordered_operands, &failure);
+ 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);
+
if (found_entity) {
operand->mode = Addressing_Type;
operand->type = found_entity->type;
@@ -6953,14 +7729,9 @@ gb_internal CallArgumentError check_polymorphic_record_type(CheckerContext *c, O
gbString s = gb_string_make_reserve(heap_allocator(), e->token.string.len+3);
s = gb_string_append_fmt(s, "%.*s(", LIT(e->token.string));
- Type *params = nullptr;
- switch (bt->kind) {
- case Type_Struct: params = bt->Struct.polymorphic_params; break;
- case Type_Union: params = bt->Union.polymorphic_params; break;
- }
-
- if (params != nullptr) for_array(i, params->Tuple.variables) {
- Entity *v = params->Tuple.variables[i];
+ TypeTuple *tuple = get_record_polymorphic_params(bt);
+ if (tuple != nullptr) for_array(i, tuple->variables) {
+ Entity *v = tuple->variables[i];
String name = v->token.string;
if (i > 0) {
s = gb_string_append_fmt(s, ", ");
@@ -6973,8 +7744,10 @@ gb_internal CallArgumentError check_polymorphic_record_type(CheckerContext *c, O
s = write_type_to_string(s, v->type, false);
}
} else if (v->kind == Entity_Constant) {
- s = gb_string_append_fmt(s, "=");
- s = write_exact_value_to_string(s, v->Constant.value);
+ if (v->Constant.value.kind != ExactValue_Invalid) {
+ s = gb_string_append_fmt(s, "=");
+ s = write_exact_value_to_string(s, v->Constant.value);
+ }
}
}
s = gb_string_append_fmt(s, ")");
@@ -7042,13 +7815,16 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c
String name = bd->name.string;
if (
name == "location" ||
+ name == "exists" ||
name == "assert" ||
name == "panic" ||
name == "defined" ||
name == "config" ||
name == "load" ||
+ name == "load_directory" ||
name == "load_hash" ||
- name == "load_or"
+ name == "hash" ||
+ name == "caller_expression"
) {
operand->mode = Addressing_Builtin;
operand->builtin_id = BuiltinProc_DIRECTIVE;
@@ -7190,6 +7966,14 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c
}
}
add_entity_use(c, operand->expr, initial_entity);
+
+ if (initial_entity->Procedure.entry_point_only) {
+ if (c->curr_proc_decl && c->curr_proc_decl->entity == c->info->entry_point) {
+ // Okay
+ } else {
+ error(operand->expr, "Procedures with the attribute '@(entry_point_only)' can only be called directly from the user-level entry point procedure");
+ }
+ }
}
if (operand->mode != Addressing_ProcGroup) {
@@ -7234,6 +8018,7 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c
pt = data.gen_entity->type;
}
}
+ pt = base_type(pt);
if (pt->kind == Type_Proc && pt->Proc.calling_convention == ProcCC_Odin) {
if ((c->scope->flags & ScopeFlag_ContextDefined) == 0) {
@@ -7261,8 +8046,11 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c
}
}
+ bool is_call_inlined = false;
+
switch (inlining) {
case ProcInlining_inline:
+ is_call_inlined = true;
if (proc != nullptr) {
Entity *e = entity_from_expr(proc);
if (e != nullptr && e->kind == Entity_Procedure) {
@@ -7270,7 +8058,7 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c
if (decl->proc_lit) {
ast_node(pl, ProcLit, decl->proc_lit);
if (pl->inlining == ProcInlining_no_inline) {
- error(call, "'#force_inline' cannot be applied to a procedure that has be marked as '#force_no_inline'");
+ error(call, "'#force_inline' cannot be applied to a procedure that has been marked as '#force_no_inline'");
}
}
}
@@ -7278,6 +8066,53 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c
break;
case ProcInlining_no_inline:
break;
+ case ProcInlining_none:
+ if (proc != nullptr) {
+ Entity *e = entity_from_expr(proc);
+ if (e != nullptr && e->kind == Entity_Procedure) {
+ DeclInfo *decl = e->decl_info;
+ if (decl->proc_lit) {
+ ast_node(pl, ProcLit, decl->proc_lit);
+ if (pl->inlining == ProcInlining_inline) {
+ is_call_inlined = true;
+ }
+ }
+ }
+ }
+ }
+
+ {
+ String invalid;
+ if (pt->kind == Type_Proc && pt->Proc.require_target_feature.len != 0) {
+ if (!check_target_feature_is_valid_for_target_arch(pt->Proc.require_target_feature, &invalid)) {
+ error(call, "Called procedure requires target feature '%.*s' which is invalid for the build target", LIT(invalid));
+ } else if (!check_target_feature_is_enabled(pt->Proc.require_target_feature, &invalid)) {
+ error(call, "Calling this procedure requires target feature '%.*s' to be enabled", LIT(invalid));
+ }
+ }
+
+ if (pt->kind == Type_Proc && pt->Proc.enable_target_feature.len != 0) {
+ if (!check_target_feature_is_valid_for_target_arch(pt->Proc.enable_target_feature, &invalid)) {
+ error(call, "Called procedure enables target feature '%.*s' which is invalid for the build target", LIT(invalid));
+ }
+
+ // NOTE: Due to restrictions in LLVM you can not inline calls with a superset of features.
+ if (is_call_inlined) {
+ if (c->curr_proc_decl == nullptr) {
+ error(call, "Calling a '#force_inline' procedure that enables target features is not allowed at file scope");
+ } else {
+ GB_ASSERT(c->curr_proc_decl->entity);
+ GB_ASSERT(c->curr_proc_decl->entity->type->kind == Type_Proc);
+ String scope_features = c->curr_proc_decl->entity->type->Proc.enable_target_feature;
+ if (!check_target_feature_is_superset_of(scope_features, pt->Proc.enable_target_feature, &invalid)) {
+ ERROR_BLOCK();
+ error(call, "Inlined procedure enables target feature '%.*s', this requires the calling procedure to at least enable the same feature", LIT(invalid));
+
+ error_line("\tSuggested Example: @(enable_target_feature=\"%.*s\")\n", LIT(invalid));
+ }
+ }
+ }
+ }
}
operand->expr = call;
@@ -7381,13 +8216,18 @@ gb_internal bool check_set_index_data(Operand *o, Type *t, bool indirection, i64
return true;
case Type_Matrix:
- *max_count = t->Matrix.column_count;
if (indirection) {
o->mode = Addressing_Variable;
} else if (o->mode != Addressing_Variable) {
o->mode = Addressing_Value;
}
- o->type = alloc_type_array(t->Matrix.elem, t->Matrix.row_count);
+ if (t->Matrix.is_row_major) {
+ *max_count = t->Matrix.row_count;
+ o->type = alloc_type_array(t->Matrix.elem, t->Matrix.column_count);
+ } else {
+ *max_count = t->Matrix.column_count;
+ o->type = alloc_type_array(t->Matrix.elem, t->Matrix.row_count);
+ }
return true;
case Type_Slice:
@@ -7397,17 +8237,6 @@ gb_internal bool check_set_index_data(Operand *o, Type *t, bool indirection, i64
}
return true;
- case Type_RelativeMultiPointer:
- {
- Type *pointer_type = base_type(t->RelativeMultiPointer.pointer_type);
- GB_ASSERT(pointer_type->kind == Type_MultiPointer);
- o->type = pointer_type->MultiPointer.elem;
- if (o->mode != Addressing_Constant) {
- o->mode = Addressing_Variable;
- }
- }
- return true;
-
case Type_DynamicArray:
o->type = t->DynamicArray.elem;
if (o->mode != Addressing_Constant) {
@@ -7420,7 +8249,7 @@ gb_internal bool check_set_index_data(Operand *o, Type *t, bool indirection, i64
*max_count = t->Struct.soa_count;
}
o->type = t->Struct.soa_elem;
- if (o->mode == Addressing_SoaVariable || o->mode == Addressing_Variable) {
+ if (o->mode == Addressing_SoaVariable || o->mode == Addressing_Variable || indirection) {
o->mode = Addressing_SoaVariable;
} else {
o->mode = Addressing_Value;
@@ -7432,8 +8261,8 @@ gb_internal bool check_set_index_data(Operand *o, Type *t, bool indirection, i64
if (is_type_pointer(original_type) && indirection) {
Type *ptr = base_type(original_type);
- if (ptr->kind == Type_Pointer && o->mode == Addressing_SoaVariable) {
- o->type = ptr->Pointer.elem;
+ if (ptr->kind == Type_MultiPointer && o->mode == Addressing_SoaVariable) {
+ o->type = ptr->MultiPointer.elem;
o->mode = Addressing_Value;
return true;
}
@@ -7636,6 +8465,8 @@ gb_internal ExprKind check_implicit_selector_expr(CheckerContext *c, Operand *o,
String name = ise->selector->Ident.token.string;
if (is_type_enum(th)) {
+ ERROR_BLOCK();
+
Type *bt = base_type(th);
GB_ASSERT(bt->kind == Type_Enum);
@@ -7644,6 +8475,15 @@ gb_internal ExprKind check_implicit_selector_expr(CheckerContext *c, Operand *o,
error(node, "Undeclared name '%.*s' for type '%s'", LIT(name), typ);
check_did_you_mean_type(name, bt->Enum.fields);
+ } else if (is_type_bit_set(th) && is_type_enum(th->BitSet.elem)) {
+ ERROR_BLOCK();
+
+ gbString typ = type_to_string(th);
+ gbString str = expr_to_string(node);
+ error(node, "Cannot convert enum value to '%s'", typ);
+ error_line("\tSuggestion: Did you mean '{ %s }'?\n", str);
+ gb_string_free(typ);
+ gb_string_free(str);
} else {
gbString typ = type_to_string(th);
gbString str = expr_to_string(node);
@@ -7658,7 +8498,7 @@ gb_internal ExprKind check_implicit_selector_expr(CheckerContext *c, Operand *o,
}
-gb_internal void check_promote_optional_ok(CheckerContext *c, Operand *x, Type **val_type_, Type **ok_type_) {
+gb_internal void check_promote_optional_ok(CheckerContext *c, Operand *x, Type **val_type_, Type **ok_type_, bool change_operand) {
switch (x->mode) {
case Addressing_MapIndex:
case Addressing_OptionalOk:
@@ -7676,22 +8516,28 @@ gb_internal void check_promote_optional_ok(CheckerContext *c, Operand *x, Type *
Type *pt = base_type(type_of_expr(expr->CallExpr.proc));
if (is_type_proc(pt)) {
Type *tuple = pt->Proc.results;
- add_type_and_value(c, x->expr, x->mode, tuple, x->value);
if (pt->Proc.result_count >= 2) {
if (ok_type_) *ok_type_ = tuple->Tuple.variables[1]->type;
}
- expr->CallExpr.optional_ok_one = false;
- x->type = tuple;
+ if (change_operand) {
+ expr->CallExpr.optional_ok_one = false;
+ x->type = tuple;
+ add_type_and_value(c, x->expr, x->mode, tuple, x->value);
+ }
return;
}
}
Type *tuple = make_optional_ok_type(x->type);
+
if (ok_type_) *ok_type_ = tuple->Tuple.variables[1]->type;
- add_type_and_value(c, x->expr, x->mode, tuple, x->value);
- x->type = tuple;
- GB_ASSERT(is_type_tuple(type_of_expr(x->expr)));
+
+ if (change_operand) {
+ add_type_and_value(c, x->expr, x->mode, tuple, x->value);
+ x->type = tuple;
+ GB_ASSERT(is_type_tuple(type_of_expr(x->expr)));
+ }
}
@@ -7779,11 +8625,10 @@ gb_internal void add_constant_switch_case(CheckerContext *ctx, SeenMap *seen, Op
}
uintptr key = hash_exact_value(operand.value);
- TypeAndToken *found = map_get(seen, key);
- if (found != nullptr) {
+ GB_ASSERT(key != 0);
+ isize count = multi_map_count(seen, key);
+ if (count) {
TEMPORARY_ALLOCATOR_GUARD();
-
- isize count = multi_map_count(seen, key);
TypeAndToken *taps = gb_alloc_array(temporary_allocator(), TypeAndToken, count);
multi_map_get_all(seen, key, taps);
@@ -7857,37 +8702,75 @@ gb_internal ExprKind check_basic_directive_expr(CheckerContext *c, Operand *o, A
o->mode = Addressing_Constant;
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) {
+ file = obfuscate_string(file, "F");
+ }
+ 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) {
+ path = obfuscate_string(path, "D");
+ }
o->type = t_untyped_string;
- o->value = exact_value_string(get_file_path_string(bd->token.pos.file_id));
+ o->value = exact_value_string(path);
} else if (name == "line") {
+ i32 line = bd->token.pos.line;
+ if (build_context.obfuscate_source_code_locations) {
+ line = obfuscate_i32(line);
+ }
o->type = t_untyped_integer;
- o->value = exact_value_i64(bd->token.pos.line);
+ o->value = exact_value_i64(line);
} else if (name == "procedure") {
if (c->curr_proc_decl == nullptr) {
error(node, "#procedure may only be used within procedures");
o->type = t_untyped_string;
o->value = exact_value_string(str_lit(""));
} else {
+ String p = c->proc_name;
+ if (build_context.obfuscate_source_code_locations) {
+ p = obfuscate_string(p, "P");
+ }
o->type = t_untyped_string;
- o->value = exact_value_string(c->proc_name);
+ o->value = exact_value_string(p);
}
} else if (name == "caller_location") {
init_core_source_code_location(c->checker);
error(node, "#caller_location may only be used as a default argument parameter");
o->type = t_source_code_location;
o->mode = Addressing_Value;
+ } else if (name == "caller_expression") {
+ error(node, "#caller_expression may only be used as a default argument parameter");
+ o->type = t_string;
+ o->mode = Addressing_Value;
+ } else if (name == "branch_location") {
+ if (!c->in_defer) {
+ error(node, "#branch_location may only be used within a 'defer' statement");
+ } else if (c->curr_proc_decl) {
+ Entity *e = c->curr_proc_decl->entity;
+ if (e != nullptr) {
+ GB_ASSERT(e->kind == Entity_Procedure);
+ e->Procedure.uses_branch_location = true;
+ }
+ }
+ o->type = t_source_code_location;
+ o->mode = Addressing_Value;
} else {
if (name == "location") {
init_core_source_code_location(c->checker);
- error(node, "'#%.*s' must be used in a call expression", LIT(name));
+ error(node, "'#location' must be used as a call, i.e. #location(proc), where #location() defaults to the procedure in which it was used.");
o->type = t_source_code_location;
o->mode = Addressing_Value;
} else if (
name == "assert" ||
name == "defined" ||
name == "config" ||
+ name == "exists" ||
name == "load" ||
name == "load_hash" ||
+ name == "load_directory" ||
name == "load_or"
) {
error(node, "'#%.*s' must be used as a call", LIT(name));
@@ -7939,11 +8822,6 @@ gb_internal ExprKind check_ternary_if_expr(CheckerContext *c, Operand *o, Ast *n
return kind;
}
- if (x.type == nullptr || x.type == t_invalid ||
- y.type == nullptr || y.type == t_invalid) {
- return kind;
- }
-
bool use_type_hint = type_hint != nullptr && (is_operand_nil(x) || is_operand_nil(y));
convert_to_typed(c, &x, use_type_hint ? type_hint : y.type);
@@ -8179,6 +9057,7 @@ gb_internal ExprKind check_or_return_expr(CheckerContext *c, Operand *o, Ast *no
// NOTE(bill): allow implicit conversion between boolean types
// within 'or_return' to improve the experience using third-party code
} else if (!check_is_assignable_to(c, &rhs, end_type)) {
+ ERROR_BLOCK();
// TODO(bill): better error message
gbString a = type_to_string(right_type);
gbString b = type_to_string(end_type);
@@ -8240,7 +9119,7 @@ gb_internal ExprKind check_or_branch_expr(CheckerContext *c, Operand *o, Ast *no
// okay
} else {
gbString s = type_to_string(right_type);
- error(node, "'%.*s' requires a boolean or nil-able type, got %s", s);
+ error(node, "'%.*s' requires a boolean or nil-able type, got %s", LIT(name), s);
gb_string_free(s);
}
}
@@ -8261,6 +9140,7 @@ gb_internal ExprKind check_or_branch_expr(CheckerContext *c, Operand *o, Ast *no
switch (be->token.kind) {
case Token_or_break:
+ node->viral_state_flags |= ViralStateFlag_ContainsOrBreak;
if ((c->stmt_flags & Stmt_BreakAllowed) == 0 && label == nullptr) {
error(be->token, "'%.*s' only allowed in non-inline loops or 'switch' statements", LIT(name));
}
@@ -8273,6 +9153,10 @@ gb_internal ExprKind check_or_branch_expr(CheckerContext *c, Operand *o, Ast *no
}
if (label != nullptr) {
+ if (c->in_defer) {
+ error(label, "A labelled '%.*s' cannot be used within a 'defer'", LIT(name));
+ return Expr_Expr;
+ }
if (label->kind != Ast_Ident) {
error(label, "A branch statement's label name must be an identifier");
return Expr_Expr;
@@ -8323,50 +9207,74 @@ gb_internal void check_compound_literal_field_values(CheckerContext *c, Slice<As
StringMap<String> fields_visited_through_raw_union = {};
defer (string_map_destroy(&fields_visited_through_raw_union));
+ String assignment_str = str_lit("structure literal");
+ if (bt->kind == Type_BitField) {
+ assignment_str = str_lit("bit_field literal");
+ }
+
for (Ast *elem : elems) {
if (elem->kind != Ast_FieldValue) {
error(elem, "Mixture of 'field = value' and value elements in a literal is not allowed");
continue;
}
ast_node(fv, FieldValue, elem);
- if (fv->field->kind != Ast_Ident) {
- gbString expr_str = expr_to_string(fv->field);
+ Ast *ident = fv->field;
+ if (ident->kind == Ast_ImplicitSelectorExpr) {
+ gbString expr_str = expr_to_string(ident);
+ error(ident, "Field names do not start with a '.', remove the '.' in structure literal", expr_str);
+ gb_string_free(expr_str);
+
+ ident = ident->ImplicitSelectorExpr.selector;
+ }
+ if (ident->kind != Ast_Ident) {
+ gbString expr_str = expr_to_string(ident);
error(elem, "Invalid field name '%s' in structure literal", expr_str);
gb_string_free(expr_str);
continue;
}
- String name = fv->field->Ident.token.string;
+ String name = ident->Ident.token.string;
Selection sel = lookup_field(type, name, o->mode == Addressing_Type);
bool is_unknown = sel.entity == nullptr;
if (is_unknown) {
- error(fv->field, "Unknown field '%.*s' in structure literal", LIT(name));
+ error(ident, "Unknown field '%.*s' in structure literal", LIT(name));
continue;
}
- Entity *field = bt->Struct.fields[sel.index[0]];
- add_entity_use(c, fv->field, field);
+ Entity *field = nullptr;
+ if (bt->kind == Type_Struct) {
+ field = bt->Struct.fields[sel.index[0]];
+ } else if (bt->kind == Type_BitField) {
+ field = bt->BitField.fields[sel.index[0]];
+ } else {
+ GB_PANIC("Unknown type");
+ }
+
+
+ add_entity_use(c, ident, field);
if (string_set_update(&fields_visited, name)) {
if (sel.index.count > 1) {
if (String *found = string_map_get(&fields_visited_through_raw_union, sel.entity->token.string)) {
- error(fv->field, "Field '%.*s' is already initialized due to a previously assigned struct #raw_union field '%.*s'", LIT(sel.entity->token.string), LIT(*found));
+ error(ident, "Field '%.*s' is already initialized due to a previously assigned struct #raw_union field '%.*s'", LIT(sel.entity->token.string), LIT(*found));
} else {
- error(fv->field, "Duplicate or reused field '%.*s' in structure literal", LIT(sel.entity->token.string));
+ error(ident, "Duplicate or reused field '%.*s' in %.*s", LIT(sel.entity->token.string), LIT(assignment_str));
}
} else {
- error(fv->field, "Duplicate field '%.*s' in structure literal", LIT(field->token.string));
+ error(ident, "Duplicate field '%.*s' in %.*s", LIT(field->token.string), LIT(assignment_str));
}
continue;
} else if (String *found = string_map_get(&fields_visited_through_raw_union, sel.entity->token.string)) {
- error(fv->field, "Field '%.*s' is already initialized due to a previously assigned struct #raw_union field '%.*s'", LIT(sel.entity->token.string), LIT(*found));
+ error(ident, "Field '%.*s' is already initialized due to a previously assigned struct #raw_union field '%.*s'", LIT(sel.entity->token.string), LIT(*found));
continue;
}
if (sel.indirect) {
- error(fv->field, "Cannot assign to the %d-nested anonymous indirect field '%.*s' in a structure literal", cast(int)sel.index.count-1, LIT(name));
+ error(ident, "Cannot assign to the %d-nested anonymous indirect field '%.*s' in a %.*s", cast(int)sel.index.count-1, LIT(name), LIT(assignment_str));
continue;
}
if (sel.index.count > 1) {
+ GB_ASSERT(bt->kind == Type_Struct);
+
if (is_constant) {
Type *ft = type;
for (i32 index : sel.index) {
@@ -8382,6 +9290,10 @@ gb_internal void check_compound_literal_field_values(CheckerContext *c, Slice<As
case Type_Array:
ft = bt->Array.elem;
break;
+ case Type_BitField:
+ is_constant = false;
+ ft = bt->BitField.fields[index]->type;
+ break;
default:
GB_PANIC("invalid type: %s", type_to_string(ft));
break;
@@ -8408,6 +9320,9 @@ gb_internal void check_compound_literal_field_values(CheckerContext *c, Slice<As
case Type_Array:
nested_ft = bt->Array.elem;
break;
+ case Type_BitField:
+ nested_ft = bt->BitField.fields[index]->type;
+ break;
default:
GB_PANIC("invalid type %s", type_to_string(nested_ft));
break;
@@ -8427,10 +9342,52 @@ gb_internal void check_compound_literal_field_values(CheckerContext *c, Slice<As
is_constant = check_is_operand_compound_lit_constant(c, &o);
}
- check_assignment(c, &o, field->type, str_lit("structure literal"));
+ u8 prev_bit_field_bit_size = c->bit_field_bit_size;
+ if (field->kind == Entity_Variable && field->Variable.bit_field_bit_size) {
+ // HACK NOTE(bill): This is a bit of a hack, but it will work fine for this use case
+ c->bit_field_bit_size = field->Variable.bit_field_bit_size;
+ }
+
+ check_assignment(c, &o, field->type, assignment_str);
+
+ c->bit_field_bit_size = prev_bit_field_bit_size;
}
}
+gb_internal bool is_expr_inferred_fixed_array(Ast *type_expr) {
+ type_expr = unparen_expr(type_expr);
+ if (type_expr == nullptr) {
+ return false;
+ }
+
+ // [?]Type
+ if (type_expr->kind == Ast_ArrayType && type_expr->ArrayType.count != nullptr) {
+ Ast *count = type_expr->ArrayType.count;
+ if (count->kind == Ast_UnaryExpr &&
+ count->UnaryExpr.op.kind == Token_Question) {
+ return true;
+ }
+ }
+ return false;
+}
+
+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) {
+ 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");
+ error_line("\tWarning: Please understand that dynamic literals will implicitly allocate using the current 'context.allocator' in that scope\n");
+ if (build_context.ODIN_DEFAULT_TO_NIL_ALLOCATOR) {
+ error_line("\tWarning: As '-default-to-panic-allocator' has been set, the dynamic compound literal may not be initialized as expected\n");
+ } else if (build_context.ODIN_DEFAULT_TO_PANIC_ALLOCATOR) {
+ error_line("\tWarning: As '-default-to-panic-allocator' has been set, the dynamic compound literal may not be initialized as expected\n");
+ }
+ return false;
+ }
+
+ return cl->elems.count > 0;
+}
+
gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) {
ExprKind kind = Expr_Expr;
ast_node(cl, CompoundLit, node);
@@ -8441,20 +9398,35 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
}
bool is_to_be_determined_array_count = false;
bool is_constant = true;
- if (cl->type != nullptr) {
+
+ Ast *type_expr = cl->type;
+
+ bool used_type_hint_expr = false;
+ if (type_expr == nullptr && c->type_hint_expr != nullptr) {
+ if (is_expr_inferred_fixed_array(c->type_hint_expr)) {
+ type_expr = clone_ast(c->type_hint_expr);
+ used_type_hint_expr = true;
+ }
+ }
+
+ if (type_expr != nullptr) {
type = nullptr;
// [?]Type
- if (cl->type->kind == Ast_ArrayType && cl->type->ArrayType.count != nullptr) {
- Ast *count = cl->type->ArrayType.count;
- if (count->kind == Ast_UnaryExpr &&
- count->UnaryExpr.op.kind == Token_Question) {
- type = alloc_type_array(check_type(c, cl->type->ArrayType.elem), -1);
- is_to_be_determined_array_count = true;
+ if (type_expr->kind == Ast_ArrayType) {
+ Ast *count = type_expr->ArrayType.count;
+ if (count != nullptr) {
+ if (count->kind == Ast_UnaryExpr &&
+ count->UnaryExpr.op.kind == Token_Question) {
+ type = alloc_type_array(check_type(c, type_expr->ArrayType.elem), -1);
+ is_to_be_determined_array_count = true;
+ }
+ } else {
+ type = alloc_type_slice(check_type(c, type_expr->ArrayType.elem));
}
if (cl->elems.count > 0) {
- if (cl->type->ArrayType.tag != nullptr) {
- Ast *tag = cl->type->ArrayType.tag;
+ if (type_expr->ArrayType.tag != nullptr) {
+ Ast *tag = type_expr->ArrayType.tag;
GB_ASSERT(tag->kind == Ast_BasicDirective);
String name = tag->BasicDirective.name.string;
if (name == "soa") {
@@ -8463,10 +9435,9 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
}
}
}
- }
- if (cl->type->kind == Ast_DynamicArrayType && cl->type->DynamicArrayType.tag != nullptr) {
+ } else if (type_expr->kind == Ast_DynamicArrayType && type_expr->DynamicArrayType.tag != nullptr) {
if (cl->elems.count > 0) {
- Ast *tag = cl->type->DynamicArrayType.tag;
+ Ast *tag = type_expr->DynamicArrayType.tag;
GB_ASSERT(tag->kind == Ast_BasicDirective);
String name = tag->BasicDirective.name.string;
if (name == "soa") {
@@ -8477,7 +9448,7 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
}
if (type == nullptr) {
- type = check_type(c, cl->type);
+ type = check_type(c, type_expr);
}
}
@@ -8503,6 +9474,12 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
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.is_raw_union) {
if (cl->elems.count > 0) {
// NOTE: unions cannot be constant
@@ -8525,6 +9502,7 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
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--) {
@@ -8610,11 +9588,6 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
elem_type = t->DynamicArray.elem;
context_name = str_lit("dynamic array literal");
is_constant = false;
-
- if (!build_context.no_dynamic_literals) {
- add_package_dependency(c, "runtime", "__dynamic_array_reserve");
- add_package_dependency(c, "runtime", "__dynamic_array_append");
- }
} else if (t->kind == Type_SimdVector) {
elem_type = t->SimdVector.elem;
context_name = str_lit("simd vector literal");
@@ -8789,8 +9762,9 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
if (t->kind == Type_DynamicArray) {
- if (build_context.no_dynamic_literals && cl->elems.count) {
- error(node, "Compound literals of dynamic types have been disabled");
+ if (check_for_dynamic_literals(c, node, cl)) {
+ add_package_dependency(c, "runtime", "__dynamic_array_reserve");
+ add_package_dependency(c, "runtime", "__dynamic_array_append");
}
}
@@ -9030,15 +10004,15 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
continue;
}
ExactValue v = f->Constant.value;
- auto found = map_get(&seen, hash_exact_value(v));
+ uintptr hash = hash_exact_value(v);
+ auto found = map_get(&seen, hash);
if (!found) {
array_add(&unhandled, f);
}
}
if (unhandled.count > 0) {
- begin_error_block();
- defer (end_error_block());
+ ERROR_BLOCK();
if (unhandled.count == 1) {
error_no_newline(node, "Unhandled enumerated array case: %.*s", LIT(unhandled[0]->token.string));
@@ -9049,9 +10023,11 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
error_line("\t%.*s\n", LIT(f->token.string));
}
}
- error_line("\n");
- error_line("\tSuggestion: Was '#partial %s{...}' wanted?\n", type_to_string(type));
+ if (!build_context.terse_errors) {
+ error_line("\n");
+ error_line("\tSuggestion: Was '#partial %s{...}' wanted?\n", type_to_string(type));
+ }
}
}
@@ -9177,9 +10153,7 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
}
}
- if (build_context.no_dynamic_literals && cl->elems.count) {
- error(node, "Compound literals of dynamic types have been disabled");
- } else {
+ if (check_for_dynamic_literals(c, node, cl)) {
add_map_reserve_dependencies(c);
add_map_set_dependencies(c);
}
@@ -9192,10 +10166,14 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
}
Type *et = base_type(t->BitSet.elem);
isize field_count = 0;
- if (et->kind == Type_Enum) {
+ if (et != nullptr && et->kind == Type_Enum) {
field_count = et->Enum.fields.count;
}
+ if (is_type_array(bit_set_to_int(t))) {
+ is_constant = false;
+ }
+
if (cl->elems[0]->kind == Ast_FieldValue) {
error(cl->elems[0], "'field = value' in a bit_set a literal is not allowed");
is_constant = false;
@@ -9212,6 +10190,22 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
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;
@@ -9220,7 +10214,9 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
if (lower <= v && v <= upper) {
// okay
} else {
- error(elem, "Bit field value out of bounds, %lld not in the range %lld .. %lld", v, lower, upper);
+ 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;
}
}
@@ -9228,6 +10224,21 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
}
break;
}
+ case Type_BitField: {
+ if (cl->elems.count == 0) {
+ break; // NOTE(bill): No need to init
+ }
+ is_constant = false;
+ if (cl->elems[0]->kind != Ast_FieldValue) {
+ gbString type_str = type_to_string(type);
+ error(node, "%s ('bit_field') 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);
+ }
+ break;
+ }
+
default: {
if (cl->elems.count == 0) {
@@ -9259,7 +10270,9 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
if (tav.mode != Addressing_Constant) {
continue;
}
- GB_ASSERT(tav.value.kind == ExactValue_Integer);
+ if (tav.value.kind != ExactValue_Integer) {
+ continue;
+ }
i64 v = big_int_to_i64(&tav.value.value_integer);
i64 lower = bt->BitSet.lower;
u64 index = cast(u64)(v-lower);
@@ -9629,8 +10642,6 @@ gb_internal ExprKind check_index_expr(CheckerContext *c, Operand *o, Ast *node,
// Okay
} else if (is_type_string(t)) {
// Okay
- } else if (is_type_relative_multi_pointer(t)) {
- // Okay
} else if (is_type_matrix(t)) {
// Okay
} else {
@@ -9673,23 +10684,30 @@ gb_internal ExprKind check_index_expr(CheckerContext *c, Operand *o, Ast *node,
bool ok = check_index_value(c, t, false, ie->index, max_count, &index, index_type_hint);
if (is_const) {
if (index < 0) {
+ ERROR_BLOCK();
gbString str = expr_to_string(o->expr);
error(o->expr, "Cannot index a constant '%s'", str);
- error_line("\tSuggestion: store the constant into a variable in order to index it with a variable index\n");
+ if (!build_context.terse_errors) {
+ error_line("\tSuggestion: store the constant into a variable in order to index it with a variable index\n");
+ }
gb_string_free(str);
o->mode = Addressing_Invalid;
o->expr = node;
return kind;
- } else if (ok) {
- ExactValue value = type_and_value_of_expr(ie->expr).value;
+ } else if (ok && !is_type_matrix(t)) {
+ TypeAndValue tav = type_and_value_of_expr(ie->expr);
+ ExactValue value = tav.value;
o->mode = Addressing_Constant;
bool success = false;
bool finish = false;
o->value = get_constant_field_single(c, value, cast(i32)index, &success, &finish);
if (!success) {
+ ERROR_BLOCK();
gbString str = expr_to_string(o->expr);
error(o->expr, "Cannot index a constant '%s' with index %lld", str, cast(long long)index);
- error_line("\tSuggestion: store the constant into a variable in order to index it with a variable index\n");
+ if (!build_context.terse_errors) {
+ error_line("\tSuggestion: store the constant into a variable in order to index it with a variable index\n");
+ }
gb_string_free(str);
o->mode = Addressing_Invalid;
o->expr = node;
@@ -9763,15 +10781,21 @@ gb_internal ExprKind check_slice_expr(CheckerContext *c, Operand *o, Ast *node,
case Type_Struct:
if (is_type_soa_struct(t)) {
valid = true;
+ if (t->Struct.soa_kind == StructSoa_Fixed) {
+ max_count = t->Struct.soa_count;
+ if (o->mode != Addressing_Variable && !is_type_pointer(o->type)) {
+ gbString str = expr_to_string(node);
+ error(node, "Cannot slice #soa array '%s', value is not addressable", str);
+ gb_string_free(str);
+ o->mode = Addressing_Invalid;
+ o->expr = node;
+ return kind;
+ }
+ }
o->type = make_soa_struct_slice(c, nullptr, nullptr, t->Struct.soa_elem);
}
break;
- case Type_RelativeMultiPointer:
- valid = true;
- o->type = type_deref(o->type);
- break;
-
case Type_EnumeratedArray:
{
gbString str = expr_to_string(o->expr);
@@ -9848,16 +10872,6 @@ gb_internal ExprKind check_slice_expr(CheckerContext *c, Operand *o, Ast *node,
x[i:n] -> []T
*/
o->type = alloc_type_slice(t->MultiPointer.elem);
- } else if (t->kind == Type_RelativeMultiPointer && se->high != nullptr) {
- /*
- x[:] -> [^]T
- x[i:] -> [^]T
- x[:n] -> []T
- x[i:n] -> []T
- */
- Type *pointer_type = base_type(t->RelativeMultiPointer.pointer_type);
- GB_ASSERT(pointer_type->kind == Type_MultiPointer);
- o->type = alloc_type_slice(pointer_type->MultiPointer.elem);
}
@@ -9875,9 +10889,12 @@ gb_internal ExprKind check_slice_expr(CheckerContext *c, Operand *o, Ast *node,
}
}
if (!all_constant) {
+ ERROR_BLOCK();
gbString str = expr_to_string(o->expr);
error(o->expr, "Cannot slice '%s' with non-constant indices", str);
- error_line("\tSuggestion: store the constant into a variable in order to index it with a variable index\n");
+ if (!build_context.terse_errors) {
+ error_line("\tSuggestion: store the constant into a variable in order to index it with a variable index\n");
+ }
gb_string_free(str);
o->mode = Addressing_Value; // NOTE(bill): Keep subsequent values going without erring
o->expr = node;
@@ -9941,7 +10958,7 @@ gb_internal ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast
case Token_context:
{
if (c->proc_name.len == 0 && c->curr_proc_sig == nullptr) {
- error(node, "'context' is only allowed within procedures %p", c->curr_proc_decl);
+ error(node, "'context' is only allowed within procedures");
return kind;
}
if (unparen_expr(c->assignment_lhs_hint) == node) {
@@ -10064,6 +11081,7 @@ gb_internal ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast
case_end;
case_ast_node(re, OrReturnExpr, node);
+ node->viral_state_flags |= ViralStateFlag_ContainsOrReturn;
return check_or_return_expr(c, o, node, type_hint);
case_end;
@@ -10114,10 +11132,10 @@ gb_internal ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast
if (o->mode != Addressing_Invalid) {
switch (tc->token.kind) {
case Token_transmute:
- check_transmute(c, node, o, type);
+ check_transmute(c, node, o, type, true);
break;
case Token_cast:
- check_cast(c, o, type);
+ check_cast(c, o, type, true);
break;
default:
error(node, "Invalid AST: Invalid casting expression");
@@ -10214,34 +11232,18 @@ gb_internal ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast
} else if (t->kind == Type_SoaPointer) {
o->mode = Addressing_SoaVariable;
o->type = type_deref(t);
- } else if (t->kind == Type_RelativePointer) {
- if (o->mode != Addressing_Variable) {
- gbString str = expr_to_string(o->expr);
- gbString typ = type_to_string(o->type);
- error(o->expr, "Cannot dereference relative pointer '%s' of type '%s' as it does not have a variable addressing mode", str, typ);
- gb_string_free(typ);
- gb_string_free(str);
- }
-
- // NOTE(bill): This is required because when dereferencing, the original type has been lost
- add_type_info_type(c, o->type);
-
- Type *ptr_type = base_type(t->RelativePointer.pointer_type);
- GB_ASSERT(ptr_type->kind == Type_Pointer);
- o->mode = Addressing_Variable;
- o->type = ptr_type->Pointer.elem;
} else {
gbString str = expr_to_string(o->expr);
gbString typ = type_to_string(o->type);
- begin_error_block();
+ ERROR_BLOCK();
error(o->expr, "Cannot dereference '%s' of type '%s'", str, typ);
if (o->type && is_type_multi_pointer(o->type)) {
- error_line("\tDid you mean '%s[0]'?\n", str);
+ if (!build_context.terse_errors) {
+ error_line("\tDid you mean '%s[0]'?\n", str);
+ }
}
- end_error_block();
-
gb_string_free(typ);
gb_string_free(str);
o->mode = Addressing_Invalid;
@@ -10298,6 +11300,7 @@ gb_internal ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast
return Expr_Expr;
case_end;
+ case Ast_DistinctType:
case Ast_TypeidType:
case Ast_PolyType:
case Ast_ProcType:
@@ -10762,6 +11765,9 @@ gb_internal gbString write_expr_to_string(gbString str, Ast *node, bool shorthan
case_end;
case_ast_node(pt, PointerType, node);
+ if (pt->tag) {
+ str = write_expr_to_string(str, pt->tag, false);
+ }
str = gb_string_append_rune(str, '^');
str = write_expr_to_string(str, pt->type, shorthand);
case_end;
@@ -10772,6 +11778,9 @@ gb_internal gbString write_expr_to_string(gbString str, Ast *node, bool shorthan
case_end;
case_ast_node(at, ArrayType, node);
+ if (at->tag) {
+ str = write_expr_to_string(str, at->tag, false);
+ }
str = gb_string_append_rune(str, '[');
if (at->count != nullptr &&
at->count->kind == Ast_UnaryExpr &&
@@ -10785,6 +11794,9 @@ gb_internal gbString write_expr_to_string(gbString str, Ast *node, bool shorthan
case_end;
case_ast_node(at, DynamicArrayType, node);
+ if (at->tag) {
+ str = write_expr_to_string(str, at->tag, false);
+ }
str = gb_string_appendc(str, "[dynamic]");
str = write_expr_to_string(str, at->elem, shorthand);
case_end;
@@ -10826,6 +11838,9 @@ gb_internal gbString write_expr_to_string(gbString str, Ast *node, bool shorthan
if (f->flags&FieldFlag_any_int) {
str = gb_string_appendc(str, "#any_int ");
}
+ if (f->flags&FieldFlag_no_broadcast) {
+ str = gb_string_appendc(str, "#no_broadcast ");
+ }
if (f->flags&FieldFlag_const) {
str = gb_string_appendc(str, "#const ");
}
@@ -10892,6 +11907,18 @@ gb_internal gbString write_expr_to_string(gbString str, Ast *node, bool shorthan
if (field->flags&FieldFlag_c_vararg) {
str = gb_string_appendc(str, "#c_vararg ");
}
+ if (field->flags&FieldFlag_any_int) {
+ str = gb_string_appendc(str, "#any_int ");
+ }
+ if (field->flags&FieldFlag_no_broadcast) {
+ str = gb_string_appendc(str, "#no_broadcast ");
+ }
+ if (field->flags&FieldFlag_const) {
+ str = gb_string_appendc(str, "#const ");
+ }
+ if (field->flags&FieldFlag_subtype) {
+ str = gb_string_appendc(str, "#subtype ");
+ }
str = write_expr_to_string(str, field->type, shorthand);
}
@@ -11035,6 +12062,32 @@ gb_internal gbString write_expr_to_string(gbString str, Ast *node, bool shorthan
case_end;
+ case_ast_node(f, BitFieldField, node);
+ str = write_expr_to_string(str, f->name, shorthand);
+ str = gb_string_appendc(str, ": ");
+ str = write_expr_to_string(str, f->type, shorthand);
+ str = gb_string_appendc(str, " | ");
+ str = write_expr_to_string(str, f->bit_size, shorthand);
+ case_end;
+ case_ast_node(bf, BitFieldType, node);
+ str = gb_string_appendc(str, "bit_field ");
+ if (!shorthand) {
+ str = write_expr_to_string(str, bf->backing_type, shorthand);
+ }
+ str = gb_string_appendc(str, " {");
+ if (shorthand) {
+ str = gb_string_appendc(str, "...");
+ } else {
+ for_array(i, bf->fields) {
+ if (i > 0) {
+ str = gb_string_appendc(str, ", ");
+ }
+ str = write_expr_to_string(str, bf->fields[i], false);
+ }
+ }
+ str = gb_string_appendc(str, "}");
+ case_end;
+
case_ast_node(ia, InlineAsmExpr, node);
str = gb_string_appendc(str, "asm(");
for_array(i, ia->param_types) {
diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp
index b93be734e..02ad72388 100644
--- a/src/check_stmt.cpp
+++ b/src/check_stmt.cpp
@@ -161,8 +161,7 @@ gb_internal bool check_is_terminating_list(Slice<Ast *> const &stmts, String con
}
gb_internal bool check_has_break_list(Slice<Ast *> const &stmts, String const &label, bool implicit) {
- for_array(i, stmts) {
- Ast *stmt = stmts[i];
+ for (Ast *stmt : stmts) {
if (check_has_break(stmt, label, implicit)) {
return true;
}
@@ -170,6 +169,21 @@ gb_internal bool check_has_break_list(Slice<Ast *> const &stmts, String const &l
return false;
}
+gb_internal bool check_has_break_expr(Ast * expr, String const &label) {
+ if (expr && expr->viral_state_flags & ViralStateFlag_ContainsOrBreak) {
+ return true;
+ }
+ return false;
+}
+
+gb_internal bool check_has_break_expr_list(Slice<Ast *> const &exprs, String const &label) {
+ for (Ast *expr : exprs) {
+ if (check_has_break_expr(expr, label)) {
+ return true;
+ }
+ }
+ return false;
+}
gb_internal bool check_has_break(Ast *stmt, String const &label, bool implicit) {
switch (stmt->kind) {
@@ -185,10 +199,20 @@ gb_internal bool check_has_break(Ast *stmt, String const &label, bool implicit)
}
break;
+ case Ast_DeferStmt:
+ return check_has_break(stmt->DeferStmt.stmt, label, implicit);
+
case Ast_BlockStmt:
return check_has_break_list(stmt->BlockStmt.stmts, label, implicit);
case Ast_IfStmt:
+ if (stmt->IfStmt.init && check_has_break(stmt->IfStmt.init, label, implicit)) {
+ return true;
+ }
+ if (stmt->IfStmt.cond && check_has_break_expr(stmt->IfStmt.cond, label)) {
+ return true;
+ }
+
if (check_has_break(stmt->IfStmt.body, label, implicit) ||
(stmt->IfStmt.else_stmt != nullptr && check_has_break(stmt->IfStmt.else_stmt, label, implicit))) {
return true;
@@ -199,6 +223,9 @@ gb_internal bool check_has_break(Ast *stmt, String const &label, bool implicit)
return check_has_break_list(stmt->CaseClause.stmts, label, implicit);
case Ast_SwitchStmt:
+ if (stmt->SwitchStmt.init && check_has_break_expr(stmt->SwitchStmt.init, label)) {
+ return true;
+ }
if (label != "" && check_has_break(stmt->SwitchStmt.body, label, false)) {
return true;
}
@@ -211,6 +238,16 @@ gb_internal bool check_has_break(Ast *stmt, String const &label, bool implicit)
break;
case Ast_ForStmt:
+ if (stmt->ForStmt.init && check_has_break(stmt->ForStmt.init, label, implicit)) {
+ return true;
+ }
+ if (stmt->ForStmt.cond && check_has_break_expr(stmt->ForStmt.cond, label)) {
+ return true;
+ }
+ if (stmt->ForStmt.post && check_has_break(stmt->ForStmt.post, label, implicit)) {
+ return true;
+ }
+
if (label != "" && check_has_break(stmt->ForStmt.body, label, false)) {
return true;
}
@@ -221,12 +258,41 @@ gb_internal bool check_has_break(Ast *stmt, String const &label, bool implicit)
return true;
}
break;
+
+ case Ast_ExprStmt:
+ if (stmt->ExprStmt.expr->viral_state_flags & ViralStateFlag_ContainsOrBreak) {
+ return true;
+ }
+ break;
+
+ case Ast_ValueDecl:
+ if (stmt->ValueDecl.is_mutable && check_has_break_expr_list(stmt->ValueDecl.values, label)) {
+ return true;
+ }
+ break;
+ case Ast_AssignStmt:
+ if (check_has_break_expr_list(stmt->AssignStmt.lhs, label)) {
+ return true;
+ }
+ if (check_has_break_expr_list(stmt->AssignStmt.rhs, label)) {
+ return true;
+ }
+ break;
}
return false;
}
-
+String label_string(Ast *node) {
+ GB_ASSERT(node != nullptr);
+ if (node->kind == Ast_Ident) {
+ return node->Ident.token.string;
+ } else if (node->kind == Ast_Label) {
+ return label_string(node->Label.name);
+ }
+ GB_ASSERT("INVALID LABEL");
+ return {};
+}
// NOTE(bill): The last expression has to be a 'return' statement
// TODO(bill): This is a mild hack and should be probably handled properly
@@ -237,13 +303,27 @@ gb_internal bool check_is_terminating(Ast *node, String const &label) {
case_end;
case_ast_node(bs, BlockStmt, node);
- return check_is_terminating_list(bs->stmts, label);
+ if (check_is_terminating_list(bs->stmts, label)) {
+ if (bs->label != nullptr) {
+ return check_is_terminating_list(bs->stmts, label_string(bs->label));
+ }
+ return true;
+ }
case_end;
case_ast_node(es, ExprStmt, node);
return check_is_terminating(unparen_expr(es->expr), label);
case_end;
+ case_ast_node(vd, ValueDecl, node);
+ return check_has_break_expr_list(vd->values, label);
+ case_end;
+
+ case_ast_node(as, AssignStmt, node);
+ return check_has_break_expr_list(as->lhs, label) ||
+ check_has_break_expr_list(as->rhs, label);
+ case_end;
+
case_ast_node(bs, BranchStmt, node);
return bs->token.kind == Token_fallthrough;
case_end;
@@ -285,6 +365,9 @@ gb_internal bool check_is_terminating(Ast *node, String const &label) {
case_ast_node(fs, ForStmt, node);
if (fs->cond == nullptr && !check_has_break(fs->body, label, true)) {
+ if (fs->label) {
+ return !check_has_break(fs->body, label_string(fs->label), false);
+ }
return true;
}
case_end;
@@ -394,8 +477,15 @@ gb_internal Type *check_assignment_variable(CheckerContext *ctx, Operand *lhs, O
rhs->proc_group = nullptr;
}
} else {
+ Ast *ident_node = nullptr;
+
if (node->kind == Ast_Ident) {
- ast_node(i, Ident, node);
+ ident_node = node;
+ } else if (node->kind == Ast_IndexExpr && node->IndexExpr.expr->kind == Ast_Ident) {
+ ident_node = node->IndexExpr.expr;
+ }
+ if (ident_node != nullptr) {
+ ast_node(i, Ident, ident_node);
e = scope_lookup(ctx->scope, i->token.string);
if (e != nullptr && e->kind == Entity_Variable) {
used = (e->flags & EntityFlag_Used) != 0; // NOTE(bill): Make backup just in case
@@ -421,7 +511,9 @@ gb_internal Type *check_assignment_variable(CheckerContext *ctx, Operand *lhs, O
return nullptr;
case Addressing_Variable:
- check_old_for_or_switch_value_usage(lhs->expr);
+ if (e && e->kind == Entity_Variable && e->Variable.is_rodata) {
+ error(lhs->expr, "Assignment to variable '%.*s' marked as @(rodata) is not allowed", LIT(e->token.string));
+ }
break;
case Addressing_MapIndex: {
@@ -443,9 +535,8 @@ gb_internal Type *check_assignment_variable(CheckerContext *ctx, Operand *lhs, O
break;
}
- case Addressing_Context: {
+ case Addressing_Context:
break;
- }
case Addressing_SoaVariable:
break;
@@ -468,16 +559,63 @@ gb_internal Type *check_assignment_variable(CheckerContext *ctx, Operand *lhs, O
}
Entity *e = entity_of_node(lhs->expr);
+ Entity *original_e = e;
+
+ Ast *name = unparen_expr(lhs->expr);
+ while (name->kind == Ast_SelectorExpr) {
+ name = name->SelectorExpr.expr;
+ e = entity_of_node(name);
+ }
+ if (e == nullptr) {
+ e = original_e;
+ }
gbString str = expr_to_string(lhs->expr);
if (e != nullptr && e->flags & EntityFlag_Param) {
+ ERROR_BLOCK();
if (e->flags & EntityFlag_Using) {
error(lhs->expr, "Cannot assign to '%s' which is from a 'using' procedure parameter", str);
} else {
error(lhs->expr, "Cannot assign to '%s' which is a procedure parameter", str);
}
+ if (is_type_pointer(e->type)) {
+ error_line("\tSuggestion: Did you mean to shadow it? '%.*s := %.*s'?\n", LIT(e->token.string), LIT(e->token.string));
+ } else {
+ error_line("\tSuggestion: Did you mean to pass '%.*s' by pointer?\n", LIT(e->token.string));
+ }
+ show_error_on_line(e->token.pos, token_pos_end(e->token));
} else {
+ ERROR_BLOCK();
error(lhs->expr, "Cannot assign to '%s'", str);
+
+ if (e && e->flags & EntityFlag_ForValue) {
+ isize offset = show_error_on_line(e->token.pos, token_pos_end(e->token));
+ if (offset < 0) {
+ if (is_type_map(e->type)) {
+ error_line("\tSuggestion: Did you mean? 'for key, &%.*s in ...'\n", LIT(e->token.string));
+ } else {
+ error_line("\tSuggestion: Did you mean? 'for &%.*s in ...'\n", LIT(e->token.string));
+ }
+ } else {
+ error_line("\t");
+ for (isize i = 0; i < offset-1; i++) {
+ error_line(" ");
+ }
+ error_line("'%.*s' is immutable, declare it as '&%.*s' to make it mutable\n", LIT(e->token.string), LIT(e->token.string));
+ }
+
+ } else if (e && e->flags & EntityFlag_SwitchValue) {
+ isize offset = show_error_on_line(e->token.pos, token_pos_end(e->token));
+ if (offset < 0) {
+ error_line("\tSuggestion: Did you mean? 'switch &%.*s in ...'\n", LIT(e->token.string));
+ } else {
+ error_line("\t");
+ for (isize i = 0; i < offset-1; i++) {
+ error_line(" ");
+ }
+ error_line("'%.*s' is immutable, declare it as '&%.*s' to make it mutable\n", LIT(e->token.string), LIT(e->token.string));
+ }
+ }
}
gb_string_free(str);
@@ -485,7 +623,17 @@ gb_internal Type *check_assignment_variable(CheckerContext *ctx, Operand *lhs, O
}
}
+ Entity *lhs_e = entity_of_node(lhs->expr);
+ u8 prev_bit_field_bit_size = ctx->bit_field_bit_size;
+ if (lhs_e && lhs_e->kind == Entity_Variable && lhs_e->Variable.bit_field_bit_size) {
+ // HACK NOTE(bill): This is a bit of a hack, but it will work fine for this use case
+ ctx->bit_field_bit_size = lhs_e->Variable.bit_field_bit_size;
+ }
+
check_assignment(ctx, rhs, assignment_type, str_lit("assignment"));
+
+ ctx->bit_field_bit_size = prev_bit_field_bit_size;
+
if (rhs->mode == Addressing_Invalid) {
return nullptr;
}
@@ -645,7 +793,7 @@ gb_internal bool check_using_stmt_entity(CheckerContext *ctx, AstUsingStmt *us,
for (auto const &entry : scope->elements) {
String name = entry.key;
Entity *decl = entry.value;
- if (!is_entity_exported(decl)) continue;
+ if (!is_entity_exported(decl, true)) continue;
Entity *found = scope_insert_with_name(ctx->scope, name, decl);
if (found != nullptr) {
@@ -670,6 +818,8 @@ gb_internal bool check_using_stmt_entity(CheckerContext *ctx, AstUsingStmt *us,
bool is_ptr = is_type_pointer(e->type);
Type *t = base_type(type_deref(e->type));
if (t->kind == Type_Struct) {
+ wait_signal_until_available(&t->Struct.fields_wait_signal);
+
Scope *found = t->Struct.scope;
GB_ASSERT(found != nullptr);
for (auto const &entry : found->elements) {
@@ -677,7 +827,8 @@ gb_internal bool check_using_stmt_entity(CheckerContext *ctx, AstUsingStmt *us,
if (f->kind == Entity_Variable) {
Entity *uvar = alloc_entity_using_variable(e, f->token, f->type, expr);
if (!is_ptr && e->flags & EntityFlag_Value) uvar->flags |= EntityFlag_Value;
- if (e->flags & EntityFlag_Param) uvar->flags |= EntityFlag_Param;
+ if (e->flags & EntityFlag_Param) uvar->flags |= EntityFlag_Param;
+ if (e->flags & EntityFlag_SoaPtrField) uvar->flags |= EntityFlag_SoaPtrField;
Entity *prev = scope_insert(ctx->scope, uvar);
if (prev != nullptr) {
gbString expr_str = expr_to_string(expr);
@@ -724,6 +875,25 @@ gb_internal bool check_using_stmt_entity(CheckerContext *ctx, AstUsingStmt *us,
return true;
}
+gb_internal void error_var_decl_identifier(Ast *name) {
+ GB_ASSERT(name != nullptr);
+ GB_ASSERT(name->kind != Ast_Ident);
+
+ ERROR_BLOCK();
+ gbString s = expr_to_string(name);
+ defer (gb_string_free(s));
+
+ error(name, "A variable declaration must be an identifier, got '%s'", s);
+ if (name->kind == Ast_Implicit) {
+ String imp = name->Implicit.string;
+ if (imp == "context") {
+ error_line("\tSuggestion: '%.*s' is a reserved keyword, would 'ctx' suffice?\n", LIT(imp));
+ } else {
+ error_line("\tNote: '%.*s' is a reserved keyword\n", LIT(imp));
+ }
+ }
+}
+
gb_internal void check_inline_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
ast_node(irs, UnrollRangeStmt, node);
check_open_scope(ctx, node);
@@ -835,7 +1005,7 @@ gb_internal void check_inline_range_stmt(CheckerContext *ctx, Ast *node, u32 mod
entity = found;
}
} else {
- error(name, "A variable declaration must be an identifier");
+ error_var_decl_identifier(name);
}
if (entity == nullptr) {
@@ -867,6 +1037,7 @@ gb_internal void check_inline_range_stmt(CheckerContext *ctx, Ast *node, u32 mod
}
if (ctx->inline_for_depth >= MAX_INLINE_FOR_DEPTH && prev_inline_for_depth < MAX_INLINE_FOR_DEPTH) {
+ ERROR_BLOCK();
if (prev_inline_for_depth > 0) {
error(node, "Nested '#unroll for' loop cannot be inlined as it exceeds the maximum '#unroll for' depth (%lld levels >= %lld maximum levels)", v, MAX_INLINE_FOR_DEPTH);
} else {
@@ -900,6 +1071,9 @@ gb_internal void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags
if (ss->tag != nullptr) {
check_expr(ctx, &x, ss->tag);
check_assignment(ctx, &x, nullptr, str_lit("switch expression"));
+ if (x.type == nullptr) {
+ return;
+ }
} else {
x.mode = Addressing_Constant;
x.type = t_bool;
@@ -1085,8 +1259,7 @@ gb_internal void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags
}
if (unhandled.count > 0) {
- begin_error_block();
- defer (end_error_block());
+ ERROR_BLOCK();
if (unhandled.count == 1) {
error_no_newline(node, "Unhandled switch case: %.*s", LIT(unhandled[0]->token.string));
@@ -1096,11 +1269,23 @@ gb_internal void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags
error_line("\t%.*s\n", LIT(f->token.string));
}
}
- error_line("\n");
-
error_line("\tSuggestion: Was '#partial switch' wanted?\n");
}
}
+
+ 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");
+ }
+ }
+ }
}
@@ -1174,7 +1359,6 @@ gb_internal void check_type_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_
}
}
- bool is_ptr = is_type_pointer(x.type);
// NOTE(bill): Check for multiple defaults
Ast *first_default = nullptr;
@@ -1293,21 +1477,12 @@ gb_internal void check_type_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_
}
bool is_reference = is_addressed;
- bool old_style = false;
-
- if (!is_reference &&
- is_ptr &&
- cc->list.count == 1 &&
- case_type != nullptr) {
- is_reference = true;
- old_style = true;
- }
if (cc->list.count > 1 || saw_nil) {
case_type = nullptr;
}
if (case_type == nullptr) {
- case_type = x.type;
+ case_type = type_deref(x.type);
}
if (switch_kind == TypeSwitch_Any) {
if (!is_type_untyped(case_type)) {
@@ -1323,9 +1498,6 @@ gb_internal void check_type_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_
if (!is_reference) {
tag_var->flags |= EntityFlag_Value;
}
- if (old_style) {
- tag_var->flags |= EntityFlag_OldForOrSwitchValue;
- }
add_entity(ctx, ctx->scope, lhs, tag_var);
add_entity_use(ctx, lhs, tag_var);
add_implicit_entity(ctx, stmt, tag_var);
@@ -1350,6 +1522,8 @@ gb_internal void check_type_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_
}
if (unhandled.count > 0) {
+ ERROR_BLOCK();
+
if (unhandled.count == 1) {
gbString s = type_to_string(unhandled[0]);
error_no_newline(node, "Unhandled switch case: %s", s);
@@ -1448,25 +1622,6 @@ gb_internal bool check_stmt_internal_builtin_proc_id(Ast *expr, BuiltinProcId *i
return id != BuiltinProc_Invalid;
}
-gb_internal bool check_expr_is_stack_variable(Ast *expr) {
- /*
- expr = unparen_expr(expr);
- Entity *e = entity_of_node(expr);
- if (e && e->kind == Entity_Variable) {
- if (e->flags & (EntityFlag_Static|EntityFlag_Using|EntityFlag_ImplicitReference|EntityFlag_ForValue)) {
- // okay
- } else if (e->Variable.thread_local_model.len != 0) {
- // okay
- } else if (e->scope) {
- if ((e->scope->flags & (ScopeFlag_Global|ScopeFlag_File|ScopeFlag_Type)) == 0) {
- return true;
- }
- }
- }
- */
- return false;
-}
-
gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
ast_node(rs, RangeStmt, node);
@@ -1480,12 +1635,15 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags)
auto vals = array_make<Type *>(temporary_allocator(), 0, 2);
auto entities = array_make<Entity *>(temporary_allocator(), 0, 2);
bool is_map = false;
- bool use_by_reference_for_value = false;
+ bool is_bit_set = false;
bool is_soa = false;
bool is_reverse = rs->reverse;
Ast *expr = unparen_expr(rs->expr);
+ Operand rhs_operand = {};
+
+ bool is_range = false;
bool is_possibly_addressable = true;
isize max_val_count = 2;
if (is_ast_range(expr)) {
@@ -1494,6 +1652,7 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags)
Operand y = {};
is_possibly_addressable = false;
+ is_range = true;
bool ok = check_range(ctx, expr, true, &x, &y, nullptr);
if (!ok) {
@@ -1525,11 +1684,25 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags)
array_add(&vals, operand.type);
array_add(&vals, t_int);
add_type_info_type(ctx, operand.type);
+ if (build_context.no_rtti) {
+ error(node, "Iteration over an enum type is not allowed runtime type information (RTTI) has been disallowed");
+ }
goto skip_expr_range_stmt;
}
} else if (operand.mode != Addressing_Invalid) {
+ if (operand.mode == Addressing_OptionalOk || operand.mode == Addressing_OptionalOkPtr) {
+ Ast *expr = unparen_expr(operand.expr);
+ if (expr->kind != Ast_TypeAssertion) { // Only for procedure calls
+ Type *end_type = nullptr;
+ check_promote_optional_ok(ctx, &operand, nullptr, &end_type, false);
+ if (is_type_boolean(end_type)) {
+ check_promote_optional_ok(ctx, &operand, nullptr, &end_type, true);
+ }
+ }
+ }
bool is_ptr = is_type_pointer(operand.type);
Type *t = base_type(type_deref(operand.type));
+
switch (t->kind) {
case Type_Basic:
if (t->Basic.kind == Basic_string || t->Basic.kind == Basic_UntypedString) {
@@ -1544,49 +1717,92 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags)
}
break;
+ case Type_BitSet:
+ array_add(&vals, t->BitSet.elem);
+ max_val_count = 1;
+ is_bit_set = true;
+ is_possibly_addressable = false;
+ add_type_info_type(ctx, operand.type);
+ if (build_context.no_rtti && is_type_enum(t->BitSet.elem)) {
+ 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);
+ if (found && are_types_identical(found->type, t->BitSet.elem)) {
+ ERROR_BLOCK();
+ gbString s = expr_to_string(expr);
+ error(rs->vals[0], "'%.*s' shadows a previous declaration which might be ambiguous with 'for (%.*s in %s)'", LIT(name), LIT(name), s);
+ error_line("\tSuggestion: Use a different identifier if iteration is wanted, or surround in parentheses if a normal for loop is wanted\n");
+ gb_string_free(s);
+ }
+ }
+ break;
+
case Type_EnumeratedArray:
- if (is_ptr) use_by_reference_for_value = true;
+ is_possibly_addressable = operand.mode == Addressing_Variable || is_ptr;
array_add(&vals, t->EnumeratedArray.elem);
array_add(&vals, t->EnumeratedArray.index);
break;
case Type_Array:
- if (is_ptr) use_by_reference_for_value = true;
- if (!is_ptr) is_possibly_addressable = operand.mode == Addressing_Variable;
+ is_possibly_addressable = operand.mode == Addressing_Variable || is_ptr;
array_add(&vals, t->Array.elem);
array_add(&vals, t_int);
break;
case Type_DynamicArray:
- if (is_ptr) use_by_reference_for_value = true;
+ is_possibly_addressable = true;
array_add(&vals, t->DynamicArray.elem);
array_add(&vals, t_int);
break;
case Type_Slice:
- if (is_ptr) use_by_reference_for_value = true;
+ is_possibly_addressable = true;
array_add(&vals, t->Slice.elem);
array_add(&vals, t_int);
break;
case Type_Map:
- if (is_ptr) use_by_reference_for_value = true;
+ is_possibly_addressable = true;
is_map = true;
array_add(&vals, t->Map.key);
array_add(&vals, t->Map.value);
if (is_reverse) {
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);
+ if (found && are_types_identical(found->type, t->Map.key)) {
+ ERROR_BLOCK();
+ gbString s = expr_to_string(expr);
+ error(rs->vals[0], "'%.*s' shadows a previous declaration which might be ambiguous with 'for (%.*s in %s)'", LIT(name), LIT(name), s);
+ error_line("\tSuggestion: Use a different identifier if iteration is wanted, or surround in parentheses if a normal for loop is wanted\n");
+ gb_string_free(s);
+ }
+ }
break;
case Type_Tuple:
{
+ is_possibly_addressable = false;
+
isize count = t->Tuple.variables.count;
- if (count < 1 || count > 3) {
+ if (count < 1) {
+ ERROR_BLOCK();
check_not_tuple(ctx, &operand);
- error_line("\tMultiple return valued parameters in a range statement are limited to a maximum of 2 usable values with a trailing boolean for the conditional\n");
+ error_line("\tMultiple return valued parameters in a range statement are limited to a minimum of 1 usable values with a trailing boolean for the conditional, got %td\n", count);
break;
}
+ enum : isize {MAXIMUM_COUNT = 100};
+ if (count > MAXIMUM_COUNT) {
+ ERROR_BLOCK();
+ check_not_tuple(ctx, &operand);
+ error_line("\tMultiple return valued parameters in a range statement are limited to a maximum of %td usable values with a trailing boolean for the conditional, got %td\n", MAXIMUM_COUNT, count);
+ break;
+ }
+
Type *cond_type = t->Tuple.variables[count-1]->type;
if (!is_type_boolean(cond_type)) {
gbString s = type_to_string(cond_type);
@@ -1595,24 +1811,21 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags)
break;
}
+ max_val_count = count;
+
for (Entity *e : t->Tuple.variables) {
array_add(&vals, e->type);
}
- is_possibly_addressable = false;
-
- if (rs->vals.count > 1 && rs->vals[1] != nullptr && count < 3) {
- gbString s = type_to_string(t);
- error(operand.expr, "Expected a 3-valued expression on the rhs, got (%s)", s);
- gb_string_free(s);
- break;
- }
-
- if (rs->vals.count > 0 && rs->vals[0] != nullptr && count < 2) {
- gbString s = type_to_string(t);
- error(operand.expr, "Expected at least a 2-valued expression on the rhs, got (%s)", s);
- gb_string_free(s);
- break;
+ bool do_break = false;
+ for (isize i = rs->vals.count-1; i >= 0; i--) {
+ if (rs->vals[i] != nullptr && count < i+2) {
+ gbString s = type_to_string(t);
+ error(operand.expr, "Expected a %td-valued expression on the rhs, got (%s)", i+2, s);
+ gb_string_free(s);
+ do_break = true;
+ break;
+ }
}
if (is_reverse) {
@@ -1623,8 +1836,12 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags)
case Type_Struct:
if (t->Struct.soa_kind != StructSoa_None) {
+ if (t->Struct.soa_kind == StructSoa_Fixed) {
+ is_possibly_addressable = operand.mode == Addressing_Variable || is_ptr;
+ } else {
+ is_possibly_addressable = true;
+ }
is_soa = true;
- if (is_ptr) use_by_reference_for_value = true;
array_add(&vals, t->Struct.soa_elem);
array_add(&vals, t_int);
}
@@ -1638,11 +1855,13 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags)
defer (gb_string_free(s));
defer (gb_string_free(t));
+ ERROR_BLOCK();
+
error(operand.expr, "Cannot iterate over '%s' of type '%s'", s, t);
if (rs->vals.count == 1) {
Type *t = type_deref(operand.type);
- if (is_type_map(t) || is_type_bit_set(t)) {
+ if (t != NULL && (is_type_map(t) || is_type_bit_set(t))) {
gbString v = expr_to_string(rs->vals[0]);
defer (gb_string_free(v));
error_line("\tSuggestion: place parentheses around the expression\n");
@@ -1687,7 +1906,9 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags)
}
if (found == nullptr) {
entity = alloc_entity_variable(ctx->scope, token, type, EntityState_Resolved);
- entity->flags |= EntityFlag_ForValue;
+ if (!is_range) {
+ entity->flags |= EntityFlag_ForValue;
+ }
entity->flags |= EntityFlag_Value;
entity->identifier = name;
entity->Variable.for_loop_parent_type = type_of_expr(expr);
@@ -1696,12 +1917,9 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags)
if (is_possibly_addressable && i == addressable_index) {
entity->flags &= ~EntityFlag_Value;
} else {
- char const *idx_name = is_map ? "key" : "index";
+ char const *idx_name = is_map ? "key" : (is_bit_set || i == 0) ? "element" : "index";
error(token, "The %s variable '%.*s' cannot be made addressable", idx_name, LIT(str));
}
- } else if (i == addressable_index && use_by_reference_for_value) {
- entity->flags |= EntityFlag_OldForOrSwitchValue;
- entity->flags &= ~EntityFlag_Value;
}
if (is_soa) {
if (i == 0) {
@@ -1719,9 +1937,7 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags)
entity = found;
}
} else {
- gbString s = expr_to_string(lhs[i]);
- error(name, "A variable declaration must be an identifier, got %s", s);
- gb_string_free(s);
+ error_var_decl_identifier(name);
}
if (entity == nullptr) {
@@ -1773,7 +1989,7 @@ gb_internal void check_value_decl_stmt(CheckerContext *ctx, Ast *node, u32 mod_f
for (Ast *name : vd->names) {
Entity *entity = nullptr;
if (name->kind != Ast_Ident) {
- error(name, "A variable declaration must be an identifier");
+ error_var_decl_identifier(name);
} else {
Token token = name->Ident.token;
String str = token.string;
@@ -1813,7 +2029,7 @@ gb_internal void check_value_decl_stmt(CheckerContext *ctx, Ast *node, u32 mod_f
}
if (new_name_count == 0) {
- begin_error_block();
+ ERROR_BLOCK();
error(node, "No new declarations on the left hand side");
bool all_underscore = true;
for (Ast *name : vd->names) {
@@ -1831,7 +2047,6 @@ gb_internal void check_value_decl_stmt(CheckerContext *ctx, Ast *node, u32 mod_f
error_line("\tSuggestion: Try changing the declaration (:=) to an assignment (=)\n");
}
- end_error_block();
}
Type *init_type = nullptr;
@@ -1845,11 +2060,17 @@ gb_internal void check_value_decl_stmt(CheckerContext *ctx, Ast *node, u32 mod_f
gb_string_free(str);
init_type = t_invalid;
}
+ if (init_type == t_invalid && entity_count == 1 && (mod_flags & (Stmt_BreakAllowed|Stmt_FallthroughAllowed))) {
+ Entity *e = entities[0];
+ if (e != nullptr && e->token.string == "default") {
+ warning(e->token, "Did you mean 'case:'?");
+ }
+ }
}
// TODO NOTE(bill): This technically checks things multple times
- AttributeContext ac = make_attribute_context(ctx->foreign_context.link_prefix);
+ AttributeContext ac = make_attribute_context(ctx->foreign_context.link_prefix, ctx->foreign_context.link_suffix);
check_decl_attributes(ctx, vd->attributes, var_decl_attribute, &ac);
for (isize i = 0; i < entity_count; i++) {
@@ -1866,7 +2087,7 @@ gb_internal void check_value_decl_stmt(CheckerContext *ctx, Ast *node, u32 mod_f
e->type = init_type;
e->state = EntityState_Resolved;
}
- ac.link_name = handle_link_name(ctx, e->token, ac.link_name, ac.link_prefix);
+ ac.link_name = handle_link_name(ctx, e->token, ac.link_name, ac.link_prefix, ac.link_suffix);
if (ac.link_name.len > 0) {
e->Variable.link_name = ac.link_name;
@@ -1884,6 +2105,13 @@ gb_internal void check_value_decl_stmt(CheckerContext *ctx, Ast *node, u32 mod_f
}
}
}
+ if (ac.rodata) {
+ if (ac.is_static) {
+ e->Variable.is_rodata = true;
+ } else {
+ error(e->token, "Only global or @(static) variables can have @(rodata) applied");
+ }
+ }
if (ac.thread_local_model != "") {
String name = e->token.string;
if (name == "_") {
@@ -1897,17 +2125,19 @@ gb_internal void check_value_decl_stmt(CheckerContext *ctx, Ast *node, u32 mod_f
e->Variable.thread_local_model = ac.thread_local_model;
}
- if (is_arch_wasm() && e->Variable.thread_local_model.len != 0) {
- // error(e->token, "@(thread_local) is not supported for this target platform");
- }
-
-
if (ac.is_static && ac.thread_local_model != "") {
error(e->token, "The 'static' attribute is not needed if 'thread_local' is applied");
}
}
+ // NOTE(bill): This is to improve error handling for things like `x: [?]T = {...}`
+ Ast *prev_type_hint_expr = ctx->type_hint_expr;
+ ctx->type_hint_expr = vd->type;
+
check_init_variables(ctx, entities, entity_count, vd->values, str_lit("variable declaration"));
+
+ ctx->type_hint_expr = prev_type_hint_expr;
+
check_arity_match(ctx, vd, false);
for (isize i = 0; i < entity_count; i++) {
@@ -1936,7 +2166,7 @@ gb_internal void check_value_decl_stmt(CheckerContext *ctx, Ast *node, u32 mod_f
TokenPos pos = f->token.pos;
Type *this_type = base_type(e->type);
Type *other_type = base_type(f->type);
- if (!are_types_identical(this_type, other_type)) {
+ if (!signature_parameter_similar_enough(this_type, other_type)) {
error(e->token,
"Foreign entity '%.*s' previously declared elsewhere with a different type\n"
"\tat %s",
@@ -2029,13 +2259,13 @@ gb_internal void check_expr_stmt(CheckerContext *ctx, Ast *node) {
}
Ast *expr = strip_or_return_expr(operand.expr);
- if (expr->kind == Ast_CallExpr) {
+ if (expr && expr->kind == Ast_CallExpr) {
BuiltinProcId builtin_id = BuiltinProc_Invalid;
bool do_require = false;
AstCallExpr *ce = &expr->CallExpr;
Type *t = base_type(type_of_expr(ce->proc));
- if (t->kind == Type_Proc) {
+ if (t && t->kind == Type_Proc) {
do_require = t->Proc.require_results;
} else if (check_stmt_internal_builtin_proc_id(ce->proc, &builtin_id)) {
auto const &bp = builtin_procs[builtin_id];
@@ -2043,11 +2273,19 @@ gb_internal void check_expr_stmt(CheckerContext *ctx, Ast *node) {
}
if (do_require) {
gbString expr_str = expr_to_string(ce->proc);
+ defer (gb_string_free(expr_str));
+ if (builtin_id) {
+ String real_name = builtin_procs[builtin_id].name;
+ if (real_name != make_string(cast(u8 const *)expr_str, gb_string_length(expr_str))) {
+ error(node, "'%s' ('%.*s.%.*s') requires that its results must be handled", expr_str,
+ LIT(builtin_proc_pkg_name[builtin_procs[builtin_id].pkg]), LIT(real_name));
+ return;
+ }
+ }
error(node, "'%s' requires that its results must be handled", expr_str);
- gb_string_free(expr_str);
}
return;
- } else if (expr->kind == Ast_SelectorCallExpr) {
+ } else if (expr && expr->kind == Ast_SelectorCallExpr) {
BuiltinProcId builtin_id = BuiltinProc_Invalid;
bool do_require = false;
@@ -2073,6 +2311,9 @@ gb_internal void check_expr_stmt(CheckerContext *ctx, Ast *node) {
}
return;
}
+
+ ERROR_BLOCK();
+
gbString expr_str = expr_to_string(operand.expr);
error(node, "Expression is not used: '%s'", expr_str);
gb_string_free(expr_str);
@@ -2266,29 +2507,6 @@ gb_internal void check_return_stmt(CheckerContext *ctx, Ast *node) {
if (is_type_untyped(o->type)) {
update_untyped_expr_type(ctx, o->expr, e->type, true);
}
-
-
- // 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
- Ast *expr = unparen_expr(o->expr);
- if (expr->kind == Ast_UnaryExpr && expr->UnaryExpr.op.kind == Token_And) {
- Ast *x = unparen_expr(expr->UnaryExpr.expr);
- if (x->kind == Ast_CompoundLit) {
- error(expr, "Cannot return the address to a stack value from a procedure");
- } else if (x->kind == Ast_IndexExpr) {
- Ast *array = x->IndexExpr.expr;
- if (is_type_array_like(type_of_expr(array)) && check_expr_is_stack_variable(array)) {
- gbString t = type_to_string(type_of_expr(array));
- error(expr, "Cannot return the address to an element of stack variable from a procedure, of type %s", t);
- gb_string_free(t);
- }
- } else {
- if (check_expr_is_stack_variable(x)) {
- error(expr, "Cannot return the address to a stack variable from a procedure");
- }
- }
- }
}
}
@@ -2296,16 +2514,73 @@ gb_internal void check_return_stmt(CheckerContext *ctx, Ast *node) {
if (o.expr == nullptr) {
continue;
}
- if (o.expr->kind != Ast_CompoundLit || !is_type_slice(o.type)) {
- continue;
+ Ast *expr = unparen_expr(o.expr);
+ while (expr->kind == Ast_CallExpr && expr->CallExpr.proc->tav.mode == Addressing_Type) {
+ if (expr->CallExpr.args.count != 1) {
+ break;
+ }
+ Ast *arg = expr->CallExpr.args[0];
+ if (arg->kind == Ast_FieldValue || !are_types_identical(arg->tav.type, expr->tav.type)) {
+ break;
+ }
+ expr = unparen_expr(arg);
}
- ast_node(cl, CompoundLit, o.expr);
- if (cl->elems.count == 0) {
- continue;
+
+ 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");
}
- gbString s = type_to_string(o.type);
- error(o.expr, "It is unsafe to return a compound literal of a slice ('%s') with elements from a procedure, as the contents of the slice uses the current stack frame's memory", s);
- gb_string_free(s);
}
}
@@ -2325,6 +2600,25 @@ gb_internal void check_for_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
check_expr(ctx, &o, fs->cond);
if (o.mode != Addressing_Invalid && !is_type_boolean(o.type)) {
error(fs->cond, "Non-boolean condition in 'for' statement");
+ } else {
+ Ast *cond = unparen_expr(o.expr);
+ if (cond && cond->kind == Ast_BinaryExpr &&
+ cond->BinaryExpr.left && cond->BinaryExpr.right &&
+ cond->BinaryExpr.op.kind == Token_GtEq &&
+ type_of_expr(cond->BinaryExpr.left) != nullptr &&
+ is_type_unsigned(type_of_expr(cond->BinaryExpr.left)) &&
+ cond->BinaryExpr.right->tav.value.kind == ExactValue_Integer &&
+ is_exact_value_zero(cond->BinaryExpr.right->tav.value)) {
+ warning(cond, "Expression is always true since unsigned numbers are always >= 0");
+ } else if (cond && cond->kind == Ast_BinaryExpr &&
+ cond->BinaryExpr.left && cond->BinaryExpr.right &&
+ cond->BinaryExpr.op.kind == Token_LtEq &&
+ type_of_expr(cond->BinaryExpr.right) != nullptr &&
+ is_type_unsigned(type_of_expr(cond->BinaryExpr.right)) &&
+ cond->BinaryExpr.left->tav.value.kind == ExactValue_Integer &&
+ is_exact_value_zero(cond->BinaryExpr.left->tav.value)) {
+ warning(cond, "Expression is always true since unsigned numbers are always >= 0");
+ }
}
}
if (fs->post != nullptr) {
@@ -2445,6 +2739,7 @@ gb_internal void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags)
error(bs->label, "A branch statement's label name must be an identifier");
return;
}
+
Ast *ident = bs->label;
String name = ident->Ident.token.string;
Operand o = {};
@@ -2476,6 +2771,10 @@ gb_internal void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags)
break;
}
+
+ if (ctx->in_defer) {
+ error(bs->label, "A labelled '%.*s' cannot be used within a 'defer'", LIT(token.string));
+ }
}
case_end;
diff --git a/src/check_type.cpp b/src/check_type.cpp
index d66b196bc..44108ccbe 100644
--- a/src/check_type.cpp
+++ b/src/check_type.cpp
@@ -1,4 +1,6 @@
gb_internal ParameterValue handle_parameter_value(CheckerContext *ctx, Type *in_type, Type **out_type_, Ast *expr, bool allow_caller_location);
+gb_internal Type *determine_type_from_polymorphic(CheckerContext *ctx, Type *poly_type, Operand const &operand);
+gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_params, bool *is_variadic_, isize *variadic_index_, bool *success_, isize *specialization_count_, Array<Operand> const *operands);
gb_internal void populate_using_array_index(CheckerContext *ctx, Ast *node, AstField *field, Type *t, String name, i32 idx) {
t = base_type(t);
@@ -17,20 +19,23 @@ gb_internal void populate_using_array_index(CheckerContext *ctx, Ast *node, AstF
}
} else {
Token tok = make_token_ident(name);
- if (field->names.count > 0) {
- tok.pos = ast_token(field->names[0]).pos;
- } else {
- tok.pos = ast_token(field->type).pos;
+ if (field) {
+ if (field->names.count > 0) {
+ tok.pos = ast_token(field->names[0]).pos;
+ } else {
+ tok.pos = ast_token(field->type).pos;
+ }
}
Entity *f = alloc_entity_array_elem(nullptr, tok, t->Array.elem, idx);
add_entity(ctx, ctx->scope, nullptr, f);
}
}
-gb_internal void populate_using_entity_scope(CheckerContext *ctx, Ast *node, AstField *field, Type *t) {
+gb_internal void populate_using_entity_scope(CheckerContext *ctx, Ast *node, AstField *field, Type *t, isize level) {
if (t == nullptr) {
return;
}
+ Type *original_type = t;
t = base_type(type_deref(t));
gbString str = nullptr;
defer (gb_string_free(str));
@@ -44,16 +49,18 @@ gb_internal void populate_using_entity_scope(CheckerContext *ctx, Ast *node, Ast
String name = f->token.string;
Entity *e = scope_lookup_current(ctx->scope, name);
if (e != nullptr && name != "_") {
+ gbString ot = type_to_string(original_type);
// TODO(bill): Better type error
if (str != nullptr) {
- error(e->token, "'%.*s' is already declared in '%s'", LIT(name), str);
+ error(e->token, "'%.*s' is already declared in '%s', through 'using' from '%s'", LIT(name), str, ot);
} else {
- error(e->token, "'%.*s' is already declared", LIT(name));
+ error(e->token, "'%.*s' is already declared, through 'using' from '%s'", LIT(name), ot);
}
+ gb_string_free(ot);
} else {
add_entity(ctx, ctx->scope, nullptr, f);
if (f->flags & EntityFlag_Using) {
- populate_using_entity_scope(ctx, node, field, f->type);
+ populate_using_entity_scope(ctx, node, field, f->type, level+1);
}
}
}
@@ -87,6 +94,8 @@ gb_internal bool does_field_type_allow_using(Type *t) {
return true;
} else if (is_type_array(t)) {
return t->Array.count <= 4;
+ } else if (is_type_bit_field(t)) {
+ return true;
}
return false;
}
@@ -184,9 +193,10 @@ gb_internal void check_struct_fields(CheckerContext *ctx, Ast *node, Slice<Entit
if (is_using && p->names.count > 0) {
Type *first_type = fields_array[fields_array.count-1]->type;
+ bool soa_ptr = is_type_soa_pointer(first_type);
Type *t = base_type(type_deref(first_type));
- if (!does_field_type_allow_using(t) &&
+ if ((soa_ptr || !does_field_type_allow_using(t)) &&
p->names.count >= 1 &&
p->names[0]->kind == Ast_Ident) {
Token name_token = p->names[0]->Ident.token;
@@ -196,7 +206,7 @@ gb_internal void check_struct_fields(CheckerContext *ctx, Ast *node, Slice<Entit
continue;
}
- populate_using_entity_scope(ctx, node, p, type);
+ populate_using_entity_scope(ctx, node, p, type, 1);
}
if (is_subtype && p->names.count > 0) {
@@ -219,13 +229,13 @@ gb_internal void check_struct_fields(CheckerContext *ctx, Ast *node, Slice<Entit
}
-gb_internal bool check_custom_align(CheckerContext *ctx, Ast *node, i64 *align_) {
+gb_internal bool check_custom_align(CheckerContext *ctx, Ast *node, i64 *align_, char const *msg) {
GB_ASSERT(align_ != nullptr);
Operand o = {};
check_expr(ctx, &o, node);
if (o.mode != Addressing_Constant) {
if (o.mode != Addressing_Invalid) {
- error(node, "#align must be a constant");
+ error(node, "#%s must be a constant", msg);
}
return false;
}
@@ -237,13 +247,13 @@ gb_internal bool check_custom_align(CheckerContext *ctx, Ast *node, i64 *align_)
if (v.used > 1) {
gbAllocator a = heap_allocator();
String str = big_int_to_string(a, &v);
- error(node, "#align too large, %.*s", LIT(str));
+ error(node, "#%s too large, %.*s", msg, LIT(str));
gb_free(a, str.text);
return false;
}
i64 align = big_int_to_i64(&v);
if (align < 1 || !gb_is_power_of_two(cast(isize)align)) {
- error(node, "#align must be a power of 2, got %lld", align);
+ error(node, "#%s must be a power of 2, got %lld", msg, align);
return false;
}
*align_ = align;
@@ -251,89 +261,26 @@ gb_internal bool check_custom_align(CheckerContext *ctx, Ast *node, i64 *align_)
}
}
- error(node, "#align must be an integer");
+ error(node, "#%s must be an integer", msg);
return false;
}
-gb_internal Entity *find_polymorphic_record_entity(CheckerContext *ctx, Type *original_type, isize param_count, Array<Operand> const &ordered_operands, bool *failure) {
- rw_mutex_shared_lock(&ctx->info->gen_types_mutex); // @@global
-
- auto *found_gen_types = map_get(&ctx->info->gen_types, original_type);
- if (found_gen_types == nullptr) {
- rw_mutex_shared_unlock(&ctx->info->gen_types_mutex); // @@global
- return nullptr;
- }
-
- rw_mutex_shared_lock(&found_gen_types->mutex); // @@local
- defer (rw_mutex_shared_unlock(&found_gen_types->mutex)); // @@local
-
- rw_mutex_shared_unlock(&ctx->info->gen_types_mutex); // @@global
-
- for (Entity *e : found_gen_types->types) {
- Type *t = base_type(e->type);
- TypeTuple *tuple = nullptr;
- switch (t->kind) {
- case Type_Struct:
- if (t->Struct.polymorphic_params) {
- tuple = &t->Struct.polymorphic_params->Tuple;
- }
- break;
- case Type_Union:
- if (t->Union.polymorphic_params) {
- tuple = &t->Union.polymorphic_params->Tuple;
- }
- break;
- }
- GB_ASSERT_MSG(tuple != nullptr, "%s :: %s", type_to_string(e->type), type_to_string(t));
- GB_ASSERT(param_count == tuple->variables.count);
-
- bool skip = false;
-
- for (isize j = 0; j < param_count; j++) {
- Entity *p = tuple->variables[j];
- Operand o = {};
- if (j < ordered_operands.count) {
- o = ordered_operands[j];
- }
- if (o.expr == nullptr) {
- continue;
- }
- Entity *oe = entity_of_node(o.expr);
- if (p == oe) {
- // NOTE(bill): This is the same type, make sure that it will be be same thing and use that
- // Saves on a lot of checking too below
- continue;
- }
+gb_internal GenTypesData *ensure_polymorphic_record_entity_has_gen_types(CheckerContext *ctx, Type *original_type) {
+ mutex_lock(&ctx->info->gen_types_mutex); // @@global
- if (p->kind == Entity_TypeName) {
- if (is_type_polymorphic(o.type)) {
- // NOTE(bill): Do not add polymorphic version to the gen_types
- skip = true;
- break;
- }
- if (!are_types_identical(o.type, p->type)) {
- skip = true;
- break;
- }
- } else if (p->kind == Entity_Constant) {
- if (!compare_exact_values(Token_CmpEq, o.value, p->Constant.value)) {
- skip = true;
- break;
- }
- if (!are_types_identical(o.type, p->type)) {
- skip = true;
- break;
- }
- } else {
- GB_PANIC("Unknown entity kind");
- }
- }
- if (!skip) {
- return e;
- }
+ GenTypesData *found_gen_types = nullptr;
+ auto *found_gen_types_ptr = map_get(&ctx->info->gen_types, original_type);
+ if (found_gen_types_ptr == 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);
}
- return nullptr;
+ found_gen_types = *found_gen_types_ptr;
+ GB_ASSERT(found_gen_types != nullptr);
+ mutex_unlock(&ctx->info->gen_types_mutex); // @@global
+ return found_gen_types;
}
@@ -363,19 +310,16 @@ gb_internal void add_polymorphic_record_entity(CheckerContext *ctx, Ast *node, T
// TODO(bill): Is this even correct? Or should the metadata be copied?
e->TypeName.objc_metadata = original_type->Named.type_name->TypeName.objc_metadata;
- rw_mutex_lock(&ctx->info->gen_types_mutex);
- auto *found_gen_types = map_get(&ctx->info->gen_types, original_type);
- if (found_gen_types) {
- rw_mutex_lock(&found_gen_types->mutex);
- array_add(&found_gen_types->types, e);
- rw_mutex_unlock(&found_gen_types->mutex);
- } else {
- GenTypesData gen_types = {};
- gen_types.types = array_make<Entity *>(heap_allocator());
- array_add(&gen_types.types, e);
- map_set(&ctx->info->gen_types, original_type, gen_types);
+ auto *found_gen_types = ensure_polymorphic_record_entity_has_gen_types(ctx, original_type);
+ mutex_lock(&found_gen_types->mutex);
+ defer (mutex_unlock(&found_gen_types->mutex));
+
+ for (Entity *prev : found_gen_types->types) {
+ if (prev == e) {
+ return;
+ }
}
- rw_mutex_unlock(&ctx->info->gen_types_mutex);
+ array_add(&found_gen_types->types, e);
}
@@ -391,9 +335,8 @@ bool check_constant_parameter_value(Type *type, Ast *expr) {
gb_internal Type *check_record_polymorphic_params(CheckerContext *ctx, Ast *polymorphic_params,
bool *is_polymorphic_,
- Ast *node, Array<Operand> *poly_operands) {
+ Array<Operand> *poly_operands) {
Type *polymorphic_params_type = nullptr;
- bool can_check_fields = true;
GB_ASSERT(is_polymorphic_ != nullptr);
if (polymorphic_params == nullptr) {
@@ -403,6 +346,17 @@ gb_internal Type *check_record_polymorphic_params(CheckerContext *ctx, Ast *poly
return polymorphic_params_type;
}
+
+ // bool is_variadic = false;
+ // isize variadic_index = 0;
+ // bool success = false;
+ // isize specialization_count = 0;
+ // polymorphic_params_type = check_get_params(ctx, ctx->scope, polymorphic_params, &is_variadic, &variadic_index, &success, &specialization_count, poly_operands);
+ // if (success) {
+ // return nullptr;
+ // }
+
+ bool can_check_fields = true;
ast_node(field_list, FieldList, polymorphic_params);
Slice<Ast *> params = field_list->list;
if (params.count != 0) {
@@ -417,17 +371,20 @@ gb_internal Type *check_record_polymorphic_params(CheckerContext *ctx, Ast *poly
auto entities = array_make<Entity *>(permanent_allocator(), 0, variable_count);
+ i32 field_group_index = -1;
for_array(i, params) {
Ast *param = params[i];
if (param->kind != Ast_Field) {
continue;
}
+ field_group_index += 1;
ast_node(p, Field, param);
Ast *type_expr = p->type;
Ast *default_value = unparen_expr(p->default_value);
Type *type = nullptr;
bool is_type_param = false;
bool is_type_polymorphic_type = false;
+ Type *specialization = nullptr;
if (type_expr == nullptr && default_value == nullptr) {
error(param, "Expected a type for this parameter");
continue;
@@ -440,7 +397,6 @@ gb_internal Type *check_record_polymorphic_params(CheckerContext *ctx, Ast *poly
}
if (type_expr->kind == Ast_TypeidType) {
is_type_param = true;
- Type *specialization = nullptr;
if (type_expr->TypeidType.specialization != nullptr) {
Ast *s = type_expr->TypeidType.specialization;
specialization = check_type(ctx, s);
@@ -481,7 +437,7 @@ gb_internal Type *check_record_polymorphic_params(CheckerContext *ctx, Ast *poly
type = t_invalid;
}
- if (is_type_polymorphic_type) {
+ if (is_type_polymorphic_type && !is_type_proc(type)) {
gbString str = type_to_string(type);
error(params[i], "Parameter types cannot be polymorphic, got %s", str);
gb_string_free(str);
@@ -518,18 +474,32 @@ gb_internal Type *check_record_polymorphic_params(CheckerContext *ctx, Ast *poly
if (is_type_polymorphic(base_type(operand.type))) {
*is_polymorphic_ = true;
can_check_fields = false;
+ } else if (specialization &&
+ !check_type_specialization_to(ctx, specialization, operand.type, false, /*modify_type*/true)) {
+ if (!ctx->no_polymorphic_errors) {
+ gbString t = type_to_string(operand.type);
+ gbString s = type_to_string(specialization);
+ error(operand.expr, "Cannot convert type '%s' to the specialization '%s'", t, s);
+ gb_string_free(s);
+ gb_string_free(t);
+ }
}
e = alloc_entity_type_name(scope, token, operand.type);
e->TypeName.is_type_alias = true;
e->flags |= EntityFlag_PolyConst;
} else {
- if (is_type_polymorphic(base_type(operand.type))) {
+ Type *t = operand.type;
+ if (is_type_proc(type)) {
+ t = determine_type_from_polymorphic(ctx, type, operand);
+ }
+ if (is_type_polymorphic(base_type(t))) {
*is_polymorphic_ = true;
can_check_fields = false;
}
if (e == nullptr) {
- e = alloc_entity_constant(scope, token, operand.type, operand.value);
+ e = alloc_entity_const_param(scope, token, t, operand.value, is_type_polymorphic(t));
e->Constant.param_value = param_value;
+ e->Constant.field_group_index = field_group_index;
}
}
} else {
@@ -538,7 +508,8 @@ gb_internal Type *check_record_polymorphic_params(CheckerContext *ctx, Ast *poly
e->TypeName.is_type_alias = true;
e->flags |= EntityFlag_PolyConst;
} else {
- e = alloc_entity_constant(scope, token, type, param_value.value);
+ e = alloc_entity_const_param(scope, token, type, param_value.value, is_type_polymorphic(type));
+ e->Constant.field_group_index = field_group_index;
e->Constant.param_value = param_value;
}
}
@@ -559,7 +530,6 @@ gb_internal Type *check_record_polymorphic_params(CheckerContext *ctx, Ast *poly
if (!*is_polymorphic_) {
*is_polymorphic_ = polymorphic_params != nullptr && poly_operands == nullptr;
}
-
return polymorphic_params_type;
}
@@ -591,6 +561,61 @@ gb_internal bool check_record_poly_operand_specialization(CheckerContext *ctx, T
return true;
}
+gb_internal Entity *find_polymorphic_record_entity(GenTypesData *found_gen_types, isize param_count, Array<Operand> const &ordered_operands) {
+ for (Entity *e : found_gen_types->types) {
+ Type *t = base_type(e->type);
+ TypeTuple *tuple = get_record_polymorphic_params(t);
+ GB_ASSERT_MSG(tuple != nullptr, "%s :: %s", type_to_string(e->type), type_to_string(t));
+ GB_ASSERT(param_count == tuple->variables.count);
+
+ bool skip = false;
+
+ for (isize j = 0; j < param_count; j++) {
+ Entity *p = tuple->variables[j];
+ Operand o = {};
+ if (j < ordered_operands.count) {
+ o = ordered_operands[j];
+ }
+ if (o.expr == nullptr) {
+ continue;
+ }
+ Entity *oe = entity_of_node(o.expr);
+ if (p == oe) {
+ // NOTE(bill): This is the same type, make sure that it will be be same thing and use that
+ // Saves on a lot of checking too below
+ continue;
+ }
+
+ if (p->kind == Entity_TypeName) {
+ if (is_type_polymorphic(o.type)) {
+ // NOTE(bill): Do not add polymorphic version to the gen_types
+ skip = true;
+ break;
+ }
+ if (!are_types_identical(o.type, p->type)) {
+ skip = true;
+ break;
+ }
+ } else if (p->kind == Entity_Constant) {
+ if (!compare_exact_values(Token_CmpEq, o.value, p->Constant.value)) {
+ skip = true;
+ break;
+ }
+ if (!are_types_identical(o.type, p->type)) {
+ skip = true;
+ break;
+ }
+ } else {
+ GB_PANIC("Unknown entity kind");
+ }
+ }
+ if (!skip) {
+ return e;
+ }
+ }
+ return nullptr;
+};
+
gb_internal void check_struct_type(CheckerContext *ctx, Type *struct_type, Ast *node, Array<Operand> *poly_operands, Type *named_type, Type *original_type_for_poly) {
GB_ASSERT(is_type_struct(struct_type));
@@ -613,22 +638,22 @@ gb_internal void check_struct_type(CheckerContext *ctx, Type *struct_type, Ast *
scope_reserve(ctx->scope, min_field_count);
- rw_mutex_lock(&struct_type->Struct.fields_mutex);
- defer (rw_mutex_unlock(&struct_type->Struct.fields_mutex));
-
if (st->is_raw_union && min_field_count > 1) {
struct_type->Struct.is_raw_union = true;
context = str_lit("struct #raw_union");
}
+ 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,
- node, poly_operands
+ poly_operands
);
+ wait_signal_set(&struct_type->Struct.polymorphic_wait_signal);
+
struct_type->Struct.is_poly_specialized = check_record_poly_operand_specialization(ctx, struct_type, poly_operands, &struct_type->Struct.is_polymorphic);
if (original_type_for_poly) {
GB_ASSERT(named_type != nullptr);
@@ -643,30 +668,63 @@ gb_internal void check_struct_type(CheckerContext *ctx, Type *struct_type, Ast *
gb_unused(where_clause_ok);
}
check_struct_fields(ctx, node, &struct_type->Struct.fields, &struct_type->Struct.tags, st->fields, min_field_count, struct_type, context);
- }
-
- if (st->align != nullptr) {
- if (st->is_packed) {
- syntax_error(st->align, "'#align' cannot be applied with '#packed'");
- return;
- }
- i64 custom_align = 1;
- if (check_custom_align(ctx, st->align, &custom_align)) {
- struct_type->Struct.custom_align = custom_align;
- }
- }
+ wait_signal_set(&struct_type->Struct.fields_wait_signal);
+ }
+
+#define ST_ALIGN(_name) if (st->_name != nullptr) { \
+ if (st->is_packed) { \
+ error(st->_name, "'#%s' cannot be applied with '#packed'", #_name); \
+ return; \
+ } \
+ i64 align = 1; \
+ if (check_custom_align(ctx, st->_name, &align, #_name)) { \
+ struct_type->Struct.custom_##_name = align; \
+ } \
+ }
+
+ ST_ALIGN(min_field_align);
+ ST_ALIGN(max_field_align);
+ ST_ALIGN(align);
+ if (struct_type->Struct.custom_align < struct_type->Struct.custom_min_field_align) {
+ error(st->align, "#align(%lld) is defined to be less than #min_field_align(%lld)",
+ cast(long long)struct_type->Struct.custom_align,
+ cast(long long)struct_type->Struct.custom_min_field_align);
+ }
+ if (struct_type->Struct.custom_max_field_align != 0 &&
+ struct_type->Struct.custom_align > struct_type->Struct.custom_max_field_align) {
+ error(st->align, "#align(%lld) is defined to be greater than #max_field_align(%lld)",
+ cast(long long)struct_type->Struct.custom_align,
+ cast(long long)struct_type->Struct.custom_max_field_align);
+ }
+ if (struct_type->Struct.custom_max_field_align != 0 &&
+ struct_type->Struct.custom_min_field_align > struct_type->Struct.custom_max_field_align) {
+ error(st->align, "#min_field_align(%lld) is defined to be greater than #max_field_align(%lld)",
+ cast(long long)struct_type->Struct.custom_min_field_align,
+ cast(long long)struct_type->Struct.custom_max_field_align);
+
+ i64 a = gb_min(struct_type->Struct.custom_min_field_align, struct_type->Struct.custom_max_field_align);
+ i64 b = gb_max(struct_type->Struct.custom_min_field_align, struct_type->Struct.custom_max_field_align);
+ // NOTE(bill): sort them to keep code consistent
+ struct_type->Struct.custom_min_field_align = a;
+ struct_type->Struct.custom_max_field_align = b;
+ }
+
+#undef ST_ALIGN
}
gb_internal void check_union_type(CheckerContext *ctx, Type *union_type, Ast *node, Array<Operand> *poly_operands, Type *named_type, Type *original_type_for_poly) {
GB_ASSERT(is_type_union(union_type));
ast_node(ut, UnionType, node);
+ union_type->Union.node = node;
union_type->Union.scope = ctx->scope;
union_type->Union.polymorphic_params = check_record_polymorphic_params(
ctx, ut->polymorphic_params,
&union_type->Union.is_polymorphic,
- node, poly_operands
+ poly_operands
);
+ wait_signal_set(&union_type->Union.polymorphic_wait_signal);
+
union_type->Union.is_poly_specialized = check_record_poly_operand_specialization(ctx, union_type, poly_operands, &union_type->Union.is_polymorphic);
if (original_type_for_poly) {
GB_ASSERT(named_type != nullptr);
@@ -701,7 +759,7 @@ gb_internal void check_union_type(CheckerContext *ctx, Type *union_type, Ast *no
gb_string_free(str);
} else {
for_array(j, variants) {
- if (are_types_identical(t, variants[j])) {
+ if (union_variant_index_types_equal(t, variants[j])) {
ok = false;
ERROR_BLOCK();
gbString str = type_to_string(t);
@@ -739,14 +797,14 @@ gb_internal void check_union_type(CheckerContext *ctx, Type *union_type, Ast *no
}
}
if (variants.count < 2) {
- error(ut->align, "A union with #no_nil must have at least 2 variants");
+ error(node, "A union with #no_nil must have at least 2 variants");
}
break;
}
if (ut->align != nullptr) {
i64 custom_align = 1;
- if (check_custom_align(ctx, ut->align, &custom_align)) {
+ if (check_custom_align(ctx, ut->align, &custom_align, "align")) {
if (variants.count == 0) {
error(ut->align, "An empty union cannot have a custom alignment");
} else {
@@ -760,12 +818,15 @@ gb_internal void check_enum_type(CheckerContext *ctx, Type *enum_type, Type *nam
ast_node(et, EnumType, node);
GB_ASSERT(is_type_enum(enum_type));
+ enum_type->Enum.base_type = t_int;
+ enum_type->Enum.scope = ctx->scope;
+
Type *base_type = t_int;
- if (et->base_type != nullptr) {
+ if (unparen_expr(et->base_type) != nullptr) {
base_type = check_type(ctx, et->base_type);
}
- if (base_type == nullptr || !is_type_integer(base_type)) {
+ if (base_type == nullptr || base_type == t_invalid || !is_type_integer(base_type)) {
error(node, "Base type for enumeration must be an integer");
return;
}
@@ -781,7 +842,6 @@ gb_internal void check_enum_type(CheckerContext *ctx, Type *enum_type, Type *nam
// NOTE(bill): Must be up here for the 'check_init_constant' system
enum_type->Enum.base_type = base_type;
- enum_type->Enum.scope = ctx->scope;
auto fields = array_make<Entity *>(permanent_allocator(), 0, et->fields.count);
@@ -898,6 +958,237 @@ gb_internal void check_enum_type(CheckerContext *ctx, Type *enum_type, Type *nam
enum_type->Enum.max_value_index = max_value_index;
}
+
+gb_internal void check_bit_field_type(CheckerContext *ctx, Type *bit_field_type, Type *named_type, Ast *node) {
+ ast_node(bf, BitFieldType, node);
+ GB_ASSERT(is_type_bit_field(bit_field_type));
+
+ Type *backing_type = check_type(ctx, bf->backing_type);
+
+ bit_field_type->BitField.backing_type = backing_type ? backing_type : t_u8;
+ bit_field_type->BitField.scope = ctx->scope;
+
+ if (backing_type == nullptr) {
+ error(bf->backing_type, "Backing type for a bit_field must be an integer or an array of an integer");
+ return;
+ }
+ if (!is_valid_bit_field_backing_type(backing_type)) {
+ error(bf->backing_type, "Backing type for a bit_field must be an integer or an array of an integer");
+ return;
+ }
+
+ auto fields = array_make<Entity *>(permanent_allocator(), 0, bf->fields.count);
+ auto bit_sizes = array_make<u8> (permanent_allocator(), 0, bf->fields.count);
+ auto tags = array_make<String> (permanent_allocator(), 0, bf->fields.count);
+
+ u64 maximum_bit_size = 8 * type_size_of(backing_type);
+ u64 total_bit_size = 0;
+
+ for_array(i, bf->fields) {
+ i32 field_src_index = cast(i32)i;
+ Ast *field = bf->fields[i];
+ if (field->kind != Ast_BitFieldField) {
+ error(field, "Invalid AST for a bit_field");
+ continue;
+ }
+ ast_node(f, BitFieldField, field);
+ if (f->name == nullptr || f->name->kind != Ast_Ident) {
+ error(field, "A bit_field's field name must be an identifier");
+ continue;
+ }
+ CommentGroup *docs = f->docs;
+ CommentGroup *comment = f->comment;
+
+ String name = f->name->Ident.token.string;
+
+ if (f->type == nullptr) {
+ error(field, "A bit_field's field must have a type");
+ continue;
+ }
+
+ Type *type = check_type(ctx, f->type);
+ if (type_size_of(type) > 8) {
+ error(f->type, "The type of a bit_field's field must be <= 8 bytes, got %lld", cast(long long)type_size_of(type));
+ }
+
+ if (is_type_untyped(type)) {
+ gbString s = type_to_string(type);
+ error(f->type, "The type of a bit_field's field must be a typed integer, enum, or boolean, got %s", s);
+ gb_string_free(s);
+ } else if (!(is_type_integer(type) || is_type_enum(type) || is_type_boolean(type))) {
+ gbString s = type_to_string(type);
+ error(f->type, "The type of a bit_field's field must be an integer, enum, or boolean, got %s", s);
+ gb_string_free(s);
+ }
+
+ if (f->bit_size == nullptr) {
+ error(field, "A bit_field's field must have a specified bit size");
+ continue;
+ }
+
+
+ Operand o = {};
+ check_expr(ctx, &o, f->bit_size);
+ if (o.mode != Addressing_Constant) {
+ error(f->bit_size, "A bit_field's specified bit size must be a constant");
+ o.mode = Addressing_Invalid;
+ }
+ if (o.value.kind == ExactValue_Float) {
+ o.value = exact_value_to_integer(o.value);
+ }
+ if (f->bit_size->kind == Ast_BinaryExpr && f->bit_size->BinaryExpr.op.kind == Token_Or) {
+ gbString s = expr_to_string(f->bit_size);
+ error(f->bit_size, "Wrap the expression in parentheses, e.g. (%s)", s);
+ gb_string_free(s);
+ }
+
+ ExactValue bit_size = o.value;
+
+ if (bit_size.kind != ExactValue_Integer) {
+ gbString s = expr_to_string(f->bit_size);
+ error(f->bit_size, "Expected an integer constant value for the specified bit size, got %s", s);
+ gb_string_free(s);
+ }
+
+ if (scope_lookup_current(ctx->scope, name) != nullptr) {
+ error(f->name, "'%.*s' is already declared in this bit_field", LIT(name));
+ } else {
+ i64 bit_size_i64 = exact_value_to_i64(bit_size);
+ u8 bit_size_u8 = 0;
+ if (bit_size_i64 <= 0) {
+ error(f->bit_size, "A bit_field's specified bit size cannot be <= 0, got %lld", cast(long long)bit_size_i64);
+ bit_size_i64 = 1;
+ }
+ if (bit_size_i64 > 64) {
+ error(f->bit_size, "A bit_field's specified bit size cannot exceed 64 bits, got %lld", cast(long long)bit_size_i64);
+ bit_size_i64 = 64;
+ }
+ i64 sz = 8*type_size_of(type);
+ if (bit_size_i64 > sz) {
+ error(f->bit_size, "A bit_field's specified bit size cannot exceed its type, got %lld, expect <=%lld", cast(long long)bit_size_i64, cast(long long)sz);
+ bit_size_i64 = sz;
+ }
+
+ bit_size_u8 = cast(u8)bit_size_i64;
+
+ Entity *e = alloc_entity_field(ctx->scope, f->name->Ident.token, type, false, field_src_index);
+ e->Variable.docs = docs;
+ e->Variable.comment = comment;
+ e->Variable.bit_field_bit_size = bit_size_u8;
+ e->flags |= EntityFlag_BitFieldField;
+
+ add_entity(ctx, ctx->scope, nullptr, e);
+ array_add(&fields, e);
+ array_add(&bit_sizes, bit_size_u8);
+
+ String tag = f->tag.string;
+ if (tag.len != 0 && !unquote_string(permanent_allocator(), &tag, 0, tag.text[0] == '`')) {
+ error(f->tag, "Invalid string literal");
+ tag = {};
+ }
+ array_add(&tags, tag);
+
+ add_entity_use(ctx, field, e);
+
+ total_bit_size += bit_size_u8;
+ }
+ }
+
+ GB_ASSERT(fields.count <= bf->fields.count);
+
+ auto bit_offsets = slice_make<i64>(permanent_allocator(), fields.count);
+ i64 curr_offset = 0;
+ for_array(i, bit_sizes) {
+ bit_offsets[i] = curr_offset;
+ curr_offset += cast(i64)bit_sizes[i];
+ }
+
+ if (total_bit_size > maximum_bit_size) {
+ gbString s = type_to_string(backing_type);
+ error(node, "The total bit size of a bit_field's fields (%llu) must fit into its backing type's (%s) bit size of %llu",
+ cast(unsigned long long)total_bit_size,
+ s,
+ cast(unsigned long long)maximum_bit_size);
+ gb_string_free(s);
+ }
+
+ enum EndianKind {
+ Endian_Unknown,
+ Endian_Native,
+ Endian_Little,
+ Endian_Big,
+ };
+ auto const &determine_endian_kind = [](Type *type) -> EndianKind {
+ if (is_type_boolean(type)) {
+ // NOTE(bill): it doesn't matter, and when it does,
+ // that api is absolutely stupid
+ return Endian_Unknown;
+ } else if (type_size_of(type) < 2) {
+ return Endian_Unknown;
+ } else if (is_type_endian_specific(type)) {
+ if (is_type_endian_little(type)) {
+ return Endian_Little;
+ } else {
+ return Endian_Big;
+ }
+ }
+ return Endian_Native;
+ };
+
+ Type *backing_type_elem = core_array_type(backing_type);
+ i64 backing_type_elem_size = type_size_of(backing_type_elem);
+ EndianKind backing_type_endian_kind = determine_endian_kind(backing_type_elem);
+ EndianKind endian_kind = Endian_Unknown;
+ for (Entity *f : fields) {
+ EndianKind field_kind = determine_endian_kind(f->type);
+ i64 field_size = type_size_of(f->type);
+
+ if (field_kind && backing_type_endian_kind != field_kind && field_size > 1 && backing_type_elem_size > 1) {
+ error(f->token, "All 'bit_field' field types must match the same endian kind as the backing type, i.e. all native, all little, or all big");
+ }
+
+ if (endian_kind == Endian_Unknown) {
+ endian_kind = field_kind;
+ } else if (field_kind && endian_kind != field_kind && field_size > 1) {
+ error(f->token, "All 'bit_field' field types must be of the same endian variety, i.e. all native, all little, or all big");
+ }
+ }
+
+
+
+ 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;
+ if (all_ones && all_booleans) {
+ for_array(i, bit_sizes) {
+ all_ones = bit_sizes[i] == 1;
+ if (!all_ones) {
+ break;
+ }
+ all_booleans = is_type_boolean(fields[i]->type);
+ if (!all_booleans) {
+ break;
+ }
+ }
+ if (all_ones && all_booleans) {
+ if (ast_file_vet_style(ctx->file)) {
+ char const *msg = "This 'bit_field' is better expressed as a 'bit_set' since all of the fields are booleans, of 1-bit in size, and the backing type is an integer (-vet-style)";
+ error(node, msg);
+ } else {
+ char const *msg = "This 'bit_field' might be better expressed as a 'bit_set' since all of the fields are booleans, of 1-bit in size, and the backing type is an integer";
+ warning(node, msg);
+ }
+ }
+ }
+ }
+
+
+ bit_field_type->BitField.fields = slice_from_array(fields);
+ bit_field_type->BitField.bit_sizes = slice_from_array(bit_sizes);
+ bit_field_type->BitField.bit_offsets = bit_offsets;
+ bit_field_type->BitField.tags = tags.data;
+}
+
gb_internal bool is_type_valid_bit_set_range(Type *t) {
if (is_type_integer(t)) {
return true;
@@ -980,11 +1271,14 @@ gb_internal void check_bit_set_type(CheckerContext *c, Type *type, Type *named_t
Type *t = default_type(lhs.type);
if (bs->underlying != nullptr) {
Type *u = check_type(c, bs->underlying);
+ // if (!is_valid_bit_field_backing_type(u)) {
if (!is_type_integer(u)) {
gbString ts = type_to_string(u);
error(bs->underlying, "Expected an underlying integer for the bit set, got %s", ts);
gb_string_free(ts);
- return;
+ if (!is_valid_bit_field_backing_type(u)) {
+ return;
+ }
}
type->BitSet.underlying = u;
}
@@ -1047,7 +1341,7 @@ gb_internal void check_bit_set_type(CheckerContext *c, Type *type, Type *named_t
}
if (!is_valid) {
if (actual_lower != lower) {
- error(bs->elem, "bit_set range is greater than %lld bits, %lld bits are required (internal the lower changed was changed 0 as an underlying type was set)", bits, bits_required);
+ error(bs->elem, "bit_set range is greater than %lld bits, %lld bits are required (internally the lower bound was changed to 0 as an underlying type was set)", bits, bits_required);
} else {
error(bs->elem, "bit_set range is greater than %lld bits, %lld bits are required", bits, bits_required);
}
@@ -1117,7 +1411,7 @@ gb_internal void check_bit_set_type(CheckerContext *c, Type *type, Type *named_t
if (upper - lower >= bits) {
i64 bits_required = upper-lower+1;
if (lower_changed) {
- error(bs->elem, "bit_set range is greater than %lld bits, %lld bits are required (internal the lower changed was changed 0 as an underlying type was set)", bits, bits_required);
+ error(bs->elem, "bit_set range is greater than %lld bits, %lld bits are required (internally the lower bound was changed to 0 as an underlying type was set)", bits, bits_required);
} else {
error(bs->elem, "bit_set range is greater than %lld bits, %lld bits are required", bits, bits_required);
}
@@ -1153,6 +1447,10 @@ gb_internal bool check_type_specialization_to(CheckerContext *ctx, Type *special
bool can_convert = check_cast_internal(ctx, &o, specialization);
return can_convert;
} else if (t->kind == Type_Struct) {
+ if (t->Struct.polymorphic_parent == nullptr &&
+ t == s) {
+ return true;
+ }
if (t->Struct.polymorphic_parent == specialization) {
return true;
}
@@ -1161,8 +1459,8 @@ gb_internal bool check_type_specialization_to(CheckerContext *ctx, Type *special
s->Struct.polymorphic_params != nullptr &&
t->Struct.polymorphic_params != nullptr) {
- TypeTuple *s_tuple = &s->Struct.polymorphic_params->Tuple;
- TypeTuple *t_tuple = &t->Struct.polymorphic_params->Tuple;
+ TypeTuple *s_tuple = get_record_polymorphic_params(s);
+ TypeTuple *t_tuple = get_record_polymorphic_params(t);
GB_ASSERT(t_tuple->variables.count == s_tuple->variables.count);
for_array(i, s_tuple->variables) {
Entity *s_e = s_tuple->variables[i];
@@ -1202,6 +1500,10 @@ gb_internal bool check_type_specialization_to(CheckerContext *ctx, Type *special
return true;
}
} else if (t->kind == Type_Union) {
+ if (t->Union.polymorphic_parent == nullptr &&
+ t == s) {
+ return true;
+ }
if (t->Union.polymorphic_parent == specialization) {
return true;
}
@@ -1210,8 +1512,8 @@ gb_internal bool check_type_specialization_to(CheckerContext *ctx, Type *special
s->Union.polymorphic_params != nullptr &&
t->Union.polymorphic_params != nullptr) {
- TypeTuple *s_tuple = &s->Union.polymorphic_params->Tuple;
- TypeTuple *t_tuple = &t->Union.polymorphic_params->Tuple;
+ TypeTuple *s_tuple = get_record_polymorphic_params(s);
+ TypeTuple *t_tuple = get_record_polymorphic_params(t);
GB_ASSERT(t_tuple->variables.count == s_tuple->variables.count);
for_array(i, s_tuple->variables) {
Entity *s_e = s_tuple->variables[i];
@@ -1276,11 +1578,30 @@ gb_internal Type *determine_type_from_polymorphic(CheckerContext *ctx, Type *pol
return poly_type;
}
if (show_error) {
+ ERROR_BLOCK();
gbString pts = type_to_string(poly_type);
gbString ots = type_to_string(operand.type, true);
defer (gb_string_free(pts));
defer (gb_string_free(ots));
error(operand.expr, "Cannot determine polymorphic type from parameter: '%s' to '%s'", ots, pts);
+
+ Type *pt = poly_type;
+ while (pt && pt->kind == Type_Generic && pt->Generic.specialized) {
+ pt = pt->Generic.specialized;
+ }
+ if (is_type_slice(pt) &&
+ (is_type_dynamic_array(operand.type) || is_type_array(operand.type))) {
+ Ast *expr = unparen_expr(operand.expr);
+ if (expr->kind == Ast_CompoundLit) {
+ gbString es = type_to_string(base_any_array_type(operand.type));
+ error_line("\tSuggestion: Try using a slice compound literal instead '[]%s{...}'\n", es);
+ gb_string_free(es);
+ } else {
+ gbString os = expr_to_string(operand.expr);
+ error_line("\tSuggestion: Try slicing the value with '%s[:]'\n", os);
+ gb_string_free(os);
+ }
+ }
}
return t_invalid;
}
@@ -1295,7 +1616,7 @@ gb_internal bool is_expr_from_a_parameter(CheckerContext *ctx, Ast *expr) {
return is_expr_from_a_parameter(ctx, lhs);
} else if (expr->kind == Ast_Ident) {
Operand x= {};
- Entity *e = check_ident(ctx, &x, expr, nullptr, nullptr, false);
+ Entity *e = check_ident(ctx, &x, expr, nullptr, nullptr, true);
if (e->flags & EntityFlag_Param) {
return true;
}
@@ -1303,6 +1624,25 @@ gb_internal bool is_expr_from_a_parameter(CheckerContext *ctx, Ast *expr) {
return false;
}
+gb_internal bool is_caller_expression(Ast *expr) {
+ if (expr->kind == Ast_BasicDirective && expr->BasicDirective.name.string == "caller_expression") {
+ return true;
+ }
+
+ Ast *call = unparen_expr(expr);
+ if (call->kind != Ast_CallExpr) {
+ return false;
+ }
+
+ ast_node(ce, CallExpr, call);
+ if (ce->proc->kind != Ast_BasicDirective) {
+ return false;
+ }
+
+ ast_node(bd, BasicDirective, ce->proc);
+ String name = bd->name.string;
+ return name == "caller_expression";
+}
gb_internal ParameterValue handle_parameter_value(CheckerContext *ctx, Type *in_type, Type **out_type_, Ast *expr, bool allow_caller_location) {
ParameterValue param_value = {};
@@ -1324,7 +1664,19 @@ gb_internal ParameterValue handle_parameter_value(CheckerContext *ctx, Type *in_
if (in_type) {
check_assignment(ctx, &o, in_type, str_lit("parameter value"));
}
+ } else if (is_caller_expression(expr)) {
+ if (expr->kind != Ast_BasicDirective) {
+ check_builtin_procedure_directive(ctx, &o, expr, t_string);
+ }
+ param_value.kind = ParameterValue_Expression;
+ o.type = t_string;
+ o.mode = Addressing_Value;
+ o.expr = expr;
+
+ if (in_type) {
+ check_assignment(ctx, &o, in_type, str_lit("parameter value"));
+ }
} else {
if (in_type) {
check_expr_with_type_hint(ctx, &o, expr, in_type);
@@ -1461,7 +1813,7 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para
bool is_using = (p->flags&FieldFlag_using) != 0;
if ((check_vet_flags(param) & VetFlag_UsingParam) && is_using) {
ERROR_BLOCK();
- error(param, "'using' on a procedure parameter is now allowed when '-vet' or '-vet-using-param' is applied");
+ error(param, "'using' on a procedure parameter is not allowed when '-vet' or '-vet-using-param' is applied");
error_line("\t'using' is considered bad practice to use as a statement/procedure parameter outside of immediate refactoring\n");
}
@@ -1469,6 +1821,7 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para
if (type_expr == nullptr) {
param_value = handle_parameter_value(ctx, nullptr, &type, default_value, true);
} else {
+ Ast *original_type_expr = type_expr;
if (type_expr->kind == Ast_Ellipsis) {
type_expr = type_expr->Ellipsis.expr;
is_variadic = true;
@@ -1477,6 +1830,14 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para
error(param, "Invalid AST: Invalid variadic parameter with multiple names");
success = false;
}
+
+ if (default_value != nullptr) {
+ error(type_expr, "A variadic parameter may not have a default value");
+ success = false;
+ }
+
+ GB_ASSERT(original_type_expr->kind == Ast_Ellipsis);
+ type_expr = ast_array_type(type_expr->file(), original_type_expr->Ellipsis.token, nullptr, type_expr);
}
if (type_expr->kind == Ast_TypeidType) {
ast_node(tt, TypeidType, type_expr);
@@ -1500,6 +1861,7 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para
if (operands != nullptr) {
ctx->allow_polymorphic_types = true;
}
+
type = check_type(ctx, type_expr);
ctx->allow_polymorphic_types = prev;
@@ -1532,12 +1894,12 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para
}
type = t_invalid;
}
- if (is_type_empty_union(type)) {
- gbString str = type_to_string(type);
- error(param, "Invalid use of an empty union '%s'", str);
- gb_string_free(str);
- type = t_invalid;
- }
+ // if (is_type_empty_union(type)) {
+ // gbString str = type_to_string(type);
+ // error(param, "Invalid use of an empty union '%s'", str);
+ // gb_string_free(str);
+ // type = t_invalid;
+ // }
if (is_type_polymorphic(type)) {
switch (param_value.kind) {
@@ -1546,6 +1908,7 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para
case ParameterValue_Nil:
break;
case ParameterValue_Location:
+ case ParameterValue_Expression:
case ParameterValue_Value:
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);
@@ -1644,10 +2007,18 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para
error(name, "'#any_int' can only be applied to variable fields");
p->flags &= ~FieldFlag_any_int;
}
+ if (p->flags&FieldFlag_no_broadcast) {
+ error(name, "'#no_broadcast' can only be applied to variable fields");
+ p->flags &= ~FieldFlag_no_broadcast;
+ }
if (p->flags&FieldFlag_by_ptr) {
error(name, "'#by_ptr' can only be applied to variable fields");
p->flags &= ~FieldFlag_by_ptr;
}
+ if (p->flags&FieldFlag_no_capture) {
+ error(name, "'#no_capture' can only be applied to variable fields");
+ p->flags &= ~FieldFlag_no_capture;
+ }
param = alloc_entity_type_name(scope, name->Ident.token, type, EntityState_Resolved);
param->TypeName.is_type_alias = true;
@@ -1701,7 +2072,13 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para
}
}
}
- if (type != t_invalid && !check_is_assignable_to(ctx, &op, type)) {
+
+ bool allow_array_programming = true;
+ if (p->flags&FieldFlag_no_broadcast) {
+ allow_array_programming = false;
+ }
+
+ 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))) {
@@ -1732,8 +2109,8 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para
}
if (p->flags&FieldFlag_no_alias) {
- if (!is_type_pointer(type)) {
- error(name, "'#no_alias' can only be applied pointer typed parameters");
+ if (!is_type_pointer(type) && !is_type_multi_pointer(type)) {
+ error(name, "'#no_alias' can only be applied pointer or multi-pointer typed parameters");
p->flags &= ~FieldFlag_no_alias; // Remove the flag
}
}
@@ -1743,6 +2120,28 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para
p->flags &= ~FieldFlag_by_ptr; // Remove the flag
}
}
+ if (p->flags&FieldFlag_no_capture) {
+ if (is_variadic && variadic_index == variables.count) {
+ if (p->flags & FieldFlag_c_vararg) {
+ error(name, "'#no_capture' cannot be applied to a #c_vararg parameter");
+ p->flags &= ~FieldFlag_no_capture;
+ } else {
+ error(name, "'#no_capture' is already implied on all variadic parameter");
+ }
+ } else if (is_type_polymorphic(type)) {
+ // ignore
+ } else {
+ if (is_type_internally_pointer_like(type)) {
+ error(name, "'#no_capture' is currently reserved for future use");
+ } else {
+ ERROR_BLOCK();
+ error(name, "'#no_capture' can only be applied to pointer-like types");
+ error_line("\t'#no_capture' does not currently do anything useful\n");
+ p->flags &= ~FieldFlag_no_capture;
+ }
+ }
+ }
+
if (is_poly_name) {
if (p->flags&FieldFlag_no_alias) {
@@ -1761,6 +2160,11 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para
error(name, "'#by_ptr' can only be applied to variable fields");
p->flags &= ~FieldFlag_by_ptr;
}
+ if (p->flags&FieldFlag_no_capture) {
+ error(name, "'#no_capture' can only be applied to variable fields");
+ p->flags &= ~FieldFlag_no_capture;
+ }
+
if (!is_type_polymorphic(type) && check_constant_parameter_value(type, params[i])) {
// failed
@@ -1772,11 +2176,26 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para
param = alloc_entity_param(scope, name->Ident.token, type, is_using, true);
param->Variable.param_value = param_value;
param->Variable.field_group_index = field_group_index;
+ param->Variable.type_expr = type_expr;
}
}
+
+ if (is_variadic && variadic_index == variables.count) {
+ param->flags |= EntityFlag_Ellipsis;
+ if (is_c_vararg) {
+ param->flags |= EntityFlag_CVarArg;
+ } else {
+ param->flags |= EntityFlag_NoCapture;
+ }
+ }
+
if (p->flags&FieldFlag_no_alias) {
param->flags |= EntityFlag_NoAlias;
}
+ if (p->flags&FieldFlag_no_broadcast) {
+ param->flags |= EntityFlag_NoBroadcast;
+ }
+
if (p->flags&FieldFlag_any_int) {
if (!is_type_integer(param->type) && !is_type_enum(param->type)) {
gbString str = type_to_string(param->type);
@@ -1791,6 +2210,10 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para
if (p->flags&FieldFlag_by_ptr) {
param->flags |= EntityFlag_ByPtr;
}
+ if (p->flags&FieldFlag_no_capture) {
+ param->flags |= EntityFlag_NoCapture;
+ }
+
param->state = EntityState_Resolved; // NOTE(bill): This should have be resolved whilst determining it
add_entity(ctx, scope, name, param);
@@ -1804,18 +2227,7 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para
if (is_variadic) {
GB_ASSERT(variadic_index >= 0);
- }
-
- if (is_variadic) {
GB_ASSERT(params.count > 0);
- // NOTE(bill): Change last variadic parameter to be a slice
- // Custom Calling convention for variadic parameters
- Entity *end = variables[variadic_index];
- end->type = alloc_type_slice(end->type);
- end->flags |= EntityFlag_Ellipsis;
- if (is_c_vararg) {
- end->flags |= EntityFlag_CVarArg;
- }
}
isize specialization_count = 0;
@@ -1953,8 +2365,27 @@ gb_internal Type *check_get_results(CheckerContext *ctx, Scope *scope, Ast *_res
return tuple;
}
+gb_internal void check_procedure_param_polymorphic_type(CheckerContext *ctx, Type *type, Ast *type_expr) {
+ if (type == nullptr || type_expr == nullptr || ctx->in_polymorphic_specialization) { return; }
+ if (!is_type_polymorphic_record_unspecialized(type)) { return; }
+ bool invalid_polymorpic_type_use = false;
+ switch (type_expr->kind) {
+ case_ast_node(pt, Ident, type_expr);
+ invalid_polymorpic_type_use = true;
+ case_end;
+ case_ast_node(pt, SelectorExpr, type_expr);
+ invalid_polymorpic_type_use = true;
+ case_end;
+ }
+
+ if (invalid_polymorpic_type_use) {
+ gbString expr_str = expr_to_string(type_expr);
+ defer (gb_string_free(expr_str));
+ error(type_expr, "Invalid use of a non-specialized polymorphic type '%s'", expr_str);
+ }
+}
// NOTE(bill): 'operands' is for generating non generic procedure type
gb_internal bool check_procedure_type(CheckerContext *ctx, Type *type, Ast *proc_type_node, Array<Operand> const *operands) {
@@ -2009,8 +2440,12 @@ gb_internal bool check_procedure_type(CheckerContext *ctx, Type *type, Ast *proc
bool success = true;
isize specialization_count = 0;
Type *params = check_get_params(c, c->scope, pt->params, &variadic, &variadic_index, &success, &specialization_count, operands);
- Type *results = check_get_results(c, c->scope, pt->results);
+ bool no_poly_return = c->disallow_polymorphic_return_types;
+ c->disallow_polymorphic_return_types = c->scope == c->polymorphic_scope;
+ // NOTE(zen3ger): if the parapoly scope is the current proc's scope, then the return types shall not declare new poly vars
+ Type *results = check_get_results(c, c->scope, pt->results);
+ c->disallow_polymorphic_return_types = no_poly_return;
isize param_count = 0;
isize result_count = 0;
@@ -2070,33 +2505,34 @@ gb_internal bool check_procedure_type(CheckerContext *ctx, Type *type, Ast *proc
type->Proc.diverging = pt->diverging;
type->Proc.optional_ok = optional_ok;
- if (param_count > 0) {
- Entity *end = params->Tuple.variables[param_count-1];
- if (end->flags&EntityFlag_CVarArg) {
+ bool is_polymorphic = false;
+ for (isize i = 0; i < param_count; i++) {
+ Entity *e = params->Tuple.variables[i];
+
+ if (e->kind != Entity_Variable) {
+ is_polymorphic = true;
+ } else if (is_type_polymorphic(e->type)) {
+ check_procedure_param_polymorphic_type(c, e->type, e->Variable.type_expr);
+ is_polymorphic = true;
+ }
+
+ if (e->flags&EntityFlag_CVarArg) {
+ if (i != param_count - 1) {
+ error(e->token, "#c_vararg can only be applied to the last parameter");
+ continue;
+ }
+
switch (cc) {
default:
type->Proc.c_vararg = true;
break;
case ProcCC_Odin:
case ProcCC_Contextless:
- error(end->token, "Calling convention does not support #c_vararg");
+ error(e->token, "Calling convention does not support #c_vararg");
break;
}
}
}
-
-
- bool is_polymorphic = false;
- for (isize i = 0; i < param_count; i++) {
- Entity *e = params->Tuple.variables[i];
- if (e->kind != Entity_Variable) {
- is_polymorphic = true;
- break;
- } else if (is_type_polymorphic(e->type)) {
- is_polymorphic = true;
- break;
- }
- }
for (isize i = 0; i < result_count; i++) {
Entity *e = results->Tuple.variables[i];
if (e->kind != Entity_Variable) {
@@ -2117,9 +2553,15 @@ gb_internal i64 check_array_count(CheckerContext *ctx, Operand *o, Ast *e) {
if (e == nullptr) {
return 0;
}
- if (e->kind == Ast_UnaryExpr &&
- e->UnaryExpr.op.kind == Token_Question) {
- return -1;
+ if (e->kind == Ast_UnaryExpr) {
+ Token op = e->UnaryExpr.op;
+ if (op.kind == Token_Question) {
+ return -1;
+ }
+ if (e->UnaryExpr.expr == nullptr) {
+ error(op, "Invalid array count '[%.*s]'", LIT(op.string));
+ return 0;
+ }
}
check_expr_or_type(ctx, o, e);
@@ -2157,6 +2599,8 @@ gb_internal i64 check_array_count(CheckerContext *ctx, Operand *o, Ast *e) {
return 0;
}
+ ERROR_BLOCK();
+
gbString s = expr_to_string(o->expr);
error(e, "Array count must be a constant integer, got %s", s);
gb_string_free(s);
@@ -2226,6 +2670,78 @@ gb_internal void map_cell_size_and_len(Type *type, i64 *size_, i64 *len_) {
if (len_) *len_ = len;
}
+gb_internal Type *get_map_cell_type(Type *type) {
+ i64 size, len;
+ i64 elem_size = type_size_of(type);
+ map_cell_size_and_len(type, &size, &len);
+
+ if (size == len*elem_size) {
+ return type;
+ }
+
+ i64 padding = size - len*elem_size;
+ GB_ASSERT(padding > 0);
+
+ // 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[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;
+ wait_signal_set(&s->Struct.fields_wait_signal);
+ gb_unused(type_size_of(s));
+
+ return s;
+}
+
+gb_internal void init_map_internal_debug_types(Type *type) {
+ GB_ASSERT(type->kind == Type_Map);
+ GB_ASSERT(t_allocator != nullptr);
+ if (type->Map.debug_metadata_type != nullptr) return;
+
+ Type *key = type->Map.key;
+ Type *value = type->Map.value;
+ GB_ASSERT(key != nullptr);
+ GB_ASSERT(value != nullptr);
+
+ Type *key_cell = get_map_cell_type(key);
+ Type *value_cell = get_map_cell_type(value);
+
+ 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[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);
+ metadata_type->Struct.fields[3] = alloc_entity_field(metadata_scope, make_token_ident("key_cell"), key_cell, false, 3, EntityState_Resolved);
+ metadata_type->Struct.fields[4] = alloc_entity_field(metadata_scope, make_token_ident("value_cell"), value_cell, false, 4, EntityState_Resolved);
+ metadata_type->Struct.scope = metadata_scope;
+ metadata_type->Struct.node = nullptr;
+ wait_signal_set(&metadata_type->Struct.fields_wait_signal);
+
+ gb_unused(type_size_of(metadata_type));
+
+ // NOTE(bill): ^struct{key: Key, value: Value, hash: uintptr}
+ metadata_type = alloc_type_pointer(metadata_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[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);
+ debug_type->Struct.scope = scope;
+ debug_type->Struct.node = nullptr;
+ wait_signal_set(&debug_type->Struct.fields_wait_signal);
+
+ gb_unused(type_size_of(debug_type));
+
+ type->Map.debug_metadata_type = debug_type;
+}
+
+
gb_internal void init_map_internal_types(Type *type) {
GB_ASSERT(type->kind == Type_Map);
GB_ASSERT(t_allocator != nullptr);
@@ -2282,6 +2798,18 @@ gb_internal void check_map_type(CheckerContext *ctx, Type *type, Ast *node) {
GB_ASSERT(type->kind == Type_Map);
ast_node(mt, MapType, node);
+ if (mt->key == NULL) {
+ if (mt->value != NULL) {
+ Type *value = check_type(ctx, mt->value);
+ gbString str = type_to_string(value);
+ error(node, "Missing map key type, got 'map[]%s'", str);
+ gb_string_free(str);
+ return;
+ }
+ error(node, "Missing map key type, got 'map[]T'");
+ return;
+ }
+
Type *key = check_type(ctx, mt->key);
Type *value = check_type(ctx, mt->value);
@@ -2307,8 +2835,6 @@ gb_internal void check_map_type(CheckerContext *ctx, Type *type, Ast *node) {
init_core_map_type(ctx->checker);
init_map_internal_types(type);
-
- // error(node, "'map' types are not yet implemented");
}
gb_internal void check_matrix_type(CheckerContext *ctx, Type **type, Ast *node) {
@@ -2368,11 +2894,126 @@ gb_internal void check_matrix_type(CheckerContext *ctx, Type **type, Ast *node)
}
type_assign:;
- *type = alloc_type_matrix(elem, row_count, column_count, generic_row, generic_column);
+ *type = alloc_type_matrix(elem, row_count, column_count, generic_row, generic_column, mt->is_row_major);
return;
}
+struct SoaTypeWorkerData {
+ CheckerContext ctx;
+ Type * type;
+ bool wait_to_finish;
+};
+
+
+gb_internal bool complete_soa_type(Checker *checker, Type *t, bool wait_to_finish) {
+ Type *original_type = t;
+ gb_unused(original_type);
+
+ t = base_type(t);
+ if (t == nullptr || !is_type_soa_struct(t)) {
+ return true;
+ }
+
+ MUTEX_GUARD(&t->Struct.soa_mutex);
+
+ if (t->Struct.fields_wait_signal.futex.load()) {
+ return true;
+ }
+
+ isize field_count = 0;
+ i32 extra_field_count = 0;
+ switch (t->Struct.soa_kind) {
+ case StructSoa_Fixed: extra_field_count = 0; break;
+ case StructSoa_Slice: extra_field_count = 1; break;
+ case StructSoa_Dynamic: extra_field_count = 3; break;
+ }
+
+ Scope *scope = t->Struct.scope;
+ i64 soa_count = t->Struct.soa_count;
+ Type *elem = t->Struct.soa_elem;
+ Type *old_struct = base_type(elem);
+ GB_ASSERT(old_struct->kind == Type_Struct);
+
+ 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());
+ }
+
+ 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);
+
+
+ auto const &add_entity = [](Scope *scope, Entity *entity) {
+ String name = entity->token.string;
+ if (!is_blank_ident(name)) {
+ Entity *ie = scope_insert(scope, entity);
+ if (ie != nullptr) {
+ redeclaration_error(name, entity, ie);
+ }
+ }
+ };
+
+
+ for_array(i, old_struct->Struct.fields) {
+ Entity *old_field = old_struct->Struct.fields[i];
+ if (old_field->kind == Entity_Variable) {
+ Type *field_type = nullptr;
+ if (t->Struct.soa_kind == StructSoa_Fixed) {
+ GB_ASSERT(soa_count >= 0);
+ field_type = alloc_type_array(old_field->type, soa_count);
+ } else {
+ field_type = alloc_type_multi_pointer(old_field->type);
+ }
+ Entity *new_field = alloc_entity_field(scope, old_field->token, field_type, false, old_field->Variable.field_index);
+ t->Struct.fields[i] = new_field;
+ add_entity(scope, new_field);
+ new_field->flags |= EntityFlag_Used;
+ if (t->Struct.soa_kind != StructSoa_Fixed) {
+ new_field->flags |= EntityFlag_SoaPtrField;
+ }
+ } else {
+ t->Struct.fields[i] = old_field;
+ }
+
+ t->Struct.tags[i] = old_struct->Struct.tags[i];
+ }
+
+ if (t->Struct.soa_kind != StructSoa_Fixed) {
+ Entity *len_field = alloc_entity_field(scope, make_token_ident("__$len"), t_int, false, cast(i32)field_count+0);
+ t->Struct.fields[field_count+0] = len_field;
+ add_entity(scope, len_field);
+ len_field->flags |= EntityFlag_Used;
+
+ if (t->Struct.soa_kind == StructSoa_Dynamic) {
+ Entity *cap_field = alloc_entity_field(scope, make_token_ident("__$cap"), t_int, false, cast(i32)field_count+1);
+ t->Struct.fields[field_count+1] = cap_field;
+ add_entity(scope, cap_field);
+ cap_field->flags |= EntityFlag_Used;
+
+ init_mem_allocator(checker);
+ Entity *allocator_field = alloc_entity_field(scope, make_token_ident("allocator"), t_allocator, false, cast(i32)field_count+2);
+ t->Struct.fields[field_count+2] = allocator_field;
+ add_entity(scope, allocator_field);
+ allocator_field->flags |= EntityFlag_Used;
+ }
+ }
+
+ // add_type_info_type(ctx, original_type);
+
+ wait_signal_set(&t->Struct.fields_wait_signal);
+ return true;
+}
+
+gb_internal WORKER_TASK_PROC(complete_soa_type_worker) {
+ SoaTypeWorkerData *wd = cast(SoaTypeWorkerData *)data;
+ complete_soa_type(wd->ctx.checker, wd->type, wd->wait_to_finish);
+ return 0;
+}
+
gb_internal Type *make_soa_struct_internal(CheckerContext *ctx, Ast *array_typ_expr, Ast *elem_expr, Type *elem, i64 count, Type *generic_type, StructSoaKind soa_kind) {
@@ -2380,15 +3021,16 @@ gb_internal Type *make_soa_struct_internal(CheckerContext *ctx, Ast *array_typ_e
bool is_polymorphic = is_type_polymorphic(elem);
- if ((!is_polymorphic || soa_kind == StructSoa_Fixed) && !is_type_struct(elem) && !is_type_raw_union(elem) && !(is_type_array(elem) && bt_elem->Array.count <= 4)) {
+ if (!is_polymorphic && !is_type_struct(elem) && !is_type_raw_union(elem) && !(is_type_array(elem) && bt_elem->Array.count <= 4)) {
gbString str = type_to_string(elem);
error(elem_expr, "Invalid type for an #soa array, expected a struct or array of length 4 or below, got '%s'", str);
gb_string_free(str);
return alloc_type_array(elem, count, generic_type);
}
- Type *soa_struct = nullptr;
- Scope *scope = nullptr;
+ Type * soa_struct = nullptr;
+ Scope *scope = nullptr;
+ bool is_complete = false;
isize field_count = 0;
i32 extra_field_count = 0;
@@ -2397,39 +3039,43 @@ gb_internal Type *make_soa_struct_internal(CheckerContext *ctx, Ast *array_typ_e
case StructSoa_Slice: extra_field_count = 1; break;
case StructSoa_Dynamic: extra_field_count = 3; break;
}
- if (is_polymorphic && soa_kind != StructSoa_Fixed) {
+
+ soa_struct = alloc_type_struct();
+ soa_struct->Struct.soa_kind = soa_kind;
+ soa_struct->Struct.soa_elem = elem;
+ soa_struct->Struct.is_polymorphic = is_polymorphic;
+ soa_struct->Struct.node = array_typ_expr;
+
+ if (count > I32_MAX) {
+ count = I32_MAX;
+ error(array_typ_expr, "Array count too large for an #soa struct, got %lld", cast(long long)count);
+ }
+ soa_struct->Struct.soa_count = cast(i32)count;
+
+ scope = create_scope(ctx->info, ctx->scope);
+ soa_struct->Struct.scope = scope;
+
+ if (elem && elem->kind == Type_Named) {
+ add_declaration_dependency(ctx, elem->Named.type_name);
+ }
+
+ if (is_polymorphic) {
field_count = 0;
- soa_struct = alloc_type_struct();
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.node = array_typ_expr;
- soa_struct->Struct.soa_kind = soa_kind;
- soa_struct->Struct.soa_elem = elem;
soa_struct->Struct.soa_count = 0;
- soa_struct->Struct.is_polymorphic = true;
- scope = create_scope(ctx->info, ctx->scope);
- soa_struct->Struct.scope = scope;
+ is_complete = true;
+
} else if (is_type_array(elem)) {
Type *old_array = base_type(elem);
field_count = cast(isize)old_array->Array.count;
- soa_struct = alloc_type_struct();
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.node = array_typ_expr;
- soa_struct->Struct.soa_kind = soa_kind;
- soa_struct->Struct.soa_elem = elem;
- if (count > I32_MAX) {
- count = I32_MAX;
- error(array_typ_expr, "Array count too large for an #soa struct, got %lld", cast(long long)count);
- }
- soa_struct->Struct.soa_count = cast(i32)count;
- scope = create_scope(ctx->info, ctx->scope);
string_map_init(&scope->elements, 8);
- soa_struct->Struct.scope = scope;
String params_xyzw[4] = {
str_lit("x"),
@@ -2444,7 +3090,7 @@ gb_internal Type *make_soa_struct_internal(CheckerContext *ctx, Ast *array_typ_e
GB_ASSERT(count >= 0);
field_type = alloc_type_array(old_array->Array.elem, count);
} else {
- field_type = alloc_type_pointer(old_array->Array.elem);
+ field_type = alloc_type_multi_pointer(old_array->Array.elem);
}
Token token = {};
token.string = params_xyzw[i];
@@ -2453,67 +3099,65 @@ gb_internal Type *make_soa_struct_internal(CheckerContext *ctx, Ast *array_typ_e
soa_struct->Struct.fields[i] = new_field;
add_entity(ctx, scope, nullptr, new_field);
add_entity_use(ctx, nullptr, new_field);
+ if (soa_kind != StructSoa_Fixed) {
+ new_field->flags |= EntityFlag_SoaPtrField;
+ }
}
+ is_complete = true;
+
} else {
GB_ASSERT(is_type_struct(elem));
Type *old_struct = base_type(elem);
- field_count = old_struct->Struct.fields.count;
- soa_struct = alloc_type_struct();
- 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.node = array_typ_expr;
- soa_struct->Struct.soa_kind = soa_kind;
- soa_struct->Struct.soa_elem = elem;
- if (count > I32_MAX) {
- count = I32_MAX;
- error(array_typ_expr, "Array count too large for an #soa struct, got %lld", cast(long long)count);
- }
- soa_struct->Struct.soa_count = cast(i32)count;
-
- scope = create_scope(ctx->info, old_struct->Struct.scope->parent);
- soa_struct->Struct.scope = scope;
-
- for_array(i, old_struct->Struct.fields) {
- Entity *old_field = old_struct->Struct.fields[i];
- if (old_field->kind == Entity_Variable) {
- Type *field_type = nullptr;
- if (soa_kind == StructSoa_Fixed) {
- GB_ASSERT(count >= 0);
- field_type = alloc_type_array(old_field->type, count);
+ 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);
+
+ for_array(i, old_struct->Struct.fields) {
+ Entity *old_field = old_struct->Struct.fields[i];
+ if (old_field->kind == Entity_Variable) {
+ Type *field_type = nullptr;
+ if (soa_kind == StructSoa_Fixed) {
+ GB_ASSERT(count >= 0);
+ field_type = alloc_type_array(old_field->type, count);
+ } else {
+ field_type = alloc_type_multi_pointer(old_field->type);
+ }
+ Entity *new_field = alloc_entity_field(scope, old_field->token, field_type, false, old_field->Variable.field_index);
+ soa_struct->Struct.fields[i] = new_field;
+ add_entity(ctx, scope, nullptr, new_field);
+ add_entity_use(ctx, nullptr, new_field);
+ if (soa_kind != StructSoa_Fixed) {
+ new_field->flags |= EntityFlag_SoaPtrField;
+ }
} else {
- field_type = alloc_type_pointer(old_field->type);
+ soa_struct->Struct.fields[i] = old_field;
}
- Entity *new_field = alloc_entity_field(scope, old_field->token, field_type, false, old_field->Variable.field_index);
- soa_struct->Struct.fields[i] = new_field;
- add_entity(ctx, scope, nullptr, new_field);
- add_entity_use(ctx, nullptr, new_field);
- } else {
- soa_struct->Struct.fields[i] = old_field;
- }
- soa_struct->Struct.tags[i] = old_struct->Struct.tags[i];
+ soa_struct->Struct.tags[i] = old_struct->Struct.tags[i];
+ }
+ is_complete = true;
}
}
- if (soa_kind != StructSoa_Fixed) {
- Entity *len_field = alloc_entity_field(scope, empty_token, t_int, false, cast(i32)field_count+0);
+ if (is_complete && soa_kind != StructSoa_Fixed) {
+ Entity *len_field = alloc_entity_field(scope, make_token_ident("__$len"), t_int, false, cast(i32)field_count+0);
soa_struct->Struct.fields[field_count+0] = len_field;
add_entity(ctx, scope, nullptr, len_field);
add_entity_use(ctx, nullptr, len_field);
if (soa_kind == StructSoa_Dynamic) {
- Entity *cap_field = alloc_entity_field(scope, empty_token, t_int, false, cast(i32)field_count+1);
+ Entity *cap_field = alloc_entity_field(scope, make_token_ident("__$cap"), t_int, false, cast(i32)field_count+1);
soa_struct->Struct.fields[field_count+1] = cap_field;
add_entity(ctx, scope, nullptr, cap_field);
add_entity_use(ctx, nullptr, cap_field);
- Token token = {};
- token.string = str_lit("allocator");
init_mem_allocator(ctx->checker);
- Entity *allocator_field = alloc_entity_field(scope, token, t_allocator, false, cast(i32)field_count+2);
+ Entity *allocator_field = alloc_entity_field(scope, make_token_ident("allocator"), t_allocator, false, cast(i32)field_count+2);
soa_struct->Struct.fields[field_count+2] = allocator_field;
add_entity(ctx, scope, nullptr, allocator_field);
add_entity_use(ctx, nullptr, allocator_field);
@@ -2525,7 +3169,18 @@ gb_internal Type *make_soa_struct_internal(CheckerContext *ctx, Ast *array_typ_e
Entity *base_type_entity = alloc_entity_type_name(scope, token, elem, EntityState_Resolved);
add_entity(ctx, scope, nullptr, base_type_entity);
- add_type_info_type(ctx, soa_struct);
+ if (is_complete) {
+ add_type_info_type(ctx, soa_struct);
+ wait_signal_set(&soa_struct->Struct.fields_wait_signal);
+ } else {
+ SoaTypeWorkerData *wd = gb_alloc_item(permanent_allocator(), SoaTypeWorkerData);
+ wd->ctx = *ctx;
+ wd->type = soa_struct;
+ wd->wait_to_finish = true;
+
+ mpsc_enqueue(&ctx->checker->soa_types_to_complete, soa_struct);
+ thread_pool_add_task(complete_soa_type_worker, wd);
+ }
return soa_struct;
}
@@ -2544,6 +3199,113 @@ gb_internal Type *make_soa_struct_dynamic_array(CheckerContext *ctx, Ast *array_
return make_soa_struct_internal(ctx, array_typ_expr, elem_expr, elem, -1, nullptr, StructSoa_Dynamic);
}
+gb_internal void check_array_type_internal(CheckerContext *ctx, Ast *e, Type **type, Type *named_type) {
+ ast_node(at, ArrayType, e);
+ if (at->count != nullptr) {
+ Operand o = {};
+ i64 count = check_array_count(ctx, &o, at->count);
+ Type *generic_type = nullptr;
+
+ Type *elem = check_type_expr(ctx, at->elem, nullptr);
+
+ if (o.mode == Addressing_Type && o.type->kind == Type_Generic) {
+ generic_type = o.type;
+ } else if (o.mode == Addressing_Type && is_type_enum(o.type)) {
+ Type *index = o.type;
+ Type *bt = base_type(index);
+ GB_ASSERT(bt->kind == Type_Enum);
+
+ Type *t = alloc_type_enumerated_array(elem, index, bt->Enum.min_value, bt->Enum.max_value, bt->Enum.fields.count, Token_Invalid);
+
+ bool is_sparse = false;
+ if (at->tag != nullptr) {
+ GB_ASSERT(at->tag->kind == Ast_BasicDirective);
+ String name = at->tag->BasicDirective.name.string;
+ if (name == "sparse") {
+ is_sparse = true;
+ } else {
+ error(at->tag, "Invalid tag applied to an enumerated array, got #%.*s", LIT(name));
+ }
+ }
+
+ if (!is_sparse && t->EnumeratedArray.count > bt->Enum.fields.count) {
+ ERROR_BLOCK();
+
+ error(e, "Non-contiguous enumeration used as an index in an enumerated array");
+ long long ea_count = cast(long long)t->EnumeratedArray.count;
+ long long enum_count = cast(long long)bt->Enum.fields.count;
+ error_line("\tenumerated array length: %lld\n", ea_count);
+ error_line("\tenum field count: %lld\n", enum_count);
+ error_line("\tSuggestion: prepend #sparse to the enumerated array to allow for non-contiguous elements\n");
+ if (2*enum_count < ea_count) {
+ error_line("\tWarning: the number of named elements is much smaller than the length of the array, are you sure this is what you want?\n");
+ error_line("\t this warning will be removed if #sparse is applied\n");
+ }
+ }
+ t->EnumeratedArray.is_sparse = is_sparse;
+
+ *type = t;
+
+ return;
+ }
+
+ if (count < 0) {
+ error(at->count, "? can only be used in conjuction with compound literals");
+ count = 0;
+ }
+
+
+ if (at->tag != nullptr) {
+ GB_ASSERT(at->tag->kind == Ast_BasicDirective);
+ String name = at->tag->BasicDirective.name.string;
+ if (name == "soa") {
+ *type = make_soa_struct_fixed(ctx, e, at->elem, elem, count, generic_type);
+ } else if (name == "simd") {
+ if (!is_type_valid_vector_elem(elem) && !is_type_polymorphic(elem)) {
+ gbString str = type_to_string(elem);
+ error(at->elem, "Invalid element type for #simd, expected an integer, float, boolean, or 'rawptr' with no specific endianness, got '%s'", str);
+ gb_string_free(str);
+ *type = alloc_type_array(elem, count, generic_type);
+ return;
+ }
+
+ 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);
+ return;
+ }
+
+ *type = alloc_type_simd_vector(count, elem, generic_type);
+
+ if (count > SIMD_ELEMENT_COUNT_MAX) {
+ error(at->count, "#simd support a maximum element count of %d, got %lld", SIMD_ELEMENT_COUNT_MAX, cast(long long)count);
+ }
+ } else {
+ error(at->tag, "Invalid tag applied to array, got #%.*s", LIT(name));
+ *type = alloc_type_array(elem, count, generic_type);
+ }
+ } else {
+ *type = alloc_type_array(elem, count, generic_type);
+ }
+ } else {
+ Type *elem = check_type(ctx, at->elem);
+
+ if (at->tag != nullptr) {
+ GB_ASSERT(at->tag->kind == Ast_BasicDirective);
+ String name = at->tag->BasicDirective.name.string;
+ if (name == "soa") {
+ *type = make_soa_struct_slice(ctx, e, at->elem, elem);
+ } else {
+ error(at->tag, "Invalid tag applied to array, got #%.*s", LIT(name));
+ *type = alloc_type_slice(elem);
+ }
+ } else {
+ *type = alloc_type_slice(elem);
+ }
+ }
+}
gb_internal bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, Type *named_type) {
GB_ASSERT_NOT_NULL(type);
if (e == nullptr) {
@@ -2625,6 +3387,9 @@ gb_internal bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, T
}
Type *t = alloc_type_generic(ctx->scope, 0, token.string, specific);
if (ctx->allow_polymorphic_types) {
+ if (ctx->disallow_polymorphic_return_types) {
+ error(ident, "Undeclared polymorphic parameter '%.*s' in return type", LIT(token.string));
+ }
Scope *ps = ctx->polymorphic_scope;
Scope *s = ctx->scope;
Scope *entity_scope = s;
@@ -2675,6 +3440,11 @@ gb_internal bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, T
case_end;
case_ast_node(pe, ParenExpr, e);
+ if (pe->expr == nullptr) {
+ error(e, "Expected an expression or type within the parentheses");
+ *type = t_invalid;
+ return true;
+ }
*type = check_type_expr(ctx, pe->expr, named_type);
set_base_type(named_type, *type);
return true;
@@ -2699,21 +3469,31 @@ gb_internal bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, T
Type *elem = t_invalid;
Operand o = {};
+
+ if (unparen_expr(pt->type) == nullptr) {
+ error(e, "Invalid pointer type");
+ return false;
+ }
+
check_expr_or_type(&c, &o, pt->type);
if (o.mode != Addressing_Invalid && o.mode != Addressing_Type) {
- // NOTE(bill): call check_type_expr again to get a consistent error message
- begin_error_block();
- elem = check_type_expr(&c, pt->type, nullptr);
if (o.mode == Addressing_Variable) {
gbString s = expr_to_string(pt->type);
- error_line("\tSuggestion: ^ is used for pointer types, did you mean '&%s'?\n", s);
+ error(e, "^ is used for pointer types, did you mean '&%s'?", s);
gb_string_free(s);
+ } else if (is_type_pointer(o.type)) {
+ gbString s = expr_to_string(pt->type);
+ error(e, "^ is used for pointer types, did you mean a dereference: '%s^'?", s);
+ gb_string_free(s);
+ } else {
+ // NOTE(bill): call check_type_expr again to get a consistent error message
+ elem = check_type_expr(&c, pt->type, nullptr);
}
- end_error_block();
} else {
elem = o.type;
}
+
if (pt->tag != nullptr) {
GB_ASSERT(pt->tag->kind == Ast_BasicDirective);
String name = pt->tag->BasicDirective.name.string;
@@ -2743,149 +3523,14 @@ gb_internal bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, T
case_end;
case_ast_node(rt, RelativeType, e);
- GB_ASSERT(rt->tag->kind == Ast_CallExpr);
- ast_node(ce, CallExpr, rt->tag);
-
- Type *base_integer = nullptr;
-
- if (ce->args.count != 1) {
- error(rt->type, "#relative expected 1 type argument, got %td", ce->args.count);
- } else {
- base_integer = check_type(ctx, ce->args[0]);
- if (!is_type_integer(base_integer)) {
- error(rt->type, "#relative base types must be an integer");
- base_integer = nullptr;
- } else if (type_size_of(base_integer) > 64) {
- error(rt->type, "#relative base integer types be less than or equal to 64-bits");
- base_integer = nullptr;
- }
- }
-
- Type *relative_type = nullptr;
- Type *base_type = check_type(ctx, rt->type);
- if (!is_type_pointer(base_type) && !is_type_multi_pointer(base_type)) {
- error(rt->type, "#relative types can only be a pointer or multi-pointer");
- relative_type = base_type;
- } else if (base_integer == nullptr) {
- relative_type = base_type;
- } else {
- if (is_type_pointer(base_type)) {
- relative_type = alloc_type_relative_pointer(base_type, base_integer);
- } else if (is_type_multi_pointer(base_type)) {
- relative_type = alloc_type_relative_multi_pointer(base_type, base_integer);
- }
- }
- GB_ASSERT(relative_type != nullptr);
-
- *type = relative_type;
+ error(e, "#relative types have been removed from the compiler. Prefer \"core:relative\".");
+ *type = t_invalid;
set_base_type(named_type, *type);
return true;
case_end;
case_ast_node(at, ArrayType, e);
- if (at->count != nullptr) {
- Operand o = {};
- i64 count = check_array_count(ctx, &o, at->count);
- Type *generic_type = nullptr;
-
- Type *elem = check_type_expr(ctx, at->elem, nullptr);
-
- if (o.mode == Addressing_Type && o.type->kind == Type_Generic) {
- generic_type = o.type;
- } else if (o.mode == Addressing_Type && is_type_enum(o.type)) {
- Type *index = o.type;
- Type *bt = base_type(index);
- GB_ASSERT(bt->kind == Type_Enum);
-
- Type *t = alloc_type_enumerated_array(elem, index, bt->Enum.min_value, bt->Enum.max_value, bt->Enum.fields.count, Token_Invalid);
-
- bool is_sparse = false;
- if (at->tag != nullptr) {
- GB_ASSERT(at->tag->kind == Ast_BasicDirective);
- String name = at->tag->BasicDirective.name.string;
- if (name == "sparse") {
- is_sparse = true;
- } else {
- error(at->tag, "Invalid tag applied to an enumerated array, got #%.*s", LIT(name));
- }
- }
-
- if (!is_sparse && t->EnumeratedArray.count > bt->Enum.fields.count) {
- error(e, "Non-contiguous enumeration used as an index in an enumerated array");
- long long ea_count = cast(long long)t->EnumeratedArray.count;
- long long enum_count = cast(long long)bt->Enum.fields.count;
- error_line("\tenumerated array length: %lld\n", ea_count);
- error_line("\tenum field count: %lld\n", enum_count);
- error_line("\tSuggestion: prepend #sparse to the enumerated array to allow for non-contiguous elements\n");
- if (2*enum_count < ea_count) {
- error_line("\tWarning: the number of named elements is much smaller than the length of the array, are you sure this is what you want?\n");
- error_line("\t this warning will be removed if #sparse is applied\n");
- }
- }
- t->EnumeratedArray.is_sparse = is_sparse;
-
- *type = t;
-
- goto array_end;
- }
-
- if (count < 0) {
- error(at->count, "? can only be used in conjuction with compound literals");
- count = 0;
- }
-
-
- if (at->tag != nullptr) {
- GB_ASSERT(at->tag->kind == Ast_BasicDirective);
- String name = at->tag->BasicDirective.name.string;
- if (name == "soa") {
- *type = make_soa_struct_fixed(ctx, e, at->elem, elem, count, generic_type);
- } else if (name == "simd") {
- if (!is_type_valid_vector_elem(elem) && !is_type_polymorphic(elem)) {
- gbString str = type_to_string(elem);
- error(at->elem, "Invalid element type for #simd, expected an integer, float, or boolean with no specific endianness, got '%s'", str);
- gb_string_free(str);
- *type = alloc_type_array(elem, count, generic_type);
- goto array_end;
- }
-
- 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);
- goto array_end;
- }
-
- *type = alloc_type_simd_vector(count, elem, generic_type);
-
- if (count > SIMD_ELEMENT_COUNT_MAX) {
- error(at->count, "#simd support a maximum element count of %d, got %lld", SIMD_ELEMENT_COUNT_MAX, cast(long long)count);
- }
- } else {
- error(at->tag, "Invalid tag applied to array, got #%.*s", LIT(name));
- *type = alloc_type_array(elem, count, generic_type);
- }
- } else {
- *type = alloc_type_array(elem, count, generic_type);
- }
- } else {
- Type *elem = check_type(ctx, at->elem);
-
- if (at->tag != nullptr) {
- GB_ASSERT(at->tag->kind == Ast_BasicDirective);
- String name = at->tag->BasicDirective.name.string;
- if (name == "soa") {
- *type = make_soa_struct_slice(ctx, e, at->elem, elem);
- } else {
- error(at->tag, "Invalid tag applied to array, got #%.*s", LIT(name));
- *type = alloc_type_slice(elem);
- }
- } else {
- *type = alloc_type_slice(elem);
- }
- }
- array_end:
+ check_array_type_internal(ctx, e, type, named_type);
set_base_type(named_type, *type);
return true;
case_end;
@@ -2960,6 +3605,20 @@ gb_internal bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, T
return true;
case_end;
+ case_ast_node(bf, BitFieldType, e);
+ bool ips = ctx->in_polymorphic_specialization;
+ defer (ctx->in_polymorphic_specialization = ips);
+ ctx->in_polymorphic_specialization = false;
+
+ *type = alloc_type_bit_field();
+ set_base_type(named_type, *type);
+ check_open_scope(ctx, e);
+ check_bit_field_type(ctx, *type, named_type, e);
+ check_close_scope(ctx);
+ (*type)->BitField.node = e;
+ return true;
+ case_end;
+
case_ast_node(pt, ProcType, e);
bool ips = ctx->in_polymorphic_specialization;
@@ -3041,9 +3700,51 @@ gb_internal Type *check_type_expr(CheckerContext *ctx, Ast *e, Type *named_type)
if (!ok) {
gbString err_str = expr_to_string(e);
+ defer (gb_string_free(err_str));
+
+ begin_error_block();
error(e, "'%s' is not a type", err_str);
- gb_string_free(err_str);
+
type = t_invalid;
+
+
+ // NOTE(bill): Check for common mistakes from C programmers
+ // e.g. T[] and T[N]
+ // e.g. *T
+ Ast *node = unparen_expr(e);
+ if (node && node->kind == Ast_IndexExpr) {
+ gbString index_str = nullptr;
+ if (node->IndexExpr.index) {
+ index_str = expr_to_string(node->IndexExpr.index);
+ }
+ defer (gb_string_free(index_str));
+
+ gbString type_str = expr_to_string(node->IndexExpr.expr);
+ defer (gb_string_free(type_str));
+
+ error_line("\tSuggestion: Did you mean '[%s]%s'?\n", index_str ? index_str : "", type_str);
+ end_error_block();
+
+ // NOTE(bill): Minimize error propagation of bad array syntax by treating this like a type
+ if (node->IndexExpr.expr != nullptr) {
+ Ast *pseudo_array_expr = ast_array_type(e->file(), ast_token(node->IndexExpr.expr), node->IndexExpr.index, node->IndexExpr.expr);
+ check_array_type_internal(ctx, pseudo_array_expr, &type, nullptr);
+ }
+ } else if (node && node->kind == Ast_UnaryExpr && node->UnaryExpr.op.kind == Token_Mul) {
+ gbString type_str = expr_to_string(node->UnaryExpr.expr);
+ defer (gb_string_free(type_str));
+
+ error_line("\tSuggestion: Did you mean '^%s'?\n", type_str);
+ end_error_block();
+
+ // NOTE(bill): Minimize error propagation of bad array syntax by treating this like a type
+ if (node->UnaryExpr.expr != nullptr) {
+ Ast *pseudo_pointer_expr = ast_pointer_type(e->file(), ast_token(node->UnaryExpr.expr), node->UnaryExpr.expr);
+ return check_type_expr(ctx, pseudo_pointer_expr, named_type);
+ }
+ } else {
+ end_error_block();
+ }
}
if (type == nullptr) {
diff --git a/src/checker.cpp b/src/checker.cpp
index 29f22bd9c..5d3263789 100644
--- a/src/checker.cpp
+++ b/src/checker.cpp
@@ -3,6 +3,8 @@
#include "entity.cpp"
#include "types.cpp"
+String get_final_microarchitecture();
+
gb_internal void check_expr(CheckerContext *c, Operand *operand, Ast *expression);
gb_internal void check_expr_or_type(CheckerContext *c, Operand *operand, Ast *expression, Type *type_hint=nullptr);
gb_internal void add_comparison_procedures_for_fields(CheckerContext *c, Type *t);
@@ -48,15 +50,6 @@ gb_internal bool check_rtti_type_disallowed(Ast *expr, Type *type, char const *f
return check_rtti_type_disallowed(ast_token(expr), type, format);
}
-gb_internal void scope_reset(Scope *scope) {
- if (scope == nullptr) return;
-
- rw_mutex_lock(&scope->mutex);
- scope->head_child.store(nullptr, std::memory_order_relaxed);
- string_map_clear(&scope->elements);
- ptr_set_clear(&scope->imported);
- rw_mutex_unlock(&scope->mutex);
-}
gb_internal void scope_reserve(Scope *scope, isize count) {
string_map_reserve(&scope->elements, 2*count);
@@ -166,9 +159,6 @@ gb_internal void import_graph_node_swap(ImportGraphNode **data, isize i, isize j
}
-
-
-
gb_internal void init_decl_info(DeclInfo *d, Scope *scope, DeclInfo *parent) {
gb_zero_item(d);
if (parent) {
@@ -182,6 +172,9 @@ gb_internal void init_decl_info(DeclInfo *d, Scope *scope, DeclInfo *parent) {
ptr_set_init(&d->deps, 0);
ptr_set_init(&d->type_info_deps, 0);
d->labels.allocator = heap_allocator();
+ d->variadic_reuses.allocator = heap_allocator();
+ d->variadic_reuse_max_bytes = 0;
+ d->variadic_reuse_max_align = 1;
}
gb_internal DeclInfo *make_decl_info(Scope *scope, DeclInfo *parent) {
@@ -224,7 +217,7 @@ gb_internal Scope *create_scope(CheckerInfo *info, Scope *parent) {
if (parent != nullptr && parent != builtin_pkg->scope) {
Scope *prev_head_child = parent->head_child.exchange(s, std::memory_order_acq_rel);
if (prev_head_child) {
- prev_head_child->next.store(s, std::memory_order_release);
+ s->next.store(prev_head_child, std::memory_order_release);
}
}
@@ -313,6 +306,7 @@ gb_internal void add_scope(CheckerContext *c, Ast *node, Scope *scope) {
case Ast_StructType: node->StructType.scope = scope; break;
case Ast_UnionType: node->UnionType.scope = scope; break;
case Ast_EnumType: node->EnumType.scope = scope; break;
+ case Ast_BitFieldType: node->BitFieldType.scope = scope; break;
default: GB_PANIC("Invalid node for add_scope: %.*s", LIT(ast_strings[node->kind]));
}
}
@@ -334,6 +328,7 @@ gb_internal Scope *scope_of_node(Ast *node) {
case Ast_StructType: return node->StructType.scope;
case Ast_UnionType: return node->UnionType.scope;
case Ast_EnumType: return node->EnumType.scope;
+ case Ast_BitFieldType: return node->BitFieldType.scope;
}
GB_PANIC("Invalid node for add_scope: %.*s", LIT(ast_strings[node->kind]));
return nullptr;
@@ -342,6 +337,7 @@ gb_internal Scope *scope_of_node(Ast *node) {
gb_internal void check_open_scope(CheckerContext *c, Ast *node) {
node = unparen_expr(node);
+ GB_ASSERT(node != nullptr);
GB_ASSERT(node->kind == Ast_Invalid ||
is_ast_stmt(node) ||
is_ast_type(node));
@@ -355,6 +351,7 @@ gb_internal void check_open_scope(CheckerContext *c, Ast *node) {
case Ast_EnumType:
case Ast_UnionType:
case Ast_BitSetType:
+ case Ast_BitFieldType:
scope->flags |= ScopeFlag_Type;
break;
}
@@ -375,6 +372,7 @@ gb_internal Entity *scope_lookup_current(Scope *s, String const &name) {
return nullptr;
}
+
gb_internal void scope_lookup_parent(Scope *scope, String const &name, Scope **scope_, Entity **entity_) {
if (scope != nullptr) {
bool gone_thru_proc = false;
@@ -502,9 +500,15 @@ end:;
return result;
}
+gb_global bool in_single_threaded_checker_stage = false;
+
gb_internal Entity *scope_insert(Scope *s, Entity *entity) {
String name = entity->token.string;
- return scope_insert_with_name(s, name, entity);
+ if (in_single_threaded_checker_stage) {
+ return scope_insert_with_name_no_mutex(s, name, entity);
+ } else {
+ return scope_insert_with_name(s, name, entity);
+ }
}
gb_internal Entity *scope_insert_no_mutex(Scope *s, Entity *entity) {
@@ -513,7 +517,7 @@ gb_internal Entity *scope_insert_no_mutex(Scope *s, Entity *entity) {
}
-GB_COMPARE_PROC(entity_variable_pos_cmp) {
+gb_internal GB_COMPARE_PROC(entity_variable_pos_cmp) {
Entity *x = *cast(Entity **)a;
Entity *y = *cast(Entity **)b;
@@ -529,20 +533,32 @@ gb_internal u64 check_vet_flags(CheckerContext *c) {
c->curr_proc_decl->proc_lit) {
file = c->curr_proc_decl->proc_lit->file();
}
- if (file && file->vet_flags_set) {
- return file->vet_flags;
- }
- return build_context.vet_flags;
+
+ return ast_file_vet_flags(file);
}
gb_internal u64 check_vet_flags(Ast *node) {
AstFile *file = node->file();
- if (file && file->vet_flags_set) {
- return file->vet_flags;
+ return ast_file_vet_flags(file);
+}
+
+gb_internal u64 check_feature_flags(CheckerContext *c, Ast *node) {
+ AstFile *file = c->file;
+ if (file == nullptr &&
+ c->curr_proc_decl &&
+ c->curr_proc_decl->proc_lit) {
+ file = c->curr_proc_decl->proc_lit->file();
+ }
+ if (file == nullptr) {
+ file = node->file();
}
- return build_context.vet_flags;
+ if (file != nullptr && file->feature_flags_set) {
+ return file->feature_flags;
+ }
+ return 0;
}
+
enum VettedEntityKind {
VettedEntity_Invalid,
@@ -649,7 +665,7 @@ gb_internal bool check_vet_shadowing(Checker *c, Entity *e, VettedEntity *ve) {
}
}
- zero_item(ve);
+ gb_zero_item(ve);
ve->kind = VettedEntity_Shadowed;
ve->entity = e;
ve->other = shadowed;
@@ -668,7 +684,7 @@ gb_internal bool check_vet_unused(Checker *c, Entity *e, VettedEntity *ve) {
}
case Entity_ImportName:
case Entity_LibraryName:
- zero_item(ve);
+ gb_zero_item(ve);
ve->kind = VettedEntity_Unused;
ve->entity = e;
return true;
@@ -677,20 +693,48 @@ gb_internal bool check_vet_unused(Checker *c, Entity *e, VettedEntity *ve) {
return false;
}
-gb_internal void check_scope_usage(Checker *c, Scope *scope, u64 vet_flags) {
- bool vet_unused = (vet_flags & VetFlag_Unused) != 0;
- bool vet_shadowing = (vet_flags & (VetFlag_Shadowing|VetFlag_Using)) != 0;
-
+gb_internal void check_scope_usage_internal(Checker *c, Scope *scope, u64 vet_flags, bool per_entity) {
+ u64 original_vet_flags = vet_flags;
Array<VettedEntity> vetted_entities = {};
array_init(&vetted_entities, heap_allocator());
+ defer (array_free(&vetted_entities));
rw_mutex_shared_lock(&scope->mutex);
for (auto const &entry : scope->elements) {
Entity *e = entry.value;
if (e == nullptr) continue;
+
+ vet_flags = original_vet_flags;
+ if (per_entity) {
+ vet_flags = ast_file_vet_flags(e->file);
+ }
+
+ bool vet_unused = (vet_flags & VetFlag_Unused) != 0;
+ bool vet_shadowing = (vet_flags & (VetFlag_Shadowing|VetFlag_Using)) != 0;
+ bool vet_unused_procedures = (vet_flags & VetFlag_UnusedProcedures) != 0;
+ if (vet_unused_procedures && e->pkg && e->pkg->kind == Package_Runtime) {
+ vet_unused_procedures = false;
+ }
+
VettedEntity ve_unused = {};
VettedEntity ve_shadowed = {};
- bool is_unused = vet_unused && check_vet_unused(c, e, &ve_unused);
+ 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) {
+ if (e->flags&EntityFlag_Used) {
+ is_unused = false;
+ } else if (e->flags & EntityFlag_Require) {
+ is_unused = false;
+ } else if (e->pkg && e->pkg->kind == Package_Init && e->token.string == "main") {
+ is_unused = false;
+ } else {
+ is_unused = true;
+ ve_unused.kind = VettedEntity_Unused;
+ ve_unused.entity = e;
+ }
+ }
bool is_shadowed = vet_shadowing && check_vet_shadowing(c, e, &ve_shadowed);
if (is_unused && is_shadowed) {
VettedEntity ve_both = ve_shadowed;
@@ -700,23 +744,43 @@ gb_internal void check_scope_usage(Checker *c, Scope *scope, u64 vet_flags) {
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) {
+ i64 sz = type_size_of(e->type);
+ // TODO(bill): When is a good size warn?
+ // Is >256 KiB good enough?
+ if (sz > 1ll<<18) {
+ gbString type_str = type_to_string(e->type);
+ warning(e->token, "Declaration of '%.*s' may cause a stack overflow due to its type '%s' having a size of %lld bytes", LIT(e->token.string), type_str, cast(long long)sz);
+ gb_string_free(type_str);
+ }
}
}
rw_mutex_shared_unlock(&scope->mutex);
- gb_sort(vetted_entities.data, vetted_entities.count, gb_size_of(VettedEntity), vetted_entity_variable_pos_cmp);
+ array_sort(vetted_entities, vetted_entity_variable_pos_cmp);
for (auto const &ve : vetted_entities) {
Entity *e = ve.entity;
Entity *other = ve.other;
String name = e->token.string;
+ vet_flags = original_vet_flags;
+ if (per_entity) {
+ vet_flags = ast_file_vet_flags(e->file);
+ }
+
if (ve.kind == VettedEntity_Shadowed_And_Unused) {
error(e->token, "'%.*s' declared but not used, possibly shadows declaration at line %d", LIT(name), other->token.pos.line);
} else if (vet_flags) {
switch (ve.kind) {
case VettedEntity_Unused:
- if (vet_flags & VetFlag_Unused) {
+ if (e->kind == Entity_Variable && (vet_flags & VetFlag_UnusedVariables) != 0) {
+ error(e->token, "'%.*s' declared but not used", LIT(name));
+ }
+ if (e->kind == Entity_Procedure && (vet_flags & VetFlag_UnusedProcedures) != 0) {
+ error(e->token, "'%.*s' declared but not used", LIT(name));
+ }
+ if ((e->kind == Entity_ImportName || e->kind == Entity_LibraryName) && (vet_flags & VetFlag_UnusedImports) != 0) {
error(e->token, "'%.*s' declared but not used", LIT(name));
}
break;
@@ -731,20 +795,13 @@ gb_internal void check_scope_usage(Checker *c, Scope *scope, u64 vet_flags) {
break;
}
}
-
- if (e->kind == Entity_Variable && (e->flags & (EntityFlag_Param|EntityFlag_Using)) == 0) {
- i64 sz = type_size_of(e->type);
- // TODO(bill): When is a good size warn?
- // Is 128 KiB good enough?
- if (sz >= 1ll<<17) {
- gbString type_str = type_to_string(e->type);
- warning(e->token, "Declaration of '%.*s' may cause a stack overflow due to its type '%s' having a size of %lld bytes", LIT(name), type_str, cast(long long)sz);
- gb_string_free(type_str);
- }
- }
}
- array_free(&vetted_entities);
+}
+
+
+gb_internal void check_scope_usage(Checker *c, Scope *scope, u64 vet_flags) {
+ check_scope_usage_internal(c, scope, vet_flags, false);
for (Scope *child = scope->head_child; child != nullptr; child = child->next) {
if (child->flags & (ScopeFlag_Proc|ScopeFlag_Type|ScopeFlag_File)) {
@@ -770,15 +827,17 @@ gb_internal void add_type_info_dependency(CheckerInfo *info, DeclInfo *d, Type *
rw_mutex_unlock(&d->type_info_deps_mutex);
}
-gb_internal AstPackage *get_core_package(CheckerInfo *info, String name) {
+
+gb_internal AstPackage *get_runtime_package(CheckerInfo *info) {
+ String name = str_lit("runtime");
gbAllocator a = heap_allocator();
- String path = get_fullpath_core(a, name);
+ String path = get_fullpath_base_collection(a, name, nullptr);
defer (gb_free(a, path.text));
auto found = string_map_get(&info->packages, path);
if (found == nullptr) {
gb_printf_err("Name: %.*s\n", LIT(name));
gb_printf_err("Fullpath: %.*s\n", LIT(path));
-
+
for (auto const &entry : info->packages) {
gb_printf_err("%.*s\n", LIT(entry.key));
}
@@ -787,14 +846,37 @@ gb_internal AstPackage *get_core_package(CheckerInfo *info, String name) {
return *found;
}
+gb_internal AstPackage *get_core_package(CheckerInfo *info, String name) {
+ if (name == "runtime") {
+ return get_runtime_package(info);
+ }
-gb_internal void add_package_dependency(CheckerContext *c, char const *package_name, char const *name) {
+ gbAllocator a = heap_allocator();
+ String path = get_fullpath_core_collection(a, name, nullptr);
+ defer (gb_free(a, path.text));
+ auto found = string_map_get(&info->packages, path);
+ if (found == nullptr) {
+ gb_printf_err("Name: %.*s\n", LIT(name));
+ gb_printf_err("Fullpath: %.*s\n", LIT(path));
+
+ for (auto const &entry : info->packages) {
+ gb_printf_err("%.*s\n", LIT(entry.key));
+ }
+ GB_ASSERT_MSG(found != nullptr, "Missing core package %.*s", LIT(name));
+ }
+ return *found;
+}
+
+gb_internal void add_package_dependency(CheckerContext *c, char const *package_name, char const *name, bool required=false) {
String n = make_string_c(name);
AstPackage *p = get_core_package(&c->checker->info, make_string_c(package_name));
Entity *e = scope_lookup(p->scope, n);
GB_ASSERT_MSG(e != nullptr, "%s", name);
GB_ASSERT(c->decl != nullptr);
e->flags |= EntityFlag_Used;
+ if (required) {
+ e->flags |= EntityFlag_Require;
+ }
add_dependency(c->info, c->decl, e);
}
@@ -815,6 +897,10 @@ gb_internal void add_declaration_dependency(CheckerContext *c, Entity *e) {
if (e == nullptr) {
return;
}
+ if (e->flags & EntityFlag_Disabled) {
+ // ignore the dependencies if it has been `@(disabled=true)`
+ return;
+ }
if (c->decl != nullptr) {
add_dependency(c->info, c->decl, e);
}
@@ -968,10 +1054,11 @@ gb_internal void init_universal(void) {
add_global_bool_constant("true", true);
add_global_bool_constant("false", false);
- add_global_string_constant("ODIN_VENDOR", bc->ODIN_VENDOR);
- add_global_string_constant("ODIN_VERSION", bc->ODIN_VERSION);
- add_global_string_constant("ODIN_ROOT", bc->ODIN_ROOT);
+ add_global_string_constant("ODIN_VENDOR", bc->ODIN_VENDOR);
+ add_global_string_constant("ODIN_VERSION", bc->ODIN_VERSION);
+ add_global_string_constant("ODIN_ROOT", bc->ODIN_ROOT);
add_global_string_constant("ODIN_BUILD_PROJECT_NAME", bc->ODIN_BUILD_PROJECT_NAME);
+ add_global_string_constant("ODIN_WINDOWS_SUBSYSTEM", bc->ODIN_WINDOWS_SUBSYSTEM);
{
GlobalEnumValue values[TargetOs_COUNT] = {
@@ -981,9 +1068,12 @@ gb_internal void init_universal(void) {
{"Linux", TargetOs_linux},
{"Essence", TargetOs_essence},
{"FreeBSD", TargetOs_freebsd},
+ {"Haiku", TargetOs_haiku},
{"OpenBSD", TargetOs_openbsd},
+ {"NetBSD", TargetOs_netbsd},
{"WASI", TargetOs_wasi},
{"JS", TargetOs_js},
+ {"Orca", TargetOs_orca},
{"Freestanding", TargetOs_freestanding},
};
@@ -1001,17 +1091,21 @@ gb_internal void init_universal(void) {
{"arm64", TargetArch_arm64},
{"wasm32", TargetArch_wasm32},
{"wasm64p32", TargetArch_wasm64p32},
+ {"riscv64", TargetArch_riscv64},
};
auto fields = add_global_enum_type(str_lit("Odin_Arch_Type"), values, gb_count_of(values));
add_global_enum_constant(fields, "ODIN_ARCH", bc->metrics.arch);
add_global_string_constant("ODIN_ARCH_STRING", target_arch_names[bc->metrics.arch]);
}
+
+ add_global_string_constant("ODIN_MICROARCH_STRING", get_final_microarchitecture());
{
GlobalEnumValue values[BuildMode_COUNT] = {
{"Executable", BuildMode_Executable},
{"Dynamic", BuildMode_DynamicLibrary},
+ {"Static", BuildMode_StaticLibrary},
{"Object", BuildMode_Object},
{"Assembly", BuildMode_Assembly},
{"LLVM_IR", BuildMode_LLVM_IR},
@@ -1067,24 +1161,59 @@ gb_internal void init_universal(void) {
scope_insert(intrinsics_pkg->scope, t_atomic_memory_order->Named.type_name);
}
+ {
+ int minimum_os_version = 0;
+ if (build_context.minimum_os_version_string != "") {
+ int major, minor, revision = 0;
+ #if defined(GB_SYSTEM_WINDOWS)
+ sscanf_s(cast(const char *)(build_context.minimum_os_version_string.text), "%d.%d.%d", &major, &minor, &revision);
+ #else
+ sscanf(cast(const char *)(build_context.minimum_os_version_string.text), "%d.%d.%d", &major, &minor, &revision);
+ #endif
+ minimum_os_version = (major*10000)+(minor*100)+revision;
+ }
+ add_global_constant("ODIN_MINIMUM_OS_VERSION", t_untyped_integer, exact_value_i64(minimum_os_version));
+ }
+
+ add_global_bool_constant("ODIN_DEBUG", bc->ODIN_DEBUG);
+ add_global_bool_constant("ODIN_DISABLE_ASSERT", bc->ODIN_DISABLE_ASSERT);
+ add_global_bool_constant("ODIN_DEFAULT_TO_NIL_ALLOCATOR", bc->ODIN_DEFAULT_TO_NIL_ALLOCATOR);
+ add_global_bool_constant("ODIN_NO_BOUNDS_CHECK", build_context.no_bounds_check);
+ add_global_bool_constant("ODIN_NO_TYPE_ASSERT", build_context.no_type_assert);
+ add_global_bool_constant("ODIN_DEFAULT_TO_PANIC_ALLOCATOR", bc->ODIN_DEFAULT_TO_PANIC_ALLOCATOR);
+ add_global_bool_constant("ODIN_NO_CRT", bc->no_crt);
+ add_global_bool_constant("ODIN_USE_SEPARATE_MODULES", bc->use_separate_modules);
+ add_global_bool_constant("ODIN_TEST", bc->command_kind == Command_test);
+ add_global_bool_constant("ODIN_NO_ENTRY_POINT", bc->no_entry_point);
+ add_global_bool_constant("ODIN_FOREIGN_ERROR_PROCEDURES", bc->ODIN_FOREIGN_ERROR_PROCEDURES);
+ add_global_bool_constant("ODIN_NO_RTTI", bc->no_rtti);
+
+ add_global_bool_constant("ODIN_VALGRIND_SUPPORT", bc->ODIN_VALGRIND_SUPPORT);
+ add_global_bool_constant("ODIN_TILDE", bc->tilde_backend);
- add_global_bool_constant("ODIN_DEBUG", bc->ODIN_DEBUG);
- add_global_bool_constant("ODIN_DISABLE_ASSERT", bc->ODIN_DISABLE_ASSERT);
- add_global_bool_constant("ODIN_DEFAULT_TO_NIL_ALLOCATOR", bc->ODIN_DEFAULT_TO_NIL_ALLOCATOR);
- add_global_bool_constant("ODIN_NO_DYNAMIC_LITERALS", bc->no_dynamic_literals);
- add_global_bool_constant("ODIN_NO_CRT", bc->no_crt);
- add_global_bool_constant("ODIN_USE_SEPARATE_MODULES", bc->use_separate_modules);
- add_global_bool_constant("ODIN_TEST", bc->command_kind == Command_test);
- add_global_bool_constant("ODIN_NO_ENTRY_POINT", bc->no_entry_point);
- add_global_bool_constant("ODIN_FOREIGN_ERROR_PROCEDURES", bc->ODIN_FOREIGN_ERROR_PROCEDURES);
- add_global_bool_constant("ODIN_NO_RTTI", bc->no_rtti);
+ add_global_constant("ODIN_COMPILE_TIMESTAMP", t_untyped_integer, exact_value_i64(odin_compile_timestamp()));
- add_global_bool_constant("ODIN_VALGRIND_SUPPORT", bc->ODIN_VALGRIND_SUPPORT);
- add_global_bool_constant("ODIN_TILDE", bc->tilde_backend);
+ {
+ String version = {};
- add_global_constant("ODIN_COMPILE_TIMESTAMP", t_untyped_integer, exact_value_i64(odin_compile_timestamp()));
+ #ifdef GIT_SHA
+ version.text = cast(u8 *)GIT_SHA;
+ version.len = gb_strlen(GIT_SHA);
+ #endif
- add_global_bool_constant("__ODIN_LLVM_F16_SUPPORTED", lb_use_new_pass_system());
+ add_global_string_constant("ODIN_VERSION_HASH", version);
+ }
+
+ {
+ bool f16_supported = lb_use_new_pass_system();
+ if (is_arch_wasm()) {
+ f16_supported = false;
+ } else if (build_context.metrics.os == TargetOs_darwin && build_context.metrics.arch == TargetArch_amd64) {
+ // NOTE(laytan): See #3222 for my ramblings on this.
+ f16_supported = false;
+ }
+ add_global_bool_constant("__ODIN_LLVM_F16_SUPPORTED", f16_supported);
+ }
{
GlobalEnumValue values[3] = {
@@ -1112,6 +1241,18 @@ gb_internal void init_universal(void) {
add_global_constant("ODIN_SANITIZER_FLAGS", named_type, exact_value_u64(bc->sanitizer_flags));
}
+ {
+ GlobalEnumValue values[5] = {
+ {"None", -1},
+ {"Minimal", 0},
+ {"Size", 1},
+ {"Speed", 2},
+ {"Aggressive", 3},
+ };
+
+ auto fields = add_global_enum_type(str_lit("Odin_Optimization_Mode"), values, gb_count_of(values));
+ add_global_enum_constant(fields, "ODIN_OPTIMIZATION_MODE", bc->optimization_level);
+ }
// Builtin Procedures
@@ -1173,7 +1314,7 @@ gb_internal void init_universal(void) {
}
if (defined_values_double_declaration) {
- gb_exit(1);
+ exit_with_errors();
}
@@ -1187,9 +1328,9 @@ gb_internal void init_universal(void) {
// intrinsics types for objective-c stuff
{
- t_objc_object = add_global_type_name(intrinsics_pkg->scope, str_lit("objc_object"), alloc_type_struct());
- t_objc_selector = add_global_type_name(intrinsics_pkg->scope, str_lit("objc_selector"), alloc_type_struct());
- t_objc_class = add_global_type_name(intrinsics_pkg->scope, str_lit("objc_class"), alloc_type_struct());
+ 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_id = alloc_type_pointer(t_objc_object);
t_objc_SEL = alloc_type_pointer(t_objc_selector);
@@ -1220,6 +1361,7 @@ gb_internal void init_checker_info(CheckerInfo *i) {
array_init(&i->init_procedures, a, 0, 0);
array_init(&i->fini_procedures, a, 0, 0);
array_init(&i->required_foreign_imports_through_force, a, 0, 0);
+ array_init(&i->defineables, a);
map_init(&i->objc_msgSend_types);
string_map_init(&i->load_file_cache);
@@ -1229,7 +1371,12 @@ gb_internal void init_checker_info(CheckerInfo *i) {
mpsc_init(&i->definition_queue, a); //); // 1<<20);
mpsc_init(&i->required_global_variable_queue, a); // 1<<10);
mpsc_init(&i->required_foreign_imports_through_force_queue, a); // 1<<10);
+ mpsc_init(&i->foreign_imports_to_check_fullpaths, a); // 1<<10);
+ mpsc_init(&i->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
+
+ string_map_init(&i->load_directory_cache);
+ map_init(&i->load_directory_map);
}
gb_internal void destroy_checker_info(CheckerInfo *i) {
@@ -1245,14 +1392,19 @@ gb_internal void destroy_checker_info(CheckerInfo *i) {
string_map_destroy(&i->packages);
array_free(&i->variable_init_order);
array_free(&i->required_foreign_imports_through_force);
+ array_free(&i->defineables);
mpsc_destroy(&i->entity_queue);
mpsc_destroy(&i->definition_queue);
mpsc_destroy(&i->required_global_variable_queue);
mpsc_destroy(&i->required_foreign_imports_through_force_queue);
+ mpsc_destroy(&i->foreign_imports_to_check_fullpaths);
+ mpsc_destroy(&i->foreign_decls_to_check);
map_destroy(&i->objc_msgSend_types);
string_map_destroy(&i->load_file_cache);
+ string_map_destroy(&i->load_directory_cache);
+ map_destroy(&i->load_directory_map);
}
gb_internal CheckerContext make_checker_context(Checker *c) {
@@ -1290,7 +1442,7 @@ gb_internal void reset_checker_context(CheckerContext *ctx, AstFile *file, Untyp
auto type_path = ctx->type_path;
array_clear(type_path);
- zero_size(&ctx->pkg, gb_size_of(CheckerContext) - gb_offset_of(CheckerContext, pkg));
+ gb_zero_size(&ctx->pkg, gb_size_of(CheckerContext) - gb_offset_of(CheckerContext, pkg));
ctx->file = nullptr;
ctx->scope = builtin_pkg->scope;
@@ -1326,6 +1478,7 @@ gb_internal void init_checker(Checker *c) {
array_init(&c->nested_proc_lits, heap_allocator(), 0, 1<<20);
mpsc_init(&c->global_untyped_queue, a); // , 1<<20);
+ mpsc_init(&c->soa_types_to_complete, a); // , 1<<20);
c->builtin_ctx = make_checker_context(c);
}
@@ -1338,6 +1491,7 @@ gb_internal void destroy_checker(Checker *c) {
array_free(&c->nested_proc_lits);
array_free(&c->procs_to_check);
mpsc_destroy(&c->global_untyped_queue);
+ mpsc_destroy(&c->soa_types_to_complete);
}
@@ -1372,6 +1526,7 @@ gb_internal Entity *implicit_entity_of_node(Ast *clause) {
}
gb_internal Entity *entity_of_node(Ast *expr) {
+retry:;
expr = unparen_expr(expr);
switch (expr->kind) {
case_ast_node(ident, Ident, expr);
@@ -1388,6 +1543,21 @@ gb_internal Entity *entity_of_node(Ast *expr) {
case_ast_node(cc, CaseClause, expr);
return cc->implicit_entity;
case_end;
+
+ case_ast_node(ce, CallExpr, expr);
+ return ce->entity_procedure_of;
+ case_end;
+
+ case_ast_node(we, TernaryWhenExpr, expr);
+ if (we->cond == nullptr) {
+ break;
+ }
+ if (we->cond->tav.value.kind != ExactValue_Bool) {
+ break;
+ }
+ expr = we->cond->tav.value.value_bool ? we->x : we->y;
+ goto retry;
+ case_end;
}
return nullptr;
}
@@ -1535,9 +1705,9 @@ gb_internal void add_type_and_value(CheckerContext *ctx, Ast *expr, AddressingMo
if (mode == Addressing_Constant || mode == Addressing_Invalid) {
expr->tav.value = value;
- } else if (mode == Addressing_Value && is_type_typeid(type)) {
+ } else if (mode == Addressing_Value && type != nullptr && is_type_typeid(type)) {
expr->tav.value = value;
- } else if (mode == Addressing_Value && is_type_proc(type)) {
+ } else if (mode == Addressing_Value && type != nullptr && is_type_proc(type)) {
expr->tav.value = value;
}
@@ -1637,6 +1807,26 @@ gb_internal bool add_entity_with_name(CheckerContext *c, Scope *scope, Ast *iden
}
return true;
}
+
+gb_internal bool add_entity_with_name(CheckerInfo *info, Scope *scope, Ast *identifier, Entity *entity, String name) {
+ if (scope == nullptr) {
+ return false;
+ }
+
+
+ if (!is_blank_ident(name)) {
+ Entity *ie = scope_insert(scope, entity);
+ if (ie != nullptr) {
+ return redeclaration_error(name, entity, ie);
+ }
+ }
+ if (identifier != nullptr) {
+ GB_ASSERT(entity->file != nullptr);
+ add_entity_definition(info, identifier, entity);
+ }
+ return true;
+}
+
gb_internal bool add_entity(CheckerContext *c, Scope *scope, Ast *identifier, Entity *entity) {
return add_entity_with_name(c, scope, identifier, entity, entity->token.string);
}
@@ -1649,13 +1839,14 @@ gb_internal void add_entity_use(CheckerContext *c, Ast *identifier, Entity *enti
entity->flags |= EntityFlag_Used;
if (entity_has_deferred_procedure(entity)) {
Entity *deferred = entity->Procedure.deferred_procedure.entity;
- add_entity_use(c, nullptr, deferred);
+ if (deferred != entity) {
+ add_entity_use(c, nullptr, deferred);
+ }
}
if (identifier == nullptr || identifier->kind != Ast_Ident) {
return;
}
- Ast *empty_ident = nullptr;
- entity->identifier.compare_exchange_strong(empty_ident, identifier);
+ entity->identifier.store(identifier);
identifier->Ident.entity = entity;
@@ -1818,8 +2009,7 @@ gb_internal void add_type_info_type_internal(CheckerContext *c, Type *t) {
add_type_info_dependency(c->info, c->decl, t);
MUTEX_GUARD_BLOCK(&c->info->type_info_mutex) {
- MapFindResult fr;
- auto found = map_try_get(&c->info->type_info_map, t, &fr);
+ auto found = map_get(&c->info->type_info_map, t);
if (found != nullptr) {
// Types have already been added
return;
@@ -1843,7 +2033,7 @@ gb_internal void add_type_info_type_internal(CheckerContext *c, Type *t) {
ti_index = c->info->type_info_types.count;
array_add(&c->info->type_info_types, t);
}
- map_set_internal_from_try_get(&c->checker->info.type_info_map, t, ti_index, fr);
+ map_set(&c->checker->info.type_info_map, t, ti_index);
if (prev) {
// NOTE(bill): If a previous one exists already, no need to continue
@@ -1961,6 +2151,8 @@ gb_internal void add_type_info_type_internal(CheckerContext *c, Type *t) {
break;
case Type_Struct:
+ if (bt->Struct.fields_wait_signal.futex.load() == 0)
+ return;
if (bt->Struct.scope != nullptr) {
for (auto const &entry : bt->Struct.scope->elements) {
Entity *e = entry.value;
@@ -1981,7 +2173,9 @@ gb_internal void add_type_info_type_internal(CheckerContext *c, Type *t) {
add_type_info_type_internal(c, bt->Struct.polymorphic_params);
for_array(i, bt->Struct.fields) {
Entity *f = bt->Struct.fields[i];
- add_type_info_type_internal(c, f->type);
+ if (f && f->type) {
+ add_type_info_type_internal(c, f->type);
+ }
}
add_comparison_procedures_for_fields(c, bt);
break;
@@ -2010,16 +2204,6 @@ gb_internal void add_type_info_type_internal(CheckerContext *c, Type *t) {
add_type_info_type_internal(c, bt->SimdVector.elem);
break;
- case Type_RelativePointer:
- add_type_info_type_internal(c, bt->RelativePointer.pointer_type);
- add_type_info_type_internal(c, bt->RelativePointer.base_integer);
- break;
-
- case Type_RelativeMultiPointer:
- add_type_info_type_internal(c, bt->RelativeMultiPointer.pointer_type);
- add_type_info_type_internal(c, bt->RelativeMultiPointer.base_integer);
- break;
-
case Type_Matrix:
add_type_info_type_internal(c, bt->Matrix.elem);
break;
@@ -2028,6 +2212,12 @@ gb_internal void add_type_info_type_internal(CheckerContext *c, Type *t) {
add_type_info_type_internal(c, bt->SoaPointer.elem);
break;
+ case Type_BitField:
+ add_type_info_type_internal(c, bt->BitField.backing_type);
+ for (Entity *f : bt->BitField.fields) {
+ add_type_info_type_internal(c, f->type);
+ }
+ break;
case Type_Generic:
break;
@@ -2104,7 +2294,7 @@ gb_internal void add_min_dep_type_info(Checker *c, Type *t) {
// IMPORTANT NOTE(bill): this must be copied as `map_set` takes a const ref
// and effectively assigns the `+1` of the value
isize const count = set->count;
- if (map_set_if_not_previously_exists(set, ti_index, count)) {
+ if (map_set_if_not_previously_exists(set, ti_index+1, count)) {
// Type already exists;
return;
}
@@ -2211,9 +2401,14 @@ gb_internal void add_min_dep_type_info(Checker *c, Type *t) {
Entity *e = entry.value;
switch (bt->Struct.soa_kind) {
case StructSoa_Dynamic:
+ add_min_dep_type_info(c, t_type_info_ptr); // append_soa
+
add_min_dep_type_info(c, t_allocator);
/*fallthrough*/
case StructSoa_Slice:
+ add_min_dep_type_info(c, t_int);
+ add_min_dep_type_info(c, t_uint);
+ /*fallthrough*/
case StructSoa_Fixed:
add_min_dep_type_info(c, alloc_type_pointer(e->type));
break;
@@ -2254,16 +2449,6 @@ gb_internal void add_min_dep_type_info(Checker *c, Type *t) {
add_min_dep_type_info(c, bt->SimdVector.elem);
break;
- case Type_RelativePointer:
- add_min_dep_type_info(c, bt->RelativePointer.pointer_type);
- add_min_dep_type_info(c, bt->RelativePointer.base_integer);
- break;
-
- case Type_RelativeMultiPointer:
- add_min_dep_type_info(c, bt->RelativeMultiPointer.pointer_type);
- add_min_dep_type_info(c, bt->RelativeMultiPointer.base_integer);
- break;
-
case Type_Matrix:
add_min_dep_type_info(c, bt->Matrix.elem);
break;
@@ -2272,6 +2457,13 @@ gb_internal void add_min_dep_type_info(Checker *c, Type *t) {
add_min_dep_type_info(c, bt->SoaPointer.elem);
break;
+ case Type_BitField:
+ add_min_dep_type_info(c, bt->BitField.backing_type);
+ for (Entity *f : bt->BitField.fields) {
+ add_min_dep_type_info(c, f->type);
+ }
+ break;
+
default:
GB_PANIC("Unhandled type: %*.s", LIT(type_strings[bt->kind]));
break;
@@ -2289,7 +2481,6 @@ gb_internal void add_dependency_to_set(Checker *c, Entity *entity) {
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;
@@ -2340,99 +2531,45 @@ gb_internal void force_add_dependency_entity(Checker *c, Scope *scope, String co
add_dependency_to_set(c, e);
}
+gb_internal void collect_testing_procedures_of_package(Checker *c, AstPackage *pkg) {
+ AstPackage *testing_package = get_core_package(&c->info, str_lit("testing"));
+ Scope *testing_scope = testing_package->scope;
+ Entity *test_signature = scope_lookup_current(testing_scope, str_lit("Test_Signature"));
+ Scope *s = pkg->scope;
+ for (auto const &entry : s->elements) {
+ Entity *e = entry.value;
+ if (e->kind != Entity_Procedure) {
+ continue;
+ }
-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);
- map_init(&c->info.minimum_dependency_type_info_set);
-
-#define FORCE_ADD_RUNTIME_ENTITIES(condition, ...) do { \
- if (condition) { \
- String entities[] = {__VA_ARGS__}; \
- for (isize i = 0; i < gb_count_of(entities); i++) { \
- force_add_dependency_entity(c, c->info.runtime_package->scope, entities[i]); \
- } \
- } \
-} while (0)
-
- // required runtime entities
- FORCE_ADD_RUNTIME_ENTITIES(true,
- // Odin types
- str_lit("Source_Code_Location"),
- str_lit("Context"),
- str_lit("Allocator"),
- str_lit("Logger"),
-
- // Odin internal procedures
- str_lit("__init_context"),
- str_lit("cstring_to_string"),
- str_lit("_cleanup_runtime"),
-
- // Pseudo-CRT required procedures
- str_lit("memset"),
- str_lit("memcpy"),
- str_lit("memmove"),
-
- // Utility procedures
- str_lit("memory_equal"),
- str_lit("memory_compare"),
- str_lit("memory_compare_zero"),
- );
-
- FORCE_ADD_RUNTIME_ENTITIES(!build_context.tilde_backend,
- // Extended data type internal procedures
- str_lit("umodti3"),
- str_lit("udivti3"),
- str_lit("modti3"),
- str_lit("divti3"),
- str_lit("fixdfti"),
- str_lit("fixunsdfti"),
- str_lit("fixunsdfdi"),
- str_lit("floattidf"),
- str_lit("floattidf_unsigned"),
- str_lit("truncsfhf2"),
- str_lit("truncdfhf2"),
- str_lit("gnu_h2f_ieee"),
- str_lit("gnu_f2h_ieee"),
- str_lit("extendhfsf2"),
-
- // WASM Specific
- str_lit("__ashlti3"),
- str_lit("__multi3"),
- );
-
- FORCE_ADD_RUNTIME_ENTITIES(!build_context.no_rtti,
- // Odin types
- str_lit("Type_Info"),
+ if ((e->flags & EntityFlag_Test) == 0) {
+ continue;
+ }
- // Global variables
- str_lit("type_table"),
- str_lit("__type_info_of"),
- );
+ String name = e->token.string;
- FORCE_ADD_RUNTIME_ENTITIES(!build_context.no_entry_point,
- // Global variables
- str_lit("args__"),
- );
+ bool is_tester = true;
- FORCE_ADD_RUNTIME_ENTITIES((build_context.no_crt && !is_arch_wasm()),
- // NOTE(bill): Only if these exist
- str_lit("_tls_index"),
- str_lit("_fltused"),
- );
+ Type *t = base_type(e->type);
+ GB_ASSERT(t->kind == Type_Proc);
+ if (are_types_identical(t, base_type(test_signature->type))) {
+ // Good
+ } else {
+ gbString str = type_to_string(t);
+ error(e->token, "Testing procedures must have a signature type of proc(^testing.T), got %s", str);
+ gb_string_free(str);
+ is_tester = false;
+ }
- FORCE_ADD_RUNTIME_ENTITIES(!build_context.no_bounds_check,
- // Bounds checking related procedures
- str_lit("bounds_check_error"),
- str_lit("matrix_bounds_check_error"),
- str_lit("slice_expr_error_hi"),
- str_lit("slice_expr_error_lo_hi"),
- str_lit("multi_pointer_slice_expr_error"),
- );
+ if (is_tester) {
+ add_dependency_to_set(c, e);
+ array_add(&c->info.testing_procedures, e);
+ }
+ }
+}
+gb_internal void generate_minimum_dependency_set_internal(Checker *c, Entity *start) {
for_array(i, c->info.definitions) {
Entity *e = c->info.definitions[i];
if (e->scope == builtin_pkg->scope) {
@@ -2490,6 +2627,11 @@ gb_internal void generate_minimum_dependency_set(Checker *c, Entity *start) {
is_init = false;
}
+ if ((e->flags & EntityFlag_Disabled) != 0) {
+ warning(e->token, "This @(init) procedure is disabled; you must call it manually");
+ is_init = false;
+ }
+
if (is_init) {
add_dependency_to_set(c, e);
array_add(&c->info.init_procedures, e);
@@ -2534,47 +2676,126 @@ gb_internal void generate_minimum_dependency_set(Checker *c, Entity *start) {
}
}
+ AstPackage *pkg = c->info.init_package;
+ collect_testing_procedures_of_package(c, pkg);
- Entity *test_signature = scope_lookup_current(testing_scope, str_lit("Test_Signature"));
+ if (build_context.test_all_packages) {
+ for (auto const &entry : c->info.packages) {
+ AstPackage *pkg = entry.value;
+ collect_testing_procedures_of_package(c, pkg);
+ }
+ }
+ } else if (start != nullptr) {
+ start->flags |= EntityFlag_Used;
+ add_dependency_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
- AstPackage *pkg = c->info.init_package;
- Scope *s = pkg->scope;
+ ptr_set_init(&c->info.minimum_dependency_set, min_dep_set_cap);
+ map_init(&c->info.minimum_dependency_type_info_set);
- for (auto const &entry : s->elements) {
- Entity *e = entry.value;
- if (e->kind != Entity_Procedure) {
- continue;
- }
+#define FORCE_ADD_RUNTIME_ENTITIES(condition, ...) do { \
+ if (condition) { \
+ String entities[] = {__VA_ARGS__}; \
+ for (isize i = 0; i < gb_count_of(entities); i++) { \
+ force_add_dependency_entity(c, c->info.runtime_package->scope, entities[i]); \
+ } \
+ } \
+} while (0)
- if ((e->flags & EntityFlag_Test) == 0) {
- continue;
- }
+ // required runtime entities
+ FORCE_ADD_RUNTIME_ENTITIES(true,
+ // Odin types
+ str_lit("Source_Code_Location"),
+ str_lit("Context"),
+ str_lit("Allocator"),
+ str_lit("Logger"),
- String name = e->token.string;
+ // Odin internal procedures
+ str_lit("__init_context"),
+ // str_lit("cstring_to_string"),
+ str_lit("_cleanup_runtime"),
- bool is_tester = true;
+ // Pseudo-CRT required procedures
+ str_lit("memset"),
- Type *t = base_type(e->type);
- GB_ASSERT(t->kind == Type_Proc);
- if (are_types_identical(t, base_type(test_signature->type))) {
- // Good
- } else {
- gbString str = type_to_string(t);
- error(e->token, "Testing procedures must have a signature type of proc(^testing.T), got %s", str);
- gb_string_free(str);
- is_tester = false;
- }
+ // Utility procedures
+ str_lit("memory_equal"),
+ str_lit("memory_compare"),
+ str_lit("memory_compare_zero"),
+ );
+
+ // Only required if no CRT is present
+ FORCE_ADD_RUNTIME_ENTITIES(build_context.no_crt,
+ str_lit("memcpy"),
+ str_lit("memmove"),
+ );
+
+ FORCE_ADD_RUNTIME_ENTITIES(build_context.metrics.arch == TargetArch_arm32,
+ str_lit("aeabi_d2h")
+ );
+
+ FORCE_ADD_RUNTIME_ENTITIES(is_arch_wasm() && !build_context.tilde_backend,
+ // // Extended data type internal procedures
+ // str_lit("umodti3"),
+ // str_lit("udivti3"),
+ // str_lit("modti3"),
+ // str_lit("divti3"),
+ // str_lit("fixdfti"),
+ // str_lit("fixunsdfti"),
+ // str_lit("fixunsdfdi"),
+ // str_lit("floattidf"),
+ // str_lit("floattidf_unsigned"),
+ // str_lit("truncsfhf2"),
+ // str_lit("truncdfhf2"),
+ // str_lit("gnu_h2f_ieee"),
+ // str_lit("gnu_f2h_ieee"),
+ // str_lit("extendhfsf2"),
+
+ // WASM Specific
+ str_lit("__ashlti3"),
+ str_lit("__multi3"),
+ str_lit("__lshrti3"),
+ );
+
+ FORCE_ADD_RUNTIME_ENTITIES(!build_context.no_rtti,
+ // Odin types
+ str_lit("Type_Info"),
+
+ // Global variables
+ str_lit("type_table"),
+ str_lit("__type_info_of"),
+ );
+
+ FORCE_ADD_RUNTIME_ENTITIES(!build_context.no_entry_point,
+ // Global variables
+ str_lit("args__"),
+ );
+
+ FORCE_ADD_RUNTIME_ENTITIES((build_context.no_crt && !is_arch_wasm()),
+ // NOTE(bill): Only if these exist
+ str_lit("_tls_index"),
+ str_lit("_fltused"),
+ );
+
+ FORCE_ADD_RUNTIME_ENTITIES(!build_context.no_bounds_check,
+ // Bounds checking related procedures
+ str_lit("bounds_check_error"),
+ str_lit("matrix_bounds_check_error"),
+ str_lit("slice_expr_error_hi"),
+ str_lit("slice_expr_error_lo_hi"),
+ str_lit("multi_pointer_slice_expr_error"),
+ );
+
+ add_dependency_to_set(c, c->info.instrumentation_enter_entity);
+ add_dependency_to_set(c, c->info.instrumentation_exit_entity);
+
+ generate_minimum_dependency_set_internal(c, start);
- if (is_tester) {
- add_dependency_to_set(c, e);
- array_add(&c->info.testing_procedures, e);
- }
- }
- } else if (start != nullptr) {
- start->flags |= EntityFlag_Used;
- add_dependency_to_set(c, start);
- }
#undef FORCE_ADD_RUNTIME_ENTITIES
}
@@ -2806,6 +3027,11 @@ gb_internal void init_core_type_info(Checker *c) {
return;
}
Entity *type_info_entity = find_core_entity(c, str_lit("Type_Info"));
+ GB_ASSERT(type_info_entity != nullptr);
+ if (type_info_entity->type == nullptr) {
+ check_single_global_entity(c, type_info_entity, type_info_entity->decl_info);
+ }
+ GB_ASSERT(type_info_entity->type != nullptr);
t_type_info = type_info_entity->type;
t_type_info_ptr = alloc_type_pointer(t_type_info);
@@ -2847,10 +3073,9 @@ gb_internal void init_core_type_info(Checker *c) {
t_type_info_map = find_core_type(c, str_lit("Type_Info_Map"));
t_type_info_bit_set = find_core_type(c, str_lit("Type_Info_Bit_Set"));
t_type_info_simd_vector = find_core_type(c, str_lit("Type_Info_Simd_Vector"));
- t_type_info_relative_pointer = find_core_type(c, str_lit("Type_Info_Relative_Pointer"));
- t_type_info_relative_multi_pointer = find_core_type(c, str_lit("Type_Info_Relative_Multi_Pointer"));
t_type_info_matrix = find_core_type(c, str_lit("Type_Info_Matrix"));
t_type_info_soa_pointer = find_core_type(c, str_lit("Type_Info_Soa_Pointer"));
+ t_type_info_bit_field = find_core_type(c, str_lit("Type_Info_Bit_Field"));
t_type_info_named_ptr = alloc_type_pointer(t_type_info_named);
t_type_info_integer_ptr = alloc_type_pointer(t_type_info_integer);
@@ -2876,10 +3101,9 @@ gb_internal void init_core_type_info(Checker *c) {
t_type_info_map_ptr = alloc_type_pointer(t_type_info_map);
t_type_info_bit_set_ptr = alloc_type_pointer(t_type_info_bit_set);
t_type_info_simd_vector_ptr = alloc_type_pointer(t_type_info_simd_vector);
- t_type_info_relative_pointer_ptr = alloc_type_pointer(t_type_info_relative_pointer);
- t_type_info_relative_multi_pointer_ptr = alloc_type_pointer(t_type_info_relative_multi_pointer);
t_type_info_matrix_ptr = alloc_type_pointer(t_type_info_matrix);
t_type_info_soa_pointer_ptr = alloc_type_pointer(t_type_info_soa_pointer);
+ t_type_info_bit_field_ptr = alloc_type_pointer(t_type_info_bit_field);
}
gb_internal void init_mem_allocator(Checker *c) {
@@ -2907,6 +3131,16 @@ gb_internal void init_core_source_code_location(Checker *c) {
t_source_code_location_ptr = alloc_type_pointer(t_source_code_location);
}
+gb_internal void init_core_load_directory_file(Checker *c) {
+ if (t_load_directory_file != nullptr) {
+ return;
+ }
+ t_load_directory_file = find_core_type(c, str_lit("Load_Directory_File"));
+ t_load_directory_file_ptr = alloc_type_pointer(t_load_directory_file);
+ t_load_directory_file_slice = alloc_type_slice(t_load_directory_file);
+}
+
+
gb_internal void init_core_map_type(Checker *c) {
if (t_map_info != nullptr) {
return;
@@ -2971,8 +3205,8 @@ gb_internal DECL_ATTRIBUTE_PROC(foreign_block_decl_attribute) {
return true;
} else if (name == "link_prefix") {
if (ev.kind == ExactValue_String) {
- String link_prefix = ev.value_string;
- if (!is_foreign_name_valid(link_prefix)) {
+ String link_prefix = string_trim_whitespace(ev.value_string);
+ if (link_prefix.len != 0 && !is_foreign_name_valid(link_prefix)) {
error(elem, "Invalid link prefix: '%.*s'", LIT(link_prefix));
} else {
c->foreign_context.link_prefix = link_prefix;
@@ -2981,6 +3215,18 @@ gb_internal DECL_ATTRIBUTE_PROC(foreign_block_decl_attribute) {
error(elem, "Expected a string value for '%.*s'", LIT(name));
}
return true;
+ } else if (name == "link_suffix") {
+ if (ev.kind == ExactValue_String) {
+ String link_suffix = string_trim_whitespace(ev.value_string);
+ if (link_suffix.len != 0 && !is_foreign_name_valid(link_suffix)) {
+ error(elem, "Invalid link suffix: '%.*s'", LIT(link_suffix));
+ } else {
+ c->foreign_context.link_suffix = link_suffix;
+ }
+ } else {
+ error(elem, "Expected a string value for '%.*s'", LIT(name));
+ }
+ return true;
} else if (name == "private") {
EntityVisiblityKind kind = EntityVisiblity_PrivateToPackage;
if (ev.kind == ExactValue_Invalid) {
@@ -2999,6 +3245,12 @@ gb_internal DECL_ATTRIBUTE_PROC(foreign_block_decl_attribute) {
}
c->foreign_context.visibility_kind = kind;
return true;
+ } else if (name == "require_results") {
+ if (value != nullptr) {
+ error(elem, "Expected no value for '%.*s'", LIT(name));
+ }
+ c->foreign_context.require_results = true;
+ return true;
}
return false;
@@ -3095,6 +3347,7 @@ gb_internal DECL_ATTRIBUTE_PROC(proc_decl_attribute) {
linkage == "link_once") {
ac->linkage = linkage;
} else {
+ ERROR_BLOCK();
error(elem, "Invalid linkage '%.*s'. Valid kinds:", LIT(linkage));
error_line("\tinternal\n");
error_line("\tstrong\n");
@@ -3267,36 +3520,34 @@ gb_internal DECL_ATTRIBUTE_PROC(proc_decl_attribute) {
if (ev.kind == ExactValue_String) {
ac->link_prefix = ev.value_string;
- if (!is_foreign_name_valid(ac->link_prefix)) {
+ if (ac->link_prefix.len != 0 && !is_foreign_name_valid(ac->link_prefix)) {
error(elem, "Invalid link prefix: %.*s", LIT(ac->link_prefix));
}
} else {
error(elem, "Expected a string value for '%.*s'", LIT(name));
}
return true;
- } else if (name == "deprecated") {
+ } else if (name == "link_suffix") {
ExactValue ev = check_decl_attribute_value(c, value);
if (ev.kind == ExactValue_String) {
- String msg = ev.value_string;
- if (msg.len == 0) {
- error(elem, "Deprecation message cannot be an empty string");
- } else {
- ac->deprecated_message = msg;
+ ac->link_suffix = ev.value_string;
+ if (ac->link_suffix.len != 0 && !is_foreign_name_valid(ac->link_suffix)) {
+ error(elem, "Invalid link suffix: %.*s", LIT(ac->link_suffix));
}
} else {
error(elem, "Expected a string value for '%.*s'", LIT(name));
}
return true;
- } else if (name == "warning") {
+ } else if (name == "deprecated") {
ExactValue ev = check_decl_attribute_value(c, value);
if (ev.kind == ExactValue_String) {
String msg = ev.value_string;
if (msg.len == 0) {
- error(elem, "Warning message cannot be an empty string");
+ error(elem, "Deprecation message cannot be an empty string");
} else {
- ac->warning_message = msg;
+ ac->deprecated_message = msg;
}
} else {
error(elem, "Expected a string value for '%.*s'", LIT(name));
@@ -3336,18 +3587,19 @@ gb_internal DECL_ATTRIBUTE_PROC(proc_decl_attribute) {
String mode = ev.value_string;
if (mode == "none") {
ac->optimization_mode = ProcedureOptimizationMode_None;
+ } else if (mode == "favor_size") {
+ ac->optimization_mode = ProcedureOptimizationMode_FavorSize;
} else if (mode == "minimal") {
- ac->optimization_mode = ProcedureOptimizationMode_Minimal;
+ error(elem, "Invalid optimization_mode 'minimal' for '%.*s', mode has been removed due to confusion, but 'none' has the same behaviour", LIT(name));
} else if (mode == "size") {
- ac->optimization_mode = ProcedureOptimizationMode_Size;
+ error(elem, "Invalid optimization_mode 'size' for '%.*s', mode has been removed due to confusion, but 'favor_size' has the same behaviour", LIT(name));
} else if (mode == "speed") {
- ac->optimization_mode = ProcedureOptimizationMode_Speed;
+ error(elem, "Invalid optimization_mode 'speed' for '%.*s', mode has been removed due to confusion, but 'favor_size' has the same behaviour", LIT(name));
} else {
+ ERROR_BLOCK();
error(elem, "Invalid optimization_mode for '%.*s'. Valid modes:", LIT(name));
error_line("\tnone\n");
- error_line("\tminimal\n");
- error_line("\tsize\n");
- error_line("\tspeed\n");
+ error_line("\tfavor_size\n");
}
} else {
error(elem, "Expected a string for '%.*s'", LIT(name));
@@ -3405,6 +3657,39 @@ gb_internal DECL_ATTRIBUTE_PROC(proc_decl_attribute) {
error(elem, "Expected a string value for '%.*s'", LIT(name));
}
return true;
+ } else if (name == "entry_point_only") {
+ if (value != nullptr) {
+ error(value, "'%.*s' expects no parameter", LIT(name));
+ }
+ ac->entry_point_only = true;
+ return true;
+ } else if (name == "no_instrumentation") {
+ ExactValue ev = check_decl_attribute_value(c, value);
+ if (ev.kind == ExactValue_Invalid) {
+ ac->no_instrumentation = Instrumentation_Disabled;
+ } else if (ev.kind == ExactValue_Bool) {
+ if (ev.value_bool) {
+ ac->no_instrumentation = Instrumentation_Disabled;
+ } else {
+ ac->no_instrumentation = Instrumentation_Enabled;
+ }
+ } else {
+ error(value, "Expected either a boolean or no parameter for '%.*s'", LIT(name));
+ return false;
+ }
+ return true;
+ } else if (name == "instrumentation_enter") {
+ if (value != nullptr) {
+ error(value, "'%.*s' expects no parameter", LIT(name));
+ }
+ ac->instrumentation_enter = true;
+ return true;
+ } else if (name == "instrumentation_exit") {
+ if (value != nullptr) {
+ error(value, "'%.*s' expects no parameter", LIT(name));
+ }
+ ac->instrumentation_exit = true;
+ return true;
}
return false;
}
@@ -3422,6 +3707,12 @@ gb_internal DECL_ATTRIBUTE_PROC(var_decl_attribute) {
}
ac->is_static = true;
return true;
+ } else if (name == "rodata") {
+ if (value != nullptr) {
+ error(elem, "'rodata' does not have any parameters");
+ }
+ ac->rodata = true;
+ return true;
} else if (name == "thread_local") {
ExactValue ev = check_decl_attribute_value(c, value);
if (ac->init_expr_list_count > 0) {
@@ -3440,6 +3731,7 @@ gb_internal DECL_ATTRIBUTE_PROC(var_decl_attribute) {
model == "localexec") {
ac->thread_local_model = model;
} else {
+ ERROR_BLOCK();
error(elem, "Invalid thread local model '%.*s'. Valid models:", LIT(model));
error_line("\tdefault\n");
error_line("\tlocaldynamic\n");
@@ -3490,6 +3782,7 @@ gb_internal DECL_ATTRIBUTE_PROC(var_decl_attribute) {
linkage == "link_once") {
ac->linkage = linkage;
} else {
+ ERROR_BLOCK();
error(elem, "Invalid linkage '%.*s'. Valid kinds:", LIT(linkage));
error_line("\tinternal\n");
error_line("\tstrong\n");
@@ -3512,13 +3805,24 @@ gb_internal DECL_ATTRIBUTE_PROC(var_decl_attribute) {
ExactValue ev = check_decl_attribute_value(c, value);
if (ev.kind == ExactValue_String) {
ac->link_prefix = ev.value_string;
- if (!is_foreign_name_valid(ac->link_prefix)) {
+ if (ac->link_prefix.len != 0 && !is_foreign_name_valid(ac->link_prefix)) {
error(elem, "Invalid link prefix: %.*s", LIT(ac->link_prefix));
}
} else {
error(elem, "Expected a string value for '%.*s'", LIT(name));
}
return true;
+ } else if (name == "link_suffix") {
+ ExactValue ev = check_decl_attribute_value(c, value);
+ if (ev.kind == ExactValue_String) {
+ ac->link_suffix = ev.value_string;
+ if (ac->link_suffix.len != 0 && !is_foreign_name_valid(ac->link_suffix)) {
+ error(elem, "Invalid link suffix: %.*s", LIT(ac->link_suffix));
+ }
+ } else {
+ error(elem, "Expected a string value for '%.*s'", LIT(name));
+ }
+ return true;
} else if (name == "link_section") {
ExactValue ev = check_decl_attribute_value(c, value);
if (ev.kind == ExactValue_String) {
@@ -3544,6 +3848,16 @@ gb_internal DECL_ATTRIBUTE_PROC(const_decl_attribute) {
} else if (name == "private") {
// NOTE(bill): Handled elsewhere `check_collect_value_decl`
return true;
+ } else if (name == "static" ||
+ name == "thread_local" ||
+ name == "require" ||
+ name == "linkage" ||
+ name == "link_name" ||
+ name == "link_prefix" ||
+ name == "link_suffix" ||
+ false) {
+ error(elem, "@(%.*s) is not supported for compile time constant value declarations", LIT(name));
+ return true;
}
return false;
}
@@ -3583,8 +3897,10 @@ gb_internal void check_decl_attributes(CheckerContext *c, Array<Ast *> const &at
if (attributes.count == 0) return;
String original_link_prefix = {};
+ String original_link_suffix = {};
if (ac) {
original_link_prefix = ac->link_prefix;
+ original_link_suffix = ac->link_suffix;
}
StringSet set = {};
@@ -3643,9 +3959,11 @@ gb_internal void check_decl_attributes(CheckerContext *c, Array<Ast *> const &at
}
if (!proc(c, elem, name, value, ac)) {
- if (!build_context.ignore_unknown_attributes) {
+ if (!build_context.ignore_unknown_attributes &&
+ !string_set_exists(&build_context.custom_attributes, name)) {
+ ERROR_BLOCK();
error(elem, "Unknown attribute element name '%.*s'", LIT(name));
- error_line("\tDid you forget to use build flag '-ignore-unknown-attributes'?\n");
+ error_line("\tDid you forget to use the build flag '-ignore-unknown-attributes' or '-custom-attribute:%.*s'?\n", LIT(name));
}
}
}
@@ -3658,6 +3976,12 @@ gb_internal void check_decl_attributes(CheckerContext *c, Array<Ast *> const &at
ac->link_prefix.len = 0;
}
}
+ if (ac->link_suffix.text == original_link_suffix.text) {
+ if (ac->link_name.len > 0) {
+ ac->link_suffix.text = nullptr;
+ ac->link_suffix.len = 0;
+ }
+ }
}
}
@@ -3713,6 +4037,8 @@ gb_internal bool check_arity_match(CheckerContext *c, AstValueDecl *vd, bool is_
gb_string_free(str);
return false;
} else if (is_global) {
+ ERROR_BLOCK();
+
Ast *n = vd->values[rhs-1];
error(n, "Expected %td expressions on the right hand side, got %td", lhs, rhs);
error_line("Note: Global declarations do not allow for multi-valued expressions");
@@ -3764,6 +4090,7 @@ gb_internal void check_builtin_attributes(CheckerContext *ctx, Entity *e, Array<
case Entity_ProcGroup:
case Entity_Procedure:
case Entity_TypeName:
+ case Entity_Constant:
// Okay
break;
default:
@@ -3832,6 +4159,7 @@ gb_internal void check_collect_value_decl(CheckerContext *c, Ast *decl) {
bool is_test = false;
bool is_init = false;
bool is_fini = false;
+ bool is_priv = false;
for_array(i, vd->attributes) {
Ast *attr = vd->attributes[i];
@@ -3876,6 +4204,8 @@ gb_internal void check_collect_value_decl(CheckerContext *c, Ast *decl) {
}
if (!success) {
error(value, "'%.*s' expects no parameter, or a string literal containing \"file\" or \"package\"", LIT(name));
+ } else {
+ is_priv = true;
}
@@ -3897,6 +4227,11 @@ gb_internal void check_collect_value_decl(CheckerContext *c, Ast *decl) {
}
}
+ if (is_priv && is_test) {
+ error(decl, "Attribute 'private' is not allowed on a test case");
+ return;
+ }
+
if (entity_visibility_kind == EntityVisiblity_Public &&
(c->scope->flags&ScopeFlag_File) &&
c->scope->file) {
@@ -3931,6 +4266,7 @@ gb_internal void check_collect_value_decl(CheckerContext *c, Ast *decl) {
Entity *e = alloc_entity_variable(c->scope, name->Ident.token, nullptr);
e->identifier = name;
e->file = c->file;
+ e->Variable.is_global = true;
if (entity_visibility_kind != EntityVisiblity_Public) {
e->flags |= EntityFlag_NotExported;
@@ -3948,6 +4284,7 @@ gb_internal void check_collect_value_decl(CheckerContext *c, Ast *decl) {
e->Variable.foreign_library_ident = fl;
e->Variable.link_prefix = c->foreign_context.link_prefix;
+ e->Variable.link_suffix = c->foreign_context.link_suffix;
}
Ast *init_expr = value;
@@ -4002,6 +4339,7 @@ gb_internal void check_collect_value_decl(CheckerContext *c, Ast *decl) {
}
ast_node(pl, ProcLit, init);
e = alloc_entity_procedure(d->scope, token, nullptr, pl->tags);
+ d->foreign_require_results = c->foreign_context.require_results;
if (fl != nullptr) {
GB_ASSERT(fl->kind == Ast_Ident);
e->Procedure.foreign_library_ident = fl;
@@ -4014,15 +4352,15 @@ gb_internal void check_collect_value_decl(CheckerContext *c, Ast *decl) {
if (c->foreign_context.default_cc > 0) {
cc = c->foreign_context.default_cc;
} else if (is_arch_wasm()) {
- begin_error_block();
+ ERROR_BLOCK();
error(init, "For wasm related targets, it is required that you either define the"
" @(default_calling_convention=<string>) on the foreign block or"
" explicitly assign it on the procedure signature");
error_line("\tSuggestion: when dealing with normal Odin code (e.g. js_wasm32), use \"contextless\"; when dealing with Emscripten like code, use \"c\"\n");
- end_error_block();
}
}
e->Procedure.link_prefix = c->foreign_context.link_prefix;
+ e->Procedure.link_suffix = c->foreign_context.link_suffix;
GB_ASSERT(cc != ProcCC_Invalid);
pl->type->ProcType.calling_convention = cc;
@@ -4066,8 +4404,7 @@ gb_internal void check_collect_value_decl(CheckerContext *c, Ast *decl) {
if (e->kind != Entity_Procedure) {
if (fl != nullptr) {
- begin_error_block();
- defer (end_error_block());
+ ERROR_BLOCK();
AstKind kind = init->kind;
error(name, "Only procedures and variables are allowed to be in a foreign block, got %.*s", LIT(ast_strings[kind]));
@@ -4128,17 +4465,22 @@ gb_internal bool correct_single_type_alias(CheckerContext *c, Entity *e) {
gb_internal bool correct_type_alias_in_scope_backwards(CheckerContext *c, Scope *s) {
bool correction = false;
- u32 n = s->elements.count;
- for (u32 i = n-1; i < n; i--) {
- correction |= correct_single_type_alias(c, s->elements.entries[i].value);
+ for (u32 n = s->elements.count, i = n-1; i < n; i--) {
+ auto const &entry = s->elements.entries[i];
+ Entity *e = entry.value;
+ if (entry.hash && e != nullptr) {
+ correction |= correct_single_type_alias(c, e);
+ }
}
return correction;
}
gb_internal bool correct_type_alias_in_scope_forwards(CheckerContext *c, Scope *s) {
bool correction = false;
- u32 n = s->elements.count;
- for (isize i = 0; i < n; i++) {
- correction |= correct_single_type_alias(c, s->elements.entries[i].value);
+ for (auto const &entry : s->elements) {
+ Entity *e = entry.value;
+ if (e != nullptr) {
+ correction |= correct_single_type_alias(c, entry.value);
+ }
}
return correction;
}
@@ -4221,7 +4563,9 @@ gb_internal void check_collect_entities(CheckerContext *c, Slice<Ast *> const &n
case_end;
case_ast_node(fb, ForeignBlockDecl, decl);
- check_add_foreign_block_decl(c, decl);
+ if (curr_file != nullptr) {
+ array_add(&curr_file->delayed_decls_queues[AstDelayQueue_ForeignBlock], decl);
+ }
case_end;
default:
@@ -4237,6 +4581,14 @@ gb_internal void check_collect_entities(CheckerContext *c, Slice<Ast *> const &n
// NOTE(bill): 'when' stmts need to be handled after the other as the condition may refer to something
// declared after this stmt in source
if (curr_file == nullptr) {
+ // For 'foreign' block statements that are not in file scope.
+ for_array(decl_index, nodes) {
+ Ast *decl = nodes[decl_index];
+ if (decl->kind == Ast_ForeignBlockDecl) {
+ check_add_foreign_block_decl(c, decl);
+ }
+ }
+
for_array(decl_index, nodes) {
Ast *decl = nodes[decl_index];
if (decl->kind == Ast_WhenStmt) {
@@ -4286,6 +4638,8 @@ gb_internal void check_single_global_entity(Checker *c, Entity *e, DeclInfo *d)
}
gb_internal void check_all_global_entities(Checker *c) {
+ in_single_threaded_checker_stage = true;
+
// NOTE(bill): This must be single threaded
// Don't bother trying
for_array(i, c->info.entities) {
@@ -4297,10 +4651,16 @@ gb_internal void check_all_global_entities(Checker *c) {
DeclInfo *d = e->decl_info;
check_single_global_entity(c, e, d);
if (e->type != nullptr && is_type_typed(e->type)) {
+ for (Type *t = nullptr; mpsc_dequeue(&c->soa_types_to_complete, &t); /**/) {
+ complete_soa_type(c, t, false);
+ }
+
(void)type_size_of(e->type);
(void)type_align_of(e->type);
}
}
+
+ in_single_threaded_checker_stage = false;
}
@@ -4387,7 +4747,7 @@ gb_internal void add_import_dependency_node(Checker *c, Ast *decl, PtrMap<AstPac
if (found == nullptr) {
Token token = ast_token(decl);
error(token, "Unable to find package: %.*s", LIT(path));
- gb_exit(1);
+ exit_with_errors();
}
AstPackage *pkg = *found;
GB_ASSERT(pkg->scope != nullptr);
@@ -4508,10 +4868,10 @@ gb_internal Array<ImportPathItem> find_import_path(Checker *c, AstPackage *start
continue;
}
- if (pkg->kind == Package_Runtime) {
- // NOTE(bill): Allow cyclic imports within the runtime package for the time being
- continue;
- }
+ // if (pkg->kind == Package_Runtime) {
+ // // NOTE(bill): Allow cyclic imports within the runtime package for the time being
+ // continue;
+ // }
ImportPathItem item = {pkg, decl};
if (pkg == end) {
@@ -4615,12 +4975,18 @@ gb_internal void check_add_import_decl(CheckerContext *ctx, Ast *decl) {
}
- if (import_name.len == 0) {
+ if (is_blank_ident(import_name) && !is_blank_ident(id->import_name.string)) {
String invalid_name = id->fullpath;
invalid_name = get_invalid_import_name(invalid_name);
- error(id->token, "Import name %.*s, is not a valid identifier. Perhaps you want to reference the package by a different name like this: import <new_name> \"%.*s\" ", LIT(invalid_name), LIT(invalid_name));
- error(token, "Import name, %.*s, cannot be use as an import name as it is not a valid identifier", LIT(id->import_name.string));
+ ERROR_BLOCK();
+
+ if (id->import_name.string.len > 0) {
+ error(token, "Import name '%.*s' cannot be use as an import name as it is not a valid identifier", LIT(id->import_name.string));
+ } else {
+ error(id->token, "Import name '%.*s' is not a valid identifier", LIT(invalid_name));
+ error_line("\tSuggestion: Rename the directory or explicitly set an import name like this 'import <new_name> %.*s'", LIT(id->relpath.string));
+ }
} else {
GB_ASSERT(id->import_name.pos.line != 0);
id->import_name.string = import_name;
@@ -4672,6 +5038,114 @@ gb_internal DECL_ATTRIBUTE_PROC(foreign_import_decl_attribute) {
return false;
}
+gb_internal void check_foreign_import_fullpaths(Checker *c) {
+ CheckerContext ctx = make_checker_context(c);
+
+ UntypedExprInfoMap untyped = {};
+ defer (map_destroy(&untyped));
+
+ for (Entity *e = nullptr; mpsc_dequeue(&c->info.foreign_imports_to_check_fullpaths, &e); /**/) {
+ GB_ASSERT(e != nullptr);
+ GB_ASSERT(e->kind == Entity_LibraryName);
+ Ast *decl = e->LibraryName.decl;
+ ast_node(fl, ForeignImportDecl, decl);
+
+ AstFile *f = decl->file();
+
+ reset_checker_context(&ctx, f, &untyped);
+ ctx.collect_delayed_decls = false;
+
+ GB_ASSERT(ctx.scope == e->scope);
+
+ if (fl->fullpaths.count == 0) {
+ String base_dir = dir_from_path(decl->file()->fullpath);
+
+ auto fullpaths = array_make<String>(permanent_allocator(), 0, fl->filepaths.count);
+
+ for (Ast *fp_node : fl->filepaths) {
+ Operand op = {};
+ check_expr(&ctx, &op, fp_node);
+ if (op.mode != Addressing_Constant && op.value.kind != ExactValue_String) {
+ gbString s = expr_to_string(op.expr);
+ error(fp_node, "Expected a constant string value, got '%s'", s);
+ gb_string_free(s);
+ continue;
+ }
+ if (!is_type_string(op.type)) {
+ gbString s = type_to_string(op.type);
+ error(fp_node, "Expected a constant string value, got value of type '%s'", s);
+ gb_string_free(s);
+ continue;
+ }
+
+ String file_str = op.value.value_string;
+ file_str = string_trim_whitespace(file_str);
+ String fullpath = file_str;
+ if (!is_arch_wasm() || string_ends_with(file_str, str_lit(".o"))) {
+ String foreign_path = {};
+ bool ok = determine_path_from_string(nullptr, decl, base_dir, file_str, &foreign_path, /*use error not syntax_error*/true);
+ if (ok) {
+ fullpath = foreign_path;
+ }
+ }
+ array_add(&fullpaths, fullpath);
+ }
+ fl->fullpaths = slice_from_array(fullpaths);
+ }
+
+ for (String const &path : fl->fullpaths) {
+ String ext = path_extension(path);
+ if (str_eq_ignore_case(ext, ".c") ||
+ str_eq_ignore_case(ext, ".cpp") ||
+ str_eq_ignore_case(ext, ".cxx") ||
+ str_eq_ignore_case(ext, ".h") ||
+ str_eq_ignore_case(ext, ".hpp") ||
+ str_eq_ignore_case(ext, ".hxx") ||
+ false
+ ) {
+ error(fl->token, "With 'foreign import', you cannot import a %.*s file/directory, you must precompile the library and link against that", LIT(ext));
+ break;
+ }
+ }
+
+ add_untyped_expressions(ctx.info, &untyped);
+
+ e->LibraryName.paths = fl->fullpaths;
+ }
+
+ for (Entity *e = nullptr; mpsc_dequeue(&c->info.foreign_decls_to_check, &e); /**/) {
+ GB_ASSERT(e != nullptr);
+ if (e->kind != Entity_Procedure) {
+ continue;
+ }
+ if (!is_arch_wasm()) {
+ continue;
+ }
+ Entity *foreign_library = e->Procedure.foreign_library;
+ GB_ASSERT(foreign_library != nullptr);
+
+ String name = e->Procedure.link_name;
+
+ String module_name = str_lit("env");
+ GB_ASSERT (foreign_library->kind == Entity_LibraryName);
+ if (foreign_library->LibraryName.paths.count != 1) {
+ error(foreign_library->token, "'foreign import' for '%.*s' architecture may only have one path, got %td",
+ LIT(target_arch_names[build_context.metrics.arch]), foreign_library->LibraryName.paths.count);
+ }
+
+ if (foreign_library->LibraryName.paths.count >= 1) {
+ module_name = foreign_library->LibraryName.paths[0];
+ }
+
+ if (!string_ends_with(module_name, str_lit(".o"))) {
+ name = concatenate3_strings(permanent_allocator(), module_name, WASM_MODULE_NAME_SEPARATOR, name);
+ }
+ e->Procedure.link_name = name;
+
+ check_foreign_procedure(&ctx, e, e->decl_info);
+ }
+}
+
gb_internal void check_add_foreign_import_decl(CheckerContext *ctx, Ast *decl) {
if (decl->state_flags & StateFlag_BeenHandled) return;
decl->state_flags |= StateFlag_BeenHandled;
@@ -4681,43 +5155,26 @@ gb_internal void check_add_foreign_import_decl(CheckerContext *ctx, Ast *decl) {
Scope *parent_scope = ctx->scope;
GB_ASSERT(parent_scope->flags&ScopeFlag_File);
- GB_ASSERT(fl->fullpaths.count > 0);
- String fullpath = fl->fullpaths[0];
- String library_name = path_to_entity_name(fl->library_name.string, fullpath);
- if (is_blank_ident(library_name)) {
- error(fl->token, "File name, %.*s, cannot be as a library name as it is not a valid identifier", LIT(fl->library_name.string));
+ String library_name = fl->library_name.string;
+ if (library_name.len == 0 && fl->fullpaths.count != 0) {
+ String fullpath = fl->fullpaths[0];
+ library_name = path_to_entity_name(fl->library_name.string, fullpath);
+ }
+ if (library_name.len == 0 || is_blank_ident(library_name)) {
+ error(fl->token, "File name, '%.*s', cannot be as a library name as it is not a valid identifier", LIT(library_name));
return;
}
- // if (fl->collection_name != "system") {
- // char *c_str = gb_alloc_array(heap_allocator(), char, fullpath.len+1);
- // defer (gb_free(heap_allocator(), c_str));
- // gb_memmove(c_str, fullpath.text, fullpath.len);
- // c_str[fullpath.len] = '\0';
-
- // gbFile f = {};
- // gbFileError file_err = gb_file_open(&f, c_str);
- // defer (gb_file_close(&f));
-
- // switch (file_err) {
- // case gbFileError_Invalid:
- // error(decl, "Invalid file or cannot be found ('%.*s')", LIT(fullpath));
- // return;
- // case gbFileError_NotExists:
- // error(decl, "File cannot be found ('%.*s')", LIT(fullpath));
- // return;
- // }
- // }
GB_ASSERT(fl->library_name.pos.line != 0);
fl->library_name.string = library_name;
Entity *e = alloc_entity_library_name(parent_scope, fl->library_name, t_invalid,
fl->fullpaths, library_name);
+ e->LibraryName.decl = decl;
add_entity_flags_from_file(ctx, e, parent_scope);
add_entity(ctx, parent_scope, nullptr, e);
-
AttributeContext ac = {};
check_decl_attributes(ctx, fl->attributes, foreign_import_decl_attribute, &ac);
if (ac.require_declaration) {
@@ -4732,12 +5189,8 @@ gb_internal void check_add_foreign_import_decl(CheckerContext *ctx, Ast *decl) {
e->LibraryName.extra_linker_flags = extra_linker_flags;
}
- if (has_asm_extension(fullpath)) {
- if (build_context.metrics.arch != TargetArch_amd64) {
- error(decl, "Assembly files are not yet supported on this platform: %.*s_%.*s",
- LIT(target_os_names[build_context.metrics.os]), LIT(target_arch_names[build_context.metrics.arch]));
- }
- }
+ mpsc_enqueue(&ctx->info->foreign_imports_to_check_fullpaths, e);
+
}
// Returns true if a new package is present
@@ -4844,9 +5297,9 @@ gb_internal bool collect_file_decl(CheckerContext *ctx, Ast *decl) {
case_end;
case_ast_node(fb, ForeignBlockDecl, decl);
- if (check_add_foreign_block_decl(ctx, decl)) {
- return true;
- }
+ GB_ASSERT(ctx->collect_delayed_decls);
+ decl->state_flags |= StateFlag_BeenHandled;
+ array_add(&curr_file->delayed_decls_queues[AstDelayQueue_ForeignBlock], decl);
case_end;
case_ast_node(ws, WhenStmt, decl);
@@ -4911,7 +5364,7 @@ gb_internal void check_create_file_scopes(Checker *c) {
for_array(i, c->parser->packages) {
AstPackage *pkg = c->parser->packages[i];
- gb_sort_array(pkg->files.data, pkg->files.count, sort_file_by_name);
+ array_sort(pkg->files, sort_file_by_name);
isize total_pkg_decl_count = 0;
for_array(j, pkg->files) {
@@ -5129,8 +5582,6 @@ gb_internal void check_import_entities(Checker *c) {
for_array(i, pkg->files) {
AstFile *f = pkg->files[i];
reset_checker_context(&ctx, f, &untyped);
- ctx.collect_delayed_decls = false;
-
correct_type_aliases_in_scope(&ctx, pkg->scope);
}
@@ -5138,6 +5589,17 @@ gb_internal void check_import_entities(Checker *c) {
AstFile *f = pkg->files[i];
reset_checker_context(&ctx, f, &untyped);
+ ctx.collect_delayed_decls = true;
+ for (Ast *decl : f->delayed_decls_queues[AstDelayQueue_ForeignBlock]) {
+ check_add_foreign_block_decl(&ctx, decl);
+ }
+ array_clear(&f->delayed_decls_queues[AstDelayQueue_ForeignBlock]);
+ }
+
+ for_array(i, pkg->files) {
+ AstFile *f = pkg->files[i];
+ reset_checker_context(&ctx, f, &untyped);
+
for (Ast *expr : f->delayed_decls_queues[AstDelayQueue_Expr]) {
Operand o = {};
check_expr(&ctx, &o, expr);
@@ -5304,6 +5766,59 @@ gb_internal void calculate_global_init_order(Checker *c) {
}
}
+gb_internal void check_procedure_later_from_entity(Checker *c, Entity *e, char const *from_msg) {
+ if (e == nullptr || e->kind != Entity_Procedure) {
+ return;
+ }
+ if (e->Procedure.is_foreign) {
+ return;
+ }
+ if ((e->flags & EntityFlag_ProcBodyChecked) != 0) {
+ return;
+ }
+ if ((e->flags & EntityFlag_Overridden) != 0) {
+ // NOTE (zen3ger) Delay checking of a proc alias until the underlying proc is checked.
+ GB_ASSERT(e->aliased_of != nullptr);
+ GB_ASSERT(e->aliased_of->kind == Entity_Procedure);
+ if ((e->aliased_of->flags & EntityFlag_ProcBodyChecked) != 0) {
+ e->flags |= EntityFlag_ProcBodyChecked;
+ return;
+ }
+ // NOTE (zen3ger) A proc alias *does not* have a body and tags!
+ check_procedure_later(c, e->file, e->token, e->decl_info, e->type, nullptr, 0);
+ return;
+ }
+ Type *type = base_type(e->type);
+ if (type == t_invalid) {
+ return;
+ }
+ GB_ASSERT_MSG(type->kind == Type_Proc, "%s", type_to_string(e->type));
+
+ if (is_type_polymorphic(type) && !type->Proc.is_poly_specialized) {
+ return;
+ }
+
+ GB_ASSERT(e->decl_info != nullptr);
+
+ ProcInfo *pi = gb_alloc_item(permanent_allocator(), ProcInfo);
+ pi->file = e->file;
+ pi->token = e->token;
+ pi->decl = e->decl_info;
+ pi->type = e->type;
+
+ Ast *pl = e->decl_info->proc_lit;
+ GB_ASSERT(pl != nullptr);
+ pi->body = pl->ProcLit.body;
+ pi->tags = pl->ProcLit.tags;
+ if (pi->body == nullptr) {
+ return;
+ }
+ if (from_msg != nullptr) {
+ debugf("CHECK PROCEDURE LATER [FROM %s]! %.*s :: %s {...}\n", from_msg, LIT(e->token.string), type_to_string(e->type));
+ }
+ check_procedure_later(c, pi);
+}
+
gb_internal bool check_proc_info(Checker *c, ProcInfo *pi, UntypedExprInfoMap *untyped) {
if (pi == nullptr) {
@@ -5410,6 +5925,15 @@ 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) {
+ if (dep && dep->kind == Entity_Procedure &&
+ (dep->flags & EntityFlag_ProcBodyChecked) == 0) {
+ check_procedure_later_from_entity(c, dep, NULL);
+ }
+ }
+ rw_mutex_shared_unlock(&ctx.decl->deps_mutex);
+
return true;
}
@@ -5432,30 +5956,7 @@ gb_internal void check_unchecked_bodies(Checker *c) {
global_procedure_body_in_worker_queue = false;
for (Entity *e : c->info.minimum_dependency_set) {
- if (e == nullptr || e->kind != Entity_Procedure) {
- continue;
- }
- if (e->Procedure.is_foreign) {
- continue;
- }
- if ((e->flags & EntityFlag_ProcBodyChecked) == 0) {
- GB_ASSERT(e->decl_info != nullptr);
-
- ProcInfo *pi = gb_alloc_item(permanent_allocator(), ProcInfo);
- pi->file = e->file;
- pi->token = e->token;
- pi->decl = e->decl_info;
- pi->type = e->type;
-
- Ast *pl = e->decl_info->proc_lit;
- GB_ASSERT(pl != nullptr);
- pi->body = pl->ProcLit.body;
- pi->tags = pl->ProcLit.tags;
- if (pi->body == nullptr) {
- continue;
- }
- check_procedure_later(c, pi);
- }
+ check_procedure_later_from_entity(c, e, "check_unchecked_bodies");
}
if (!global_procedure_body_in_worker_queue) {
@@ -5497,35 +5998,27 @@ gb_internal void check_safety_all_procedures_for_unchecked(Checker *c) {
}
}
-gb_internal void check_test_procedures(Checker *c) {
- if (build_context.test_names.entries.count == 0) {
- return;
- }
+gb_internal GB_COMPARE_PROC(init_procedures_cmp);
+gb_internal GB_COMPARE_PROC(fini_procedures_cmp);
- AstPackage *pkg = c->info.init_package;
- Scope *s = pkg->scope;
+gb_internal void remove_neighbouring_duplicate_entires_from_sorted_array(Array<Entity *> *array) {
+ Entity *prev = nullptr;
- for (String const &name : build_context.test_names) {
- Entity *e = scope_lookup(s, name);
- if (e == nullptr) {
- Token tok = {};
- if (pkg->files.count != 0) {
- tok = pkg->files[0]->tokens[0];
- }
- error(tok, "Unable to find the test '%.*s' in 'package %.*s' ", LIT(name), LIT(pkg->name));
- }
- }
-
- for (isize i = 0; i < c->info.testing_procedures.count; /**/) {
- Entity *e = c->info.testing_procedures[i];
- String name = e->token.string;
- if (!string_set_exists(&build_context.test_names, name)) {
- array_ordered_remove(&c->info.testing_procedures, i);
+ for (isize i = 0; i < array->count; /**/) {
+ Entity *curr = array->data[i];
+ if (prev == curr) {
+ array_ordered_remove(array, i);
} else {
+ prev = curr;
i += 1;
}
}
+}
+
+gb_internal void check_test_procedures(Checker *c) {
+ array_sort(c->info.testing_procedures, init_procedures_cmp);
+ remove_neighbouring_duplicate_entires_from_sorted_array(&c->info.testing_procedures);
}
@@ -5690,6 +6183,11 @@ gb_internal void check_deferred_procedures(Checker *c) {
case DeferredProcedure_in_out_by_ptr: attribute = "deferred_in_out_by_ptr"; break;
}
+ if (src == dst) {
+ error(src->token, "'%.*s' cannot be used as its own %s", LIT(dst->token.string), attribute);
+ continue;
+ }
+
if (is_type_polymorphic(src->type) || is_type_polymorphic(dst->type)) {
error(src->token, "'%s' cannot be used with a polymorphic procedure", attribute);
continue;
@@ -5737,7 +6235,7 @@ gb_internal void check_deferred_procedures(Checker *c) {
}
if ((src_params == nullptr && dst_params != nullptr) ||
(src_params != nullptr && dst_params == nullptr)) {
- error(src->token, "Deferred procedure '%.*s' parameters do not match the inputs of initial procedure '%.*s'", LIT(src->token.string), LIT(dst->token.string));
+ error(src->token, "Deferred procedure '%.*s' parameters do not match the inputs of initial procedure '%.*s'", LIT(dst->token.string), LIT(src->token.string));
continue;
}
@@ -5750,8 +6248,8 @@ gb_internal void check_deferred_procedures(Checker *c) {
gbString s = type_to_string(src_params);
gbString d = type_to_string(dst_params);
error(src->token, "Deferred procedure '%.*s' parameters do not match the inputs of initial procedure '%.*s':\n\t(%s) =/= (%s)",
- LIT(src->token.string), LIT(dst->token.string),
- s, d
+ LIT(dst->token.string), LIT(src->token.string),
+ d, s
);
gb_string_free(d);
gb_string_free(s);
@@ -5767,7 +6265,7 @@ gb_internal void check_deferred_procedures(Checker *c) {
}
if ((src_results == nullptr && dst_params != nullptr) ||
(src_results != nullptr && dst_params == nullptr)) {
- error(src->token, "Deferred procedure '%.*s' parameters do not match the results of initial procedure '%.*s'", LIT(src->token.string), LIT(dst->token.string));
+ error(src->token, "Deferred procedure '%.*s' parameters do not match the results of initial procedure '%.*s'", LIT(dst->token.string), LIT(src->token.string));
continue;
}
@@ -5780,8 +6278,8 @@ gb_internal void check_deferred_procedures(Checker *c) {
gbString s = type_to_string(src_results);
gbString d = type_to_string(dst_params);
error(src->token, "Deferred procedure '%.*s' parameters do not match the results of initial procedure '%.*s':\n\t(%s) =/= (%s)",
- LIT(src->token.string), LIT(dst->token.string),
- s, d
+ LIT(dst->token.string), LIT(src->token.string),
+ d, s
);
gb_string_free(d);
gb_string_free(s);
@@ -5833,8 +6331,8 @@ gb_internal void check_deferred_procedures(Checker *c) {
gbString s = type_to_string(tsrc);
gbString d = type_to_string(dst_params);
error(src->token, "Deferred procedure '%.*s' parameters do not match the results of initial procedure '%.*s':\n\t(%s) =/= (%s)",
- LIT(src->token.string), LIT(dst->token.string),
- s, d
+ LIT(dst->token.string), LIT(src->token.string),
+ d, s
);
gb_string_free(d);
gb_string_free(s);
@@ -5873,11 +6371,14 @@ gb_internal void check_unique_package_names(Checker *c) {
continue;
}
+
+ begin_error_block();
error(curr, "Duplicate declaration of 'package %.*s'", LIT(name));
error_line("\tA package name must be unique\n"
"\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(prev, "found at previous location");
+ error_line("%s found at previous location\n", token_pos_to_string(ast_token(prev).pos));
+ end_error_block();
}
}
@@ -5898,6 +6399,9 @@ gb_internal void check_add_definitions_from_queues(Checker *c) {
}
gb_internal void check_merge_queues_into_arrays(Checker *c) {
+ for (Type *t = nullptr; mpsc_dequeue(&c->soa_types_to_complete, &t); /**/) {
+ complete_soa_type(c, t, false);
+ }
check_add_entities_from_queues(c);
check_add_definitions_from_queues(c);
}
@@ -5943,10 +6447,14 @@ gb_internal GB_COMPARE_PROC(fini_procedures_cmp) {
return init_procedures_cmp(b, a);
}
-
gb_internal void check_sort_init_and_fini_procedures(Checker *c) {
- gb_sort_array(c->info.init_procedures.data, c->info.init_procedures.count, init_procedures_cmp);
- gb_sort_array(c->info.fini_procedures.data, c->info.fini_procedures.count, fini_procedures_cmp);
+ array_sort(c->info.init_procedures, init_procedures_cmp);
+ array_sort(c->info.fini_procedures, fini_procedures_cmp);
+
+ // NOTE(bill): remove possible duplicates from the init/fini lists
+ // NOTE(bill): because the arrays are sorted, you only need to check the previous element
+ remove_neighbouring_duplicate_entires_from_sorted_array(&c->info.init_procedures);
+ remove_neighbouring_duplicate_entires_from_sorted_array(&c->info.fini_procedures);
}
gb_internal void add_type_info_for_type_definitions(Checker *c) {
@@ -6041,18 +6549,22 @@ gb_internal void check_parsed_files(Checker *c) {
TIME_SECTION("check procedure bodies");
check_procedure_bodies(c);
+ TIME_SECTION("check foreign import fullpaths");
+ check_foreign_import_fullpaths(c);
+
TIME_SECTION("add entities from procedure bodies");
check_merge_queues_into_arrays(c);
TIME_SECTION("check scope usage");
for (auto const &entry : c->info.files) {
AstFile *f = entry.value;
- u64 vet_flags = build_context.vet_flags;
- if (f->vet_flags_set) {
- vet_flags = f->vet_flags;
- }
+ 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("add basic type information");
// Add "Basic" type information
@@ -6091,9 +6603,6 @@ gb_internal void check_parsed_files(Checker *c) {
TIME_SECTION("calculate global init order");
calculate_global_init_order(c);
- TIME_SECTION("check test procedures");
- check_test_procedures(c);
-
TIME_SECTION("add type info for type definitions");
add_type_info_for_type_definitions(c);
check_merge_queues_into_arrays(c);
@@ -6107,8 +6616,21 @@ gb_internal void check_parsed_files(Checker *c) {
TIME_SECTION("check bodies have all been checked");
check_unchecked_bodies(c);
+ TIME_SECTION("check #soa types");
+
check_merge_queues_into_arrays(c);
+ thread_pool_wait();
+
+ TIME_SECTION("update minimum dependency set");
+ generate_minimum_dependency_set_internal(c, c->info.entry_point);
+ // NOTE(laytan): has to be ran after generate_minimum_dependency_set,
+ // because that collects the test procedures.
+ TIME_SECTION("check test procedures");
+ check_test_procedures(c);
+
+ check_merge_queues_into_arrays(c);
+ thread_pool_wait();
TIME_SECTION("check entry point");
if (build_context.build_mode == BuildMode_Executable && !build_context.no_entry_point && build_context.command_kind != Command_test) {
@@ -6130,6 +6652,8 @@ gb_internal void check_parsed_files(Checker *c) {
error(token, "Undefined entry point procedure 'main'");
}
+ } else if (build_context.build_mode == BuildMode_DynamicLibrary && build_context.no_entry_point) {
+ c->info.entry_point = nullptr;
}
thread_pool_wait();
@@ -6150,6 +6674,17 @@ gb_internal void check_parsed_files(Checker *c) {
GB_ASSERT(c->info.entity_queue.count.load(std::memory_order_relaxed) == 0);
GB_ASSERT(c->info.definition_queue.count.load(std::memory_order_relaxed) == 0);
+ TIME_SECTION("check instrumentation calls");
+ {
+ if ((c->info.instrumentation_enter_entity != nullptr) ^
+ (c->info.instrumentation_exit_entity != nullptr)) {
+ Entity *e = c->info.instrumentation_enter_entity;
+ if (!e) e = c->info.instrumentation_exit_entity;
+ error(e->token, "Both @(instrumentation_enter) and @(instrumentation_exit) must be defined");
+ }
+ }
+
+
TIME_SECTION("add untyped expression values");
// Add untyped expression values
for (UntypedExprInfo u = {}; mpsc_dequeue(&c->global_untyped_queue, &u); /**/) {
diff --git a/src/checker.hpp b/src/checker.hpp
index a6a5f6788..3951fcefe 100644
--- a/src/checker.hpp
+++ b/src/checker.hpp
@@ -51,6 +51,12 @@ enum StmtFlag {
enum BuiltinProcPkg {
BuiltinProcPkg_builtin,
BuiltinProcPkg_intrinsics,
+ BuiltinProcPkg_COUNT
+};
+
+String builtin_proc_pkg_name[BuiltinProcPkg_COUNT] = {
+ str_lit("builtin"),
+ str_lit("intrinsics"),
};
struct BuiltinProc {
@@ -103,9 +109,16 @@ struct DeferredProcedure {
};
+enum InstrumentationFlag : i32 {
+ Instrumentation_Enabled = -1,
+ Instrumentation_Default = 0,
+ Instrumentation_Disabled = +1,
+};
+
struct AttributeContext {
String link_name;
String link_prefix;
+ String link_suffix;
String link_section;
String linkage;
isize init_expr_list_count;
@@ -113,19 +126,24 @@ struct AttributeContext {
String deprecated_message;
String warning_message;
DeferredProcedure deferred_procedure;
- bool is_export : 1;
- bool is_static : 1;
- bool require_results : 1;
- bool require_declaration : 1;
- bool has_disabled_proc : 1;
- bool disabled_proc : 1;
- bool test : 1;
- bool init : 1;
- bool fini : 1;
- bool set_cold : 1;
+ bool is_export : 1;
+ bool is_static : 1;
+ bool require_results : 1;
+ bool require_declaration : 1;
+ bool has_disabled_proc : 1;
+ bool disabled_proc : 1;
+ bool test : 1;
+ bool init : 1;
+ bool fini : 1;
+ bool set_cold : 1;
+ bool entry_point_only : 1;
+ bool instrumentation_enter : 1;
+ bool instrumentation_exit : 1;
+ bool rodata : 1;
u32 optimization_mode; // ProcedureOptimizationMode
i64 foreign_import_priority_index;
String extra_linker_flags;
+ InstrumentationFlag no_instrumentation;
String objc_class;
String objc_name;
@@ -136,9 +154,10 @@ struct AttributeContext {
String enable_target_feature; // will be enabled for the procedure only
};
-gb_internal gb_inline AttributeContext make_attribute_context(String link_prefix) {
+gb_internal gb_inline AttributeContext make_attribute_context(String link_prefix, String link_suffix) {
AttributeContext ac = {};
ac.link_prefix = link_prefix;
+ ac.link_suffix = link_suffix;
return ac;
}
@@ -162,6 +181,11 @@ char const *ProcCheckedState_strings[ProcCheckedState_COUNT] {
"Checked",
};
+struct VariadicReuseData {
+ Type *slice_type; // ..elem_type
+ i64 max_count;
+};
+
// DeclInfo is used to store information of certain declarations to allow for "any order" usage
struct DeclInfo {
DeclInfo * parent; // NOTE(bill): only used for procedure literals at the moment
@@ -180,9 +204,12 @@ struct DeclInfo {
Array<Ast *> attributes;
Ast * proc_lit; // Ast_ProcLit
Type * gen_proc_type; // Precalculated
+
bool is_using;
bool where_clauses_evaluated;
+ bool foreign_require_results;
std::atomic<ProcCheckedState> proc_checked_state;
+
BlockingMutex proc_checked_mutex;
isize defer_used;
bool defer_use_checked;
@@ -200,6 +227,10 @@ struct DeclInfo {
Array<BlockLabel> labels;
+ Array<VariadicReuseData> variadic_reuses;
+ i64 variadic_reuse_max_bytes;
+ i64 variadic_reuse_max_align;
+
// NOTE(bill): this is to prevent a race condition since these procedure literals can be created anywhere at any time
struct lbModule *code_gen_module;
};
@@ -292,7 +323,9 @@ struct ForeignContext {
Ast * curr_library;
ProcCallingConvention default_cc;
String link_prefix;
+ String link_suffix;
EntityVisiblityKind visibility_kind;
+ bool require_results;
};
typedef Array<Entity *> CheckerTypePath;
@@ -323,13 +356,35 @@ struct ObjcMsgData {
ObjcMsgKind kind;
Type *proc_type;
};
+
+enum LoadFileTier {
+ LoadFileTier_Invalid,
+ LoadFileTier_Exists,
+ LoadFileTier_Contents,
+};
+
struct LoadFileCache {
+ LoadFileTier tier;
+ bool exists;
String path;
gbFileError file_error;
String data;
StringMap<u64> hashes;
};
+
+struct LoadDirectoryFile {
+ String file_name;
+ String data;
+};
+
+struct LoadDirectoryCache {
+ String path;
+ gbFileError file_error;
+ Array<LoadFileCache *> files;
+};
+
+
struct GenProcsData {
Array<Entity *> procs;
RwMutex mutex;
@@ -337,7 +392,18 @@ struct GenProcsData {
struct GenTypesData {
Array<Entity *> types;
- RwMutex mutex;
+ RecursiveMutex mutex;
+};
+
+struct Defineable {
+ String name;
+ ExactValue default_value;
+ TokenPos pos;
+ CommentGroup *docs;
+
+ // These strings are only computed from previous fields when defineables are being shown or exported.
+ String default_value_str;
+ String pos_str;
};
// CheckerInfo stores all the symbol information for a type-checked program
@@ -366,6 +432,9 @@ struct CheckerInfo {
Array<Entity *> entities;
Array<Entity *> required_foreign_imports_through_force;
+ BlockingMutex defineables_mutex;
+ Array<Defineable> defineables;
+
// Below are accessed within procedures
RwMutex global_untyped_mutex;
@@ -377,8 +446,8 @@ struct CheckerInfo {
RecursiveMutex lazy_mutex; // Mutex required for lazy type checking of specific files
- RwMutex gen_types_mutex;
- PtrMap<Type *, GenTypesData > gen_types;
+ BlockingMutex gen_types_mutex;
+ PtrMap<Type *, GenTypesData *> gen_types;
BlockingMutex type_info_mutex; // NOT recursive
Array<Type *> type_info_types;
@@ -391,6 +460,8 @@ struct CheckerInfo {
MPSCQueue<Entity *> entity_queue;
MPSCQueue<Entity *> required_global_variable_queue;
MPSCQueue<Entity *> required_foreign_imports_through_force_queue;
+ MPSCQueue<Entity *> foreign_imports_to_check_fullpaths;
+ MPSCQueue<Entity *> foreign_decls_to_check;
MPSCQueue<Ast *> intrinsics_entry_point_usage;
@@ -402,6 +473,17 @@ struct CheckerInfo {
BlockingMutex all_procedures_mutex;
Array<ProcInfo *> all_procedures;
+
+ BlockingMutex instrumentation_mutex;
+ Entity *instrumentation_enter_entity;
+ Entity *instrumentation_exit_entity;
+
+
+ BlockingMutex load_directory_mutex;
+ StringMap<LoadDirectoryCache *> load_directory_cache;
+ PtrMap<Ast *, LoadDirectoryCache *> load_directory_map; // Key: Ast_CallExpr *
+
+
};
struct CheckerContext {
@@ -419,6 +501,7 @@ struct CheckerContext {
u32 state_flags;
bool in_defer;
Type * type_hint;
+ Ast * type_hint_expr;
String proc_name;
DeclInfo * curr_proc_decl;
@@ -439,10 +522,12 @@ struct CheckerContext {
bool in_enum_type;
bool collect_delayed_decls;
bool allow_polymorphic_types;
+ bool disallow_polymorphic_return_types; // NOTE(zen3ger): no poly type decl in return types
bool no_polymorphic_errors;
bool hide_polymorphic_errors;
bool in_polymorphic_specialization;
bool allow_arrow_right_selector_expr;
+ u8 bit_field_bit_size;
Scope * polymorphic_scope;
Ast *assignment_lhs_hint;
@@ -466,6 +551,7 @@ struct Checker {
MPSCQueue<UntypedExprInfo> global_untyped_queue;
+ MPSCQueue<Type *> soa_types_to_complete;
};
@@ -526,3 +612,9 @@ gb_internal void init_core_context(Checker *c);
gb_internal void init_mem_allocator(Checker *c);
gb_internal void add_untyped_expressions(CheckerInfo *cinfo, UntypedExprInfoMap *untyped);
+
+
+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
diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp
index c89ab2429..2dfd570e4 100644
--- a/src/checker_builtin_procs.hpp
+++ b/src/checker_builtin_procs.hpp
@@ -34,11 +34,6 @@ enum BuiltinProcId {
BuiltinProc_soa_zip,
BuiltinProc_soa_unzip,
-
- BuiltinProc_transpose,
- BuiltinProc_outer_product,
- BuiltinProc_hadamard_product,
- BuiltinProc_matrix_flatten,
BuiltinProc_unreachable,
@@ -48,6 +43,15 @@ enum BuiltinProcId {
// "Intrinsics"
BuiltinProc_is_package_imported,
+
+ BuiltinProc_has_target_feature,
+
+ BuiltinProc_constant_log2,
+
+ BuiltinProc_transpose,
+ BuiltinProc_outer_product,
+ BuiltinProc_hadamard_product,
+ BuiltinProc_matrix_flatten,
BuiltinProc_soa_struct,
@@ -68,6 +72,9 @@ enum BuiltinProcId {
BuiltinProc_overflow_sub,
BuiltinProc_overflow_mul,
+ BuiltinProc_saturating_add,
+ BuiltinProc_saturating_sub,
+
BuiltinProc_sqrt,
BuiltinProc_fused_mul_add,
@@ -92,6 +99,7 @@ enum BuiltinProcId {
BuiltinProc_prefetch_write_instruction,
BuiltinProc_prefetch_write_data,
+BuiltinProc__atomic_begin,
BuiltinProc_atomic_type_is_lock_free,
BuiltinProc_atomic_thread_fence,
BuiltinProc_atomic_signal_fence,
@@ -117,6 +125,7 @@ enum BuiltinProcId {
BuiltinProc_atomic_compare_exchange_strong_explicit,
BuiltinProc_atomic_compare_exchange_weak,
BuiltinProc_atomic_compare_exchange_weak_explicit,
+BuiltinProc__atomic_end,
BuiltinProc_fixed_point_mul,
BuiltinProc_fixed_point_div,
@@ -136,8 +145,8 @@ BuiltinProc__simd_begin,
BuiltinProc_simd_shl_masked, // C logic
BuiltinProc_simd_shr_masked, // C logic
- BuiltinProc_simd_add_sat, // saturation arithmetic
- BuiltinProc_simd_sub_sat, // saturation arithmetic
+ BuiltinProc_simd_saturating_add, // saturation arithmetic
+ BuiltinProc_simd_saturating_sub, // saturation arithmetic
BuiltinProc_simd_bit_and,
BuiltinProc_simd_bit_or,
@@ -169,6 +178,9 @@ BuiltinProc__simd_begin,
BuiltinProc_simd_reduce_or,
BuiltinProc_simd_reduce_xor,
+ BuiltinProc_simd_reduce_any,
+ BuiltinProc_simd_reduce_all,
+
BuiltinProc_simd_shuffle,
BuiltinProc_simd_select,
@@ -183,6 +195,12 @@ BuiltinProc__simd_begin,
BuiltinProc_simd_lanes_rotate_left,
BuiltinProc_simd_lanes_rotate_right,
+ BuiltinProc_simd_gather,
+ BuiltinProc_simd_scatter,
+ BuiltinProc_simd_masked_load,
+ BuiltinProc_simd_masked_store,
+ BuiltinProc_simd_masked_expand_load,
+ BuiltinProc_simd_masked_compress_store,
// Platform specific SIMD intrinsics
BuiltinProc_simd_x86__MM_SHUFFLE,
@@ -190,6 +208,7 @@ BuiltinProc__simd_end,
// Platform specific intrinsics
BuiltinProc_syscall,
+ BuiltinProc_syscall_bsd,
BuiltinProc_x86_cpuid,
BuiltinProc_x86_xgetbv,
@@ -254,14 +273,27 @@ BuiltinProc__type_simple_boolean_begin,
BuiltinProc__type_simple_boolean_end,
+ BuiltinProc_type_is_matrix_row_major,
+ BuiltinProc_type_is_matrix_column_major,
+
BuiltinProc_type_has_field,
BuiltinProc_type_field_type,
BuiltinProc_type_is_specialization_of,
BuiltinProc_type_is_variant_of,
+ BuiltinProc_type_union_tag_type,
+ BuiltinProc_type_union_tag_offset,
+ BuiltinProc_type_union_base_tag_value,
+ BuiltinProc_type_union_variant_count,
+ BuiltinProc_type_variant_type_of,
+ BuiltinProc_type_variant_index_of,
+
+ BuiltinProc_type_bit_set_elem_type,
+ BuiltinProc_type_bit_set_underlying_type,
BuiltinProc_type_struct_field_count,
+ BuiltinProc_type_struct_has_implicit_padding,
BuiltinProc_type_proc_parameter_count,
BuiltinProc_type_proc_return_count,
@@ -276,13 +308,19 @@ BuiltinProc__type_simple_boolean_end,
BuiltinProc_type_field_index_of,
+ BuiltinProc_type_bit_set_backing_type,
+
BuiltinProc_type_equal_proc,
BuiltinProc_type_hasher_proc,
BuiltinProc_type_map_info,
BuiltinProc_type_map_cell_info,
+ BuiltinProc_type_has_shared_fields,
+
BuiltinProc__type_end,
+ BuiltinProc_procedure_of,
+
BuiltinProc___entry_point,
BuiltinProc_objc_send,
@@ -335,11 +373,6 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT("soa_zip"), 1, true, Expr_Expr, BuiltinProcPkg_builtin},
{STR_LIT("soa_unzip"), 1, false, Expr_Expr, BuiltinProcPkg_builtin},
-
- {STR_LIT("transpose"), 1, false, Expr_Expr, BuiltinProcPkg_builtin},
- {STR_LIT("outer_product"), 2, false, Expr_Expr, BuiltinProcPkg_builtin},
- {STR_LIT("hadamard_product"), 2, false, Expr_Expr, BuiltinProcPkg_builtin},
- {STR_LIT("matrix_flatten"), 1, false, Expr_Expr, BuiltinProcPkg_builtin},
{STR_LIT("unreachable"), 0, false, Expr_Expr, BuiltinProcPkg_builtin, /*diverging*/true},
@@ -350,6 +383,15 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
// "Intrinsics"
{STR_LIT("is_package_imported"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+
+ {STR_LIT("has_target_feature"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+
+ {STR_LIT("constant_log2"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+
+ {STR_LIT("transpose"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("outer_product"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("hadamard_product"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("matrix_flatten"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("soa_struct"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, // Type
@@ -371,6 +413,9 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT("overflow_sub"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("overflow_mul"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("saturating_add"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("saturating_sub"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+
{STR_LIT("sqrt"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("fused_mul_add"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
@@ -395,6 +440,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT("prefetch_write_instruction"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
{STR_LIT("prefetch_write_data"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
+ {STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_type_is_lock_free"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_thread_fence"), 1, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_signal_fence"), 1, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
@@ -420,6 +466,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT("atomic_compare_exchange_strong_explicit"), 5, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
{STR_LIT("atomic_compare_exchange_weak"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
{STR_LIT("atomic_compare_exchange_weak_explicit"), 5, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
+ {STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
{STR_LIT("fixed_point_mul"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("fixed_point_div"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
@@ -439,8 +486,8 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT("simd_shl_masked"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_shr_masked"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("simd_add_sat"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("simd_sub_sat"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("simd_saturating_add"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("simd_saturating_sub"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_bit_and"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_bit_or"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
@@ -473,6 +520,10 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT("simd_reduce_or"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_reduce_xor"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("simd_reduce_any"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("simd_reduce_all"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+
+
{STR_LIT("simd_shuffle"), 2, true, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_select"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
@@ -487,12 +538,20 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT("simd_lanes_rotate_left"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_lanes_rotate_right"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("simd_gather"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("simd_scatter"), 3, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
+ {STR_LIT("simd_masked_load"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("simd_masked_store"), 3, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
+ {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_x86__MM_SHUFFLE"), 4, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
- {STR_LIT("syscall"), 1, true, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
+ {STR_LIT("syscall"), 1, true, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
+ {STR_LIT("syscall_bsd"), 1, true, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
{STR_LIT("x86_cpuid"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("x86_xgetbv"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
@@ -552,14 +611,27 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT("type_has_nil"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
+ {STR_LIT("type_is_matrix_row_major"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("type_is_matrix_column_major"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+
{STR_LIT("type_has_field"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_field_type"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_is_specialization_of"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("type_is_variant_of"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("type_is_variant_of"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("type_union_tag_type"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("type_union_tag_offset"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("type_union_base_tag_value"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("type_union_variant_count"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("type_variant_type_of"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("type_variant_index_of"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+
+ {STR_LIT("type_bit_set_elem_type"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("type_bit_set_underlying_type"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("type_struct_field_count"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("type_struct_field_count"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("type_struct_has_implicit_padding"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_proc_parameter_count"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_proc_return_count"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
@@ -574,14 +646,19 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT("type_field_index_of"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("type_bit_set_backing_type"), 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(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
+ {STR_LIT("procedure_of"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+
{STR_LIT("__entry_point"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
{STR_LIT("objc_send"), 3, true, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
diff --git a/src/common.cpp b/src/common.cpp
index 90632def3..0ef39bd10 100644
--- a/src/common.cpp
+++ b/src/common.cpp
@@ -14,6 +14,8 @@
#undef NOMINMAX
#endif
+#include <string.h>
+
#define GB_WINDOWS_H_INCLUDED
#define GB_IMPLEMENTATION
#include "gb/gb.h"
@@ -353,6 +355,27 @@ gb_global bool global_module_path_set = false;
#include "thread_pool.cpp"
+gb_internal String obfuscate_string(String const &s, char const *prefix) {
+ if (s.len == 0) {
+ return s;
+ }
+ GB_ASSERT(prefix != nullptr);
+ u64 hash = gb_fnv64a(s.text, s.len);
+ gbString res = gb_string_make(permanent_allocator(), prefix);
+ res = gb_string_append_fmt(res, "x%llx", cast(long long unsigned)hash);
+ return make_string_c(res);
+}
+
+gb_internal i32 obfuscate_i32(i32 i) {
+ i32 x = cast(i32)gb_fnv64a(&i, sizeof(i));
+ if (x < 0) {
+ x = 1-x;
+ }
+ return cast(i32)x;
+}
+
+
+
struct StringIntern {
StringIntern *next;
isize len;
@@ -913,7 +936,7 @@ gb_internal void did_you_mean_append(DidYouMeanAnswers *d, String const &target)
array_add(&d->distances, dat);
}
gb_internal Slice<DistanceAndTarget> did_you_mean_results(DidYouMeanAnswers *d) {
- gb_sort_array(d->distances.data, d->distances.count, gb_isize_cmp(gb_offset_of(DistanceAndTarget, distance)));
+ array_sort(d->distances, gb_isize_cmp(gb_offset_of(DistanceAndTarget, distance)));
isize count = 0;
for (isize i = 0; i < d->distances.count; i++) {
isize distance = d->distances[i].distance;
diff --git a/src/common_memory.cpp b/src/common_memory.cpp
index c6ee88f03..47b2796a9 100644
--- a/src/common_memory.cpp
+++ b/src/common_memory.cpp
@@ -2,13 +2,6 @@
#include <malloc.h>
#endif
-gb_internal gb_inline void zero_size(void *ptr, isize len) {
- memset(ptr, 0, len);
-}
-
-#define zero_item(ptr) zero_size((ptr), gb_size_of(ptr))
-
-
template <typename U, typename V>
gb_internal gb_inline U bit_cast(V &v) { return reinterpret_cast<U &>(v); }
@@ -39,6 +32,8 @@ gb_internal void virtual_memory_init(void) {
}
+gb_internal Thread *get_current_thread(void);
+
struct MemoryBlock {
MemoryBlock *prev;
@@ -50,8 +45,9 @@ struct MemoryBlock {
struct Arena {
MemoryBlock * curr_block;
isize minimum_block_size;
- BlockingMutex mutex;
+ // BlockingMutex mutex;
isize temp_count;
+ Thread * parent_thread;
};
enum { DEFAULT_MINIMUM_BLOCK_SIZE = 8ll*1024ll*1024ll };
@@ -73,10 +69,20 @@ gb_internal isize arena_align_forward_offset(Arena *arena, isize alignment) {
return alignment_offset;
}
+gb_internal void thread_init_arenas(Thread *t) {
+ t->permanent_arena = gb_alloc_item(heap_allocator(), Arena);
+ t->temporary_arena = gb_alloc_item(heap_allocator(), Arena);
+
+ t->permanent_arena->parent_thread = t;
+ t->temporary_arena->parent_thread = t;
+
+ t->permanent_arena->minimum_block_size = DEFAULT_MINIMUM_BLOCK_SIZE;
+ t->temporary_arena->minimum_block_size = DEFAULT_MINIMUM_BLOCK_SIZE;
+}
+
gb_internal void *arena_alloc(Arena *arena, isize min_size, isize alignment) {
GB_ASSERT(gb_is_power_of_two(alignment));
-
- mutex_lock(&arena->mutex);
+ GB_ASSERT(arena->parent_thread == get_current_thread());
isize size = 0;
if (arena->curr_block != nullptr) {
@@ -102,9 +108,7 @@ gb_internal void *arena_alloc(Arena *arena, isize min_size, isize alignment) {
curr_block->used += size;
GB_ASSERT(curr_block->used <= curr_block->size);
-
- mutex_unlock(&arena->mutex);
-
+
// NOTE(bill): memory will be zeroed by default due to virtual memory
return ptr;
}
@@ -163,6 +167,10 @@ gb_internal void platform_virtual_memory_protect(void *memory, isize size);
GB_ASSERT(is_protected);
}
#else
+ #if !defined(MAP_ANONYMOUS) && defined(MAP_ANON)
+ #define MAP_ANONYMOUS MAP_ANON
+ #endif
+
gb_internal void platform_virtual_memory_init(void) {
global_platform_memory_block_sentinel.prev = &global_platform_memory_block_sentinel;
global_platform_memory_block_sentinel.next = &global_platform_memory_block_sentinel;
@@ -255,7 +263,7 @@ struct ArenaTemp {
ArenaTemp arena_temp_begin(Arena *arena) {
GB_ASSERT(arena);
- MUTEX_GUARD(&arena->mutex);
+ GB_ASSERT(arena->parent_thread == get_current_thread());
ArenaTemp temp = {};
temp.arena = arena;
@@ -270,7 +278,7 @@ ArenaTemp arena_temp_begin(Arena *arena) {
void arena_temp_end(ArenaTemp const &temp) {
GB_ASSERT(temp.arena);
Arena *arena = temp.arena;
- MUTEX_GUARD(&arena->mutex);
+ GB_ASSERT(arena->parent_thread == get_current_thread());
if (temp.block) {
bool memory_block_found = false;
@@ -306,7 +314,7 @@ void arena_temp_end(ArenaTemp const &temp) {
void arena_temp_ignore(ArenaTemp const &temp) {
GB_ASSERT(temp.arena);
Arena *arena = temp.arena;
- MUTEX_GUARD(&arena->mutex);
+ GB_ASSERT(arena->parent_thread == get_current_thread());
GB_ASSERT_MSG(arena->temp_count > 0, "double-use of arena_temp_end");
arena->temp_count -= 1;
@@ -366,14 +374,65 @@ gb_internal GB_ALLOCATOR_PROC(arena_allocator_proc) {
}
-gb_global gb_thread_local Arena permanent_arena = {nullptr, DEFAULT_MINIMUM_BLOCK_SIZE};
+enum ThreadArenaKind : uintptr {
+ ThreadArena_Permanent,
+ ThreadArena_Temporary,
+};
+
+gb_global Arena default_permanent_arena = {nullptr, DEFAULT_MINIMUM_BLOCK_SIZE};
+gb_global Arena default_temporary_arena = {nullptr, DEFAULT_MINIMUM_BLOCK_SIZE};
+
+
+gb_internal Arena *get_arena(ThreadArenaKind kind) {
+ Thread *t = get_current_thread();
+ switch (kind) {
+ case ThreadArena_Permanent: return t ? t->permanent_arena : &default_permanent_arena;
+ case ThreadArena_Temporary: return t ? t->temporary_arena : &default_temporary_arena;
+ }
+ GB_PANIC("INVALID ARENA KIND");
+ return nullptr;
+}
+
+
+
+gb_internal GB_ALLOCATOR_PROC(thread_arena_allocator_proc) {
+ void *ptr = nullptr;
+ ThreadArenaKind kind = cast(ThreadArenaKind)cast(uintptr)allocator_data;
+ Arena *arena = get_arena(kind);
+
+ switch (type) {
+ case gbAllocation_Alloc:
+ ptr = arena_alloc(arena, size, alignment);
+ break;
+ case gbAllocation_Free:
+ break;
+ case gbAllocation_Resize:
+ if (size == 0) {
+ ptr = nullptr;
+ } else if (size <= old_size) {
+ ptr = old_memory;
+ } else {
+ ptr = arena_alloc(arena, size, alignment);
+ gb_memmove(ptr, old_memory, old_size);
+ }
+ break;
+ case gbAllocation_FreeAll:
+ GB_PANIC("use arena_free_all directly");
+ arena_free_all(arena);
+ break;
+ }
+
+ return ptr;
+}
+
+
+
gb_internal gbAllocator permanent_allocator() {
- return arena_allocator(&permanent_arena);
+ return {thread_arena_allocator_proc, cast(void *)cast(uintptr)ThreadArena_Permanent};
}
-gb_global gb_thread_local Arena temporary_arena = {nullptr, DEFAULT_MINIMUM_BLOCK_SIZE};
gb_internal gbAllocator temporary_allocator() {
- return arena_allocator(&temporary_arena);
+ return {thread_arena_allocator_proc, cast(void *)cast(uintptr)ThreadArena_Permanent};
}
@@ -381,7 +440,7 @@ gb_internal gbAllocator temporary_allocator() {
// #define TEMPORARY_ALLOCATOR_GUARD()
-#define TEMPORARY_ALLOCATOR_GUARD() TEMP_ARENA_GUARD(&temporary_arena)
+#define TEMPORARY_ALLOCATOR_GUARD() TEMP_ARENA_GUARD(get_arena(ThreadArena_Temporary))
#define PERMANENT_ALLOCATOR_GUARD()
diff --git a/src/docs.cpp b/src/docs.cpp
index f00d4e15a..004134a5c 100644
--- a/src/docs.cpp
+++ b/src/docs.cpp
@@ -237,7 +237,7 @@ gb_internal void print_doc_package(CheckerInfo *info, AstPackage *pkg) {
}
array_add(&entities, e);
}
- gb_sort_array(entities.data, entities.count, cmp_entities_for_printing);
+ array_sort(entities, cmp_entities_for_printing);
bool show_docs = (build_context.cmd_doc_flags & CmdDocFlag_Short) == 0;
@@ -358,7 +358,7 @@ gb_internal void generate_documentation(Checker *c) {
}
}
- gb_sort_array(pkgs.data, pkgs.count, cmp_ast_package_by_name);
+ array_sort(pkgs, cmp_ast_package_by_name);
for_array(i, pkgs) {
print_doc_package(info, pkgs[i]);
diff --git a/src/docs_format.cpp b/src/docs_format.cpp
index d0bca214b..6378971d0 100644
--- a/src/docs_format.cpp
+++ b/src/docs_format.cpp
@@ -14,8 +14,8 @@ struct OdinDocVersionType {
};
#define OdinDocVersionType_Major 0
-#define OdinDocVersionType_Minor 2
-#define OdinDocVersionType_Patch 4
+#define OdinDocVersionType_Minor 3
+#define OdinDocVersionType_Patch 1
struct OdinDocHeaderBase {
u8 magic[8];
@@ -79,11 +79,11 @@ enum OdinDocTypeKind : u32 {
OdinDocType_SOAStructFixed = 17,
OdinDocType_SOAStructSlice = 18,
OdinDocType_SOAStructDynamic = 19,
- OdinDocType_RelativePointer = 20,
- OdinDocType_RelativeMultiPointer = 21,
+
OdinDocType_MultiPointer = 22,
OdinDocType_Matrix = 23,
OdinDocType_SoaPointer = 24,
+ OdinDocType_BitField = 25,
};
enum OdinDocTypeFlag_Basic : u32 {
@@ -162,13 +162,17 @@ enum OdinDocEntityFlag : u64 {
OdinDocEntityFlag_Foreign = 1ull<<0,
OdinDocEntityFlag_Export = 1ull<<1,
- OdinDocEntityFlag_Param_Using = 1ull<<2,
- OdinDocEntityFlag_Param_Const = 1ull<<3,
- OdinDocEntityFlag_Param_AutoCast = 1ull<<4,
- OdinDocEntityFlag_Param_Ellipsis = 1ull<<5,
- OdinDocEntityFlag_Param_CVararg = 1ull<<6,
- OdinDocEntityFlag_Param_NoAlias = 1ull<<7,
- OdinDocEntityFlag_Param_AnyInt = 1ull<<8,
+ OdinDocEntityFlag_Param_Using = 1ull<<2,
+ OdinDocEntityFlag_Param_Const = 1ull<<3,
+ OdinDocEntityFlag_Param_AutoCast = 1ull<<4,
+ OdinDocEntityFlag_Param_Ellipsis = 1ull<<5,
+ OdinDocEntityFlag_Param_CVararg = 1ull<<6,
+ OdinDocEntityFlag_Param_NoAlias = 1ull<<7,
+ OdinDocEntityFlag_Param_AnyInt = 1ull<<8,
+ OdinDocEntityFlag_Param_ByPtr = 1ull<<9,
+ OdinDocEntityFlag_Param_NoBroadcast = 1ull<<10,
+
+ OdinDocEntityFlag_BitField_Field = 1ull<<19,
OdinDocEntityFlag_Type_Alias = 1ull<<20,
@@ -192,7 +196,7 @@ struct OdinDocEntity {
u32 reserved_for_init;
OdinDocString comment; // line comment
OdinDocString docs; // preceding comment
- i32 field_group_index;
+ i32 field_group_index; // For `bit_field`s this is the "bit_size"
OdinDocEntityIndex foreign_library;
OdinDocString link_name;
OdinDocArray<OdinDocAttribute> attributes;
diff --git a/src/docs_writer.cpp b/src/docs_writer.cpp
index 6816ae8eb..341b3fa6b 100644
--- a/src/docs_writer.cpp
+++ b/src/docs_writer.cpp
@@ -26,11 +26,11 @@ struct OdinDocWriter {
StringMap<OdinDocString> string_cache;
- PtrMap<AstFile *, OdinDocFileIndex> file_cache;
- PtrMap<AstPackage *, OdinDocPkgIndex> pkg_cache;
- PtrMap<Entity *, OdinDocEntityIndex> entity_cache;
- PtrMap<Type *, OdinDocTypeIndex> type_cache;
- PtrMap<Type *, Type *> stable_type_cache;
+ OrderedInsertPtrMap<AstFile *, OdinDocFileIndex> file_cache;
+ OrderedInsertPtrMap<AstPackage *, OdinDocPkgIndex> pkg_cache;
+ OrderedInsertPtrMap<Entity *, OdinDocEntityIndex> entity_cache;
+ OrderedInsertPtrMap<Type *, OdinDocTypeIndex> type_cache;
+ OrderedInsertPtrMap<Type *, Type *> stable_type_cache;
OdinDocWriterItemTracker<OdinDocFile> files;
OdinDocWriterItemTracker<OdinDocPkg> pkgs;
@@ -57,11 +57,11 @@ gb_internal void odin_doc_writer_prepare(OdinDocWriter *w) {
string_map_init(&w->string_cache);
- map_init(&w->file_cache);
- map_init(&w->pkg_cache);
- map_init(&w->entity_cache);
- map_init(&w->type_cache);
- map_init(&w->stable_type_cache);
+ map_init(&w->file_cache, 1<<10);
+ map_init(&w->pkg_cache, 1<<10);
+ map_init(&w->entity_cache, 1<<18);
+ map_init(&w->type_cache, 1<<18);
+ map_init(&w->stable_type_cache, 1<<18);
odin_doc_writer_item_tracker_init(&w->files, 1);
odin_doc_writer_item_tracker_init(&w->pkgs, 1);
@@ -485,6 +485,13 @@ gb_internal OdinDocTypeIndex odin_doc_type(OdinDocWriter *w, Type *type) {
return 0;
}
+ if (type->kind == Type_Named) {
+ Entity *e = type->Named.type_name;
+ if (e->TypeName.is_type_alias) {
+ type = type->Named.base;
+ }
+ }
+
// Type **mapped_type = map_get(&w->stable_type_cache, type); // may map to itself
// if (mapped_type && *mapped_type) {
// type = *mapped_type;
@@ -506,13 +513,6 @@ gb_internal OdinDocTypeIndex odin_doc_type(OdinDocWriter *w, Type *type) {
if (!x | !y) {
continue;
}
-
- if (x->kind == Type_Named) {
- Entity *e = x->Named.type_name;
- if (e->TypeName.is_type_alias) {
- x = x->Named.base;
- }
- }
if (y->kind == Type_Named) {
Entity *e = y->Named.type_name;
if (e->TypeName.is_type_alias) {
@@ -615,6 +615,20 @@ gb_internal OdinDocTypeIndex odin_doc_type(OdinDocWriter *w, Type *type) {
doc_type.types = odin_write_slice(w, types, gb_count_of(types));
}
break;
+ case Type_BitField:
+ doc_type.kind = OdinDocType_BitField;
+ {
+ auto fields = array_make<OdinDocEntityIndex>(heap_allocator(), type->BitField.fields.count);
+ defer (array_free(&fields));
+
+ for_array(i, type->BitField.fields) {
+ fields[i] = odin_doc_add_entity(w, type->BitField.fields[i]);
+ }
+ doc_type.entities = odin_write_slice(w, fields.data, fields.count);
+ doc_type.types = odin_doc_type_as_slice(w, type->BitField.backing_type);
+ }
+ break;
+
case Type_Struct:
doc_type.kind = OdinDocType_Struct;
if (type->Struct.soa_kind != StructSoa_None) {
@@ -762,24 +776,6 @@ gb_internal OdinDocTypeIndex odin_doc_type(OdinDocWriter *w, Type *type) {
doc_type.types = odin_doc_type_as_slice(w, type->SimdVector.elem);
// TODO(bill):
break;
- case Type_RelativePointer:
- doc_type.kind = OdinDocType_RelativePointer;
- {
- OdinDocTypeIndex types[2] = {};
- types[0] = odin_doc_type(w, type->RelativePointer.pointer_type);
- types[1] = odin_doc_type(w, type->RelativePointer.base_integer);
- doc_type.types = odin_write_slice(w, types, gb_count_of(types));
- }
- break;
- case Type_RelativeMultiPointer:
- doc_type.kind = OdinDocType_RelativeMultiPointer;
- {
- OdinDocTypeIndex types[2] = {};
- types[0] = odin_doc_type(w, type->RelativeMultiPointer.pointer_type);
- types[1] = odin_doc_type(w, type->RelativeMultiPointer.base_integer);
- doc_type.types = odin_write_slice(w, types, gb_count_of(types));
- }
- break;
case Type_Matrix:
doc_type.kind = OdinDocType_Matrix;
@@ -815,7 +811,6 @@ gb_internal OdinDocEntityIndex odin_doc_add_entity(OdinDocWriter *w, Entity *e)
OdinDocEntityIndex doc_entity_index = odin_doc_write_item(w, &w->entities, &doc_entity, &dst);
map_set(&w->entity_cache, e, doc_entity_index);
-
Ast *type_expr = nullptr;
Ast *init_expr = nullptr;
Ast *decl_node = nullptr;
@@ -863,6 +858,10 @@ gb_internal OdinDocEntityIndex odin_doc_add_entity(OdinDocWriter *w, Entity *e)
}
break;
case Entity_Variable:
+ if (e->flags & EntityFlag_BitFieldField) {
+ flags |= OdinDocEntityFlag_BitField_Field;
+ }
+
if (e->Variable.is_foreign) { flags |= OdinDocEntityFlag_Foreign; }
if (e->Variable.is_export) { flags |= OdinDocEntityFlag_Export; }
if (e->Variable.thread_local_model != "") {
@@ -873,7 +872,12 @@ gb_internal OdinDocEntityIndex odin_doc_add_entity(OdinDocWriter *w, Entity *e)
if (init_expr == nullptr) {
init_expr = e->Variable.init_expr;
}
- field_group_index = e->Variable.field_group_index;
+
+ if (e->flags & EntityFlag_BitFieldField) {
+ field_group_index = -cast(i32)e->Variable.bit_field_bit_size;
+ } else {
+ field_group_index = e->Variable.field_group_index;
+ }
break;
case Entity_Constant:
field_group_index = e->Constant.field_group_index;
@@ -902,11 +906,13 @@ gb_internal OdinDocEntityIndex odin_doc_add_entity(OdinDocWriter *w, Entity *e)
break;
}
- if (e->flags & EntityFlag_Using) { flags |= OdinDocEntityFlag_Param_Using; }
- if (e->flags & EntityFlag_ConstInput) { flags |= OdinDocEntityFlag_Param_Const; }
- if (e->flags & EntityFlag_Ellipsis) { flags |= OdinDocEntityFlag_Param_Ellipsis; }
- if (e->flags & EntityFlag_NoAlias) { flags |= OdinDocEntityFlag_Param_NoAlias; }
- if (e->flags & EntityFlag_AnyInt) { flags |= OdinDocEntityFlag_Param_AnyInt; }
+ if (e->flags & EntityFlag_Using) { flags |= OdinDocEntityFlag_Param_Using; }
+ if (e->flags & EntityFlag_ConstInput) { flags |= OdinDocEntityFlag_Param_Const; }
+ if (e->flags & EntityFlag_Ellipsis) { flags |= OdinDocEntityFlag_Param_Ellipsis; }
+ if (e->flags & EntityFlag_NoAlias) { flags |= OdinDocEntityFlag_Param_NoAlias; }
+ if (e->flags & EntityFlag_AnyInt) { flags |= OdinDocEntityFlag_Param_AnyInt; }
+ if (e->flags & EntityFlag_ByPtr) { flags |= OdinDocEntityFlag_Param_ByPtr; }
+ if (e->flags & EntityFlag_NoBroadcast) { flags |= OdinDocEntityFlag_Param_NoBroadcast; }
if (e->scope && (e->scope->flags & (ScopeFlag_File|ScopeFlag_Pkg)) && !is_entity_exported(e)) {
flags |= OdinDocEntityFlag_Private;
@@ -962,9 +968,8 @@ gb_internal void odin_doc_update_entities(OdinDocWriter *w) {
auto entities = array_make<Entity *>(heap_allocator(), 0, w->entity_cache.count);
defer (array_free(&entities));
- for (u32 i = 0; i < w->entity_cache.count; i++) {
- Entity *e = w->entity_cache.entries[i].key;
- array_add(&entities, e);
+ for (auto const &entry : w->entity_cache) {
+ array_add(&entities, entry.key);
}
for (Entity *e : entities) {
GB_ASSERT(e != nullptr);
@@ -974,8 +979,9 @@ gb_internal void odin_doc_update_entities(OdinDocWriter *w) {
}
for (u32 i = 0; i < w->entity_cache.count; i++) {
- Entity *e = w->entity_cache.entries[i].key;
- OdinDocEntityIndex entity_index = w->entity_cache.entries[i].value;
+ auto entry = w->entity_cache.entries[i];
+ Entity *e = entry.key;
+ OdinDocEntityIndex entity_index = entry.value;
OdinDocTypeIndex type_index = odin_doc_type(w, e->type);
OdinDocEntityIndex foreign_library = 0;
@@ -983,6 +989,9 @@ gb_internal void odin_doc_update_entities(OdinDocWriter *w) {
switch (e->kind) {
case Entity_Variable:
+ if (w->state == OdinDocWriterState_Writing) {
+ GB_ASSERT(type_index != 0);
+ }
foreign_library = odin_doc_add_entity(w, e->Variable.foreign_library);
break;
case Entity_Procedure:
@@ -1002,8 +1011,17 @@ gb_internal void odin_doc_update_entities(OdinDocWriter *w) {
break;
}
+ if (w->state == OdinDocWriterState_Preparing) {
+ GB_ASSERT(entity_index == 0);
+ } else {
+ GB_ASSERT(entity_index != 0);
+ }
+
OdinDocEntity *dst = odin_doc_get_item(w, &w->entities, entity_index);
if (dst) {
+ if (dst->kind == OdinDocEntity_Variable) {
+ GB_ASSERT(type_index != 0);
+ }
dst->type = type_index;
dst->foreign_library = foreign_library;
dst->grouped_entities = grouped_entities;
@@ -1084,7 +1102,7 @@ gb_internal void odin_doc_write_docs(OdinDocWriter *w) {
}
debugf("odin_doc_update_entities sort pkgs %s\n", w->state ? "preparing" : "writing");
- gb_sort_array(pkgs.data, pkgs.count, cmp_ast_package_by_name);
+ array_sort(pkgs, cmp_ast_package_by_name);
for_array(i, pkgs) {
gbAllocator allocator = heap_allocator();
@@ -1147,7 +1165,7 @@ gb_internal void odin_doc_write_to_file(OdinDocWriter *w, char const *filename)
gbFileError err = gb_file_open_mode(&f, gbFileMode_Write, filename);
if (err != gbFileError_None) {
gb_printf_err("Failed to write .odin-doc to: %s\n", filename);
- gb_exit(1);
+ exit_with_errors();
return;
}
defer (gb_file_close(&f));
diff --git a/src/entity.cpp b/src/entity.cpp
index ce27da3f2..802b381f9 100644
--- a/src/entity.cpp
+++ b/src/entity.cpp
@@ -43,8 +43,9 @@ enum EntityFlag : u64 {
EntityFlag_NoAlias = 1ull<<9,
EntityFlag_TypeField = 1ull<<10,
EntityFlag_Value = 1ull<<11,
+ EntityFlag_BitFieldField = 1ull<<12,
-
+ EntityFlag_NoCapture = 1ull<<13, // #no_capture
EntityFlag_PolyConst = 1ull<<15,
EntityFlag_NotExported = 1ull<<16,
@@ -59,7 +60,7 @@ enum EntityFlag : u64 {
EntityFlag_ProcBodyChecked = 1ull<<21,
EntityFlag_CVarArg = 1ull<<22,
-
+ EntityFlag_NoBroadcast = 1ull<<23,
EntityFlag_AnyInt = 1ull<<24,
EntityFlag_Disabled = 1ull<<25,
@@ -84,8 +85,6 @@ enum EntityFlag : u64 {
EntityFlag_Require = 1ull<<50,
EntityFlag_ByPtr = 1ull<<51, // enforce parameter is passed by pointer
- EntityFlag_OldForOrSwitchValue = 1ull<<52,
-
EntityFlag_Overridden = 1ull<<63,
};
@@ -105,6 +104,7 @@ enum ParameterValueKind {
ParameterValue_Constant,
ParameterValue_Nil,
ParameterValue_Location,
+ ParameterValue_Expression,
ParameterValue_Value,
};
@@ -134,9 +134,7 @@ enum EntityConstantFlags : u32 {
enum ProcedureOptimizationMode : u8 {
ProcedureOptimizationMode_Default,
ProcedureOptimizationMode_None,
- ProcedureOptimizationMode_Minimal,
- ProcedureOptimizationMode_Size,
- ProcedureOptimizationMode_Speed,
+ ProcedureOptimizationMode_FavorSize,
};
@@ -209,9 +207,11 @@ struct Entity {
CommentGroup *comment;
} Constant;
struct {
+ Ast *type_expr; // only used for some variables within procedure bodies
Ast *init_expr; // only used for some variables within procedure bodies
i32 field_index;
i32 field_group_index;
+ u8 bit_field_bit_size;
ParameterValue param_value;
@@ -222,11 +222,14 @@ struct Entity {
Ast * foreign_library_ident;
String link_name;
String link_prefix;
+ String link_suffix;
String link_section;
CommentGroup *docs;
CommentGroup *comment;
bool is_foreign;
bool is_export;
+ bool is_global;
+ bool is_rodata;
} Variable;
struct {
Type * type_parameter_specialization;
@@ -241,6 +244,7 @@ struct Entity {
Ast * foreign_library_ident;
String link_name;
String link_prefix;
+ String link_suffix;
DeferredProcedure deferred_procedure;
struct GenProcsData *gen_procs;
@@ -249,8 +253,10 @@ struct Entity {
bool is_foreign : 1;
bool is_export : 1;
bool generated_from_polymorphic : 1;
- bool target_feature_disabled : 1;
- String target_feature;
+ bool entry_point_only : 1;
+ bool has_instrumentation : 1;
+ bool is_memcpy_like : 1;
+ bool uses_branch_location : 1;
} Procedure;
struct {
Array<Entity *> entities;
@@ -264,6 +270,7 @@ struct Entity {
Scope *scope;
} ImportName;
struct {
+ Ast *decl;
Slice<String> paths;
String name;
i64 priority_index;
@@ -331,6 +338,9 @@ gb_internal Entity *alloc_entity(EntityKind kind, Scope *scope, Token token, Typ
entity->token = token;
entity->type = type;
entity->id = 1 + global_entity_id.fetch_add(1);
+ if (token.pos.file_id) {
+ entity->file = thread_safe_get_ast_file_from_id(token.pos.file_id);
+ }
return entity;
}
@@ -476,3 +486,25 @@ gb_internal Entity *strip_entity_wrapping(Ast *expr) {
Entity *e = entity_from_expr(expr);
return strip_entity_wrapping(e);
}
+
+
+gb_internal bool is_entity_local_variable(Entity *e) {
+ if (e == nullptr) {
+ return false;
+ }
+ if (e->kind != Entity_Variable) {
+ return false;
+ }
+ if (e->Variable.is_global) {
+ return false;
+ }
+ if (e->scope == nullptr) {
+ return true;
+ }
+ if (e->flags & (EntityFlag_ForValue|EntityFlag_SwitchValue|EntityFlag_Static)) {
+ return false;
+ }
+
+ return ((e->scope->flags &~ ScopeFlag_ContextDefined) == 0) ||
+ (e->scope->flags & ScopeFlag_Proc) != 0;
+}
diff --git a/src/error.cpp b/src/error.cpp
index e63682829..1492b00c7 100644
--- a/src/error.cpp
+++ b/src/error.cpp
@@ -1,28 +1,77 @@
+enum ErrorValueKind : u32 {
+ ErrorValue_Error,
+ ErrorValue_Warning,
+};
+
+struct ErrorValue {
+ ErrorValueKind kind;
+ TokenPos pos;
+ TokenPos end;
+ Array<u8> msg;
+ bool seen_newline;
+};
+
struct ErrorCollector {
- TokenPos prev;
+ // TokenPos prev; // no point collecting because of the mulithreaded nature
std::atomic<i64> count;
std::atomic<i64> warning_count;
std::atomic<bool> in_block;
- BlockingMutex mutex;
- BlockingMutex error_out_mutex;
- BlockingMutex string_mutex;
- RecursiveMutex block_mutex;
-
- RecursiveMutex error_buffer_mutex;
- Array<u8> error_buffer;
- Array<String> errors;
+
+ RecursiveMutex mutex;
+ BlockingMutex path_mutex;
+
+ Array<ErrorValue> error_values;
+ ErrorValue curr_error_value;
+ std::atomic<bool> curr_error_value_set;
};
gb_global ErrorCollector global_error_collector;
+gb_internal void push_error_value(TokenPos const &pos, ErrorValueKind kind = ErrorValue_Error) {
+ GB_ASSERT_MSG(global_error_collector.curr_error_value_set.load() == false, "Possible race condition in error handling system, please report this with an issue");
+ ErrorValue ev = {kind, pos};
+ ev.msg.allocator = heap_allocator();
+
+ global_error_collector.curr_error_value = ev;
+ global_error_collector.curr_error_value_set.store(true);
+}
+
+gb_internal void pop_error_value(void) {
+ mutex_lock(&global_error_collector.mutex);
+ if (global_error_collector.curr_error_value_set.load()) {
+ array_add(&global_error_collector.error_values, global_error_collector.curr_error_value);
+
+ global_error_collector.curr_error_value = {};
+ global_error_collector.curr_error_value_set.store(false);
+ }
+ mutex_unlock(&global_error_collector.mutex);
+}
+
+
+gb_internal void try_pop_error_value(void) {
+ if (!global_error_collector.in_block.load()) {
+ pop_error_value();
+ }
+}
+
+gb_internal ErrorValue *get_error_value(void) {
+ GB_ASSERT_MSG(global_error_collector.curr_error_value_set.load() == true, "Possible race condition in error handling system, please report this with an issue");
+ return &global_error_collector.curr_error_value;
+}
+
+
+
gb_internal bool any_errors(void) {
return global_error_collector.count.load() != 0;
}
+gb_internal bool any_warnings(void) {
+ return global_error_collector.warning_count.load() != 0;
+}
+
gb_internal void init_global_error_collector(void) {
- array_init(&global_error_collector.errors, heap_allocator());
- array_init(&global_error_collector.error_buffer, heap_allocator());
+ array_init(&global_error_collector.error_values, heap_allocator());
array_init(&global_file_path_strings, heap_allocator(), 1, 4096);
array_init(&global_files, heap_allocator(), 1, 4096);
}
@@ -37,7 +86,8 @@ 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.string_mutex);
+ mutex_lock(&global_error_collector.path_mutex);
+ mutex_lock(&global_files_mutex);
if (index >= global_file_path_strings.count) {
array_resize(&global_file_path_strings, index+1);
@@ -48,14 +98,16 @@ gb_internal bool set_file_path_string(i32 index, String const &path) {
ok = true;
}
- mutex_unlock(&global_error_collector.string_mutex);
+ mutex_unlock(&global_files_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.string_mutex);
+ mutex_lock(&global_error_collector.path_mutex);
+ mutex_lock(&global_files_mutex);
if (index >= global_files.count) {
array_resize(&global_files, index+1);
@@ -65,34 +117,38 @@ gb_internal bool thread_safe_set_ast_file_from_id(i32 index, AstFile *file) {
global_files[index] = file;
ok = true;
}
-
- mutex_unlock(&global_error_collector.string_mutex);
+ mutex_unlock(&global_files_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.string_mutex);
+ mutex_lock(&global_error_collector.path_mutex);
+ mutex_lock(&global_files_mutex);
String path = {};
if (index < global_file_path_strings.count) {
path = global_file_path_strings[index];
}
- mutex_unlock(&global_error_collector.string_mutex);
+ mutex_unlock(&global_files_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.string_mutex);
+ mutex_lock(&global_error_collector.path_mutex);
+ mutex_lock(&global_files_mutex);
AstFile *file = nullptr;
if (index < global_files.count) {
file = global_files[index];
}
- mutex_unlock(&global_error_collector.string_mutex);
+ mutex_unlock(&global_files_mutex);
+ mutex_unlock(&global_error_collector.path_mutex);
return file;
}
@@ -102,6 +158,8 @@ gb_internal AstFile *thread_safe_get_ast_file_from_id(i32 index) {
gb_internal bool global_warnings_as_errors(void);
gb_internal bool global_ignore_warnings(void);
gb_internal bool show_error_line(void);
+gb_internal bool terse_errors(void);
+gb_internal bool json_errors(void);
gb_internal bool has_ansi_terminal_colours(void);
gb_internal gbString get_file_line_as_string(TokenPos const &pos, i32 *offset);
@@ -113,96 +171,49 @@ gb_internal void syntax_error(Token const &token, char const *fmt, ...);
gb_internal void syntax_error(TokenPos pos, char const *fmt, ...);
gb_internal void syntax_warning(Token const &token, char const *fmt, ...);
gb_internal void compiler_error(char const *fmt, ...);
+gb_internal void print_all_errors(void);
-gb_internal void begin_error_block(void) {
- mutex_lock(&global_error_collector.block_mutex);
- global_error_collector.in_block.store(true);
-}
-gb_internal void end_error_block(void) {
- mutex_lock(&global_error_collector.error_buffer_mutex);
- isize n = global_error_collector.error_buffer.count;
- if (n > 0) {
- u8 *text = global_error_collector.error_buffer.data;
+#define ERROR_OUT_PROC(name) void name(char const *fmt, va_list va)
+typedef ERROR_OUT_PROC(ErrorOutProc);
- bool add_extra_newline = false;
+gb_internal ERROR_OUT_PROC(default_error_out_va) {
+ char buf[4096] = {};
+ isize len = gb_snprintf_va(buf, gb_size_of(buf), fmt, va);
+ isize n = len-1;
- if (show_error_line()) {
- if (n >= 2 && !(text[n-2] == '\n' && text[n-1] == '\n')) {
- add_extra_newline = true;
- }
- } else {
- isize newline_count = 0;
- for (isize i = 0; i < n; i++) {
- if (text[i] == '\n') {
- newline_count += 1;
+ if (n > 0) {
+ ErrorValue *ev = get_error_value();
+ if (terse_errors()) {
+ for (isize i = 0; i < n && !ev->seen_newline; i++) {
+ u8 c = cast(u8)buf[i];
+ if (c == '\n') {
+ ev->seen_newline = true;
}
+ array_add(&ev->msg, c);
}
- if (newline_count > 1) {
- add_extra_newline = true;
- }
- }
-
- if (add_extra_newline) {
- // add an extra new line as padding when the error line is being shown
- error_line("\n");
+ } else {
+ array_add_elems(&ev->msg, (u8 *)buf, n);
}
+ }
+}
- n = global_error_collector.error_buffer.count;
- text = gb_alloc_array(permanent_allocator(), u8, n+1);
- gb_memmove(text, global_error_collector.error_buffer.data, n);
- text[n] = 0;
-
+gb_global ErrorOutProc *error_out_va = default_error_out_va;
- mutex_lock(&global_error_collector.error_out_mutex);
- String s = {text, n};
- array_add(&global_error_collector.errors, s);
- mutex_unlock(&global_error_collector.error_out_mutex);
+gb_internal void begin_error_block(void) {
+ mutex_lock(&global_error_collector.mutex);
+ global_error_collector.in_block.store(true);
+}
- global_error_collector.error_buffer.count = 0;
- }
- mutex_unlock(&global_error_collector.error_buffer_mutex);
+gb_internal void end_error_block(void) {
+ pop_error_value();
global_error_collector.in_block.store(false);
- mutex_unlock(&global_error_collector.block_mutex);
+ mutex_unlock(&global_error_collector.mutex);
}
#define ERROR_BLOCK() begin_error_block(); defer (end_error_block())
-#define ERROR_OUT_PROC(name) void name(char const *fmt, va_list va)
-typedef ERROR_OUT_PROC(ErrorOutProc);
-
-gb_internal ERROR_OUT_PROC(default_error_out_va) {
- gbFile *f = gb_file_get_standard(gbFileStandard_Error);
-
- char buf[4096] = {};
- isize len = gb_snprintf_va(buf, gb_size_of(buf), fmt, va);
- isize n = len-1;
- if (global_error_collector.in_block) {
- mutex_lock(&global_error_collector.error_buffer_mutex);
-
- isize cap = global_error_collector.error_buffer.count + n;
- array_reserve(&global_error_collector.error_buffer, cap);
- u8 *data = global_error_collector.error_buffer.data + global_error_collector.error_buffer.count;
- gb_memmove(data, buf, n);
- global_error_collector.error_buffer.count += n;
-
- mutex_unlock(&global_error_collector.error_buffer_mutex);
- } else {
- mutex_lock(&global_error_collector.error_out_mutex);
- {
- u8 *text = gb_alloc_array(permanent_allocator(), u8, n+1);
- gb_memmove(text, buf, n);
- text[n] = 0;
- array_add(&global_error_collector.errors, make_string(text, n));
- }
- mutex_unlock(&global_error_collector.error_out_mutex);
-
- }
- gb_file_write(f, buf, n);
-}
-
-gb_global ErrorOutProc *error_out_va = default_error_out_va;
gb_internal void error_out(char const *fmt, ...) {
va_list va;
@@ -226,6 +237,7 @@ enum TerminalColour {
TerminalColour_Blue,
TerminalColour_Purple,
TerminalColour_Black,
+ TerminalColour_Grey,
};
gb_internal void terminal_set_colours(TerminalStyle style, TerminalColour foreground) {
@@ -245,6 +257,7 @@ gb_internal void terminal_set_colours(TerminalStyle style, TerminalColour foregr
case TerminalColour_Blue: error_out("\x1b[%s;34m", ss); break;
case TerminalColour_Purple: error_out("\x1b[%s;35m", ss); break;
case TerminalColour_Black: error_out("\x1b[%s;30m", ss); break;
+ case TerminalColour_Grey: error_out("\x1b[%s;90m", ss); break;
}
}
}
@@ -255,91 +268,245 @@ gb_internal void terminal_reset_colours(void) {
}
-gb_internal bool show_error_on_line(TokenPos const &pos, TokenPos end) {
+gb_internal isize show_error_on_line(TokenPos const &pos, TokenPos end) {
+ get_error_value()->end = end;
if (!show_error_line()) {
- return false;
+ return -1;
}
- i32 offset = 0;
- gbString the_line = get_file_line_as_string(pos, &offset);
+ i32 error_start_index_bytes = 0;
+ gbString the_line = get_file_line_as_string(pos, &error_start_index_bytes);
defer (gb_string_free(the_line));
- if (the_line != nullptr) {
- char const *line_text = the_line;
- isize line_len = gb_string_length(the_line);
+ if (the_line == nullptr || gb_string_length(the_line) == 0) {
+ terminal_set_colours(TerminalStyle_Normal, TerminalColour_Grey);
+ error_out("\t( empty line )\n");
+ terminal_reset_colours();
- // TODO(bill): This assumes ASCII
+ if (the_line == nullptr) {
+ return -1;
+ } else {
+ return cast(isize)error_start_index_bytes;
+ }
+ }
- enum {
- MAX_LINE_LENGTH = 80,
- MAX_TAB_WIDTH = 8,
- ELLIPSIS_PADDING = 8, // `... ...`
- MAX_LINE_LENGTH_PADDED = MAX_LINE_LENGTH-MAX_TAB_WIDTH-ELLIPSIS_PADDING,
+ // These two will be used like an Odin slice later.
+ char const *line_text = the_line;
+ i32 line_length_bytes = cast(i32)gb_string_length(the_line);
+
+ ucg_grapheme* graphemes;
+ i32 line_length_runes = 0;
+ i32 line_length_graphemes = 0;
+ i32 line_width = 0;
+
+ int ucg_result = ucg_decode_grapheme_clusters(
+ permanent_allocator(), (const uint8_t*)line_text, line_length_bytes,
+ &graphemes, &line_length_runes, &line_length_graphemes, &line_width);
+
+ if (ucg_result < 0) {
+ // There was a UTF-8 parsing error.
+ // Insert a dummy grapheme so the start of the invalid rune can be pointed at.
+ graphemes = (ucg_grapheme*)gb_resize(permanent_allocator(),
+ graphemes,
+ sizeof(ucg_grapheme) * (line_length_graphemes),
+ sizeof(ucg_grapheme) * (1 + line_length_graphemes));
+
+ ucg_grapheme append = {
+ error_start_index_bytes,
+ line_length_runes,
+ 1,
};
- error_out("\t");
+ graphemes[line_length_graphemes] = append;
+ }
+
+ // The units below are counted in visual, monospace cells.
+ enum {
+ MAX_LINE_LENGTH = 80,
+ MAX_TAB_WIDTH = 8,
+ ELLIPSIS_PADDING = 8, // `... ...`
+ MIN_LEFT_VIEW = 8,
- terminal_set_colours(TerminalStyle_Bold, TerminalColour_White);
+ // A rough estimate of how many characters we'll insert, at most:
+ MAX_INSERTED_WIDTH = MAX_TAB_WIDTH + ELLIPSIS_PADDING,
+ MAX_LINE_LENGTH_PADDED = MAX_LINE_LENGTH - MAX_INSERTED_WIDTH,
+ };
- i32 error_length = gb_max(end.offset - pos.offset, 1);
+ i32 error_start_index_graphemes = 0;
+ for (i32 i = 0; i < line_length_graphemes; i += 1) {
+ if (graphemes[i].byte_index == error_start_index_bytes) {
+ error_start_index_graphemes = i;
+ break;
+ }
+ }
- isize squiggle_extra = 0;
+ if (error_start_index_graphemes == 0 && error_start_index_bytes != 0 && line_length_graphemes != 0) {
+ // The error index in graphemes was not found, but we did find a valid Unicode string.
+ //
+ // This is an edge case where the error is sitting on a newline or the
+ // end of the line, as that is the only location we could not have checked.
+ error_start_index_graphemes = line_length_graphemes;
+ }
- if (line_len > MAX_LINE_LENGTH_PADDED) {
- i32 left = MAX_TAB_WIDTH;
- if (offset > 0) {
- line_text += offset-left;
- line_len -= offset-left;
- offset = left+MAX_TAB_WIDTH/2;
+ error_out("\t");
+
+ bool show_right_ellipsis = false;
+
+ i32 squiggle_padding = 0;
+ i32 window_open_bytes = 0;
+ i32 window_close_bytes = 0;
+ if (line_width > MAX_LINE_LENGTH_PADDED) {
+ // Now that we know the line is over the length limit, we have to
+ // compose a visual window in which to display the error.
+ i32 window_size_left = 0;
+ i32 window_size_right = 0;
+ i32 window_open_graphemes = 0;
+
+ for (i32 i = error_start_index_graphemes - 1; i > 0; i -= 1) {
+ window_size_left += graphemes[i].width;
+ if (window_size_left >= MIN_LEFT_VIEW) {
+ window_open_graphemes = i;
+ window_open_bytes = graphemes[i].byte_index;
+ break;
}
- if (line_len > MAX_LINE_LENGTH_PADDED) {
- line_len = MAX_LINE_LENGTH_PADDED;
- if (error_length > line_len-left) {
- error_length = cast(i32)line_len - left;
- squiggle_extra = 1;
- }
+ }
+
+ for (i32 i = error_start_index_graphemes; i < line_length_graphemes; i += 1) {
+ window_size_right += graphemes[i].width;
+ if (window_size_right >= MAX_LINE_LENGTH_PADDED - MIN_LEFT_VIEW) {
+ window_close_bytes = graphemes[i].byte_index;
+ break;
}
- if (offset > 0) {
- error_out("... %.*s ...", cast(i32)line_len, line_text);
- } else {
- error_out("%.*s ...", cast(i32)line_len, line_text);
+ }
+ if (window_close_bytes == 0) {
+ // The window ends at the end of the line.
+ window_close_bytes = line_length_bytes;
+ }
+
+ if (window_size_right < MAX_LINE_LENGTH_PADDED - MIN_LEFT_VIEW) {
+ // Hit the end of the string early on the right side; expand backwards.
+ for (i32 i = window_open_graphemes - 1; i > 0; i -= 1) {
+ window_size_left += graphemes[i].width;
+ if (window_size_left + window_size_right >= MAX_LINE_LENGTH_PADDED) {
+ window_open_graphemes = i;
+ window_open_bytes = graphemes[i].byte_index;
+ break;
+ }
}
- } else {
- error_out("%.*s", cast(i32)line_len, line_text);
}
- error_out("\n\t");
- for (i32 i = 0; i < offset; i++) {
- error_out(" ");
+ GB_ASSERT_MSG(window_close_bytes >= window_open_bytes, "Error line truncation window has wrong byte indices. (open, close: %i, %i)", window_open_bytes, window_close_bytes);
+
+ if (window_close_bytes != line_length_bytes) {
+ show_right_ellipsis = true;
}
- terminal_set_colours(TerminalStyle_Bold, TerminalColour_Green);
+ // Close the window, going left.
+ line_length_bytes = window_close_bytes;
- error_out("^");
- if (end.file_id == pos.file_id) {
- if (end.line > pos.line) {
- for (i32 i = offset; i < line_len; i++) {
- error_out("~");
- }
- } else if (end.line == pos.line && end.column > pos.column) {
- for (i32 i = 1; i < error_length-1+squiggle_extra; i++) {
- error_out("~");
- }
- if (error_length > 1 && squiggle_extra == 0) {
- error_out("^");
+ // Adjust the slice of text. In Odin, this would be:
+ // `line_text = line_text[window_left_bytes:]`
+ line_text += window_open_bytes;
+ line_length_bytes -= window_open_bytes;
+ GB_ASSERT_MSG(line_length_bytes >= 0, "Bounds-checking error: line_length_bytes");
+
+ if (window_open_bytes > 0) {
+ error_out("... ");
+ squiggle_padding += 4;
+ }
+ } else {
+ // No truncation needed.
+ window_open_bytes = 0;
+ window_close_bytes = line_length_bytes;
+ }
+
+ for (i32 i = error_start_index_graphemes; i > 0; i -= 1) {
+ if (graphemes[i].byte_index == window_open_bytes) {
+ break;
+ }
+ squiggle_padding += graphemes[i].width;
+ }
+
+ // Start printing code.
+
+ terminal_set_colours(TerminalStyle_Normal, TerminalColour_White);
+ error_out("%.*s", line_length_bytes, line_text);
+
+ i32 squiggle_length = 0;
+ bool trailing_squiggle = false;
+
+ if (end.file_id == pos.file_id) {
+ // The error has an endpoint.
+
+ if (end.line > pos.line) {
+ // Error goes to next line.
+ // Always show the ellipsis in this case
+ show_right_ellipsis = true;
+
+ for (i32 i = error_start_index_graphemes; i < line_length_graphemes; i += 1) {
+ squiggle_length += graphemes[i].width;
+ trailing_squiggle = true;
+ }
+
+ } else if (end.line == pos.line && end.column > pos.column) {
+ // Error terminates before line end.
+ i32 adjusted_end_index = graphemes[error_start_index_graphemes].byte_index + end.column - pos.column;
+
+ for (i32 i = error_start_index_graphemes; i < line_length_graphemes; i += 1) {
+ if (graphemes[i].byte_index >= adjusted_end_index) {
+ break;
+ } else if (graphemes[i].byte_index >= window_close_bytes) {
+ trailing_squiggle = true;
+ break;
}
+ squiggle_length += graphemes[i].width;
}
}
+ } else {
+ // The error is at one spot; no range known.
+ squiggle_length = 1;
+ }
- terminal_reset_colours();
+ if (show_right_ellipsis) {
+ error_out(" ...");
+ }
- error_out("\n");
- return true;
+ error_out("\n\t");
+
+ for (i32 i = squiggle_padding; i > 0; i -= 1) {
+ error_out(" ");
+ }
+
+ terminal_set_colours(TerminalStyle_Bold, TerminalColour_Green);
+
+ if (squiggle_length > 0) {
+ error_out("^");
+ squiggle_length -= 1;
}
- return false;
+ for (/**/; squiggle_length > 1; squiggle_length -= 1) {
+ error_out("~");
+ }
+ if (squiggle_length > 0) {
+ if (trailing_squiggle) {
+ error_out("~ ...");
+ } else {
+ error_out("^");
+ }
+ }
+
+ // NOTE(Feoramund): Specifically print a newline, then reset colours,
+ // instead of the other way around. Otherwise the printing mechanism
+ // will collapse the newline for reasons currently beyond my ken.
+ error_out("\n");
+ terminal_reset_colours();
+
+ return squiggle_padding;
}
+gb_internal void error_out_empty(void) {
+ error_out("");
+}
gb_internal void error_out_pos(TokenPos pos) {
terminal_set_colours(TerminalStyle_Bold, TerminalColour_White);
error_out("%s ", token_pos_to_string(pos));
@@ -356,27 +523,31 @@ gb_internal void error_out_coloured(char const *str, TerminalStyle style, Termin
gb_internal void error_va(TokenPos const &pos, TokenPos end, char const *fmt, va_list va) {
global_error_collector.count.fetch_add(1);
+ mutex_lock(&global_error_collector.mutex);
if (global_error_collector.count > MAX_ERROR_COLLECTOR_COUNT()) {
+ print_all_errors();
gb_exit(1);
}
- mutex_lock(&global_error_collector.mutex);
- // NOTE(bill): Duplicate error, skip it
+
+ push_error_value(pos, ErrorValue_Error);
if (pos.line == 0) {
+ error_out_empty();
error_out_coloured("Error: ", TerminalStyle_Normal, TerminalColour_Red);
error_out_va(fmt, va);
error_out("\n");
- } else if (global_error_collector.prev != pos) {
- global_error_collector.prev = pos;
- error_out_pos(pos);
- if (has_ansi_terminal_colours()) {
+ } else {
+ // global_error_collector.prev = pos;
+ if (json_errors()) {
+ error_out_empty();
+ } else {
+ error_out_pos(pos);
error_out_coloured("Error: ", TerminalStyle_Normal, TerminalColour_Red);
}
error_out_va(fmt, va);
error_out("\n");
show_error_on_line(pos, end);
- } else {
- global_error_collector.count.fetch_sub(1);
}
+ try_pop_error_value();
mutex_unlock(&global_error_collector.mutex);
}
@@ -385,23 +556,33 @@ gb_internal void warning_va(TokenPos const &pos, TokenPos end, char const *fmt,
error_va(pos, end, fmt, va);
return;
}
+ if (global_ignore_warnings()) {
+ return;
+ }
+
global_error_collector.warning_count.fetch_add(1);
mutex_lock(&global_error_collector.mutex);
- if (!global_ignore_warnings()) {
- // NOTE(bill): Duplicate error, skip it
- if (pos.line == 0) {
- error_out_coloured("Warning: ", TerminalStyle_Normal, TerminalColour_Yellow);
- error_out_va(fmt, va);
- error_out("\n");
- } else if (global_error_collector.prev != pos) {
- global_error_collector.prev = pos;
+
+ push_error_value(pos, ErrorValue_Warning);
+
+ if (pos.line == 0) {
+ error_out_empty();
+ error_out_coloured("Warning: ", TerminalStyle_Normal, TerminalColour_Yellow);
+ error_out_va(fmt, va);
+ error_out("\n");
+ } else {
+ // global_error_collector.prev = pos;
+ if (json_errors()) {
+ error_out_empty();
+ } else {
error_out_pos(pos);
error_out_coloured("Warning: ", TerminalStyle_Normal, TerminalColour_Yellow);
- error_out_va(fmt, va);
- error_out("\n");
- show_error_on_line(pos, end);
}
+ error_out_va(fmt, va);
+ error_out("\n");
+ show_error_on_line(pos, end);
}
+ try_pop_error_value();
mutex_unlock(&global_error_collector.mutex);
}
@@ -412,69 +593,99 @@ gb_internal void error_line_va(char const *fmt, va_list va) {
gb_internal void error_no_newline_va(TokenPos const &pos, char const *fmt, va_list va) {
global_error_collector.count.fetch_add(1);
+ mutex_lock(&global_error_collector.mutex);
if (global_error_collector.count.load() > MAX_ERROR_COLLECTOR_COUNT()) {
+ print_all_errors();
gb_exit(1);
}
- mutex_lock(&global_error_collector.mutex);
- // NOTE(bill): Duplicate error, skip it
+
+ push_error_value(pos, ErrorValue_Error);
+
if (pos.line == 0) {
+ error_out_empty();
error_out_coloured("Error: ", TerminalStyle_Normal, TerminalColour_Red);
error_out_va(fmt, va);
- } else if (global_error_collector.prev != pos) {
- global_error_collector.prev = pos;
- error_out_pos(pos);
+ } else {
+ // global_error_collector.prev = pos;
+ if (json_errors()) {
+ error_out_empty();
+ } else {
+ error_out_pos(pos);
+ }
if (has_ansi_terminal_colours()) {
error_out_coloured("Error: ", TerminalStyle_Normal, TerminalColour_Red);
}
error_out_va(fmt, va);
}
+
+ try_pop_error_value();
mutex_unlock(&global_error_collector.mutex);
}
gb_internal void syntax_error_va(TokenPos const &pos, TokenPos end, char const *fmt, va_list va) {
global_error_collector.count.fetch_add(1);
+ mutex_lock(&global_error_collector.mutex);
if (global_error_collector.count > MAX_ERROR_COLLECTOR_COUNT()) {
+ print_all_errors();
gb_exit(1);
}
- mutex_lock(&global_error_collector.mutex);
- // NOTE(bill): Duplicate error, skip it
- if (global_error_collector.prev != pos) {
- global_error_collector.prev = pos;
- error_out_pos(pos);
+
+ push_error_value(pos, ErrorValue_Warning);
+
+ if (pos.line == 0) {
+ error_out_empty();
error_out_coloured("Syntax Error: ", TerminalStyle_Normal, TerminalColour_Red);
error_out_va(fmt, va);
error_out("\n");
- // show_error_on_line(pos, end);
- } else if (pos.line == 0) {
+ } else {
+ // global_error_collector.prev = pos;
+ if (json_errors()) {
+ error_out_empty();
+ } else {
+ error_out_pos(pos);
+ }
error_out_coloured("Syntax Error: ", TerminalStyle_Normal, TerminalColour_Red);
error_out_va(fmt, va);
error_out("\n");
+ show_error_on_line(pos, end);
}
+
+ try_pop_error_value();
mutex_unlock(&global_error_collector.mutex);
}
gb_internal void syntax_error_with_verbose_va(TokenPos const &pos, TokenPos end, char const *fmt, va_list va) {
global_error_collector.count.fetch_add(1);
+ mutex_lock(&global_error_collector.mutex);
if (global_error_collector.count > MAX_ERROR_COLLECTOR_COUNT()) {
+ print_all_errors();
gb_exit(1);
}
- mutex_lock(&global_error_collector.mutex);
- // NOTE(bill): Duplicate error, skip it
+
+ push_error_value(pos, ErrorValue_Warning);
+
if (pos.line == 0) {
- error_out_coloured("Syntax_Error: ", TerminalStyle_Normal, TerminalColour_Red);
+ error_out_empty();
+ error_out_coloured("Syntax Error: ", TerminalStyle_Normal, TerminalColour_Red);
error_out_va(fmt, va);
error_out("\n");
- } else if (global_error_collector.prev != pos) {
- global_error_collector.prev = pos;
- error_out_pos(pos);
+ } else {
+ // global_error_collector.prev = pos;
+ if (json_errors()) {
+ error_out_empty();
+ } else {
+ error_out_pos(pos);
+ }
if (has_ansi_terminal_colours()) {
- error_out_coloured("Syntax_Error: ", TerminalStyle_Normal, TerminalColour_Red);
+ error_out_coloured("Syntax Error: ", TerminalStyle_Normal, TerminalColour_Red);
}
error_out_va(fmt, va);
error_out("\n");
show_error_on_line(pos, end);
}
+
+ try_pop_error_value();
mutex_unlock(&global_error_collector.mutex);
}
@@ -484,23 +695,34 @@ gb_internal void syntax_warning_va(TokenPos const &pos, TokenPos end, char const
syntax_error_va(pos, end, fmt, va);
return;
}
+ if (global_ignore_warnings()) {
+ return;
+ }
mutex_lock(&global_error_collector.mutex);
global_error_collector.warning_count++;
- if (!global_ignore_warnings()) {
- // NOTE(bill): Duplicate error, skip it
- if (global_error_collector.prev != pos) {
- global_error_collector.prev = pos;
+
+
+ push_error_value(pos, ErrorValue_Warning);
+
+ if (pos.line == 0) {
+ error_out_empty();
+ error_out_coloured("Syntax Warning: ", TerminalStyle_Normal, TerminalColour_Yellow);
+ error_out_va(fmt, va);
+ error_out("\n");
+ } else {
+ // global_error_collector.prev = pos;
+ if (json_errors()) {
+ error_out_empty();
+ } else {
error_out_pos(pos);
- error_out_coloured("Syntax Warning: ", TerminalStyle_Normal, TerminalColour_Yellow);
- error_out_va(fmt, va);
- error_out("\n");
- // show_error_on_line(pos, end);
- } else if (pos.line == 0) {
- error_out_coloured("Syntax Warning: ", TerminalStyle_Normal, TerminalColour_Yellow);
- error_out_va(fmt, va);
- error_out("\n");
}
+ error_out_coloured("Syntax Warning: ", TerminalStyle_Normal, TerminalColour_Yellow);
+ error_out_va(fmt, va);
+ error_out("\n");
+ // show_error_on_line(pos, end);
}
+
+ try_pop_error_value();
mutex_unlock(&global_error_collector.mutex);
}
@@ -568,6 +790,10 @@ gb_internal void syntax_error_with_verbose(TokenPos pos, TokenPos end, char cons
gb_internal void compiler_error(char const *fmt, ...) {
+ if (any_errors() || any_warnings()) {
+ print_all_errors();
+ }
+
va_list va;
va_start(va, fmt);
@@ -577,3 +803,220 @@ gb_internal void compiler_error(char const *fmt, ...) {
GB_DEBUG_TRAP();
gb_exit(1);
}
+
+
+gb_internal void exit_with_errors(void) {
+ if (any_errors() || any_warnings()) {
+ print_all_errors();
+ }
+ gb_exit(1);
+}
+
+
+
+gb_internal int error_value_cmp(void const *a, void const *b) {
+ ErrorValue *x = cast(ErrorValue *)a;
+ ErrorValue *y = cast(ErrorValue *)b;
+ return token_pos_cmp(x->pos, y->pos);
+}
+
+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("map index")},
+ {str_lit("a "), str_lit("map literal")},
+ {str_lit("a "), str_lit("matrix literal")},
+ {str_lit("a "), str_lit("polymorphic type argument")},
+ {str_lit("a "), str_lit("procedure argument")},
+ {str_lit("a "), str_lit("simd vector literal")},
+ {str_lit("a "), str_lit("slice literal")},
+ {str_lit("a "), str_lit("structure literal")},
+ {str_lit("a "), str_lit("variable declaration")},
+ {str_lit("an "), str_lit("'any' literal")},
+ {str_lit("an "), str_lit("array literal")},
+ {str_lit("an "), str_lit("enumerated array literal")},
+
+};
+
+// Returns definite or indefinite article matching `context_name`, or "" if not found.
+gb_internal String error_article(String context_name) {
+ for (int i = 0; i < gb_count_of(error_article_table); i += 1) {
+ if (context_name == error_article_table[i][1]) {
+ return error_article_table[i][0];
+ }
+ }
+ return str_lit("");
+}
+
+gb_internal bool errors_already_printed = false;
+
+gb_internal void print_all_errors(void) {
+ if (errors_already_printed) {
+ if (global_error_collector.warning_count.load() == global_error_collector.error_values.count) {
+ for (ErrorValue &ev : global_error_collector.error_values) {
+ array_free(&ev.msg);
+ }
+ array_clear(&global_error_collector.error_values);
+ errors_already_printed = false;
+ }
+ return;
+ }
+
+ auto const &escape_char = [](gbString res, u8 c) -> gbString {
+ switch (c) {
+ case '\n': res = gb_string_append_length(res, "\\n", 2); break;
+ case '"': res = gb_string_append_length(res, "\\\"", 2); break;
+ case '\\': res = gb_string_append_length(res, "\\\\", 2); break;
+ case '\b': res = gb_string_append_length(res, "\\b", 2); break;
+ case '\f': res = gb_string_append_length(res, "\\f", 2); break;
+ case '\r': res = gb_string_append_length(res, "\\r", 2); break;
+ case '\t': res = gb_string_append_length(res, "\\t", 2); break;
+ default:
+ if ('\x00' <= c && c <= '\x1f') {
+ res = gb_string_append_fmt(res, "\\u%04x", c);
+ } else {
+ res = gb_string_append_length(res, &c, 1);
+ }
+ break;
+ }
+ return res;
+ };
+
+ GB_ASSERT(any_errors() || any_warnings());
+
+ array_sort(global_error_collector.error_values, error_value_cmp);
+
+
+ { // NOTE(bill): merge neighbouring errors
+ isize default_lines_to_skip = 1;
+ if (show_error_line()) {
+ // NOTE(bill): this will always be 2 extra lines
+ default_lines_to_skip += 2;
+ }
+
+ ErrorValue *prev_ev = nullptr;
+ for (isize i = 0; i < global_error_collector.error_values.count; /**/) {
+ ErrorValue &ev = global_error_collector.error_values[i];
+
+ if (prev_ev && prev_ev->pos == ev.pos) {
+ String_Iterator it = {{ev.msg.data, ev.msg.count}, 0};
+
+ for (isize lines_to_skip = default_lines_to_skip; lines_to_skip > 0; lines_to_skip -= 1) {
+ String line = string_split_iterator(&it, '\n');
+ if (line.len == 0) {
+ break;
+ }
+ }
+
+ // Merge additional text (suggestions for example) into the previous error.
+ String current = {prev_ev->msg.data, prev_ev->msg.count};
+ String addition = {it.str.text+it.pos, it.str.len-it.pos};
+ if (addition.len > 0 && !string_contains_string(current, addition)) {
+ array_add_elems(&prev_ev->msg, addition.text, addition.len);
+ }
+
+ array_free(&ev.msg);
+ array_ordered_remove(&global_error_collector.error_values, i);
+ } else {
+ prev_ev = &ev;
+ i += 1;
+ }
+ }
+ }
+
+ gbString res = gb_string_make(heap_allocator(), "");
+ defer (gb_string_free(res));
+
+ if (json_errors()) {
+ res = gb_string_append_fmt(res, "{\n");
+ res = gb_string_append_fmt(res, "\t\"error_count\": %td,\n", global_error_collector.error_values.count);
+ res = gb_string_append_fmt(res, "\t\"errors\": [\n");
+
+ for_array(i, global_error_collector.error_values) {
+ ErrorValue ev = global_error_collector.error_values[i];
+
+ res = gb_string_append_fmt(res, "\t\t{\n");
+
+ res = gb_string_append_fmt(res, "\t\t\t\"type\": \"");
+ if (ev.kind == ErrorValue_Warning) {
+ res = gb_string_append_fmt(res, "warning");
+ } else {
+ res = gb_string_append_fmt(res, "error");
+ }
+ res = gb_string_append_fmt(res, "\",\n");
+
+ if (ev.pos.file_id) {
+ res = gb_string_append_fmt(res, "\t\t\t\"pos\": {\n");
+ res = gb_string_append_fmt(res, "\t\t\t\t\"file\": \"");
+ String file = get_file_path_string(ev.pos.file_id);
+ for (isize k = 0; k < file.len; k++) {
+ res = escape_char(res, file.text[k]);
+ }
+ res = gb_string_append_fmt(res, "\",\n");
+ res = gb_string_append_fmt(res, "\t\t\t\t\"offset\": %d,\n", ev.pos.offset);
+ res = gb_string_append_fmt(res, "\t\t\t\t\"line\": %d,\n", ev.pos.line);
+ res = gb_string_append_fmt(res, "\t\t\t\t\"column\": %d,\n", ev.pos.column);
+ i32 end_column = gb_max(ev.end.column, ev.pos.column);
+ res = gb_string_append_fmt(res, "\t\t\t\t\"end_column\": %d\n", end_column);
+ res = gb_string_append_fmt(res, "\t\t\t},\n");
+ } else {
+ res = gb_string_append_fmt(res, "\t\t\t\"pos\": null,\n");
+ }
+
+ res = gb_string_append_fmt(res, "\t\t\t\"msgs\": [\n");
+
+ auto lines = split_lines_from_array(ev.msg, heap_allocator());
+ defer (array_free(&lines));
+
+ if (lines.count > 0) {
+ res = gb_string_append_fmt(res, "\t\t\t\t\"");
+
+ for (isize j = 0; j < lines.count; j++) {
+ String line = lines[j];
+ for (isize k = 0; k < line.len; k++) {
+ u8 c = line.text[k];
+ res = escape_char(res, c);
+ }
+ if (j+1 < lines.count) {
+ res = gb_string_append_fmt(res, "\",\n");
+ res = gb_string_append_fmt(res, "\t\t\t\t\"");
+ }
+ }
+ res = gb_string_append_fmt(res, "\"\n");
+ }
+ res = gb_string_append_fmt(res, "\t\t\t]\n");
+ res = gb_string_append_fmt(res, "\t\t}");
+ if (i+1 != global_error_collector.error_values.count) {
+ res = gb_string_append_fmt(res, ",");
+ }
+ res = gb_string_append_fmt(res, "\n");
+ }
+
+ res = gb_string_append_fmt(res, "\t]\n");
+ res = gb_string_append_fmt(res, "}\n");
+ } else {
+ for_array(i, global_error_collector.error_values) {
+ ErrorValue ev = global_error_collector.error_values[i];
+
+ String_Iterator it = {{ev.msg.data, ev.msg.count}, 0};
+
+ for (isize line_idx = 0; /**/; line_idx++) {
+ String line = string_split_iterator(&it, '\n');
+ if (line.len == 0) {
+ break;
+ }
+ line = string_trim_trailing_whitespace(line);
+ res = gb_string_append_length(res, line.text, line.len);
+ res = gb_string_append_length(res, " \n", 2);
+ if (line_idx == 0 && terse_errors()) {
+ break;
+ }
+ }
+ }
+ }
+ gbFile *f = gb_file_get_standard(gbFileStandard_Error);
+ gb_file_write(f, res, gb_string_length(res));
+
+ errors_already_printed = true;
+}
diff --git a/src/exact_value.cpp b/src/exact_value.cpp
index cd499272f..ceaed84c1 100644
--- a/src/exact_value.cpp
+++ b/src/exact_value.cpp
@@ -54,37 +54,50 @@ gb_global ExactValue const empty_exact_value = {};
gb_internal uintptr hash_exact_value(ExactValue v) {
mutex_lock(&hash_exact_value_mutex);
defer (mutex_unlock(&hash_exact_value_mutex));
+
+ uintptr res = 0;
switch (v.kind) {
case ExactValue_Invalid:
return 0;
case ExactValue_Bool:
- return gb_fnv32a(&v.value_bool, gb_size_of(v.value_bool));
+ res = gb_fnv32a(&v.value_bool, gb_size_of(v.value_bool));
+ break;
case ExactValue_String:
- return gb_fnv32a(v.value_string.text, v.value_string.len);
+ res = gb_fnv32a(v.value_string.text, v.value_string.len);
+ break;
case ExactValue_Integer:
{
u32 key = gb_fnv32a(v.value_integer.dp, gb_size_of(*v.value_integer.dp) * v.value_integer.used);
u8 last = (u8)v.value_integer.sign;
- return (key ^ last) * 0x01000193;
+ res = (key ^ last) * 0x01000193;
+ break;
}
case ExactValue_Float:
- return gb_fnv32a(&v.value_float, gb_size_of(v.value_float));
+ res = gb_fnv32a(&v.value_float, gb_size_of(v.value_float));
+ break;
case ExactValue_Pointer:
- return ptr_map_hash_key(v.value_pointer);
+ res = ptr_map_hash_key(v.value_pointer);
+ break;
case ExactValue_Complex:
- return gb_fnv32a(v.value_complex, gb_size_of(Complex128));
+ res = gb_fnv32a(v.value_complex, gb_size_of(Complex128));
+ break;
case ExactValue_Quaternion:
- return gb_fnv32a(v.value_quaternion, gb_size_of(Quaternion256));
+ res = gb_fnv32a(v.value_quaternion, gb_size_of(Quaternion256));
+ break;
case ExactValue_Compound:
- return ptr_map_hash_key(v.value_compound);
+ res = ptr_map_hash_key(v.value_compound);
+ break;
case ExactValue_Procedure:
- return ptr_map_hash_key(v.value_procedure);
+ res = ptr_map_hash_key(v.value_procedure);
+ break;
case ExactValue_Typeid:
- return ptr_map_hash_key(v.value_typeid);
+ res = ptr_map_hash_key(v.value_typeid);
+ break;
+ default:
+ res = gb_fnv32a(&v, gb_size_of(ExactValue));
}
- return gb_fnv32a(&v, gb_size_of(ExactValue));
-
+ return res & 0x7fffffff;
}
@@ -108,12 +121,14 @@ gb_internal ExactValue exact_value_string(String string) {
gb_internal ExactValue exact_value_i64(i64 i) {
ExactValue result = {ExactValue_Integer};
+ result.value_integer = {0};
big_int_from_i64(&result.value_integer, i);
return result;
}
gb_internal ExactValue exact_value_u64(u64 i) {
ExactValue result = {ExactValue_Integer};
+ result.value_integer = {0};
big_int_from_u64(&result.value_integer, i);
return result;
}
@@ -164,6 +179,7 @@ gb_internal ExactValue exact_value_typeid(Type *type) {
gb_internal ExactValue exact_value_integer_from_string(String const &string) {
ExactValue result = {ExactValue_Integer};
+ result.value_integer = {0};
bool success;
big_int_from_string(&result.value_integer, string, &success);
if (!success) {
@@ -174,7 +190,7 @@ gb_internal ExactValue exact_value_integer_from_string(String const &string) {
-gb_internal f64 float_from_string(String const &string) {
+gb_internal f64 float_from_string(String const &string, bool *success = nullptr) {
if (string.len < 128) {
char buf[128] = {};
isize n = 0;
@@ -187,7 +203,13 @@ gb_internal f64 float_from_string(String const &string) {
buf[n++] = cast(char)c;
}
buf[n] = 0;
- return atof(buf);
+
+ char *end_ptr;
+ f64 f = strtod(buf, &end_ptr);
+ if (success != nullptr) {
+ *success = *end_ptr == '\0';
+ }
+ return f;
} else {
TEMPORARY_ALLOCATOR_GUARD();
char *buf = gb_alloc_array(temporary_allocator(), char, string.len+1);
@@ -201,7 +223,13 @@ gb_internal f64 float_from_string(String const &string) {
buf[n++] = cast(char)c;
}
buf[n] = 0;
- return atof(buf);
+
+ char *end_ptr;
+ f64 f = strtod(buf, &end_ptr);
+ if (success != nullptr) {
+ *success = *end_ptr == '\0';
+ }
+ return f;
}
/*
isize i = 0;
@@ -313,7 +341,11 @@ gb_internal ExactValue exact_value_float_from_string(String string) {
return exact_value_integer_from_string(string);
}
- f64 f = float_from_string(string);
+ bool success;
+ f64 f = float_from_string(string, &success);
+ if (!success) {
+ return {ExactValue_Invalid};
+ }
return exact_value_float(f);
}
@@ -338,7 +370,11 @@ gb_internal ExactValue exact_value_from_basic_literal(TokenKind kind, String con
}
case Token_Rune: {
Rune r = GB_RUNE_INVALID;
- utf8_decode(string.text, string.len, &r);
+ if (string.len == 1) {
+ r = cast(Rune)string.text[0];
+ } else {
+ utf8_decode(string.text, string.len, &r);
+ }
return exact_value_i64(r);
}
}
@@ -556,6 +592,7 @@ gb_internal ExactValue exact_unary_operator_value(TokenKind op, ExactValue v, i3
return v;
case ExactValue_Integer: {
ExactValue i = {ExactValue_Integer};
+ i.value_integer = {0};
big_int_neg(&i.value_integer, &v.value_integer);
return i;
}
@@ -587,6 +624,7 @@ gb_internal ExactValue exact_unary_operator_value(TokenKind op, ExactValue v, i3
case ExactValue_Integer: {
GB_ASSERT(precision != 0);
ExactValue i = {ExactValue_Integer};
+ i.value_integer = {0};
big_int_not(&i.value_integer, &v.value_integer, precision, !is_unsigned);
return i;
}
@@ -653,6 +691,7 @@ gb_internal void match_exact_values(ExactValue *x, ExactValue *y) {
case ExactValue_String:
case ExactValue_Quaternion:
case ExactValue_Pointer:
+ case ExactValue_Compound:
case ExactValue_Procedure:
case ExactValue_Typeid:
return;
diff --git a/src/gb/gb.h b/src/gb/gb.h
index 5dae7a5c4..f74026c7d 100644
--- a/src/gb/gb.h
+++ b/src/gb/gb.h
@@ -39,7 +39,7 @@ extern "C" {
#endif
#endif
-#if defined(_WIN64) || defined(__x86_64__) || defined(_M_X64) || defined(__64BIT__) || defined(__powerpc64__) || defined(__ppc64__) || defined(__aarch64__)
+#if defined(_WIN64) || defined(__x86_64__) || defined(_M_X64) || defined(__64BIT__) || defined(__powerpc64__) || defined(__ppc64__) || defined(__aarch64__) || (defined(__riscv) && __riscv_xlen == 64)
#ifndef GB_ARCH_64_BIT
#define GB_ARCH_64_BIT 1
#endif
@@ -83,6 +83,14 @@ extern "C" {
#ifndef GB_SYSTEM_OPENBSD
#define GB_SYSTEM_OPENBSD 1
#endif
+ #elif defined(__NetBSD__)
+ #ifndef GB_SYSTEM_NETBSD
+ #define GB_SYSTEM_NETBSD 1
+ #endif
+ #elif defined(__HAIKU__) || defined(__haiku__)
+ #ifndef GB_SYSTEM_HAIKU
+ #define GB_SYSTEM_HAIKU 1
+ #endif
#else
#error This UNIX operating system is not supported
#endif
@@ -136,12 +144,17 @@ extern "C" {
#define GB_CACHE_LINE_SIZE 64
#endif
+#elif defined(__riscv)
+ #ifndef GB_CPU_RISCV
+ #define GB_CPU_RISCV 1
+ #endif
+ #ifndef GB_CACHE_LINE_SIZE
+ #define GB_CACHE_LINE_SIZE 64
+ #endif
#else
#error Unknown CPU Type
#endif
-
-
#ifndef GB_STATIC_ASSERT
#define GB_STATIC_ASSERT3(cond, msg) typedef char static_assertion_##msg[(!!(cond))*2-1]
// NOTE(bill): Token pasting madness!!
@@ -206,7 +219,7 @@ extern "C" {
#endif
#include <stdlib.h> // NOTE(bill): malloc on linux
#include <sys/mman.h>
- #if !defined(GB_SYSTEM_OSX) && !defined(__FreeBSD__) && !defined(__OpenBSD__)
+ #if !defined(GB_SYSTEM_OSX) && !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__NetBSD__) && !defined(__HAIKU__)
#include <sys/sendfile.h>
#endif
#include <sys/stat.h>
@@ -247,6 +260,19 @@ extern "C" {
#include <pthread_np.h>
#define lseek64 lseek
#endif
+
+#if defined(GB_SYSTEM_NETBSD)
+ #include <stdio.h>
+ #include <lwp.h>
+ #define lseek64 lseek
+#endif
+
+#if defined(GB_SYSTEM_HAIKU)
+ #include <stdio.h>
+ #include <pthread.h>
+ #include <kernel/OS.h>
+ #define lseek64 lseek
+#endif
#if defined(GB_SYSTEM_UNIX)
#include <semaphore.h>
@@ -448,7 +474,7 @@ typedef i32 b32; // NOTE(bill): Prefer this!!!
#define gb_inline __forceinline
#endif
#else
- #define gb_inline __attribute__ ((__always_inline__))
+ #define gb_inline inline __attribute__ ((__always_inline__))
#endif
#endif
@@ -469,6 +495,13 @@ typedef i32 b32; // NOTE(bill): Prefer this!!!
#endif
#endif
+#if !defined(gb_no_asan)
+ #if defined(_MSC_VER)
+ #define gb_no_asan __declspec(no_sanitize_address)
+ #else
+ #define gb_no_asan __attribute__((disable_sanitizer_instrumentation))
+ #endif
+#endif
// NOTE(bill): Easy to grep
// NOTE(bill): Not needed in macros
@@ -801,6 +834,20 @@ typedef struct gbAffinity {
isize thread_count;
isize threads_per_core;
} gbAffinity;
+#elif defined(GB_SYSTEM_NETBSD)
+typedef struct gbAffinity {
+ b32 is_accurate;
+ isize core_count;
+ isize thread_count;
+ isize threads_per_core;
+} gbAffinity;
+#elif defined(GB_SYSTEM_HAIKU)
+typedef struct gbAffinity {
+ b32 is_accurate;
+ isize core_count;
+ isize thread_count;
+ isize threads_per_core;
+} gbAffinity;
#else
#error TODO(bill): Unknown system
#endif
@@ -2494,7 +2541,11 @@ gb_inline void const *gb_pointer_add_const(void const *ptr, isize bytes) {
gb_inline void const *gb_pointer_sub_const(void const *ptr, isize bytes) { return cast(void const *)(cast(u8 const *)ptr - bytes); }
gb_inline isize gb_pointer_diff (void const *begin, void const *end) { return cast(isize)(cast(u8 const *)end - cast(u8 const *)begin); }
-gb_inline void gb_zero_size(void *ptr, isize size) { gb_memset(ptr, 0, size); }
+gb_inline void gb_zero_size(void *ptr, isize size) {
+ if (size != 0) {
+ memset(ptr, 0, size);
+ }
+}
#if defined(_MSC_VER) && !defined(__clang__)
@@ -2522,7 +2573,7 @@ gb_inline void *gb_memcopy(void *dest, void const *source, isize n) {
void *dest_copy = dest;
__asm__ __volatile__("rep movsb" : "+D"(dest_copy), "+S"(source), "+c"(n) : : "memory");
-#elif defined(GB_CPU_ARM)
+#elif defined(GB_CPU_ARM) || defined(GB_CPU_RISCV)
u8 *s = cast(u8 *)source;
u8 *d = cast(u8 *)dest;
for (isize i = 0; i < n; i++) {
@@ -2984,6 +3035,12 @@ gb_inline u32 gb_thread_current_id(void) {
__asm__("mov %%fs:0x10,%0" : "=r"(thread_id));
#elif defined(GB_SYSTEM_LINUX)
thread_id = gettid();
+#elif defined(GB_SYSTEM_HAIKU)
+ thread_id = find_thread(NULL);
+#elif defined(GB_SYSTEM_FREEBSD)
+ thread_id = pthread_getthreadid_np();
+#elif defined(GB_SYSTEM_NETBSD)
+ thread_id = (u32)_lwp_self();
#else
#error Unsupported architecture for gb_thread_current_id()
#endif
@@ -3142,11 +3199,11 @@ void gb_affinity_init(gbAffinity *a) {
a->core_count = 1;
a->threads_per_core = 1;
- if (sysctlbyname("hw.logicalcpu", &count, &count_size, NULL, 0) == 0) {
+ if (sysctlbyname("kern.smp.cpus", &count, &count_size, NULL, 0) == 0) {
if (count > 0) {
a->thread_count = count;
// Get # of physical cores
- if (sysctlbyname("hw.physicalcpu", &count, &count_size, NULL, 0) == 0) {
+ if (sysctlbyname("kern.smp.cores", &count, &count_size, NULL, 0) == 0) {
if (count > 0) {
a->core_count = count;
a->threads_per_core = a->thread_count / count;
@@ -3157,6 +3214,14 @@ void gb_affinity_init(gbAffinity *a) {
}
}
}
+ } else if (sysctlbyname("hw.ncpu", &count, &count_size, NULL, 0) == 0) {
+ // SMP disabled or unavailable.
+ if (count > 0) {
+ a->is_accurate = true;
+ a->thread_count = count;
+ a->core_count = count;
+ a->threads_per_core = 1;
+ }
}
}
@@ -3184,7 +3249,9 @@ b32 gb_affinity_set(gbAffinity *a, isize core, isize thread_index) {
//info.affinity_tag = cast(integer_t)index;
//result = thread_policy_set(thread, THREAD_AFFINITY_POLICY, cast(thread_policy_t)&info, THREAD_AFFINITY_POLICY_COUNT);
+#if !defined(GB_SYSTEM_HAIKU)
result = pthread_setaffinity_np(thread, sizeof(cpuset_t), &mn);
+#endif
return result == 0;
}
@@ -3240,6 +3307,54 @@ isize gb_affinity_thread_count_for_core(gbAffinity *a, isize core) {
GB_ASSERT(0 <= core && core < a->core_count);
return a->threads_per_core;
}
+
+#elif defined(GB_SYSTEM_NETBSD)
+#include <unistd.h>
+
+void gb_affinity_init(gbAffinity *a) {
+ a->core_count = sysconf(_SC_NPROCESSORS_ONLN);
+ a->threads_per_core = 1;
+ a->is_accurate = a->core_count > 0;
+ a->core_count = a->is_accurate ? a->core_count : 1;
+ a->thread_count = a->core_count;
+}
+
+void gb_affinity_destroy(gbAffinity *a) {
+ gb_unused(a);
+}
+
+b32 gb_affinity_set(gbAffinity *a, isize core, isize thread_index) {
+ return true;
+}
+
+isize gb_affinity_thread_count_for_core(gbAffinity *a, isize core) {
+ GB_ASSERT(0 <= core && core < a->core_count);
+ return a->threads_per_core;
+}
+
+#elif defined(GB_SYSTEM_HAIKU)
+#include <unistd.h>
+
+void gb_affinity_init(gbAffinity *a) {
+ a->core_count = sysconf(_SC_NPROCESSORS_ONLN);
+ a->threads_per_core = 1;
+ a->is_accurate = a->core_count > 0;
+ a->core_count = a->is_accurate ? a->core_count : 1;
+ a->thread_count = a->core_count;
+}
+
+void gb_affinity_destroy(gbAffinity *a) {
+ gb_unused(a);
+}
+
+b32 gb_affinity_set(gbAffinity *a, isize core, isize thread_index) {
+ return true;
+}
+
+isize gb_affinity_thread_count_for_core(gbAffinity *a, isize core) {
+ GB_ASSERT(0 <= core && core < a->core_count);
+ return a->threads_per_core;
+}
#else
#error TODO(bill): Unknown system
#endif
@@ -3528,7 +3643,7 @@ gb_inline void gb_str_to_upper(char *str) {
}
-gb_inline isize gb_strlen(char const *str) {
+gb_no_asan isize gb_strlen(char const *str) {
char const *begin = str;
isize const *w;
if (str == NULL) {
@@ -5457,7 +5572,7 @@ gb_inline b32 gb_file_copy(char const *existing_filename, char const *new_filena
}
}
- gb_free(buf);
+ gb_mfree(buf);
close(new_fd);
close(existing_fd);
@@ -5634,7 +5749,7 @@ char *gb_path_get_full_name(gbAllocator a, char const *path) {
isize path_len = gb_strlen(path);
isize cwd_len = gb_strlen(cwd);
len = cwd_len + 1 + path_len + 1;
- result = gb_alloc_array(a, char, len);
+ result = gb_alloc_array(a, char, len+1);
gb_memmove(result, (void *)cwd, cwd_len);
result[cwd_len] = '/';
@@ -6168,11 +6283,18 @@ gb_no_inline isize gb_snprintf_va(char *text, isize max_len, char const *fmt, va
#elif defined(__aarch64__)
gb_inline u64 gb_rdtsc(void) {
int64_t virtual_timer_value;
- asm volatile("mrs %0, cntvct_el0" : "=r"(virtual_timer_value));
- return virtual_timer_value;
+ asm volatile("mrs %0, cntvct_el0" : "=r"(virtual_timer_value));
+ return virtual_timer_value;
+ }
+#elif defined(__riscv)
+ gb_inline u64 gb_rdtsc(void) {
+ u64 result = 0;
+ __asm__ volatile("rdcycle %0" : "=r"(result));
+ return result;
}
#else
-#error "gb_rdtsc not supported"
+#warning "gb_rdtsc not supported"
+ gb_inline u64 gb_rdtsc(void) { return 0; }
#endif
#if defined(GB_SYSTEM_WINDOWS)
diff --git a/src/linker.cpp b/src/linker.cpp
index eb3687ae2..261d6e7a4 100644
--- a/src/linker.cpp
+++ b/src/linker.cpp
@@ -7,9 +7,19 @@ struct LinkerData {
Array<String> output_temp_paths;
String output_base;
String output_name;
+#if defined(GB_SYSTEM_OSX)
+ b8 needs_system_library_linked;
+#endif
};
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;
+}
+#endif
gb_internal void linker_data_init(LinkerData *ld, CheckerInfo *info, String const &init_fullpath) {
gbAllocator ha = heap_allocator();
@@ -18,6 +28,10 @@ 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
+
if (build_context.out_filepath.len == 0) {
ld->output_name = remove_directory_from_path(init_fullpath);
ld->output_name = remove_extension_from_path(ld->output_name);
@@ -56,15 +70,68 @@ gb_internal i32 linker_stage(LinkerData *gen) {
if (is_arch_wasm()) {
timings_start_section(timings, str_lit("wasm-ld"));
+ gbString lib_str = gb_string_make(heap_allocator(), "");
+
+ gbString extra_orca_flags = gb_string_make(temporary_allocator(), "");
+
+ gbString inputs = gb_string_make(temporary_allocator(), "");
+ inputs = gb_string_append_fmt(inputs, "\"%.*s.o\"", LIT(output_filename));
+
+
+ for (Entity *e : gen->foreign_libraries) {
+ GB_ASSERT(e->kind == Entity_LibraryName);
+ // NOTE(bill): Add these before the linking values
+ String extra_linker_flags = string_trim_whitespace(e->LibraryName.extra_linker_flags);
+ if (extra_linker_flags.len != 0) {
+ lib_str = gb_string_append_fmt(lib_str, " %.*s", LIT(extra_linker_flags));
+ }
+
+ for_array(i, e->LibraryName.paths) {
+ String lib = e->LibraryName.paths[i];
+
+ if (lib.len == 0) {
+ continue;
+ }
+
+ if (!string_ends_with(lib, str_lit(".o"))) {
+ continue;
+ }
+
+ inputs = gb_string_append_fmt(inputs, " \"%.*s\"", LIT(lib));
+ }
+ }
+
+ if (build_context.metrics.os == TargetOs_orca) {
+ gbString orca_sdk_path = gb_string_make(temporary_allocator(), "");
+ if (!system_exec_command_line_app_output("orca sdk-path", &orca_sdk_path)) {
+ gb_printf_err("executing `orca sdk-path` failed, make sure Orca is installed and added to your path\n");
+ return 1;
+ }
+ if (gb_string_length(orca_sdk_path) == 0) {
+ gb_printf_err("executing `orca sdk-path` did not produce output\n");
+ return 1;
+ }
+ inputs = gb_string_append_fmt(inputs, " \"%s/orca-libc/lib/crt1.o\" \"%s/orca-libc/lib/libc.o\"", orca_sdk_path, orca_sdk_path);
+
+ extra_orca_flags = gb_string_append_fmt(extra_orca_flags, " -L \"%s/bin\" -lorca_wasm --export-dynamic", orca_sdk_path);
+ }
+
+
#if defined(GB_SYSTEM_WINDOWS)
result = system_exec_command_line_app("wasm-ld",
- "\"%.*s\\bin\\wasm-ld\" \"%.*s.o\" -o \"%.*s\" %.*s %.*s",
+ "\"%.*s\\bin\\wasm-ld\" %s -o \"%.*s\" %.*s %.*s %s %s",
LIT(build_context.ODIN_ROOT),
- LIT(output_filename), LIT(output_filename), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags));
+ inputs, LIT(output_filename), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags),
+ lib_str,
+ extra_orca_flags);
#else
result = system_exec_command_line_app("wasm-ld",
- "wasm-ld \"%.*s.o\" -o \"%.*s\" %.*s %.*s",
- LIT(output_filename), LIT(output_filename), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags));
+ "wasm-ld %s -o \"%.*s\" %.*s %.*s %s %s",
+ inputs, LIT(output_filename),
+ LIT(build_context.link_flags),
+ LIT(build_context.extra_linker_flags),
+ lib_str,
+ extra_orca_flags);
#endif
return result;
}
@@ -100,8 +167,10 @@ gb_internal i32 linker_stage(LinkerData *gen) {
if (is_windows) {
String section_name = str_lit("msvc-link");
- if (build_context.use_lld) {
- section_name = str_lit("lld-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);
@@ -126,9 +195,11 @@ gb_internal i32 linker_stage(LinkerData *gen) {
}
- StringSet libs = {};
- string_set_init(&libs, 64);
- defer (string_set_destroy(&libs));
+ StringSet min_libs_set = {};
+ string_set_init(&min_libs_set, 64);
+ defer (string_set_destroy(&min_libs_set));
+
+ String prev_lib = {};
StringSet asm_files = {};
string_set_init(&asm_files, 64);
@@ -136,6 +207,11 @@ gb_internal i32 linker_stage(LinkerData *gen) {
for (Entity *e : gen->foreign_libraries) {
GB_ASSERT(e->kind == Entity_LibraryName);
+ // NOTE(bill): Add these before the linking values
+ String extra_linker_flags = string_trim_whitespace(e->LibraryName.extra_linker_flags);
+ if (extra_linker_flags.len != 0) {
+ lib_str = gb_string_append_fmt(lib_str, " %.*s", LIT(extra_linker_flags));
+ }
for_array(i, e->LibraryName.paths) {
String lib = string_trim_whitespace(e->LibraryName.paths[i]);
// IMPORTANT NOTE(bill): calling `string_to_lower` here is not an issue because
@@ -147,14 +223,27 @@ gb_internal i32 linker_stage(LinkerData *gen) {
if (has_asm_extension(lib)) {
if (!string_set_update(&asm_files, lib)) {
- String asm_file = asm_files.entries[i].value;
- String obj_file = concatenate_strings(permanent_allocator(), asm_file, str_lit(".obj"));
- String obj_format;
-#if defined(GB_ARCH_64_BIT)
- obj_format = str_lit("win64");
-#elif defined(GB_ARCH_32_BIT)
+ String asm_file = lib;
+ String obj_file = {};
+ String temp_dir = temporary_directory(temporary_allocator());
+ if (temp_dir.len != 0) {
+ String filename = filename_without_directory(asm_file);
+
+ gbString str = gb_string_make(heap_allocator(), "");
+ str = gb_string_append_length(str, temp_dir.text, temp_dir.len);
+ str = gb_string_appendc(str, "/");
+ str = gb_string_append_length(str, filename.text, filename.len);
+ str = gb_string_append_fmt(str, "-%p.obj", asm_file.text);
+ obj_file = make_string_c(str);
+ } else {
+ obj_file = concatenate_strings(permanent_allocator(), asm_file, str_lit(".obj"));
+ }
+
+ String obj_format = str_lit("win64");
+ #if defined(GB_ARCH_32_BIT)
obj_format = str_lit("win32");
-#endif // GB_ARCH_*_BIT
+ #endif
+
result = system_exec_command_line_app("nasm",
"\"%.*s\\bin\\nasm\\windows\\nasm.exe\" \"%.*s\" "
"-f \"%.*s\" "
@@ -172,21 +261,16 @@ gb_internal i32 linker_stage(LinkerData *gen) {
}
array_add(&gen->output_object_paths, obj_file);
}
- } else {
- if (!string_set_update(&libs, lib)) {
+ } else if (!string_set_update(&min_libs_set, lib) ||
+ !build_context.min_link_libs) {
+ if (prev_lib != lib) {
lib_str = gb_string_append_fmt(lib_str, " \"%.*s\"", LIT(lib));
}
+ prev_lib = lib;
}
}
}
- for (Entity *e : gen->foreign_libraries) {
- GB_ASSERT(e->kind == Entity_LibraryName);
- if (e->LibraryName.extra_linker_flags.len != 0) {
- lib_str = gb_string_append_fmt(lib_str, " %.*s", LIT(e->LibraryName.extra_linker_flags));
- }
- }
-
if (build_context.build_mode == BuildMode_DynamicLibrary) {
link_settings = gb_string_append_fmt(link_settings, " /DLL");
} else {
@@ -195,13 +279,15 @@ gb_internal i32 linker_stage(LinkerData *gen) {
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));
+ link_settings = gb_string_append_fmt(link_settings, " /PDB:\"%.*s\"", LIT(pdb_path));
}
- if (build_context.no_crt) {
- link_settings = gb_string_append_fmt(link_settings, " /nodefaultlib");
- } else {
- link_settings = gb_string_append_fmt(link_settings, " /defaultlib:libcmt");
+ if (build_context.build_mode != BuildMode_StaticLibrary) {
+ if (build_context.no_crt) {
+ link_settings = gb_string_append_fmt(link_settings, " /nodefaultlib");
+ } else {
+ link_settings = gb_string_append_fmt(link_settings, " /defaultlib:libcmt");
+ }
}
if (build_context.ODIN_DEBUG) {
@@ -220,64 +306,38 @@ gb_internal i32 linker_stage(LinkerData *gen) {
String windows_sdk_bin_path = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_Win_SDK_Bin_Path]);
defer (gb_free(heap_allocator(), windows_sdk_bin_path.text));
- char const *subsystem_str = build_context.use_subsystem_windows ? "WINDOWS" : "CONSOLE";
- if (!build_context.use_lld) { // msvc
- String res_path = {};
- defer (gb_free(heap_allocator(), res_path.text));
- if (build_context.has_resource) {
- String temp_res_path = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_RES]);
- res_path = concatenate3_strings(heap_allocator(), str_lit("\""), temp_res_path, str_lit("\""));
- gb_free(heap_allocator(), temp_res_path.text);
-
- String rc_path = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_RC]);
- defer (gb_free(heap_allocator(), rc_path.text));
-
- result = system_exec_command_line_app("msvc-link",
- "\"%.*src.exe\" /nologo /fo \"%.*s\" \"%.*s\"",
- LIT(windows_sdk_bin_path),
- LIT(res_path),
- LIT(rc_path)
- );
-
- if (result) {
- return result;
- }
- }
-
- switch (build_context.build_mode) {
- case BuildMode_Executable:
- link_settings = gb_string_append_fmt(link_settings, " /NOIMPLIB /NOEXP");
- break;
- }
-
- result = system_exec_command_line_app("msvc-link",
- "\"%.*slink.exe\" %s %.*s -OUT:\"%.*s\" %s "
- "/nologo /incremental:no /opt:ref /subsystem:%s "
+ switch (build_context.linker_choice) {
+ case Linker_lld:
+ result = system_exec_command_line_app("msvc-lld-link",
+ "\"%.*s\\bin\\lld-link\" %s -OUT:\"%.*s\" %s "
+ "/nologo /incremental:no /opt:ref /subsystem:%.*s "
"%.*s "
"%.*s "
"%s "
"",
- LIT(vs_exe_path), object_files, LIT(res_path), LIT(output_filename),
+ LIT(build_context.ODIN_ROOT), object_files, LIT(output_filename),
link_settings,
- subsystem_str,
+ LIT(build_context.ODIN_WINDOWS_SUBSYSTEM),
LIT(build_context.link_flags),
LIT(build_context.extra_linker_flags),
lib_str
);
+
if (result) {
return result;
}
- } else { // lld
- result = system_exec_command_line_app("msvc-lld-link",
- "\"%.*s\\bin\\lld-link\" %s -OUT:\"%.*s\" %s "
- "/nologo /incremental:no /opt:ref /subsystem:%s "
+ break;
+ case Linker_radlink:
+ result = system_exec_command_line_app("msvc-rad-link",
+ "\"%.*s\\bin\\radlink\" %s -OUT:\"%.*s\" %s "
+ "/nologo /incremental:no /opt:ref /subsystem:%.*s "
"%.*s "
"%.*s "
"%s "
"",
LIT(build_context.ODIN_ROOT), object_files, LIT(output_filename),
link_settings,
- subsystem_str,
+ LIT(build_context.ODIN_WINDOWS_SUBSYSTEM),
LIT(build_context.link_flags),
LIT(build_context.extra_linker_flags),
lib_str
@@ -286,10 +346,80 @@ gb_internal i32 linker_stage(LinkerData *gen) {
if (result) {
return result;
}
+ break;
+ default: { // msvc
+ String res_path = quote_path(heap_allocator(), build_context.build_paths[BuildPath_RES]);
+ String rc_path = quote_path(heap_allocator(), build_context.build_paths[BuildPath_RC]);
+ defer (gb_free(heap_allocator(), res_path.text));
+ defer (gb_free(heap_allocator(), rc_path.text));
+
+ if (build_context.has_resource) {
+ if (build_context.build_paths[BuildPath_RC].basename == "") {
+ debugf("Using precompiled resource %.*s\n", LIT(res_path));
+ } else {
+ debugf("Compiling resource %.*s\n", LIT(res_path));
+
+ result = system_exec_command_line_app("msvc-link",
+ "\"%.*src.exe\" /nologo /fo %.*s %.*s",
+ LIT(windows_sdk_bin_path),
+ LIT(res_path),
+ LIT(rc_path)
+ );
+
+ if (result) {
+ return result;
+ }
+ }
+ } else {
+ res_path = {};
+ }
+
+ String linker_name = str_lit("link.exe");
+ switch (build_context.build_mode) {
+ case BuildMode_Executable:
+ link_settings = gb_string_append_fmt(link_settings, " /NOIMPLIB /NOEXP");
+ break;
+ }
+
+ switch (build_context.build_mode) {
+ case BuildMode_StaticLibrary:
+ linker_name = str_lit("lib.exe");
+ break;
+ default:
+ link_settings = gb_string_append_fmt(link_settings, " /incremental:no /opt:ref");
+ break;
+ }
+
+
+ result = system_exec_command_line_app("msvc-link",
+ "\"%.*s%.*s\" %s %.*s -OUT:\"%.*s\" %s "
+ "/nologo /subsystem:%.*s "
+ "%.*s "
+ "%.*s "
+ "%s "
+ "",
+ LIT(vs_exe_path), LIT(linker_name), object_files, LIT(res_path), LIT(output_filename),
+ link_settings,
+ LIT(build_context.ODIN_WINDOWS_SUBSYSTEM),
+ LIT(build_context.link_flags),
+ LIT(build_context.extra_linker_flags),
+ lib_str
+ );
+ if (result) {
+ return result;
+ }
+ break;
+ }
}
} else {
timings_start_section(timings, str_lit("ld-link"));
+ // Link using `clang`, unless overridden by `ODIN_CLANG_PATH` environment variable.
+ const char* clang_path = gb_get_env("ODIN_CLANG_PATH", permanent_allocator());
+ if (clang_path == NULL) {
+ clang_path = "clang";
+ }
+
// 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)
@@ -306,12 +436,19 @@ gb_internal i32 linker_stage(LinkerData *gen) {
string_set_init(&asm_files, 64);
defer (string_set_destroy(&asm_files));
- StringSet libs = {};
- string_set_init(&libs, 64);
- defer (string_set_destroy(&libs));
+ StringSet min_libs_set = {};
+ string_set_init(&min_libs_set, 64);
+ defer (string_set_destroy(&min_libs_set));
+
+ String prev_lib = {};
for (Entity *e : gen->foreign_libraries) {
GB_ASSERT(e->kind == Entity_LibraryName);
+ // NOTE(bill): Add these before the linking values
+ String extra_linker_flags = string_trim_whitespace(e->LibraryName.extra_linker_flags);
+ if (extra_linker_flags.len != 0) {
+ lib_str = gb_string_append_fmt(lib_str, " %.*s", LIT(extra_linker_flags));
+ }
for (String lib : e->LibraryName.paths) {
lib = string_trim_whitespace(lib);
if (lib.len == 0) {
@@ -322,44 +459,99 @@ gb_internal i32 linker_stage(LinkerData *gen) {
continue; // already handled
}
String asm_file = lib;
- String obj_file = concatenate_strings(permanent_allocator(), asm_file, str_lit(".o"));
+ String obj_file = {};
+
+ String temp_dir = temporary_directory(temporary_allocator());
+ if (temp_dir.len != 0) {
+ String filename = filename_without_directory(asm_file);
+
+ gbString str = gb_string_make(heap_allocator(), "");
+ str = gb_string_append_length(str, temp_dir.text, temp_dir.len);
+ str = gb_string_appendc(str, "/");
+ str = gb_string_append_length(str, filename.text, filename.len);
+ str = gb_string_append_fmt(str, "-%p.o", asm_file.text);
+ obj_file = make_string_c(str);
+ } else {
+ obj_file = concatenate_strings(permanent_allocator(), asm_file, str_lit(".o"));
+ }
+
String obj_format;
-#if defined(GB_ARCH_64_BIT)
+ #if 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)
+ #elif defined(GB_ARCH_32_BIT)
if (is_osx) {
obj_format = str_lit("macho32");
} else {
obj_format = str_lit("elf32");
}
-#endif // GB_ARCH_*_BIT
- // Note(bumbread): I'm assuming nasm is installed on the host machine.
- // Shipping binaries on unix-likes gets into the weird territorry of
- // "which version of glibc" is it linked with.
- result = system_exec_command_line_app("nasm",
- "nasm \"%.*s\" "
- "-f \"%.*s\" "
- "-o \"%.*s\" "
- "%.*s "
- "",
- LIT(asm_file),
- LIT(obj_format),
- LIT(obj_file),
- LIT(build_context.extra_assembler_flags)
- );
+ #endif // GB_ARCH_*_BIT
+
+ if (build_context.metrics.arch == TargetArch_riscv64) {
+ result = system_exec_command_line_app("clang",
+ "%s \"%.*s\" "
+ "-c -o \"%.*s\" "
+ "-target %.*s -march=rv64gc "
+ "%.*s "
+ "",
+ clang_path,
+ LIT(asm_file),
+ LIT(obj_file),
+ LIT(build_context.metrics.target_triplet),
+ LIT(build_context.extra_assembler_flags)
+ );
+ } else if (is_osx) {
+ // `as` comes with MacOS.
+ result = system_exec_command_line_app("as",
+ "as \"%.*s\" "
+ "-o \"%.*s\" "
+ "%.*s "
+ "",
+ LIT(asm_file),
+ LIT(obj_file),
+ LIT(build_context.extra_assembler_flags)
+ );
+ } else {
+ // Note(bumbread): I'm assuming nasm is installed on the host machine.
+ // Shipping binaries on unix-likes gets into the weird territorry of
+ // "which version of glibc" is it linked with.
+ result = system_exec_command_line_app("nasm",
+ "nasm \"%.*s\" "
+ "-f \"%.*s\" "
+ "-o \"%.*s\" "
+ "%.*s "
+ "",
+ LIT(asm_file),
+ LIT(obj_format),
+ LIT(obj_file),
+ LIT(build_context.extra_assembler_flags)
+ );
+ if (result) {
+ gb_printf_err("executing `nasm` to assemble foreing import of %.*s failed.\n\tSuggestion: `nasm` does not ship with the compiler and should be installed with your system's package manager.\n", LIT(asm_file));
+ return result;
+ }
+ }
array_add(&gen->output_object_paths, obj_file);
} else {
- if (string_set_update(&libs, lib)) {
+ if (string_set_update(&min_libs_set, lib) && build_context.min_link_libs) {
+ continue;
+ }
+
+ if (prev_lib == lib) {
+ continue;
+ }
+ prev_lib = lib;
+
+ // 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")) {
continue;
}
- // NOTE(zangent): Sometimes, you have to use -framework on MacOS.
- // This allows you to specify '-f' in a #foreign_system_library,
- // without having to implement any new syntax specifically for MacOS.
if (build_context.metrics.os == TargetOs_darwin) {
if (string_ends_with(lib, str_lit(".framework"))) {
// framework thingie
@@ -382,14 +574,8 @@ gb_internal i32 linker_stage(LinkerData *gen) {
// available at runtime wherever the executable is run, so we make require those to be
// local to the executable (unless the system collection is used, in which case we search
// the system library paths for the library file).
- if (string_ends_with(lib, str_lit(".a")) || string_ends_with(lib, str_lit(".o"))) {
- // static libs and object files, absolute full path relative to the file in which the lib was imported from
+ if (string_ends_with(lib, str_lit(".a")) || string_ends_with(lib, str_lit(".o")) || string_ends_with(lib, str_lit(".so")) || string_contains_string(lib, str_lit(".so."))) {
lib_str = gb_string_append_fmt(lib_str, " -l:\"%.*s\" ", LIT(lib));
- } else if (string_ends_with(lib, str_lit(".so"))) {
- // dynamic lib, relative path to executable
- // NOTE(vassvik): it is the user's responsibility to make sure the shared library files are visible
- // at runtime to the executable
- lib_str = gb_string_append_fmt(lib_str, " -l:\"%s/%.*s\" ", cwd, 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));
@@ -399,13 +585,6 @@ gb_internal i32 linker_stage(LinkerData *gen) {
}
}
- for (Entity *e : gen->foreign_libraries) {
- GB_ASSERT(e->kind == Entity_LibraryName);
- if (e->LibraryName.extra_linker_flags.len != 0) {
- lib_str = gb_string_append_fmt(lib_str, " %.*s", LIT(e->LibraryName.extra_linker_flags));
- }
- }
-
gbString object_files = gb_string_make(heap_allocator(), "");
defer (gb_string_free(object_files));
for (String object_path : gen->output_object_paths) {
@@ -418,6 +597,10 @@ gb_internal i32 linker_stage(LinkerData *gen) {
link_settings = gb_string_append_fmt(link_settings, "-nostdlib ");
}
+ if (build_context.build_mode == BuildMode_StaticLibrary) {
+ compiler_error("TODO(bill): -build-mode:static on non-windows targets");
+ }
+
// NOTE(dweiler): We use clang as a frontend for the linker as there are
// other runtime and compiler support libraries that need to be linked in
// very specific orders such as libgcc_s, ld-linux-so, unwind, etc.
@@ -448,35 +631,72 @@ gb_internal i32 linker_stage(LinkerData *gen) {
link_settings = gb_string_appendc(link_settings, "-Wl,-fini,'_odin_exit_point' ");
}
- } else if (build_context.metrics.os != TargetOs_openbsd) {
- // OpenBSD defaults to PIE executable. do not pass -no-pie for it.
- link_settings = gb_string_appendc(link_settings, "-no-pie ");
+ }
+
+ if (build_context.build_mode == BuildMode_Executable && build_context.reloc_mode == RelocMode_PIC) {
+ // Do not disable PIE, let the linker choose. (most likely you want it enabled)
+ } else if (build_context.build_mode != BuildMode_DynamicLibrary) {
+ if (build_context.metrics.os != TargetOs_openbsd
+ && build_context.metrics.os != TargetOs_haiku
+ && build_context.metrics.arch != TargetArch_riscv64
+ ) {
+ // OpenBSD and Haiku default to PIE executable. do not pass -no-pie for it.
+ link_settings = gb_string_appendc(link_settings, "-no-pie ");
+ }
}
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, "-lSystem -lm -Wl,-syslibroot /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk -L/usr/local/lib");
- } else {
- platform_lib_str = gb_string_appendc(platform_lib_str, "-lc -lm");
+ platform_lib_str = gb_string_appendc(platform_lib_str, "-Wl,-syslibroot /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk -L/usr/local/lib ");
+
+ // Homebrew's default library path, checking if it exists to avoid linking warnings.
+ if (gb_file_exists("/opt/homebrew/lib")) {
+ platform_lib_str = gb_string_appendc(platform_lib_str, "-L/opt/homebrew/lib ");
+ }
+
+ // MacPort's default library path, checking if it exists to avoid linking warnings.
+ if (gb_file_exists("/opt/local/lib")) {
+ platform_lib_str = gb_string_appendc(platform_lib_str, "-L/opt/local/lib ");
+ }
+
+ // 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));
+ }
+
+ 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 ");
+ }
}
- if (build_context.metrics.os == TargetOs_darwin) {
- // This sets a requirement of Mountain Lion and up, but the compiler doesn't work without this limit.
- if (build_context.minimum_os_version_string.len) {
- link_settings = gb_string_append_fmt(link_settings, " -mmacosx-version-min=%.*s ", LIT(build_context.minimum_os_version_string));
- } else if (build_context.metrics.arch == TargetArch_arm64) {
- link_settings = gb_string_appendc(link_settings, " -mmacosx-version-min=12.0.0 ");
+ if (!build_context.no_rpath) {
+ // Set the rpath to the $ORIGIN/@loader_path (the path of the executable),
+ // so that dynamic libraries are looked for at that path.
+ 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 (!build_context.no_crt) {
+ platform_lib_str = gb_string_appendc(platform_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 ");
} else {
- link_settings = gb_string_appendc(link_settings, " -mmacosx-version-min=10.12.0 ");
+ platform_lib_str = gb_string_appendc(platform_lib_str, "-lc ");
}
- // This points the linker to where the entry point is
- link_settings = gb_string_appendc(link_settings, " -e _main ");
}
- gbString link_command_line = gb_string_make(heap_allocator(), "clang -Wno-unused-command-line-argument ");
+ gbString link_command_line = gb_string_make(heap_allocator(), clang_path);
defer (gb_string_free(link_command_line));
+ 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));
link_command_line = gb_string_append_fmt(link_command_line, " %s ", platform_lib_str);
@@ -485,7 +705,12 @@ 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);
- result = system_exec_command_line_app("ld-link", link_command_line);
+ 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 {
+ result = system_exec_command_line_app("ld-link", link_command_line);
+ }
if (result) {
return result;
@@ -494,7 +719,7 @@ gb_internal i32 linker_stage(LinkerData *gen) {
if (is_osx && build_context.ODIN_DEBUG) {
// NOTE: macOS links DWARF symbols dynamically. Dsymutil will map the stubs in the exe
// to the symbols in the object file
- result = system_exec_command_line_app("dsymutil", "dsymutil %.*s", LIT(output_filename));
+ result = system_exec_command_line_app("dsymutil", "dsymutil \"%.*s\"", LIT(output_filename));
if (result) {
return result;
diff --git a/src/llvm-c/Analysis.h b/src/llvm-c/Analysis.h
index 270b145a4..6b93b5c3d 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 "llvm-c/ExternC.h"
-#include "llvm-c/Types.h"
+#include "ExternC.h"
+#include "Types.h"
LLVM_C_EXTERN_C_BEGIN
diff --git a/src/llvm-c/BitReader.h b/src/llvm-c/BitReader.h
index 088107468..725f3fa84 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 "llvm-c/ExternC.h"
-#include "llvm-c/Types.h"
+#include "ExternC.h"
+#include "Types.h"
LLVM_C_EXTERN_C_BEGIN
diff --git a/src/llvm-c/BitWriter.h b/src/llvm-c/BitWriter.h
index ea84b6593..ba4a61afc 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 "llvm-c/ExternC.h"
-#include "llvm-c/Types.h"
+#include "ExternC.h"
+#include "Types.h"
LLVM_C_EXTERN_C_BEGIN
diff --git a/src/llvm-c/Comdat.h b/src/llvm-c/Comdat.h
index 8002bc058..30df20799 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 "llvm-c/ExternC.h"
-#include "llvm-c/Types.h"
+#include "ExternC.h"
+#include "Types.h"
LLVM_C_EXTERN_C_BEGIN
diff --git a/src/llvm-c/Config/AsmParsers.def b/src/llvm-c/Config/AsmParsers.def
index 7aaab6e50..a641f395b 100644
--- a/src/llvm-c/Config/AsmParsers.def
+++ b/src/llvm-c/Config/AsmParsers.def
@@ -27,18 +27,20 @@
LLVM_ASM_PARSER(AArch64)
LLVM_ASM_PARSER(AMDGPU)
LLVM_ASM_PARSER(ARM)
+LLVM_ASM_PARSER(AVR)
LLVM_ASM_PARSER(BPF)
LLVM_ASM_PARSER(Hexagon)
LLVM_ASM_PARSER(Lanai)
+LLVM_ASM_PARSER(LoongArch)
LLVM_ASM_PARSER(Mips)
LLVM_ASM_PARSER(MSP430)
LLVM_ASM_PARSER(PowerPC)
LLVM_ASM_PARSER(RISCV)
LLVM_ASM_PARSER(Sparc)
LLVM_ASM_PARSER(SystemZ)
+LLVM_ASM_PARSER(VE)
LLVM_ASM_PARSER(WebAssembly)
LLVM_ASM_PARSER(X86)
-LLVM_ASM_PARSER(AVR)
#undef LLVM_ASM_PARSER
diff --git a/src/llvm-c/Config/AsmPrinters.def b/src/llvm-c/Config/AsmPrinters.def
index 3ecc3644f..c463c3e89 100644
--- a/src/llvm-c/Config/AsmPrinters.def
+++ b/src/llvm-c/Config/AsmPrinters.def
@@ -27,9 +27,11 @@
LLVM_ASM_PRINTER(AArch64)
LLVM_ASM_PRINTER(AMDGPU)
LLVM_ASM_PRINTER(ARM)
+LLVM_ASM_PRINTER(AVR)
LLVM_ASM_PRINTER(BPF)
LLVM_ASM_PRINTER(Hexagon)
LLVM_ASM_PRINTER(Lanai)
+LLVM_ASM_PRINTER(LoongArch)
LLVM_ASM_PRINTER(Mips)
LLVM_ASM_PRINTER(MSP430)
LLVM_ASM_PRINTER(NVPTX)
@@ -37,10 +39,10 @@ LLVM_ASM_PRINTER(PowerPC)
LLVM_ASM_PRINTER(RISCV)
LLVM_ASM_PRINTER(Sparc)
LLVM_ASM_PRINTER(SystemZ)
+LLVM_ASM_PRINTER(VE)
LLVM_ASM_PRINTER(WebAssembly)
LLVM_ASM_PRINTER(X86)
LLVM_ASM_PRINTER(XCore)
-LLVM_ASM_PRINTER(AVR)
#undef LLVM_ASM_PRINTER
diff --git a/src/llvm-c/Config/Disassemblers.def b/src/llvm-c/Config/Disassemblers.def
index 4485af241..dce865414 100644
--- a/src/llvm-c/Config/Disassemblers.def
+++ b/src/llvm-c/Config/Disassemblers.def
@@ -27,19 +27,21 @@
LLVM_DISASSEMBLER(AArch64)
LLVM_DISASSEMBLER(AMDGPU)
LLVM_DISASSEMBLER(ARM)
+LLVM_DISASSEMBLER(AVR)
LLVM_DISASSEMBLER(BPF)
LLVM_DISASSEMBLER(Hexagon)
LLVM_DISASSEMBLER(Lanai)
+LLVM_DISASSEMBLER(LoongArch)
LLVM_DISASSEMBLER(Mips)
LLVM_DISASSEMBLER(MSP430)
LLVM_DISASSEMBLER(PowerPC)
LLVM_DISASSEMBLER(RISCV)
LLVM_DISASSEMBLER(Sparc)
LLVM_DISASSEMBLER(SystemZ)
+LLVM_DISASSEMBLER(VE)
LLVM_DISASSEMBLER(WebAssembly)
LLVM_DISASSEMBLER(X86)
LLVM_DISASSEMBLER(XCore)
-LLVM_DISASSEMBLER(AVR)
#undef LLVM_DISASSEMBLER
diff --git a/src/llvm-c/Config/TargetExegesis.def b/src/llvm-c/Config/TargetExegesis.def
new file mode 100644
index 000000000..d4d3f99a7
--- /dev/null
+++ b/src/llvm-c/Config/TargetExegesis.def
@@ -0,0 +1,33 @@
+/*===----- llvm/Config/TargetExegesis.def - LLVM Target Exegesis-*- C++ -*-===*\
+|* *|
+|* Part of the LLVM Project, under the Apache License v2.0 with LLVM *|
+|* Exceptions. *|
+|* See https://llvm.org/LICENSE.txt for license information. *|
+|* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception *|
+|* *|
+|*===----------------------------------------------------------------------===*|
+|* *|
+|* This file enumerates all of the target's of llvm-exegesis *|
+|* supported by this build of LLVM. Clients of this file should define *|
+|* the LLVM_EXEGISIS macro to be a function-like macro with a *|
+|* single parameter (the name of the target whose assembly can be *|
+|* generated); including this file will then enumerate all of the *|
+|* targets with target llvm-exegsis support. *|
+|* *|
+|* The set of targets supported by LLVM is generated at configuration *|
+|* time, at which point this header is generated. Do not modify this *|
+|* header directly. *|
+|* *|
+\*===----------------------------------------------------------------------===*/
+
+#ifndef LLVM_EXEGESIS
+# error Please define the macro LLVM_EXEGESIS(TargetName)
+#endif
+
+LLVM_EXEGESIS(AArch64)
+LLVM_EXEGESIS(Mips)
+LLVM_EXEGESIS(PowerPC)
+LLVM_EXEGESIS(X86)
+
+
+#undef LLVM_EXEGESIS
diff --git a/src/llvm-c/Config/TargetMCAs.def b/src/llvm-c/Config/TargetMCAs.def
new file mode 100644
index 000000000..1aefdbc25
--- /dev/null
+++ b/src/llvm-c/Config/TargetMCAs.def
@@ -0,0 +1,32 @@
+/*===------ llvm/Config/TargetMCAs.def - LLVM Target MCAs -------*- C++ -*-===*\
+|* *|
+|* Part of the LLVM Project, under the Apache License v2.0 with LLVM *|
+|* Exceptions. *|
+|* See https://llvm.org/LICENSE.txt for license information. *|
+|* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception *|
+|* *|
+|*===----------------------------------------------------------------------===*|
+|* *|
+|* This file enumerates all of the target MCAs *|
+|* supported by this build of LLVM. Clients of this file should define *|
+|* the LLVM_TARGETMCA macro to be a function-like macro with a *|
+|* single parameter (the name of the target whose assembly can be *|
+|* generated); including this file will then enumerate all of the *|
+|* targets with target MCAs. *|
+|* *|
+|* The set of targets supported by LLVM is generated at configuration *|
+|* time, at which point this header is generated. Do not modify this *|
+|* header directly. *|
+|* *|
+\*===----------------------------------------------------------------------===*/
+
+#ifndef LLVM_TARGETMCA
+# error Please define the macro LLVM_TARGETMCA(TargetName)
+#endif
+
+LLVM_TARGETMCA(AMDGPU)
+LLVM_TARGETMCA(RISCV)
+LLVM_TARGETMCA(X86)
+
+
+#undef LLVM_TARGETMCA
diff --git a/src/llvm-c/Config/Targets.def b/src/llvm-c/Config/Targets.def
index 4962add96..250aac5c9 100644
--- a/src/llvm-c/Config/Targets.def
+++ b/src/llvm-c/Config/Targets.def
@@ -26,9 +26,11 @@
LLVM_TARGET(AArch64)
LLVM_TARGET(AMDGPU)
LLVM_TARGET(ARM)
+LLVM_TARGET(AVR)
LLVM_TARGET(BPF)
LLVM_TARGET(Hexagon)
LLVM_TARGET(Lanai)
+LLVM_TARGET(LoongArch)
LLVM_TARGET(Mips)
LLVM_TARGET(MSP430)
LLVM_TARGET(NVPTX)
@@ -36,10 +38,10 @@ LLVM_TARGET(PowerPC)
LLVM_TARGET(RISCV)
LLVM_TARGET(Sparc)
LLVM_TARGET(SystemZ)
+LLVM_TARGET(VE)
LLVM_TARGET(WebAssembly)
LLVM_TARGET(X86)
LLVM_TARGET(XCore)
-LLVM_TARGET(AVR)
#undef LLVM_TARGET
diff --git a/src/llvm-c/Config/abi-breaking.h b/src/llvm-c/Config/abi-breaking.h
index a09cffa7e..c501cc354 100644
--- a/src/llvm-c/Config/abi-breaking.h
+++ b/src/llvm-c/Config/abi-breaking.h
@@ -1,4 +1,4 @@
-/*===------- llvm-c/Config//abi-breaking.h - llvm configuration -------*- C -*-===*/
+/*===------- llvm/Config/abi-breaking.h - llvm configuration -------*- C -*-===*/
/* */
/* Part of the LLVM Project, under the Apache License v2.0 with LLVM */
/* Exceptions. */
@@ -20,7 +20,7 @@
/* Allow selectively disabling link-time mismatch checking so that header-only
ADT content from LLVM can be used without linking libSupport. */
-#if !LLVM_DISABLE_ABI_BREAKING_CHECKS_ENFORCING
+#if !defined(LLVM_DISABLE_ABI_BREAKING_CHECKS_ENFORCING) || !LLVM_DISABLE_ABI_BREAKING_CHECKS_ENFORCING
// ABI_BREAKING_CHECKS protection: provides link-time failure when clients build
// mismatch with LLVM
diff --git a/src/llvm-c/Config/llvm-config.h b/src/llvm-c/Config/llvm-config.h
index 331d05093..e4edb83c5 100644
--- a/src/llvm-c/Config/llvm-config.h
+++ b/src/llvm-c/Config/llvm-config.h
@@ -1,4 +1,4 @@
-/*===------- llvm-c/Config//llvm-config.h - llvm configuration -------*- C -*-===*/
+/*===------- llvm/Config/llvm-config.h - llvm configuration -------*- C -*-===*/
/* */
/* Part of the LLVM Project, under the Apache License v2.0 with LLVM */
/* Exceptions. */
@@ -17,10 +17,8 @@
/* Define if LLVM_ENABLE_DUMP is enabled */
/* #undef LLVM_ENABLE_DUMP */
-/* Define if we link Polly to the tools */
-/* #undef LINK_POLLY_INTO_TOOLS */
-
/* Target triple LLVM will generate code for by default */
+/* Doesn't use `cmakedefine` because it is allowed to be empty. */
#define LLVM_DEFAULT_TARGET_TRIPLE "x86_64-pc-windows-msvc"
/* Define if threads enabled */
@@ -53,6 +51,84 @@
/* LLVM name for the native target MC init function, if available */
#define LLVM_NATIVE_TARGETMC LLVMInitializeX86TargetMC
+/* LLVM name for the native target MCA init function, if available */
+/* #undef LLVM_NATIVE_TARGETMCA */
+
+/* Define if the AArch64 target is built in */
+#define LLVM_HAS_AARCH64_TARGET 1
+
+/* Define if the AMDGPU target is built in */
+#define LLVM_HAS_AMDGPU_TARGET 1
+
+/* Define if the ARC target is built in */
+#define LLVM_HAS_ARC_TARGET 0
+
+/* Define if the ARM target is built in */
+#define LLVM_HAS_ARM_TARGET 1
+
+/* Define if the AVR target is built in */
+#define LLVM_HAS_AVR_TARGET 1
+
+/* Define if the BPF target is built in */
+#define LLVM_HAS_BPF_TARGET 1
+
+/* Define if the CSKY target is built in */
+#define LLVM_HAS_CSKY_TARGET 0
+
+/* Define if the DirectX target is built in */
+#define LLVM_HAS_DIRECTX_TARGET 0
+
+/* Define if the Hexagon target is built in */
+#define LLVM_HAS_HEXAGON_TARGET 1
+
+/* Define if the Lanai target is built in */
+#define LLVM_HAS_LANAI_TARGET 1
+
+/* Define if the LoongArch target is built in */
+#define LLVM_HAS_LOONGARCH_TARGET 1
+
+/* Define if the M68k target is built in */
+#define LLVM_HAS_M68K_TARGET 0
+
+/* Define if the Mips target is built in */
+#define LLVM_HAS_MIPS_TARGET 1
+
+/* Define if the MSP430 target is built in */
+#define LLVM_HAS_MSP430_TARGET 1
+
+/* Define if the NVPTX target is built in */
+#define LLVM_HAS_NVPTX_TARGET 1
+
+/* Define if the PowerPC target is built in */
+#define LLVM_HAS_POWERPC_TARGET 1
+
+/* Define if the RISCV target is built in */
+#define LLVM_HAS_RISCV_TARGET 1
+
+/* Define if the Sparc target is built in */
+#define LLVM_HAS_SPARC_TARGET 1
+
+/* Define if the SPIRV target is built in */
+#define LLVM_HAS_SPIRV_TARGET 0
+
+/* Define if the SystemZ target is built in */
+#define LLVM_HAS_SYSTEMZ_TARGET 1
+
+/* Define if the VE target is built in */
+#define LLVM_HAS_VE_TARGET 1
+
+/* Define if the WebAssembly target is built in */
+#define LLVM_HAS_WEBASSEMBLY_TARGET 1
+
+/* Define if the X86 target is built in */
+#define LLVM_HAS_X86_TARGET 1
+
+/* Define if the XCore target is built in */
+#define LLVM_HAS_XCORE_TARGET 1
+
+/* Define if the Xtensa target is built in */
+#define LLVM_HAS_XTENSA_TARGET 0
+
/* Define if this is Unixish platform */
/* #undef LLVM_ON_UNIX */
@@ -66,20 +142,60 @@
#define LLVM_USE_PERF 0
/* Major version of the LLVM API */
-#define LLVM_VERSION_MAJOR 17
+#define LLVM_VERSION_MAJOR 18
/* Minor version of the LLVM API */
-#define LLVM_VERSION_MINOR 0
+#define LLVM_VERSION_MINOR 1
/* Patch version of the LLVM API */
-#define LLVM_VERSION_PATCH 1
+#define LLVM_VERSION_PATCH 8
/* LLVM version string */
-#define LLVM_VERSION_STRING "17.0.1"
+#define LLVM_VERSION_STRING "18.1.8"
/* Whether LLVM records statistics for use with GetStatistics(),
* PrintStatistics() or PrintStatisticsJSON()
*/
#define LLVM_FORCE_ENABLE_STATS 0
+/* Define if we have z3 and want to build it */
+/* #undef LLVM_WITH_Z3 */
+
+/* Define if we have curl and want to use it */
+/* #undef LLVM_ENABLE_CURL */
+
+/* Define if we have cpp-httplib and want to use it */
+/* #undef LLVM_ENABLE_HTTPLIB */
+
+/* Define if zlib compression is available */
+#define LLVM_ENABLE_ZLIB 0
+
+/* Define if zstd compression is available */
+#define LLVM_ENABLE_ZSTD 0
+
+/* Define if LLVM is using tflite */
+/* #undef LLVM_HAVE_TFLITE */
+
+/* Define to 1 if you have the <sysexits.h> header file. */
+/* #undef HAVE_SYSEXITS_H */
+
+/* Define if building libLLVM shared library */
+/* #undef LLVM_BUILD_LLVM_DYLIB */
+
+/* Define if building LLVM with BUILD_SHARED_LIBS */
+/* #undef LLVM_BUILD_SHARED_LIBS */
+
+/* Define if building LLVM with LLVM_FORCE_USE_OLD_TOOLCHAIN_LIBS */
+/* #undef LLVM_FORCE_USE_OLD_TOOLCHAIN */
+
+/* Define if llvm_unreachable should be optimized with undefined behavior
+ * in non assert builds */
+#define LLVM_UNREACHABLE_OPTIMIZE 1
+
+/* Define to 1 if you have the DIA SDK installed, and to 0 if you don't. */
+#define LLVM_ENABLE_DIA_SDK 1
+
+/* Define if plugins enabled */
+/* #undef LLVM_ENABLE_PLUGINS */
+
#endif
diff --git a/src/llvm-c/Core.h b/src/llvm-c/Core.h
index fbba8ca42..25b8248fd 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 "llvm-c/Deprecated.h"
-#include "llvm-c/ErrorHandling.h"
-#include "llvm-c/ExternC.h"
+#include "Deprecated.h"
+#include "ErrorHandling.h"
+#include "ExternC.h"
-#include "llvm-c/Types.h"
+#include "Types.h"
LLVM_C_EXTERN_C_BEGIN
@@ -216,7 +216,6 @@ typedef enum {
LLVMColdCallConv = 9,
LLVMGHCCallConv = 10,
LLVMHiPECallConv = 11,
- LLVMWebKitJSCallConv = 12,
LLVMAnyRegCallConv = 13,
LLVMPreserveMostCallConv = 14,
LLVMPreserveAllCallConv = 15,
@@ -468,8 +467,45 @@ enum {
LLVMAttributeFunctionIndex = -1,
};
+/**
+ * Tail call kind for LLVMSetTailCallKind and LLVMGetTailCallKind.
+ *
+ * Note that 'musttail' implies 'tail'.
+ *
+ * @see CallInst::TailCallKind
+ */
+typedef enum {
+ LLVMTailCallKindNone = 0,
+ LLVMTailCallKindTail = 1,
+ LLVMTailCallKindMustTail = 2,
+ LLVMTailCallKindNoTail = 3,
+} LLVMTailCallKind;
+
typedef unsigned LLVMAttributeIndex;
+enum {
+ LLVMFastMathAllowReassoc = (1 << 0),
+ LLVMFastMathNoNaNs = (1 << 1),
+ LLVMFastMathNoInfs = (1 << 2),
+ LLVMFastMathNoSignedZeros = (1 << 3),
+ LLVMFastMathAllowReciprocal = (1 << 4),
+ LLVMFastMathAllowContract = (1 << 5),
+ LLVMFastMathApproxFunc = (1 << 6),
+ LLVMFastMathNone = 0,
+ LLVMFastMathAll = LLVMFastMathAllowReassoc | LLVMFastMathNoNaNs |
+ LLVMFastMathNoInfs | LLVMFastMathNoSignedZeros |
+ LLVMFastMathAllowReciprocal | LLVMFastMathAllowContract |
+ LLVMFastMathApproxFunc,
+};
+
+/**
+ * Flags to indicate what fast-math-style optimizations are allowed
+ * on operations.
+ *
+ * See https://llvm.org/docs/LangRef.html#fast-math-flags
+ */
+typedef unsigned LLVMFastMathFlags;
+
/**
* @}
*/
@@ -890,13 +926,59 @@ void LLVMAppendModuleInlineAsm(LLVMModuleRef M, const char *Asm, size_t Len);
*
* @see InlineAsm::get()
*/
-LLVMValueRef LLVMGetInlineAsm(LLVMTypeRef Ty, char *AsmString,
- size_t AsmStringSize, char *Constraints,
+LLVMValueRef LLVMGetInlineAsm(LLVMTypeRef Ty, const char *AsmString,
+ size_t AsmStringSize, const char *Constraints,
size_t ConstraintsSize, LLVMBool HasSideEffects,
LLVMBool IsAlignStack,
LLVMInlineAsmDialect Dialect, LLVMBool CanThrow);
/**
+ * Get the template string used for an inline assembly snippet
+ *
+ */
+const char *LLVMGetInlineAsmAsmString(LLVMValueRef InlineAsmVal, size_t *Len);
+
+/**
+ * Get the raw constraint string for an inline assembly snippet
+ *
+ */
+const char *LLVMGetInlineAsmConstraintString(LLVMValueRef InlineAsmVal,
+ size_t *Len);
+
+/**
+ * Get the dialect used by the inline asm snippet
+ *
+ */
+LLVMInlineAsmDialect LLVMGetInlineAsmDialect(LLVMValueRef InlineAsmVal);
+
+/**
+ * Get the function type of the inline assembly snippet. The same type that
+ * was passed into LLVMGetInlineAsm originally
+ *
+ * @see LLVMGetInlineAsm
+ *
+ */
+LLVMTypeRef LLVMGetInlineAsmFunctionType(LLVMValueRef InlineAsmVal);
+
+/**
+ * Get if the inline asm snippet has side effects
+ *
+ */
+LLVMBool LLVMGetInlineAsmHasSideEffects(LLVMValueRef InlineAsmVal);
+
+/**
+ * Get if the inline asm snippet needs an aligned stack
+ *
+ */
+LLVMBool LLVMGetInlineAsmNeedsAlignedStack(LLVMValueRef InlineAsmVal);
+
+/**
+ * Get if the inline asm snippet may unwind the stack
+ *
+ */
+LLVMBool LLVMGetInlineAsmCanUnwind(LLVMValueRef InlineAsmVal);
+
+/**
* Obtain the context to which this module is associated.
*
* @see Module::getContext()
@@ -2216,45 +2298,26 @@ LLVMValueRef LLVMConstNUWSub(LLVMValueRef LHSConstant, LLVMValueRef RHSConstant)
LLVMValueRef LLVMConstMul(LLVMValueRef LHSConstant, LLVMValueRef RHSConstant);
LLVMValueRef LLVMConstNSWMul(LLVMValueRef LHSConstant, LLVMValueRef RHSConstant);
LLVMValueRef LLVMConstNUWMul(LLVMValueRef LHSConstant, LLVMValueRef RHSConstant);
-LLVMValueRef LLVMConstAnd(LLVMValueRef LHSConstant, LLVMValueRef RHSConstant);
-LLVMValueRef LLVMConstOr(LLVMValueRef LHSConstant, LLVMValueRef RHSConstant);
LLVMValueRef LLVMConstXor(LLVMValueRef LHSConstant, LLVMValueRef RHSConstant);
LLVMValueRef LLVMConstICmp(LLVMIntPredicate Predicate,
LLVMValueRef LHSConstant, LLVMValueRef RHSConstant);
LLVMValueRef LLVMConstFCmp(LLVMRealPredicate Predicate,
LLVMValueRef LHSConstant, LLVMValueRef RHSConstant);
LLVMValueRef LLVMConstShl(LLVMValueRef LHSConstant, LLVMValueRef RHSConstant);
-LLVMValueRef LLVMConstLShr(LLVMValueRef LHSConstant, LLVMValueRef RHSConstant);
-LLVMValueRef LLVMConstAShr(LLVMValueRef LHSConstant, LLVMValueRef RHSConstant);
LLVMValueRef LLVMConstGEP2(LLVMTypeRef Ty, LLVMValueRef ConstantVal,
LLVMValueRef *ConstantIndices, unsigned NumIndices);
LLVMValueRef LLVMConstInBoundsGEP2(LLVMTypeRef Ty, LLVMValueRef ConstantVal,
LLVMValueRef *ConstantIndices,
unsigned NumIndices);
LLVMValueRef LLVMConstTrunc(LLVMValueRef ConstantVal, LLVMTypeRef ToType);
-LLVMValueRef LLVMConstSExt(LLVMValueRef ConstantVal, LLVMTypeRef ToType);
-LLVMValueRef LLVMConstZExt(LLVMValueRef ConstantVal, LLVMTypeRef ToType);
-LLVMValueRef LLVMConstFPTrunc(LLVMValueRef ConstantVal, LLVMTypeRef ToType);
-LLVMValueRef LLVMConstFPExt(LLVMValueRef ConstantVal, LLVMTypeRef ToType);
-LLVMValueRef LLVMConstUIToFP(LLVMValueRef ConstantVal, LLVMTypeRef ToType);
-LLVMValueRef LLVMConstSIToFP(LLVMValueRef ConstantVal, LLVMTypeRef ToType);
-LLVMValueRef LLVMConstFPToUI(LLVMValueRef ConstantVal, LLVMTypeRef ToType);
-LLVMValueRef LLVMConstFPToSI(LLVMValueRef ConstantVal, LLVMTypeRef ToType);
LLVMValueRef LLVMConstPtrToInt(LLVMValueRef ConstantVal, LLVMTypeRef ToType);
LLVMValueRef LLVMConstIntToPtr(LLVMValueRef ConstantVal, LLVMTypeRef ToType);
LLVMValueRef LLVMConstBitCast(LLVMValueRef ConstantVal, LLVMTypeRef ToType);
LLVMValueRef LLVMConstAddrSpaceCast(LLVMValueRef ConstantVal, LLVMTypeRef ToType);
-LLVMValueRef LLVMConstZExtOrBitCast(LLVMValueRef ConstantVal,
- LLVMTypeRef ToType);
-LLVMValueRef LLVMConstSExtOrBitCast(LLVMValueRef ConstantVal,
- LLVMTypeRef ToType);
LLVMValueRef LLVMConstTruncOrBitCast(LLVMValueRef ConstantVal,
LLVMTypeRef ToType);
LLVMValueRef LLVMConstPointerCast(LLVMValueRef ConstantVal,
LLVMTypeRef ToType);
-LLVMValueRef LLVMConstIntCast(LLVMValueRef ConstantVal, LLVMTypeRef ToType,
- LLVMBool isSigned);
-LLVMValueRef LLVMConstFPCast(LLVMValueRef ConstantVal, LLVMTypeRef ToType);
LLVMValueRef LLVMConstExtractElement(LLVMValueRef VectorConstant,
LLVMValueRef IndexConstant);
LLVMValueRef LLVMConstInsertElement(LLVMValueRef VectorConstant,
@@ -2965,6 +3028,74 @@ LLVMValueRef LLVMMDNode(LLVMValueRef *Vals, unsigned Count);
*/
/**
+ * @defgroup LLVMCCoreOperandBundle Operand Bundles
+ *
+ * Functions in this group operate on LLVMOperandBundleRef instances that
+ * correspond to llvm::OperandBundleDef instances.
+ *
+ * @see llvm::OperandBundleDef
+ *
+ * @{
+ */
+
+/**
+ * Create a new operand bundle.
+ *
+ * Every invocation should be paired with LLVMDisposeOperandBundle() or memory
+ * will be leaked.
+ *
+ * @param Tag Tag name of the operand bundle
+ * @param TagLen Length of Tag
+ * @param Args Memory address of an array of bundle operands
+ * @param NumArgs Length of Args
+ */
+LLVMOperandBundleRef LLVMCreateOperandBundle(const char *Tag, size_t TagLen,
+ LLVMValueRef *Args,
+ unsigned NumArgs);
+
+/**
+ * Destroy an operand bundle.
+ *
+ * This must be called for every created operand bundle or memory will be
+ * leaked.
+ */
+void LLVMDisposeOperandBundle(LLVMOperandBundleRef Bundle);
+
+/**
+ * Obtain the tag of an operand bundle as a string.
+ *
+ * @param Bundle Operand bundle to obtain tag of.
+ * @param Len Out parameter which holds the length of the returned string.
+ * @return The tag name of Bundle.
+ * @see OperandBundleDef::getTag()
+ */
+const char *LLVMGetOperandBundleTag(LLVMOperandBundleRef Bundle, size_t *Len);
+
+/**
+ * Obtain the number of operands for an operand bundle.
+ *
+ * @param Bundle Operand bundle to obtain operand count of.
+ * @return The number of operands.
+ * @see OperandBundleDef::input_size()
+ */
+unsigned LLVMGetNumOperandBundleArgs(LLVMOperandBundleRef Bundle);
+
+/**
+ * Obtain the operand for an operand bundle at the given index.
+ *
+ * @param Bundle Operand bundle to obtain operand of.
+ * @param Index An operand index, must be less than
+ * LLVMGetNumOperandBundleArgs().
+ * @return The operand.
+ */
+LLVMValueRef LLVMGetOperandBundleArgAtIndex(LLVMOperandBundleRef Bundle,
+ unsigned Index);
+
+/**
+ * @}
+ */
+
+/**
* @defgroup LLVMCCoreValueBasicBlock Basic Block
*
* A basic block represents a single entry single exit section of code.
@@ -3412,6 +3543,24 @@ LLVMTypeRef LLVMGetCalledFunctionType(LLVMValueRef C);
LLVMValueRef LLVMGetCalledValue(LLVMValueRef Instr);
/**
+ * Obtain the number of operand bundles attached to this instruction.
+ *
+ * This only works on llvm::CallInst and llvm::InvokeInst instructions.
+ *
+ * @see llvm::CallBase::getNumOperandBundles()
+ */
+unsigned LLVMGetNumOperandBundles(LLVMValueRef C);
+
+/**
+ * Obtain the operand bundle attached to this instruction at the given index.
+ * Use LLVMDisposeOperandBundle to free the operand bundle.
+ *
+ * This only works on llvm::CallInst and llvm::InvokeInst instructions.
+ */
+LLVMOperandBundleRef LLVMGetOperandBundleAtIndex(LLVMValueRef C,
+ unsigned Index);
+
+/**
* Obtain whether a call instruction is a tail call.
*
* This only works on llvm::CallInst instructions.
@@ -3430,6 +3579,20 @@ LLVMBool LLVMIsTailCall(LLVMValueRef CallInst);
void LLVMSetTailCall(LLVMValueRef CallInst, LLVMBool IsTailCall);
/**
+ * Obtain a tail call kind of the call instruction.
+ *
+ * @see llvm::CallInst::setTailCallKind()
+ */
+LLVMTailCallKind LLVMGetTailCallKind(LLVMValueRef CallInst);
+
+/**
+ * Set the call kind of the call instruction.
+ *
+ * @see llvm::CallInst::getTailCallKind()
+ */
+void LLVMSetTailCallKind(LLVMValueRef CallInst, LLVMTailCallKind kind);
+
+/**
* Return the normal destination basic block.
*
* This only works on llvm::InvokeInst instructions.
@@ -3761,6 +3924,10 @@ LLVMValueRef LLVMBuildInvoke2(LLVMBuilderRef, LLVMTypeRef Ty, LLVMValueRef Fn,
LLVMValueRef *Args, unsigned NumArgs,
LLVMBasicBlockRef Then, LLVMBasicBlockRef Catch,
const char *Name);
+LLVMValueRef LLVMBuildInvokeWithOperandBundles(
+ LLVMBuilderRef, LLVMTypeRef Ty, LLVMValueRef Fn, LLVMValueRef *Args,
+ unsigned NumArgs, LLVMBasicBlockRef Then, LLVMBasicBlockRef Catch,
+ LLVMOperandBundleRef *Bundles, unsigned NumBundles, const char *Name);
LLVMValueRef LLVMBuildUnreachable(LLVMBuilderRef);
/* Exception Handling */
@@ -3920,6 +4087,55 @@ void LLVMSetNSW(LLVMValueRef ArithInst, LLVMBool HasNSW);
LLVMBool LLVMGetExact(LLVMValueRef DivOrShrInst);
void LLVMSetExact(LLVMValueRef DivOrShrInst, LLVMBool IsExact);
+/**
+ * Gets if the instruction has the non-negative flag set.
+ * Only valid for zext instructions.
+ */
+LLVMBool LLVMGetNNeg(LLVMValueRef NonNegInst);
+/**
+ * Sets the non-negative flag for the instruction.
+ * Only valid for zext instructions.
+ */
+void LLVMSetNNeg(LLVMValueRef NonNegInst, LLVMBool IsNonNeg);
+
+/**
+ * Get the flags for which fast-math-style optimizations are allowed for this
+ * value.
+ *
+ * Only valid on floating point instructions.
+ * @see LLVMCanValueUseFastMathFlags
+ */
+LLVMFastMathFlags LLVMGetFastMathFlags(LLVMValueRef FPMathInst);
+
+/**
+ * Sets the flags for which fast-math-style optimizations are allowed for this
+ * value.
+ *
+ * Only valid on floating point instructions.
+ * @see LLVMCanValueUseFastMathFlags
+ */
+void LLVMSetFastMathFlags(LLVMValueRef FPMathInst, LLVMFastMathFlags FMF);
+
+/**
+ * Check if a given value can potentially have fast math flags.
+ *
+ * Will return true for floating point arithmetic instructions, and for select,
+ * phi, and call instructions whose type is a floating point type, or a vector
+ * or array thereof. See https://llvm.org/docs/LangRef.html#fast-math-flags
+ */
+LLVMBool LLVMCanValueUseFastMathFlags(LLVMValueRef Inst);
+
+/**
+ * Gets whether the instruction has the disjoint flag set.
+ * Only valid for or instructions.
+ */
+LLVMBool LLVMGetIsDisjoint(LLVMValueRef Inst);
+/**
+ * Sets the disjoint flag for the instruction.
+ * Only valid for or instructions.
+ */
+void LLVMSetIsDisjoint(LLVMValueRef Inst, LLVMBool IsDisjoint);
+
/* Memory */
LLVMValueRef LLVMBuildMalloc(LLVMBuilderRef, LLVMTypeRef Ty, const char *Name);
LLVMValueRef LLVMBuildArrayMalloc(LLVMBuilderRef, LLVMTypeRef Ty,
@@ -4045,6 +4261,11 @@ LLVMValueRef LLVMBuildPhi(LLVMBuilderRef, LLVMTypeRef Ty, const char *Name);
LLVMValueRef LLVMBuildCall2(LLVMBuilderRef, LLVMTypeRef, LLVMValueRef Fn,
LLVMValueRef *Args, unsigned NumArgs,
const char *Name);
+LLVMValueRef
+LLVMBuildCallWithOperandBundles(LLVMBuilderRef, LLVMTypeRef, LLVMValueRef Fn,
+ LLVMValueRef *Args, unsigned NumArgs,
+ LLVMOperandBundleRef *Bundles,
+ unsigned NumBundles, const char *Name);
LLVMValueRef LLVMBuildSelect(LLVMBuilderRef, LLVMValueRef If,
LLVMValueRef Then, LLVMValueRef Else,
const char *Name);
diff --git a/src/llvm-c/DebugInfo.h b/src/llvm-c/DebugInfo.h
index 592429470..93bd9e2ad 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 "llvm-c/ExternC.h"
-#include "llvm-c/Types.h"
+#include "ExternC.h"
+#include "Types.h"
LLVM_C_EXTERN_C_BEGIN
diff --git a/src/llvm-c/Disassembler.h b/src/llvm-c/Disassembler.h
index b1cb35da6..e6642f4ed 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 "llvm-c/DisassemblerTypes.h"
-#include "llvm-c/ExternC.h"
+#include "DisassemblerTypes.h"
+#include "ExternC.h"
/**
* @defgroup LLVMCDisassembler Disassembler
diff --git a/src/llvm-c/DisassemblerTypes.h b/src/llvm-c/DisassemblerTypes.h
index 6999a350e..6b7ad6104 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 "llvm-c/DataTypes.h"
+#include "DataTypes.h"
#ifdef __cplusplus
#include <cstddef>
#else
diff --git a/src/llvm-c/Error.h b/src/llvm-c/Error.h
index c3baaf651..00746c701 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 "llvm-c/ExternC.h"
+#include "ExternC.h"
LLVM_C_EXTERN_C_BEGIN
diff --git a/src/llvm-c/ErrorHandling.h b/src/llvm-c/ErrorHandling.h
index d9b9f2275..7f9b50a95 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 "llvm-c/ExternC.h"
+#include "ExternC.h"
LLVM_C_EXTERN_C_BEGIN
diff --git a/src/llvm-c/ExecutionEngine.h b/src/llvm-c/ExecutionEngine.h
index c5fc9bdb4..8e72faefd 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 "llvm-c/ExternC.h"
-#include "llvm-c/Target.h"
-#include "llvm-c/TargetMachine.h"
-#include "llvm-c/Types.h"
+#include "ExternC.h"
+#include "Target.h"
+#include "TargetMachine.h"
+#include "Types.h"
LLVM_C_EXTERN_C_BEGIN
diff --git a/src/llvm-c/IRReader.h b/src/llvm-c/IRReader.h
index 905b84fa5..ec1110c7a 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 "llvm-c/ExternC.h"
-#include "llvm-c/Types.h"
+#include "ExternC.h"
+#include "Types.h"
LLVM_C_EXTERN_C_BEGIN
diff --git a/src/llvm-c/LLJIT.h b/src/llvm-c/LLJIT.h
index a06133aac..ee207e10e 100644
--- a/src/llvm-c/LLJIT.h
+++ b/src/llvm-c/LLJIT.h
@@ -1,4 +1,4 @@
-/*===----------- llvm-c/LLJIT.h - OrcV2 LLJIT C bindings --------*- C++ -*-===*\
+/*===----------- llvm-c/LLJIT.h - OrcV2 LLJIT C bindings ----------*- C -*-===*\
|* *|
|* Part of the LLVM Project, under the Apache License v2.0 with LLVM *|
|* Exceptions. *|
@@ -24,10 +24,10 @@
#ifndef LLVM_C_LLJIT_H
#define LLVM_C_LLJIT_H
-#include "llvm-c/Error.h"
-#include "llvm-c/Orc.h"
-#include "llvm-c/TargetMachine.h"
-#include "llvm-c/Types.h"
+#include "Error.h"
+#include "Orc.h"
+#include "TargetMachine.h"
+#include "Types.h"
LLVM_C_EXTERN_C_BEGIN
diff --git a/src/llvm-c/LLJITUtils.h b/src/llvm-c/LLJITUtils.h
new file mode 100644
index 000000000..57ffedff8
--- /dev/null
+++ b/src/llvm-c/LLJITUtils.h
@@ -0,0 +1,52 @@
+/*===------- llvm-c/LLJITUtils.h - Advanced LLJIT features --------*- C -*-===*\
+|* *|
+|* Part of the LLVM Project, under the Apache License v2.0 with LLVM *|
+|* Exceptions. *|
+|* See https://llvm.org/LICENSE.txt for license information. *|
+|* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception *|
+|* *|
+|*===----------------------------------------------------------------------===*|
+|* *|
+|* This header declares the C interface for extra utilities to be used with *|
+|* the LLJIT class from the llvm-c/LLJIT.h header. It requires to following *|
+|* link libraries in addition to libLLVMOrcJIT.a: *|
+|* - libLLVMOrcDebugging.a *|
+|* *|
+|* Many exotic languages can interoperate with C code but have a harder time *|
+|* with C++ due to name mangling. So in addition to C, this interface enables *|
+|* tools written in such languages. *|
+|* *|
+|* Note: This interface is experimental. It is *NOT* stable, and may be *|
+|* changed without warning. Only C API usage documentation is *|
+|* provided. See the C++ documentation for all higher level ORC API *|
+|* details. *|
+|* *|
+\*===----------------------------------------------------------------------===*/
+
+#ifndef LLVM_C_LLJITUTILS_H
+#define LLVM_C_LLJITUTILS_H
+
+#include "LLJIT.h"
+
+LLVM_C_EXTERN_C_BEGIN
+
+/**
+ * @defgroup LLVMCExecutionEngineLLJITUtils LLJIT Utilities
+ * @ingroup LLVMCExecutionEngineLLJIT
+ *
+ * @{
+ */
+
+/**
+ * Install the plugin that submits debug objects to the executor. Executors must
+ * expose the llvm_orc_registerJITLoaderGDBWrapper symbol.
+ */
+LLVMErrorRef LLVMOrcLLJITEnableDebugSupport(LLVMOrcLLJITRef J);
+
+/**
+ * @}
+ */
+
+LLVM_C_EXTERN_C_END
+
+#endif /* LLVM_C_LLJITUTILS_H */
diff --git a/src/llvm-c/Linker.h b/src/llvm-c/Linker.h
index acff5d5e2..463a2cff9 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 "llvm-c/ExternC.h"
-#include "llvm-c/Types.h"
+#include "ExternC.h"
+#include "Types.h"
LLVM_C_EXTERN_C_BEGIN
diff --git a/src/llvm-c/Object.h b/src/llvm-c/Object.h
index f871d5230..1948c3c34 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 "llvm-c/ExternC.h"
-#include "llvm-c/Types.h"
-#include "llvm-c/Config//llvm-config.h"
+#include "ExternC.h"
+#include "Types.h"
+#include "Config/llvm-config.h"
LLVM_C_EXTERN_C_BEGIN
diff --git a/src/llvm-c/Orc.h b/src/llvm-c/Orc.h
index 0dcfb0686..ecd110b4d 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 "llvm-c/Error.h"
-#include "llvm-c/TargetMachine.h"
-#include "llvm-c/Types.h"
+#include "Error.h"
+#include "TargetMachine.h"
+#include "Types.h"
LLVM_C_EXTERN_C_BEGIN
@@ -346,7 +346,7 @@ typedef struct LLVMOrcOpaqueLookupState *LLVMOrcLookupStateRef;
* into.
*
* The JDLookupFlags argument can be inspected to determine whether the original
- * lookup included non-exported symobls.
+ * lookup included non-exported symbols.
*
* Finally, the LookupSet argument contains the set of symbols that could not
* be found in JD already (the set of generation candidates).
@@ -508,7 +508,7 @@ void LLVMOrcSymbolStringPoolClearDeadEntries(LLVMOrcSymbolStringPoolRef SSP);
* Intern a string in the ExecutionSession's SymbolStringPool and return a
* reference to it. This increments the ref-count of the pool entry, and the
* returned value should be released once the client is done with it by
- * calling LLVMOrReleaseSymbolStringPoolEntry.
+ * calling LLVMOrcReleaseSymbolStringPoolEntry.
*
* Since strings are uniqued within the SymbolStringPool
* LLVMOrcSymbolStringPoolEntryRefs can be compared by value to test string
@@ -796,7 +796,7 @@ void LLVMOrcDisposeSymbols(LLVMOrcSymbolStringPoolEntryRef *Symbols);
* method returns an error then clients should log it and call
* LLVMOrcMaterializationResponsibilityFailMaterialization. If no dependencies
* have been registered for the symbols covered by this
- * MaterializationResponsibiility then this method is guaranteed to return
+ * MaterializationResponsibility then this method is guaranteed to return
* LLVMErrorSuccess.
*/
LLVMErrorRef LLVMOrcMaterializationResponsibilityNotifyResolved(
@@ -813,7 +813,7 @@ LLVMErrorRef LLVMOrcMaterializationResponsibilityNotifyResolved(
* method returns an error then clients should log it and call
* LLVMOrcMaterializationResponsibilityFailMaterialization.
* If no dependencies have been registered for the symbols covered by this
- * MaterializationResponsibiility then this method is guaranteed to return
+ * MaterializationResponsibility then this method is guaranteed to return
* LLVMErrorSuccess.
*/
LLVMErrorRef LLVMOrcMaterializationResponsibilityNotifyEmitted(
@@ -839,7 +839,7 @@ LLVMErrorRef LLVMOrcMaterializationResponsibilityDefineMaterializing(
/**
* Notify all not-yet-emitted covered by this MaterializationResponsibility
* instance that an error has occurred.
- * This will remove all symbols covered by this MaterializationResponsibilty
+ * This will remove all symbols covered by this MaterializationResponsibility
* from the target JITDylib, and send an error to any queries waiting on
* these symbols.
*/
diff --git a/src/llvm-c/OrcEE.h b/src/llvm-c/OrcEE.h
index d451187aa..aef24c7aa 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 "llvm-c/Error.h"
-#include "llvm-c/ExecutionEngine.h"
-#include "llvm-c/Orc.h"
-#include "llvm-c/TargetMachine.h"
-#include "llvm-c/Types.h"
+#include "Error.h"
+#include "ExecutionEngine.h"
+#include "Orc.h"
+#include "TargetMachine.h"
+#include "Types.h"
LLVM_C_EXTERN_C_BEGIN
diff --git a/src/llvm-c/Remarks.h b/src/llvm-c/Remarks.h
index ffe647a65..548a4041a 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 "llvm-c/ExternC.h"
-#include "llvm-c/Types.h"
+#include "ExternC.h"
+#include "Types.h"
#ifdef __cplusplus
#include <cstddef>
#else
diff --git a/src/llvm-c/Support.h b/src/llvm-c/Support.h
index 17657861b..31a75354c 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 "llvm-c/DataTypes.h"
-#include "llvm-c/ExternC.h"
-#include "llvm-c/Types.h"
+#include "DataTypes.h"
+#include "ExternC.h"
+#include "Types.h"
LLVM_C_EXTERN_C_BEGIN
diff --git a/src/llvm-c/Target.h b/src/llvm-c/Target.h
index 2bc3d1ae7..4d03741c4 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 "llvm-c/ExternC.h"
-#include "llvm-c/Types.h"
-#include "llvm-c/Config//llvm-config.h"
+#include "ExternC.h"
+#include "Types.h"
+#include "Config/llvm-config.h"
LLVM_C_EXTERN_C_BEGIN
@@ -40,34 +40,34 @@ typedef struct LLVMOpaqueTargetLibraryInfotData *LLVMTargetLibraryInfoRef;
/* Declare all of the target-initialization functions that are available. */
#define LLVM_TARGET(TargetName) \
void LLVMInitialize##TargetName##TargetInfo(void);
-#include "llvm-c/Config//Targets.def"
+#include "Config/Targets.def"
#undef LLVM_TARGET /* Explicit undef to make SWIG happier */
#define LLVM_TARGET(TargetName) void LLVMInitialize##TargetName##Target(void);
-#include "llvm-c/Config//Targets.def"
+#include "Config/Targets.def"
#undef LLVM_TARGET /* Explicit undef to make SWIG happier */
#define LLVM_TARGET(TargetName) \
void LLVMInitialize##TargetName##TargetMC(void);
-#include "llvm-c/Config//Targets.def"
+#include "Config/Targets.def"
#undef LLVM_TARGET /* Explicit undef to make SWIG happier */
/* Declare all of the available assembly printer initialization functions. */
#define LLVM_ASM_PRINTER(TargetName) \
void LLVMInitialize##TargetName##AsmPrinter(void);
-#include "llvm-c/Config//AsmPrinters.def"
+#include "Config/AsmPrinters.def"
#undef LLVM_ASM_PRINTER /* Explicit undef to make SWIG happier */
/* Declare all of the available assembly parser initialization functions. */
#define LLVM_ASM_PARSER(TargetName) \
void LLVMInitialize##TargetName##AsmParser(void);
-#include "llvm-c/Config//AsmParsers.def"
+#include "Config/AsmParsers.def"
#undef LLVM_ASM_PARSER /* Explicit undef to make SWIG happier */
/* Declare all of the available disassembler initialization functions. */
#define LLVM_DISASSEMBLER(TargetName) \
void LLVMInitialize##TargetName##Disassembler(void);
-#include "llvm-c/Config//Disassemblers.def"
+#include "Config/Disassemblers.def"
#undef LLVM_DISASSEMBLER /* Explicit undef to make SWIG happier */
/** LLVMInitializeAllTargetInfos - The main program should call this function if
@@ -75,7 +75,7 @@ typedef struct LLVMOpaqueTargetLibraryInfotData *LLVMTargetLibraryInfoRef;
support. */
static inline void LLVMInitializeAllTargetInfos(void) {
#define LLVM_TARGET(TargetName) LLVMInitialize##TargetName##TargetInfo();
-#include "llvm-c/Config//Targets.def"
+#include "Config/Targets.def"
#undef LLVM_TARGET /* Explicit undef to make SWIG happier */
}
@@ -84,7 +84,7 @@ static inline void LLVMInitializeAllTargetInfos(void) {
support. */
static inline void LLVMInitializeAllTargets(void) {
#define LLVM_TARGET(TargetName) LLVMInitialize##TargetName##Target();
-#include "llvm-c/Config//Targets.def"
+#include "Config/Targets.def"
#undef LLVM_TARGET /* Explicit undef to make SWIG happier */
}
@@ -93,7 +93,7 @@ static inline void LLVMInitializeAllTargets(void) {
support. */
static inline void LLVMInitializeAllTargetMCs(void) {
#define LLVM_TARGET(TargetName) LLVMInitialize##TargetName##TargetMC();
-#include "llvm-c/Config//Targets.def"
+#include "Config/Targets.def"
#undef LLVM_TARGET /* Explicit undef to make SWIG happier */
}
@@ -102,7 +102,7 @@ static inline void LLVMInitializeAllTargetMCs(void) {
available via the TargetRegistry. */
static inline void LLVMInitializeAllAsmPrinters(void) {
#define LLVM_ASM_PRINTER(TargetName) LLVMInitialize##TargetName##AsmPrinter();
-#include "llvm-c/Config//AsmPrinters.def"
+#include "Config/AsmPrinters.def"
#undef LLVM_ASM_PRINTER /* Explicit undef to make SWIG happier */
}
@@ -111,7 +111,7 @@ static inline void LLVMInitializeAllAsmPrinters(void) {
available via the TargetRegistry. */
static inline void LLVMInitializeAllAsmParsers(void) {
#define LLVM_ASM_PARSER(TargetName) LLVMInitialize##TargetName##AsmParser();
-#include "llvm-c/Config//AsmParsers.def"
+#include "Config/AsmParsers.def"
#undef LLVM_ASM_PARSER /* Explicit undef to make SWIG happier */
}
@@ -121,7 +121,7 @@ static inline void LLVMInitializeAllAsmParsers(void) {
static inline void LLVMInitializeAllDisassemblers(void) {
#define LLVM_DISASSEMBLER(TargetName) \
LLVMInitialize##TargetName##Disassembler();
-#include "llvm-c/Config//Disassemblers.def"
+#include "Config/Disassemblers.def"
#undef LLVM_DISASSEMBLER /* Explicit undef to make SWIG happier */
}
diff --git a/src/llvm-c/TargetMachine.h b/src/llvm-c/TargetMachine.h
index bfbe1421a..aa628e216 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 "llvm-c/ExternC.h"
-#include "llvm-c/Target.h"
-#include "llvm-c/Types.h"
+#include "ExternC.h"
+#include "Target.h"
+#include "Types.h"
LLVM_C_EXTERN_C_BEGIN
@@ -31,6 +31,7 @@ LLVM_C_EXTERN_C_BEGIN
* @{
*/
+typedef struct LLVMOpaqueTargetMachineOptions *LLVMTargetMachineOptionsRef;
typedef struct LLVMOpaqueTargetMachine *LLVMTargetMachineRef;
typedef struct LLVMTarget *LLVMTargetRef;
@@ -66,6 +67,12 @@ typedef enum {
LLVMObjectFile
} LLVMCodeGenFileType;
+typedef enum {
+ LLVMGlobalISelAbortEnable,
+ LLVMGlobalISelAbortDisable,
+ LLVMGlobalISelAbortDisableWithDiag,
+} LLVMGlobalISelAbortMode;
+
/** Returns the first llvm::Target in the registered targets list. */
LLVMTargetRef LLVMGetFirstTarget(void);
/** Returns the next llvm::Target given a previous one (or null if there's none) */
@@ -98,6 +105,55 @@ LLVMBool LLVMTargetHasTargetMachine(LLVMTargetRef T);
LLVMBool LLVMTargetHasAsmBackend(LLVMTargetRef T);
/*===-- Target Machine ----------------------------------------------------===*/
+/**
+ * Create a new set of options for an llvm::TargetMachine.
+ *
+ * The returned option structure must be released with
+ * LLVMDisposeTargetMachineOptions() after the call to
+ * LLVMCreateTargetMachineWithOptions().
+ */
+LLVMTargetMachineOptionsRef LLVMCreateTargetMachineOptions(void);
+
+/**
+ * Dispose of an LLVMTargetMachineOptionsRef instance.
+ */
+void LLVMDisposeTargetMachineOptions(LLVMTargetMachineOptionsRef Options);
+
+void LLVMTargetMachineOptionsSetCPU(LLVMTargetMachineOptionsRef Options,
+ const char *CPU);
+
+/**
+ * Set the list of features for the target machine.
+ *
+ * \param Features a comma-separated list of features.
+ */
+void LLVMTargetMachineOptionsSetFeatures(LLVMTargetMachineOptionsRef Options,
+ const char *Features);
+
+void LLVMTargetMachineOptionsSetABI(LLVMTargetMachineOptionsRef Options,
+ const char *ABI);
+
+void LLVMTargetMachineOptionsSetCodeGenOptLevel(
+ LLVMTargetMachineOptionsRef Options, LLVMCodeGenOptLevel Level);
+
+void LLVMTargetMachineOptionsSetRelocMode(LLVMTargetMachineOptionsRef Options,
+ LLVMRelocMode Reloc);
+
+void LLVMTargetMachineOptionsSetCodeModel(LLVMTargetMachineOptionsRef Options,
+ LLVMCodeModel CodeModel);
+
+/**
+ * Create a new llvm::TargetMachine.
+ *
+ * \param T the target to create a machine for.
+ * \param Triple a triple describing the target machine.
+ * \param Options additional configuration (see
+ * LLVMCreateTargetMachineOptions()).
+ */
+LLVMTargetMachineRef
+LLVMCreateTargetMachineWithOptions(LLVMTargetRef T, const char *Triple,
+ LLVMTargetMachineOptionsRef Options);
+
/** Creates a new llvm::TargetMachine. See llvm::Target::createTargetMachine */
LLVMTargetMachineRef LLVMCreateTargetMachine(LLVMTargetRef T,
const char *Triple, const char *CPU, const char *Features,
@@ -132,6 +188,21 @@ LLVMTargetDataRef LLVMCreateTargetDataLayout(LLVMTargetMachineRef T);
void LLVMSetTargetMachineAsmVerbosity(LLVMTargetMachineRef T,
LLVMBool VerboseAsm);
+/** Enable fast-path instruction selection. */
+void LLVMSetTargetMachineFastISel(LLVMTargetMachineRef T, LLVMBool Enable);
+
+/** Enable global instruction selection. */
+void LLVMSetTargetMachineGlobalISel(LLVMTargetMachineRef T, LLVMBool Enable);
+
+/** Set abort behaviour when global instruction selection fails to lower/select
+ * an instruction. */
+void LLVMSetTargetMachineGlobalISelAbort(LLVMTargetMachineRef T,
+ LLVMGlobalISelAbortMode Mode);
+
+/** Enable the MachineOutliner pass. */
+void LLVMSetTargetMachineMachineOutliner(LLVMTargetMachineRef T,
+ LLVMBool Enable);
+
/** Emits an asm or object file for the given module to the filename. This
wraps several c++ only classes (among them a file stream). Returns any
error in ErrorMessage. Use LLVMDisposeMessage to dispose the message. */
diff --git a/src/llvm-c/Transforms/PassBuilder.h b/src/llvm-c/Transforms/PassBuilder.h
index d0466dd7f..8ad2a9982 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 "llvm-c/Error.h"
-#include "llvm-c/TargetMachine.h"
-#include "llvm-c/Types.h"
+#include "../Error.h"
+#include "../TargetMachine.h"
+#include "../Types.h"
/**
* @defgroup LLVMCCoreNewPM New Pass Manager
diff --git a/src/llvm-c/Types.h b/src/llvm-c/Types.h
index 4e9967372..77aa7c9b4 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 "llvm-c/DataTypes.h"
-#include "llvm-c/ExternC.h"
+#include "DataTypes.h"
+#include "ExternC.h"
LLVM_C_EXTERN_C_BEGIN
@@ -133,6 +133,11 @@ typedef struct LLVMOpaquePassManager *LLVMPassManagerRef;
typedef struct LLVMOpaqueUse *LLVMUseRef;
/**
+ * @see llvm::OperandBundleDef
+ */
+typedef struct LLVMOpaqueOperandBundle *LLVMOperandBundleRef;
+
+/**
* Used to represent an attributes.
*
* @see llvm::Attribute
diff --git a/src/llvm-c/lto.h b/src/llvm-c/lto.h
index 5ceb02224..89f76c695 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 "llvm-c/ExternC.h"
+#include "ExternC.h"
#ifdef __cplusplus
#include <cstddef>
diff --git a/src/llvm_abi.cpp b/src/llvm_abi.cpp
index 857b255f3..0b2bb7956 100644
--- a/src/llvm_abi.cpp
+++ b/src/llvm_abi.cpp
@@ -15,6 +15,7 @@ struct lbArgType {
LLVMAttributeRef align_attribute; // Optional
i64 byval_alignment;
bool is_byval;
+ bool no_capture;
};
@@ -159,6 +160,11 @@ gb_internal void lb_add_function_type_attributes(LLVMValueRef fn, lbFunctionType
LLVMAddAttributeAtIndex(fn, arg_index+1, arg->align_attribute);
}
+ if (arg->no_capture) {
+ LLVMAddAttributeAtIndex(fn, arg_index+1, nocapture_attr);
+ }
+
+
if (ft->multiple_return_original_type) {
if (ft->original_arg_count <= i) {
LLVMAddAttributeAtIndex(fn, arg_index+1, noalias_attr);
@@ -192,7 +198,7 @@ gb_internal void lb_add_function_type_attributes(LLVMValueRef fn, lbFunctionType
// }
LLVMSetFunctionCallConv(fn, cc_kind);
if (calling_convention == ProcCC_Odin) {
- unsigned context_index = offset+arg_count;
+ unsigned context_index = arg_index;
LLVMAddAttributeAtIndex(fn, context_index, noalias_attr);
LLVMAddAttributeAtIndex(fn, context_index, nonnull_attr);
LLVMAddAttributeAtIndex(fn, context_index, nocapture_attr);
@@ -275,7 +281,7 @@ gb_internal i64 lb_alignof(LLVMTypeRef type) {
case LLVMIntegerTypeKind:
{
unsigned w = LLVMGetIntTypeWidth(type);
- return gb_clamp((w + 7)/8, 1, build_context.ptr_size);
+ return gb_clamp((w + 7)/8, 1, build_context.max_align);
}
case LLVMHalfTypeKind:
return 2;
@@ -326,7 +332,7 @@ gb_internal i64 lb_alignof(LLVMTypeRef type) {
}
-#define LB_ABI_INFO(name) lbFunctionType *name(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count, LLVMTypeRef return_type, bool return_is_defined, bool return_is_tuple, ProcCallingConvention calling_convention, Type *original_type)
+#define LB_ABI_INFO(name) lbFunctionType *name(lbModule *m, LLVMTypeRef *arg_types, unsigned arg_count, LLVMTypeRef return_type, bool return_is_defined, bool return_is_tuple, ProcCallingConvention calling_convention, Type *original_type)
typedef LB_ABI_INFO(lbAbiInfoType);
#define LB_ABI_COMPUTE_RETURN_TYPE(name) lbArgType name(lbFunctionType *ft, LLVMContextRef c, LLVMTypeRef return_type, bool return_is_defined, bool return_is_tuple)
@@ -374,6 +380,7 @@ namespace lbAbi386 {
gb_internal LB_ABI_COMPUTE_RETURN_TYPE(compute_return_type);
gb_internal LB_ABI_INFO(abi_info) {
+ LLVMContextRef c = m->ctx;
lbFunctionType *ft = gb_alloc_item(permanent_allocator(), lbFunctionType);
ft->ctx = c;
ft->args = compute_arg_types(c, arg_types, arg_count);
@@ -455,6 +462,7 @@ namespace lbAbiAmd64Win64 {
gb_internal LB_ABI_COMPUTE_RETURN_TYPE(compute_return_type);
gb_internal LB_ABI_INFO(abi_info) {
+ LLVMContextRef c = m->ctx;
lbFunctionType *ft = gb_alloc_item(permanent_allocator(), lbFunctionType);
ft->ctx = c;
ft->args = compute_arg_types(c, arg_types, arg_count);
@@ -523,6 +531,7 @@ namespace lbAbiAmd64SysV {
RegClass_SSEInt16,
RegClass_SSEInt32,
RegClass_SSEInt64,
+ RegClass_SSEInt128,
RegClass_SSEUp,
RegClass_X87,
RegClass_X87Up,
@@ -558,14 +567,23 @@ namespace lbAbiAmd64SysV {
Amd64TypeAttribute_StructRect,
};
- gb_internal LB_ABI_COMPUTE_RETURN_TYPE(compute_return_type);
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 Array<RegClass> classify(LLVMTypeRef t);
gb_internal LLVMTypeRef llreg(LLVMContextRef c, Array<RegClass> const &reg_classes, LLVMTypeRef type);
+ gb_internal LB_ABI_COMPUTE_RETURN_TYPE(compute_return_type) {
+ if (!return_is_defined) {
+ return lb_arg_type_direct(LLVMVoidTypeInContext(c));
+ }
+ LB_ABI_MODIFY_RETURN_IF_TUPLE_MACRO();
+
+ return amd64_type(c, return_type, Amd64TypeAttribute_StructRect, ft->calling_convention);
+ }
+
gb_internal LB_ABI_INFO(abi_info) {
+ LLVMContextRef c = m->ctx;
lbFunctionType *ft = gb_alloc_item(permanent_allocator(), lbFunctionType);
ft->ctx = c;
ft->calling_convention = calling_convention;
@@ -574,12 +592,7 @@ namespace lbAbiAmd64SysV {
for (unsigned i = 0; i < arg_count; i++) {
ft->args[i] = amd64_type(c, arg_types[i], Amd64TypeAttribute_ByVal, calling_convention);
}
-
- if (return_is_defined) {
- ft->ret = amd64_type(c, return_type, Amd64TypeAttribute_StructRect, calling_convention);
- } else {
- ft->ret = lb_arg_type_direct(LLVMVoidTypeInContext(c));
- }
+ ft->ret = compute_return_type(ft, c, return_type, return_is_defined, return_is_tuple);
return ft;
}
@@ -608,6 +621,10 @@ namespace lbAbiAmd64SysV {
}
switch (kind) {
case LLVMIntegerTypeKind:
+ if (LLVM_VERSION_MAJOR >= 18 && sz >= 16) {
+ return true;
+ }
+ return false;
case LLVMHalfTypeKind:
case LLVMFloatTypeKind:
case LLVMDoubleTypeKind:
@@ -646,10 +663,10 @@ namespace lbAbiAmd64SysV {
if (is_mem_cls(cls, attribute_kind)) {
LLVMAttributeRef attribute = nullptr;
if (attribute_kind == Amd64TypeAttribute_ByVal) {
- // if (!is_calling_convention_odin(calling_convention)) {
- return lb_arg_type_indirect_byval(c, type);
- // }
- // attribute = nullptr;
+ if (is_calling_convention_odin(calling_convention)) {
+ return lb_arg_type_indirect(type, attribute);
+ }
+ return lb_arg_type_indirect_byval(c, type);
} else if (attribute_kind == Amd64TypeAttribute_StructRect) {
attribute = lb_create_enum_attribute_with_type(c, "sret", type);
}
@@ -796,10 +813,10 @@ namespace lbAbiAmd64SysV {
}
}
+ i64 sz = lb_sizeof(type);
if (all_ints) {
- i64 sz = lb_sizeof(type);
for_array(i, reg_classes) {
- GB_ASSERT(sz != 0);
+ GB_ASSERT(sz > 0);
// TODO(bill): is this even correct? BECAUSE LLVM DOES NOT DOCUMENT ANY OF THIS!!!
if (sz >= 8) {
array_add(&types, LLVMIntTypeInContext(c, 64));
@@ -811,12 +828,16 @@ namespace lbAbiAmd64SysV {
}
} else {
for (isize i = 0; i < reg_classes.count; /**/) {
+ GB_ASSERT(sz > 0);
RegClass reg_class = reg_classes[i];
switch (reg_class) {
case RegClass_Int:
- // TODO(bill): is this even correct? BECAUSE LLVM DOES NOT DOCUMENT ANY OF THIS!!!
- array_add(&types, LLVMIntTypeInContext(c, 64));
- break;
+ {
+ i64 rs = gb_min(sz, 8);
+ array_add(&types, LLVMIntTypeInContext(c, cast(unsigned)(rs*8)));
+ sz -= rs;
+ break;
+ }
case RegClass_SSEFv:
case RegClass_SSEDv:
case RegClass_SSEInt8:
@@ -856,15 +877,18 @@ namespace lbAbiAmd64SysV {
unsigned vec_len = llvec_len(reg_classes, i+1);
LLVMTypeRef vec_type = LLVMVectorType(elem_type, vec_len * elems_per_word);
array_add(&types, vec_type);
+ sz -= lb_sizeof(vec_type);
i += vec_len;
continue;
}
break;
case RegClass_SSEFs:
array_add(&types, LLVMFloatTypeInContext(c));
+ sz -= 4;
break;
case RegClass_SSEDs:
array_add(&types, LLVMDoubleTypeInContext(c));
+ sz -= 8;
break;
default:
GB_PANIC("Unhandled RegClass");
@@ -876,7 +900,8 @@ namespace lbAbiAmd64SysV {
if (types.count == 1) {
return types[0];
}
- return LLVMStructTypeInContext(c, types.data, cast(unsigned)types.count, false);
+
+ return LLVMStructTypeInContext(c, types.data, cast(unsigned)types.count, sz == 0);
}
gb_internal void classify_with(LLVMTypeRef t, Array<RegClass> *cls, i64 ix, i64 off) {
@@ -893,7 +918,15 @@ namespace lbAbiAmd64SysV {
}
switch (LLVMGetTypeKind(t)) {
- case LLVMIntegerTypeKind:
+ case LLVMIntegerTypeKind: {
+ i64 s = t_size;
+ while (s > 0) {
+ unify(cls, ix + off/8, RegClass_Int);
+ off += 8;
+ s -= 8;
+ }
+ break;
+ }
case LLVMPointerTypeKind:
case LLVMHalfTypeKind:
unify(cls, ix + off/8, RegClass_Int);
@@ -979,28 +1012,6 @@ namespace lbAbiAmd64SysV {
break;
}
}
-
- gb_internal LB_ABI_COMPUTE_RETURN_TYPE(compute_return_type) {
- if (!return_is_defined) {
- return lb_arg_type_direct(LLVMVoidTypeInContext(c));
- } else if (lb_is_type_kind(return_type, LLVMStructTypeKind)) {
- i64 sz = lb_sizeof(return_type);
- switch (sz) {
- case 1: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 8), nullptr, nullptr);
- case 2: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 16), nullptr, nullptr);
- case 4: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 32), nullptr, nullptr);
- case 8: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 64), nullptr, nullptr);
- }
-
- 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);
- } else if (build_context.metrics.os == TargetOs_windows && lb_is_type_kind(return_type, LLVMIntegerTypeKind) && lb_sizeof(return_type) == 16) {
- return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 128), nullptr, nullptr);
- }
- return non_struct(c, return_type);
- }
};
@@ -1010,6 +1021,7 @@ namespace lbAbiArm64 {
gb_internal bool is_homogenous_aggregate(LLVMContextRef c, LLVMTypeRef type, LLVMTypeRef *base_type_, unsigned *member_count_);
gb_internal LB_ABI_INFO(abi_info) {
+ LLVMContextRef c = m->ctx;
lbFunctionType *ft = gb_alloc_item(permanent_allocator(), lbFunctionType);
ft->ctx = c;
ft->args = compute_arg_types(c, arg_types, arg_count);
@@ -1145,17 +1157,28 @@ namespace lbAbiArm64 {
i64 size = lb_sizeof(return_type);
if (size <= 16) {
LLVMTypeRef cast_type = nullptr;
- if (size <= 1) {
- cast_type = LLVMInt8TypeInContext(c);
- } else if (size <= 2) {
- cast_type = LLVMInt16TypeInContext(c);
- } else if (size <= 4) {
- cast_type = LLVMInt32TypeInContext(c);
+
+ if (size == 0) {
+ cast_type = LLVMStructTypeInContext(c, nullptr, 0, false);
} else if (size <= 8) {
- cast_type = LLVMInt64TypeInContext(c);
+ cast_type = LLVMIntTypeInContext(c, cast(unsigned)(size*8));
} else {
unsigned count = cast(unsigned)((size+7)/8);
- cast_type = llvm_array_type(LLVMInt64TypeInContext(c), count);
+
+ 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 {
@@ -1188,17 +1211,27 @@ namespace lbAbiArm64 {
i64 size = lb_sizeof(type);
if (size <= 16) {
LLVMTypeRef cast_type = nullptr;
- if (size <= 1) {
- cast_type = LLVMIntTypeInContext(c, 8);
- } else if (size <= 2) {
- cast_type = LLVMIntTypeInContext(c, 16);
- } else if (size <= 4) {
- cast_type = LLVMIntTypeInContext(c, 32);
+ if (size == 0) {
+ cast_type = LLVMStructTypeInContext(c, nullptr, 0, false);
} else if (size <= 8) {
- cast_type = LLVMIntTypeInContext(c, 64);
+ cast_type = LLVMIntTypeInContext(c, cast(unsigned)(size*8));
} else {
unsigned count = cast(unsigned)((size+7)/8);
- cast_type = llvm_array_type(LLVMIntTypeInContext(c, 64), count);
+
+ 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);
}
args[i] = lb_arg_type_direct(type, cast_type, nullptr, nullptr);
} else {
@@ -1223,20 +1256,22 @@ namespace lbAbiWasm {
enum {MAX_DIRECT_STRUCT_SIZE = 32};
gb_internal LB_ABI_INFO(abi_info) {
+ LLVMContextRef c = m->ctx;
lbFunctionType *ft = gb_alloc_item(permanent_allocator(), lbFunctionType);
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);
- ft->calling_convention = calling_convention;
return ft;
}
gb_internal lbArgType non_struct(LLVMContextRef c, LLVMTypeRef type, bool is_return) {
- if (!is_return && type == LLVMIntTypeInContext(c, 128)) {
- LLVMTypeRef cast_type = LLVMVectorType(LLVMInt64TypeInContext(c), 2);
+ if (type == LLVMIntTypeInContext(c, 128)) {
+ // LLVMTypeRef cast_type = LLVMVectorType(LLVMInt64TypeInContext(c), 2);
+ LLVMTypeRef cast_type = nullptr;
return lb_arg_type_direct(type, cast_type, nullptr, nullptr);
}
-
+
if (!is_return && lb_sizeof(type) > 8) {
return lb_arg_type_indirect(type, nullptr);
}
@@ -1257,7 +1292,7 @@ namespace lbAbiWasm {
case LLVMPointerTypeKind:
return true;
case LLVMIntegerTypeKind:
- return lb_sizeof(type) <= 8;
+ return lb_sizeof(type) <= 16;
}
return false;
}
@@ -1364,14 +1399,14 @@ namespace lbAbiWasm {
} 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)) {
return lb_arg_type_direct(return_type);
- }
-
- i64 sz = lb_sizeof(return_type);
- switch (sz) {
- case 1: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 8), nullptr, nullptr);
- case 2: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 16), nullptr, nullptr);
- case 4: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 32), nullptr, nullptr);
- case 8: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 64), nullptr, nullptr);
+ } else if (ft->calling_convention != ProcCC_CDecl) {
+ i64 sz = lb_sizeof(return_type);
+ switch (sz) {
+ case 1: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 8), nullptr, nullptr);
+ case 2: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 16), nullptr, nullptr);
+ case 4: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 32), nullptr, nullptr);
+ case 8: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 64), nullptr, nullptr);
+ }
}
LB_ABI_MODIFY_RETURN_IF_TUPLE_MACRO();
@@ -1388,6 +1423,7 @@ namespace lbAbiArm32 {
gb_internal lbArgType compute_return_type(LLVMContextRef c, LLVMTypeRef return_type, bool return_is_defined);
gb_internal LB_ABI_INFO(abi_info) {
+ LLVMContextRef c = m->ctx;
lbFunctionType *ft = gb_alloc_item(permanent_allocator(), lbFunctionType);
ft->ctx = c;
ft->args = compute_arg_types(c, arg_types, arg_count, calling_convention);
@@ -1465,8 +1501,256 @@ namespace lbAbiArm32 {
}
};
+namespace lbAbiRiscv64 {
+
+ gb_internal bool is_register(LLVMTypeRef type) {
+ LLVMTypeKind kind = LLVMGetTypeKind(type);
+ switch (kind) {
+ case LLVMIntegerTypeKind:
+ case LLVMHalfTypeKind:
+ case LLVMFloatTypeKind:
+ case LLVMDoubleTypeKind:
+ case LLVMPointerTypeKind:
+ return true;
+ }
+ return false;
+ }
+
+ gb_internal bool is_float(LLVMTypeRef type) {
+ LLVMTypeKind kind = LLVMGetTypeKind(type);
+ switch (kind) {
+ case LLVMHalfTypeKind:
+ case LLVMFloatTypeKind:
+ case LLVMDoubleTypeKind:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ gb_internal lbArgType non_struct(LLVMContextRef c, LLVMTypeRef type) {
+ LLVMAttributeRef attr = nullptr;
+ LLVMTypeRef i1 = LLVMInt1TypeInContext(c);
+ if (type == i1) {
+ attr = lb_create_enum_attribute(c, "zeroext");
+ }
+ return lb_arg_type_direct(type, nullptr, nullptr, attr);
+ }
+
+ gb_internal void flatten(lbModule *m, Array<LLVMTypeRef> *fields, LLVMTypeRef type, bool with_padding) {
+ LLVMTypeKind kind = LLVMGetTypeKind(type);
+ switch (kind) {
+ case LLVMStructTypeKind: {
+ if (LLVMIsPackedStruct(type)) {
+ array_add(fields, type);
+ break;
+ }
+
+ if (!with_padding) {
+ auto field_remapping = map_get(&m->struct_field_remapping, cast(void *)type);
+ if (field_remapping) {
+ auto remap = *field_remapping;
+ for_array(i, remap) {
+ flatten(m, fields, LLVMStructGetTypeAtIndex(type, remap[i]), with_padding);
+ }
+ break;
+ } else {
+ debugf("no field mapping for type: %s\n", LLVMPrintTypeToString(type));
+ }
+ }
+
+ unsigned elem_count = LLVMCountStructElementTypes(type);
+ for (unsigned i = 0; i < elem_count; i += 1) {
+ flatten(m, fields, LLVMStructGetTypeAtIndex(type, i), with_padding);
+ }
+ break;
+ }
+ case LLVMArrayTypeKind: {
+ unsigned len = LLVMGetArrayLength(type);
+ LLVMTypeRef elem = OdinLLVMGetArrayElementType(type);
+ for (unsigned i = 0; i < len; i += 1) {
+ flatten(m, fields, elem, with_padding);
+ }
+ break;
+ }
+ default:
+ array_add(fields, type);
+ }
+ }
+
+ gb_internal lbArgType compute_arg_type(lbModule *m, LLVMTypeRef type, int *gprs_left, int *fprs_left, Type *odin_type) {
+ LLVMContextRef c = m->ctx;
+
+ int xlen = 8; // 8 byte int register size for riscv64.
+
+ // NOTE: we are requiring both of these to be enabled so we can just hard-code 8.
+ // int flen = 0;
+ // if (check_target_feature_is_enabled(str_lit("d"), nullptr)) {
+ // flen = 8; // Double precision floats are enabled.
+ // } else if (check_target_feature_is_enabled(str_lit("f"), nullptr)) {
+ // flen = 4; // Single precision floats are enabled.
+ // }
+ int flen = 8;
+
+ LLVMTypeKind kind = LLVMGetTypeKind(type);
+ i64 size = lb_sizeof(type);
+
+ if (size == 0) {
+ return lb_arg_type_direct(type, LLVMStructTypeInContext(c, nullptr, 0, false), nullptr, nullptr);
+ }
+
+ LLVMTypeRef orig_type = type;
+
+ // Flatten down the type so it is easier to check all the ABI conditions.
+ // Note that we also need to remove all implicit padding fields Odin adds so we keep ABI
+ // compatibility for struct declarations.
+ if (kind == LLVMStructTypeKind && size <= gb_max(2*xlen, 2*flen)) {
+ Array<LLVMTypeRef> fields = array_make<LLVMTypeRef>(temporary_allocator(), 0, LLVMCountStructElementTypes(type));
+ flatten(m, &fields, type, false);
+
+ if (fields.count == 1) {
+ type = fields[0];
+ } else {
+ type = LLVMStructTypeInContext(c, fields.data, cast(unsigned)fields.count, false);
+ }
+
+ kind = LLVMGetTypeKind(type);
+ size = lb_sizeof(type);
+ GB_ASSERT_MSG(size == lb_sizeof(orig_type), "flattened: %s of size %d, original: %s of size %d", LLVMPrintTypeToString(type), size, LLVMPrintTypeToString(orig_type), lb_sizeof(orig_type));
+ }
+
+ if (is_float(type) && size <= flen && *fprs_left >= 1) {
+ *fprs_left -= 1;
+ return non_struct(c, orig_type);
+ }
+
+ if (kind == LLVMStructTypeKind && size <= 2*flen) {
+ unsigned elem_count = LLVMCountStructElementTypes(type);
+ if (elem_count == 2) {
+ LLVMTypeRef ty1 = LLVMStructGetTypeAtIndex(type, 0);
+ i64 ty1s = lb_sizeof(ty1);
+ LLVMTypeRef ty2 = LLVMStructGetTypeAtIndex(type, 1);
+ i64 ty2s = lb_sizeof(ty2);
+
+ if (is_float(ty1) && is_float(ty2) && ty1s <= flen && ty2s <= flen && *fprs_left >= 2) {
+ *fprs_left -= 2;
+ return lb_arg_type_direct(orig_type, type, nullptr, nullptr);
+ }
+
+ if (is_float(ty1) && is_register(ty2) && ty1s <= flen && ty2s <= xlen && *fprs_left >= 1 && *gprs_left >= 1) {
+ *fprs_left -= 1;
+ *gprs_left -= 1;
+ return lb_arg_type_direct(orig_type, type, nullptr, nullptr);
+ }
+
+ if (is_register(ty1) && is_float(ty2) && ty1s <= xlen && ty2s <= flen && *gprs_left >= 1 && *fprs_left >= 1) {
+ *fprs_left -= 1;
+ *gprs_left -= 1;
+ return lb_arg_type_direct(orig_type, type, nullptr, nullptr);
+ }
+ }
+ }
+
+ // At this point all the cases for floating point registers are exhausted, fit it into
+ // integer registers or the stack.
+ // LLVM automatically handles putting args on the stack so we don't check the amount of registers that are left here.
+
+ if (size <= xlen) {
+ *gprs_left -= 1;
+ if (is_register(type)) {
+ return non_struct(c, orig_type);
+ } else {
+ return lb_arg_type_direct(orig_type, LLVMIntTypeInContext(c, cast(unsigned)(size*8)), nullptr, nullptr);
+ }
+ } else if (size <= 2*xlen) {
+ LLVMTypeRef *fields = gb_alloc_array(temporary_allocator(), LLVMTypeRef, 2);
+ fields[0] = LLVMIntTypeInContext(c, cast(unsigned)(xlen*8));
+ fields[1] = LLVMIntTypeInContext(c, cast(unsigned)((size-xlen)*8));
+
+ *gprs_left -= 2;
+ return lb_arg_type_direct(orig_type, LLVMStructTypeInContext(c, fields, 2, false), nullptr, nullptr);
+ } else {
+ return lb_arg_type_indirect(orig_type, nullptr);
+ }
+ }
+
+ gb_internal Array<lbArgType> compute_arg_types(lbModule *m, LLVMTypeRef *arg_types, unsigned arg_count, ProcCallingConvention calling_convention, Type *odin_type, int *gprs, int *fprs) {
+ auto args = array_make<lbArgType>(lb_function_type_args_allocator(), arg_count);
+
+ for (unsigned i = 0; i < arg_count; i++) {
+ LLVMTypeRef type = arg_types[i];
+ args[i] = compute_arg_type(m, type, gprs, fprs, odin_type);
+ }
+
+ return args;
+ }
+
+ gb_internal lbArgType compute_return_type(lbFunctionType *ft, lbModule *m, LLVMTypeRef return_type, bool return_is_defined, bool return_is_tuple, Type *odin_type, int *agprs) {
+ LLVMContextRef c = m->ctx;
+
+ if (!return_is_defined) {
+ return lb_arg_type_direct(LLVMVoidTypeInContext(c));
+ }
+
+ // There are two registers for return types.
+ int gprs = 2;
+ int fprs = 2;
+ lbArgType ret = compute_arg_type(m, return_type, &gprs, &fprs, odin_type);
+
+ // Return didn't fit into the return registers, so caller allocates and it is returned via
+ // an out-pointer.
+ if (ret.kind == lbArg_Indirect) {
+
+ // Transform multiple return into out pointers if possible.
+ if (return_is_tuple) {
+ if (lb_is_type_kind(return_type, LLVMStructTypeKind)) {
+ int field_count = cast(int)LLVMCountStructElementTypes(return_type);
+ if (field_count > 1 && field_count <= *agprs) {
+ ft->original_arg_count = ft->args.count;
+ ft->multiple_return_original_type = return_type;
+
+ for (int 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);
+ *agprs -= 1;
+ }
+ GB_ASSERT(*agprs >= 0);
+
+ // override the return type for the last field
+ LLVMTypeRef new_return_type = LLVMStructGetTypeAtIndex(return_type, field_count-1);
+ return compute_return_type(ft, m, new_return_type, true, false, odin_type, agprs);
+ }
+ }
+ }
+
+ LLVMAttributeRef attr = lb_create_enum_attribute_with_type(c, "sret", ret.type);
+ return lb_arg_type_indirect(ret.type, attr);
+ }
+
+ return ret;
+ }
+
+ gb_internal LB_ABI_INFO(abi_info) {
+ lbFunctionType *ft = gb_alloc_item(permanent_allocator(), lbFunctionType);
+ ft->ctx = m->ctx;
+ ft->calling_convention = calling_convention;
+
+ int gprs = 8;
+ int fprs = 8;
+
+ ft->args = compute_arg_types(m, arg_types, arg_count, calling_convention, original_type, &gprs, &fprs);
+ ft->ret = compute_return_type(ft, m, return_type, return_is_defined, return_is_tuple, original_type, &gprs);
+
+ return ft;
+ }
+}
+
gb_internal LB_ABI_INFO(lb_get_abi_info_internal) {
+ LLVMContextRef c = m->ctx;
+
switch (calling_convention) {
case ProcCC_None:
case ProcCC_InlineAsm:
@@ -1487,33 +1771,35 @@ gb_internal LB_ABI_INFO(lb_get_abi_info_internal) {
}
case ProcCC_Win64:
GB_ASSERT(build_context.metrics.arch == TargetArch_amd64);
- return lbAbiAmd64Win64::abi_info(c, arg_types, arg_count, return_type, return_is_defined, return_is_tuple, calling_convention, original_type);
+ return lbAbiAmd64Win64::abi_info(m, arg_types, arg_count, return_type, return_is_defined, return_is_tuple, calling_convention, original_type);
case ProcCC_SysV:
GB_ASSERT(build_context.metrics.arch == TargetArch_amd64);
- return lbAbiAmd64SysV::abi_info(c, arg_types, arg_count, return_type, return_is_defined, return_is_tuple, calling_convention, original_type);
+ return lbAbiAmd64SysV::abi_info(m, arg_types, arg_count, return_type, return_is_defined, return_is_tuple, calling_convention, original_type);
}
switch (build_context.metrics.arch) {
case TargetArch_amd64:
if (build_context.metrics.os == TargetOs_windows) {
- return lbAbiAmd64Win64::abi_info(c, arg_types, arg_count, return_type, return_is_defined, return_is_tuple, calling_convention, original_type);
+ return lbAbiAmd64Win64::abi_info(m, arg_types, arg_count, return_type, return_is_defined, return_is_tuple, calling_convention, original_type);
} else if (build_context.metrics.abi == TargetABI_Win64) {
- return lbAbiAmd64Win64::abi_info(c, arg_types, arg_count, return_type, return_is_defined, return_is_tuple, calling_convention, original_type);
+ return lbAbiAmd64Win64::abi_info(m, arg_types, arg_count, return_type, return_is_defined, return_is_tuple, calling_convention, original_type);
} else if (build_context.metrics.abi == TargetABI_SysV) {
- return lbAbiAmd64SysV::abi_info(c, arg_types, arg_count, return_type, return_is_defined, return_is_tuple, calling_convention, original_type);
+ return lbAbiAmd64SysV::abi_info(m, arg_types, arg_count, return_type, return_is_defined, return_is_tuple, calling_convention, original_type);
} else {
- return lbAbiAmd64SysV::abi_info(c, arg_types, arg_count, return_type, return_is_defined, return_is_tuple, calling_convention, original_type);
+ return lbAbiAmd64SysV::abi_info(m, arg_types, arg_count, return_type, return_is_defined, return_is_tuple, calling_convention, original_type);
}
case TargetArch_i386:
- return lbAbi386::abi_info(c, arg_types, arg_count, return_type, return_is_defined, return_is_tuple, calling_convention, original_type);
+ return lbAbi386::abi_info(m, arg_types, arg_count, return_type, return_is_defined, return_is_tuple, calling_convention, original_type);
case TargetArch_arm32:
- return lbAbiArm32::abi_info(c, arg_types, arg_count, return_type, return_is_defined, return_is_tuple, calling_convention, original_type);
+ return lbAbiArm32::abi_info(m, arg_types, arg_count, return_type, return_is_defined, return_is_tuple, calling_convention, original_type);
case TargetArch_arm64:
- return lbAbiArm64::abi_info(c, arg_types, arg_count, return_type, return_is_defined, return_is_tuple, calling_convention, original_type);
+ return lbAbiArm64::abi_info(m, arg_types, arg_count, return_type, return_is_defined, return_is_tuple, calling_convention, original_type);
case TargetArch_wasm32:
- return lbAbiWasm::abi_info(c, arg_types, arg_count, return_type, return_is_defined, return_is_tuple, calling_convention, original_type);
+ return lbAbiWasm::abi_info(m, arg_types, arg_count, return_type, return_is_defined, return_is_tuple, calling_convention, original_type);
case TargetArch_wasm64p32:
- return lbAbiWasm::abi_info(c, arg_types, arg_count, return_type, return_is_defined, return_is_tuple, calling_convention, original_type);
+ return lbAbiWasm::abi_info(m, arg_types, arg_count, return_type, return_is_defined, return_is_tuple, calling_convention, original_type);
+ case TargetArch_riscv64:
+ return lbAbiRiscv64::abi_info(m, arg_types, arg_count, return_type, return_is_defined, return_is_tuple, calling_convention, original_type);
}
GB_PANIC("Unsupported ABI");
@@ -1523,7 +1809,7 @@ gb_internal LB_ABI_INFO(lb_get_abi_info_internal) {
gb_internal LB_ABI_INFO(lb_get_abi_info) {
lbFunctionType *ft = lb_get_abi_info_internal(
- c,
+ m,
arg_types, arg_count,
return_type, return_is_defined,
ALLOW_SPLIT_MULTI_RETURNS && return_is_tuple && is_calling_convention_odin(calling_convention),
@@ -1535,7 +1821,7 @@ gb_internal LB_ABI_INFO(lb_get_abi_info) {
// This is to make it consistent when and how it is handled
if (calling_convention == ProcCC_Odin) {
// append the `context` pointer
- lbArgType context_param = lb_arg_type_direct(LLVMPointerType(LLVMInt8TypeInContext(c), 0));
+ lbArgType context_param = lb_arg_type_direct(LLVMPointerType(LLVMInt8TypeInContext(m->ctx), 0));
array_add(&ft->args, context_param);
}
diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp
index 276abc2d4..696ced0df 100644
--- a/src/llvm_backend.cpp
+++ b/src/llvm_backend.cpp
@@ -1,11 +1,14 @@
#define MULTITHREAD_OBJECT_GENERATION 1
+#ifndef MULTITHREAD_OBJECT_GENERATION
+#define MULTITHREAD_OBJECT_GENERATION 0
+#endif
#ifndef USE_SEPARATE_MODULES
#define USE_SEPARATE_MODULES build_context.use_separate_modules
#endif
-#ifndef MULTITHREAD_OBJECT_GENERATION
-#define MULTITHREAD_OBJECT_GENERATION 0
+#ifndef LLVM_IGNORE_VERIFICATION
+#define LLVM_IGNORE_VERIFICATION 0
#endif
@@ -21,6 +24,79 @@
#include "llvm_backend_stmt.cpp"
#include "llvm_backend_proc.cpp"
+String get_default_microarchitecture() {
+ String default_march = str_lit("generic");
+ if (build_context.metrics.arch == TargetArch_amd64) {
+ // NOTE(bill): x86-64-v2 is more than enough for everyone
+ //
+ // x86-64: CMOV, CMPXCHG8B, FPU, FXSR, MMX, FXSR, SCE, SSE, SSE2
+ // x86-64-v2: (close to Nehalem) CMPXCHG16B, LAHF-SAHF, POPCNT, SSE3, SSE4.1, SSE4.2, SSSE3
+ // x86-64-v3: (close to Haswell) AVX, AVX2, BMI1, BMI2, F16C, FMA, LZCNT, MOVBE, XSAVE
+ // x86-64-v4: AVX512F, AVX512BW, AVX512CD, AVX512DQ, AVX512VL
+ if (ODIN_LLVM_MINIMUM_VERSION_12) {
+ if (build_context.metrics.os == TargetOs_freestanding) {
+ default_march = str_lit("x86-64");
+ } else {
+ default_march = str_lit("x86-64-v2");
+ }
+ }
+ } else if (build_context.metrics.arch == TargetArch_riscv64) {
+ default_march = str_lit("generic-rv64");
+ }
+
+ return default_march;
+}
+
+String get_final_microarchitecture() {
+ BuildContext *bc = &build_context;
+
+ String microarch = bc->microarch;
+ if (microarch.len == 0) {
+ microarch = get_default_microarchitecture();
+ } else if (microarch == str_lit("native")) {
+ microarch = make_string_c(LLVMGetHostCPUName());
+ }
+ return microarch;
+}
+
+gb_internal String get_default_features() {
+ BuildContext *bc = &build_context;
+
+ int off = 0;
+ for (int i = 0; i < bc->metrics.arch; i += 1) {
+ off += target_microarch_counts[i];
+ }
+
+ String microarch = get_final_microarchitecture();
+
+ // NOTE(laytan): for riscv64 to work properly with Odin, we need to enforce some features.
+ // and we also overwrite the generic target to include more features so we don't default to
+ // a potato feature set.
+ if (bc->metrics.arch == TargetArch_riscv64) {
+ if (microarch == str_lit("generic-rv64")) {
+ // This is what clang does by default (on -march=rv64gc for General Computing), seems good to also default to.
+ String features = str_lit("64bit,a,c,d,f,m,relax,zicsr,zifencei");
+
+ // Update the features string so LLVM uses it later.
+ if (bc->target_features_string.len > 0) {
+ bc->target_features_string = concatenate3_strings(permanent_allocator(), features, str_lit(","), bc->target_features_string);
+ } else {
+ bc->target_features_string = features;
+ }
+
+ return features;
+ }
+ }
+
+ for (int i = off; i < off+target_microarch_counts[bc->metrics.arch]; i += 1) {
+ if (microarch_features_list[i].microarch == microarch) {
+ return microarch_features_list[i].features;
+ }
+ }
+
+ GB_PANIC("unknown microarch: %.*s", LIT(microarch));
+ return {};
+}
gb_internal void lb_add_foreign_library_path(lbModule *m, Entity *e) {
if (e == nullptr) {
@@ -82,19 +158,28 @@ gb_internal void lb_set_entity_from_other_modules_linkage_correctly(lbModule *ot
if (other_module == nullptr) {
return;
}
- char const *cname = alloc_cstring(temporary_allocator(), name);
+ char const *cname = alloc_cstring(permanent_allocator(), name);
+ mpsc_enqueue(&other_module->gen->entities_to_correct_linkage, lbEntityCorrection{other_module, e, cname});
+}
- LLVMValueRef other_global = nullptr;
- if (e->kind == Entity_Variable) {
- other_global = LLVMGetNamedGlobal(other_module->mod, cname);
- } else if (e->kind == Entity_Procedure) {
- other_global = LLVMGetNamedFunction(other_module->mod, cname);
- }
- if (other_global) {
- LLVMSetLinkage(other_global, LLVMExternalLinkage);
+gb_internal void lb_correct_entity_linkage(lbGenerator *gen) {
+ for (lbEntityCorrection ec = {}; mpsc_dequeue(&gen->entities_to_correct_linkage, &ec); /**/) {
+ LLVMValueRef other_global = nullptr;
+ if (ec.e->kind == Entity_Variable) {
+ other_global = LLVMGetNamedGlobal(ec.other_module->mod, ec.cname);
+ if (other_global) {
+ LLVMSetLinkage(other_global, LLVMWeakAnyLinkage);
+ }
+ } else if (ec.e->kind == Entity_Procedure) {
+ other_global = LLVMGetNamedFunction(ec.other_module->mod, ec.cname);
+ if (other_global) {
+ LLVMSetLinkage(other_global, LLVMWeakAnyLinkage);
+ }
+ }
}
}
+
gb_internal void lb_emit_init_context(lbProcedure *p, lbAddr addr) {
TEMPORARY_ALLOCATOR_GUARD();
@@ -315,7 +400,7 @@ gb_internal void lb_add_callsite_force_inline(lbProcedure *p, lbValue ret_value)
gb_internal lbValue lb_hasher_proc_for_type(lbModule *m, Type *type) {
type = core_type(type);
- GB_ASSERT_MSG(is_type_valid_for_keys(type), "%s", type_to_string(type));
+ GB_ASSERT_MSG(is_type_comparable(type), "%s", type_to_string(type));
Type *pt = alloc_type_pointer(type);
@@ -914,14 +999,16 @@ gb_internal lbValue lb_const_hash(lbModule *m, lbValue key, Type *key_type) {
gb_internal lbValue lb_gen_map_key_hash(lbProcedure *p, lbValue const &map_ptr, lbValue key, lbValue *key_ptr_) {
TEMPORARY_ALLOCATOR_GUARD();
- lbValue key_ptr = lb_address_from_load_or_generate_local(p, key);
+ Type* key_type = base_type(type_deref(map_ptr.type))->Map.key;
+
+ lbValue real_key = lb_emit_conv(p, key, key_type);
+
+ lbValue key_ptr = lb_address_from_load_or_generate_local(p, real_key);
key_ptr = lb_emit_conv(p, key_ptr, t_rawptr);
if (key_ptr_) *key_ptr_ = key_ptr;
- Type* key_type = base_type(type_deref(map_ptr.type))->Map.key;
-
- lbValue hashed_key = lb_const_hash(p->module, key, key_type);
+ lbValue hashed_key = lb_const_hash(p->module, real_key, key_type);
if (hashed_key.value == nullptr) {
lbValue hasher = lb_hasher_proc_for_type(p->module, key_type);
@@ -1009,8 +1096,6 @@ gb_internal void lb_internal_dynamic_map_set(lbProcedure *p, lbValue const &map_
}
gb_internal lbValue lb_dynamic_map_reserve(lbProcedure *p, lbValue const &map_ptr, isize const capacity, TokenPos const &pos) {
- GB_ASSERT(!build_context.no_dynamic_literals);
-
TEMPORARY_ALLOCATOR_GUARD();
String proc_name = {};
@@ -1034,36 +1119,6 @@ struct lbGlobalVariable {
bool is_initialized;
};
-gb_internal lbProcedure *lb_create_startup_type_info(lbModule *m) {
- if (build_context.no_rtti) {
- return nullptr;
- }
- Type *proc_type = alloc_type_proc(nullptr, nullptr, 0, nullptr, 0, false, ProcCC_CDecl);
-
- lbProcedure *p = lb_create_dummy_procedure(m, str_lit(LB_STARTUP_TYPE_INFO_PROC_NAME), proc_type);
- p->is_startup = true;
- LLVMSetLinkage(p->value, LLVMInternalLinkage);
-
- lb_add_attribute_to_proc(m, p->value, "nounwind");
- if (!LB_USE_GIANT_PACKED_STRUCT) {
- lb_add_attribute_to_proc(m, p->value, "optnone");
- lb_add_attribute_to_proc(m, p->value, "noinline");
- }
-
- lb_begin_procedure_body(p);
-
- lb_setup_type_info_data(p);
-
- lb_end_procedure_body(p);
-
- if (!m->debug_builder && LLVMVerifyFunction(p->value, LLVMReturnStatusAction)) {
- gb_printf_err("LLVM CODE GEN FAILED FOR PROCEDURE: %s\n", "main");
- LLVMDumpValue(p->value);
- gb_printf_err("\n\n\n\n");
- LLVMVerifyFunction(p->value, LLVMAbortProcessAction);
- }
- return p;
-}
gb_internal lbProcedure *lb_create_objc_names(lbModule *main_module) {
if (build_context.metrics.os != TargetOs_darwin) {
@@ -1105,7 +1160,54 @@ gb_internal void lb_finalize_objc_names(lbProcedure *p) {
lb_end_procedure_body(p);
}
-gb_internal lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProcedure *startup_type_info, lbProcedure *objc_names, Array<lbGlobalVariable> &global_variables) { // Startup Runtime
+gb_internal void lb_verify_function(lbModule *m, lbProcedure *p, bool dump_ll=false) {
+ if (LLVM_IGNORE_VERIFICATION) {
+ return;
+ }
+
+ if (!m->debug_builder && LLVMVerifyFunction(p->value, LLVMReturnStatusAction)) {
+ char *llvm_error = nullptr;
+
+ gb_printf_err("LLVM CODE GEN FAILED FOR PROCEDURE: %.*s\n", LIT(p->name));
+ LLVMDumpValue(p->value);
+ gb_printf_err("\n");
+ if (dump_ll) {
+ gb_printf_err("\n\n\n");
+ String filepath_ll = lb_filepath_ll_for_module(m);
+ if (LLVMPrintModuleToFile(m->mod, cast(char const *)filepath_ll.text, &llvm_error)) {
+ gb_printf_err("LLVM Error: %s\n", llvm_error);
+ }
+ }
+ LLVMVerifyFunction(p->value, LLVMPrintMessageAction);
+ exit_with_errors();
+ }
+}
+
+gb_internal WORKER_TASK_PROC(lb_llvm_module_verification_worker_proc) {
+ char *llvm_error = nullptr;
+ defer (LLVMDisposeMessage(llvm_error));
+ lbModule *m = cast(lbModule *)data;
+
+ if (LLVMVerifyModule(m->mod, LLVMReturnStatusAction, &llvm_error)) {
+ gb_printf_err("LLVM Error:\n%s\n", llvm_error);
+ if (build_context.keep_temp_files) {
+ TIME_SECTION("LLVM Print Module to File");
+ String filepath_ll = lb_filepath_ll_for_module(m);
+ if (LLVMPrintModuleToFile(m->mod, cast(char const *)filepath_ll.text, &llvm_error)) {
+ gb_printf_err("LLVM Error: %s\n", llvm_error);
+ exit_with_errors();
+ return false;
+ }
+ }
+ exit_with_errors();
+ return 1;
+ }
+ return 0;
+}
+
+
+
+gb_internal lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProcedure *objc_names, Array<lbGlobalVariable> &global_variables) { // Startup Runtime
Type *proc_type = alloc_type_proc(nullptr, nullptr, 0, nullptr, 0, false, ProcCC_Odin);
lbProcedure *p = lb_create_dummy_procedure(main_module, str_lit(LB_STARTUP_RUNTIME_PROC_NAME), proc_type);
@@ -1115,9 +1217,7 @@ gb_internal lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProc
lb_begin_procedure_body(p);
- if (startup_type_info) {
- LLVMBuildCall2(p->builder, lb_type_internal_for_procedures_raw(main_module, startup_type_info->type), startup_type_info->value, nullptr, 0, "");
- }
+ lb_setup_type_info_data(main_module);
if (objc_names) {
LLVMBuildCall2(p->builder, lb_type_internal_for_procedures_raw(main_module, objc_names->type), objc_names->value, nullptr, 0, "");
@@ -1142,6 +1242,10 @@ gb_internal lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProc
if (is_type_untyped_nil(init.type)) {
LLVMSetInitializer(var.var.value, LLVMConstNull(global_type));
var.is_initialized = true;
+
+ if (e->Variable.is_rodata) {
+ LLVMSetGlobalConstant(var.var.value, true);
+ }
continue;
}
GB_PANIC("Invalid init value, got %s", expr_to_string(init_expr));
@@ -1156,6 +1260,10 @@ gb_internal lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProc
}
LLVMSetInitializer(var.var.value, init.value);
var.is_initialized = true;
+
+ if (e->Variable.is_rodata) {
+ LLVMSetGlobalConstant(var.var.value, true);
+ }
continue;
}
} else {
@@ -1177,7 +1285,7 @@ gb_internal lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProc
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(main_module, var_type));
+ 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);
@@ -1188,8 +1296,9 @@ gb_internal lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProc
var.is_initialized = true;
}
+
+
}
-
CheckerInfo *info = main_module->gen->info;
for (Entity *e : info->init_procedures) {
@@ -1200,13 +1309,7 @@ gb_internal lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProc
lb_end_procedure_body(p);
- if (!main_module->debug_builder && LLVMVerifyFunction(p->value, LLVMReturnStatusAction)) {
- gb_printf_err("LLVM CODE GEN FAILED FOR PROCEDURE: %s\n", "main");
- LLVMDumpValue(p->value);
- gb_printf_err("\n\n\n\n");
- LLVMVerifyFunction(p->value, LLVMAbortProcessAction);
- }
-
+ lb_verify_function(main_module, p);
return p;
}
@@ -1229,35 +1332,43 @@ gb_internal lbProcedure *lb_create_cleanup_runtime(lbModule *main_module) { // C
lb_end_procedure_body(p);
- if (!main_module->debug_builder && LLVMVerifyFunction(p->value, LLVMReturnStatusAction)) {
- gb_printf_err("LLVM CODE GEN FAILED FOR PROCEDURE: %s\n", "main");
- LLVMDumpValue(p->value);
- gb_printf_err("\n\n\n\n");
- LLVMVerifyFunction(p->value, LLVMAbortProcessAction);
- }
-
+ lb_verify_function(main_module, p);
return p;
}
gb_internal WORKER_TASK_PROC(lb_generate_procedures_and_types_per_module) {
lbModule *m = cast(lbModule *)data;
- for (Entity *e : m->global_procedures_and_types_to_create) {
- if (e->kind == Entity_TypeName) {
- (void)lb_get_entity_name(m, e);
- lb_type(m, e->type);
- }
+ for (Entity *e : m->global_types_to_create) {
+ (void)lb_get_entity_name(m, e);
+ (void)lb_type(m, e->type);
}
- for (Entity *e : m->global_procedures_and_types_to_create) {
- if (e->kind == Entity_Procedure) {
- (void)lb_get_entity_name(m, e);
- array_add(&m->procedures_to_generate, lb_create_procedure(m, e));
- }
+ for (Entity *e : m->global_procedures_to_create) {
+ (void)lb_get_entity_name(m, e);
+ array_add(&m->procedures_to_generate, lb_create_procedure(m, e));
}
return 0;
}
+gb_internal GB_COMPARE_PROC(llvm_global_entity_cmp) {
+ Entity *x = *cast(Entity **)a;
+ Entity *y = *cast(Entity **)b;
+ if (x == y) {
+ return 0;
+ }
+ if (x->kind != y->kind) {
+ return cast(i32)(x->kind - y->kind);
+ }
+
+ i32 cmp = 0;
+ cmp = token_pos_cmp(x->token.pos, y->token.pos);
+ if (!cmp) {
+ return cmp;
+ }
+ return cmp;
+}
+
gb_internal void lb_create_global_procedures_and_types(lbGenerator *gen, CheckerInfo *info, bool do_threading) {
auto *min_dep_set = &info->minimum_dependency_set;
@@ -1306,17 +1417,32 @@ gb_internal void lb_create_global_procedures_and_types(lbGenerator *gen, Checker
if (USE_SEPARATE_MODULES) {
m = lb_module_of_entity(gen, e);
}
+ GB_ASSERT(m != nullptr);
- array_add(&m->global_procedures_and_types_to_create, e);
+ if (e->kind == Entity_Procedure) {
+ array_add(&m->global_procedures_to_create, e);
+ } else if (e->kind == Entity_TypeName) {
+ array_add(&m->global_types_to_create, e);
+ }
}
for (auto const &entry : gen->modules) {
lbModule *m = entry.value;
- if (do_threading) {
+ array_sort(m->global_types_to_create, llvm_global_entity_cmp);
+ array_sort(m->global_procedures_to_create, llvm_global_entity_cmp);
+ }
+
+ if (do_threading) {
+ for (auto const &entry : gen->modules) {
+ lbModule *m = entry.value;
thread_pool_add_task(lb_generate_procedures_and_types_per_module, m);
- } else {
+ }
+ } else {
+ for (auto const &entry : gen->modules) {
+ lbModule *m = entry.value;
lb_generate_procedures_and_types_per_module(m);
}
+
}
thread_pool_wait();
@@ -1337,7 +1463,9 @@ gb_internal bool lb_is_module_empty(lbModule *m) {
}
for (auto g = LLVMGetFirstGlobal(m->mod); g != nullptr; g = LLVMGetNextGlobal(g)) {
- if (LLVMGetLinkage(g) == LLVMExternalLinkage) {
+ LLVMLinkage linkage = LLVMGetLinkage(g);
+ if (linkage == LLVMExternalLinkage ||
+ linkage == LLVMWeakAnyLinkage) {
continue;
}
if (!LLVMIsExternallyInitialized(g)) {
@@ -1363,7 +1491,7 @@ gb_internal WORKER_TASK_PROC(lb_llvm_emit_worker_proc) {
if (LLVMTargetMachineEmitToFile(wd->target_machine, wd->m->mod, cast(char *)wd->filepath_obj.text, wd->code_gen_file_type, &llvm_error)) {
gb_printf_err("LLVM Error: %s\n", llvm_error);
- gb_exit(1);
+ exit_with_errors();
}
debugf("Generated File: %.*s\n", LIT(wd->filepath_obj));
return 0;
@@ -1391,10 +1519,6 @@ gb_internal WORKER_TASK_PROC(lb_llvm_function_pass_per_module) {
lb_populate_function_pass_manager(m, m->function_pass_managers[lbFunctionPassManager_default], false, build_context.optimization_level);
lb_populate_function_pass_manager(m, m->function_pass_managers[lbFunctionPassManager_default_without_memcpy], true, build_context.optimization_level);
lb_populate_function_pass_manager_specific(m, m->function_pass_managers[lbFunctionPassManager_none], -1);
- lb_populate_function_pass_manager_specific(m, m->function_pass_managers[lbFunctionPassManager_minimal], 0);
- lb_populate_function_pass_manager_specific(m, m->function_pass_managers[lbFunctionPassManager_size], 1);
- lb_populate_function_pass_manager_specific(m, m->function_pass_managers[lbFunctionPassManager_speed], 2);
- lb_populate_function_pass_manager_specific(m, m->function_pass_managers[lbFunctionPassManager_aggressive], 3);
for (i32 i = 0; i < lbFunctionPassManager_COUNT; i++) {
LLVMFinalizeFunctionPassManager(m->function_pass_managers[i]);
@@ -1402,7 +1526,6 @@ gb_internal WORKER_TASK_PROC(lb_llvm_function_pass_per_module) {
}
if (m == &m->gen->default_module) {
- lb_llvm_function_pass_per_function_internal(m, m->gen->startup_type_info);
lb_llvm_function_pass_per_function_internal(m, m->gen->startup_runtime);
lb_llvm_function_pass_per_function_internal(m, m->gen->cleanup_runtime);
lb_llvm_function_pass_per_function_internal(m, m->gen->objc_names);
@@ -1419,15 +1542,12 @@ gb_internal WORKER_TASK_PROC(lb_llvm_function_pass_per_module) {
if (p->entity && p->entity->kind == Entity_Procedure) {
switch (p->entity->Procedure.optimization_mode) {
case ProcedureOptimizationMode_None:
- case ProcedureOptimizationMode_Minimal:
- pass_manager_kind = lbFunctionPassManager_minimal;
- break;
- case ProcedureOptimizationMode_Size:
- pass_manager_kind = lbFunctionPassManager_size;
- lb_add_attribute_to_proc(p->module, p->value, "optsize");
+ pass_manager_kind = lbFunctionPassManager_none;
+ GB_ASSERT(lb_proc_has_attribute(p->module, p->value, "optnone"));
+ GB_ASSERT(lb_proc_has_attribute(p->module, p->value, "noinline"));
break;
- case ProcedureOptimizationMode_Speed:
- pass_manager_kind = lbFunctionPassManager_speed;
+ case ProcedureOptimizationMode_FavorSize:
+ GB_ASSERT(lb_proc_has_attribute(p->module, p->value, "optsize"));
break;
}
}
@@ -1478,13 +1598,12 @@ gb_internal WORKER_TASK_PROC(lb_llvm_module_pass_worker_proc) {
auto passes = array_make<char const *>(heap_allocator(), 0, 64);
defer (array_free(&passes));
-
-
LLVMPassBuilderOptionsRef pb_options = LLVMCreatePassBuilderOptions();
defer (LLVMDisposePassBuilderOptions(pb_options));
switch (build_context.optimization_level) {
case -1:
+ array_add(&passes, "function(annotation-remarks)");
break;
case 0:
array_add(&passes, "always-inline");
@@ -1493,6 +1612,7 @@ gb_internal WORKER_TASK_PROC(lb_llvm_module_pass_worker_proc) {
case 1:
// default<Os>
// Passes removed: coro, openmp, sroa
+#if LLVM_VERSION_MAJOR == 17
array_add(&passes, u8R"(
annotation2metadata,
forceattrs,
@@ -1508,13 +1628,14 @@ 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(
+ 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,
@@ -1615,10 +1736,142 @@ function(
),
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,
@@ -1743,11 +1996,144 @@ function(
),
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,
@@ -1875,6 +2261,135 @@ function(
),
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;
}
@@ -1935,7 +2450,7 @@ verify
gb_printf_err("LLVM Error: %s\n", llvm_error);
}
}
- gb_exit(1);
+ exit_with_errors();
return 1;
}
#endif
@@ -1954,16 +2469,19 @@ gb_internal WORKER_TASK_PROC(lb_generate_procedures_worker_proc) {
}
gb_internal void lb_generate_procedures(lbGenerator *gen, bool do_threading) {
- for (auto const &entry : gen->modules) {
- lbModule *m = entry.value;
- if (do_threading) {
+ if (do_threading) {
+ for (auto const &entry : gen->modules) {
+ lbModule *m = entry.value;
thread_pool_add_task(lb_generate_procedures_worker_proc, m);
- } else {
+ }
+
+ thread_pool_wait();
+ } else {
+ for (auto const &entry : gen->modules) {
+ lbModule *m = entry.value;
lb_generate_procedures_worker_proc(m);
}
}
-
- thread_pool_wait();
}
gb_internal WORKER_TASK_PROC(lb_generate_missing_procedures_to_check_worker_proc) {
@@ -1977,61 +2495,71 @@ gb_internal WORKER_TASK_PROC(lb_generate_missing_procedures_to_check_worker_proc
}
gb_internal void lb_generate_missing_procedures(lbGenerator *gen, bool do_threading) {
- for (auto const &entry : gen->modules) {
- lbModule *m = entry.value;
- // NOTE(bill): procedures may be added during generation
- if (do_threading) {
+ if (do_threading) {
+ for (auto const &entry : gen->modules) {
+ lbModule *m = entry.value;
+ // NOTE(bill): procedures may be added during generation
thread_pool_add_task(lb_generate_missing_procedures_to_check_worker_proc, m);
- } else {
+ }
+ thread_pool_wait();
+ } else {
+ for (auto const &entry : gen->modules) {
+ lbModule *m = entry.value;
+ // NOTE(bill): procedures may be added during generation
lb_generate_missing_procedures_to_check_worker_proc(m);
}
}
-
- thread_pool_wait();
}
gb_internal void lb_debug_info_complete_types_and_finalize(lbGenerator *gen) {
for (auto const &entry : gen->modules) {
lbModule *m = entry.value;
if (m->debug_builder != nullptr) {
- lb_debug_complete_types(m);
- }
- }
- for (auto const &entry : gen->modules) {
- lbModule *m = entry.value;
- if (m->debug_builder != nullptr) {
LLVMDIBuilderFinalize(m->debug_builder);
}
}
}
gb_internal void lb_llvm_function_passes(lbGenerator *gen, bool do_threading) {
- for (auto const &entry : gen->modules) {
- lbModule *m = entry.value;
- if (do_threading) {
+ if (do_threading) {
+ for (auto const &entry : gen->modules) {
+ lbModule *m = entry.value;
thread_pool_add_task(lb_llvm_function_pass_per_module, m);
- } else {
+ }
+ thread_pool_wait();
+ } else {
+ for (auto const &entry : gen->modules) {
+ lbModule *m = entry.value;
lb_llvm_function_pass_per_module(m);
}
}
- thread_pool_wait();
}
gb_internal void lb_llvm_module_passes(lbGenerator *gen, bool do_threading) {
- for (auto const &entry : gen->modules) {
- lbModule *m = entry.value;
- auto wd = gb_alloc_item(permanent_allocator(), lbLLVMModulePassWorkerData);
- wd->m = m;
- wd->target_machine = m->target_machine;
+ if (do_threading) {
+ for (auto const &entry : gen->modules) {
+ lbModule *m = entry.value;
+ auto wd = gb_alloc_item(permanent_allocator(), lbLLVMModulePassWorkerData);
+ wd->m = m;
+ wd->target_machine = m->target_machine;
- if (do_threading) {
- thread_pool_add_task(lb_llvm_module_pass_worker_proc, wd);
- } else {
+ if (do_threading) {
+ thread_pool_add_task(lb_llvm_module_pass_worker_proc, wd);
+ } else {
+ lb_llvm_module_pass_worker_proc(wd);
+ }
+ }
+ thread_pool_wait();
+ } else {
+ for (auto const &entry : gen->modules) {
+ lbModule *m = entry.value;
+ auto wd = gb_alloc_item(permanent_allocator(), lbLLVMModulePassWorkerData);
+ wd->m = m;
+ wd->target_machine = m->target_machine;
lb_llvm_module_pass_worker_proc(wd);
}
}
- thread_pool_wait();
}
gb_internal String lb_filepath_ll_for_module(lbModule *m) {
@@ -2055,20 +2583,37 @@ gb_internal String lb_filepath_ll_for_module(lbModule *m) {
return path;
}
+
gb_internal String lb_filepath_obj_for_module(lbModule *m) {
- String path = concatenate3_strings(permanent_allocator(),
- build_context.build_paths[BuildPath_Output].basename,
- STR_LIT("/"),
- build_context.build_paths[BuildPath_Output].name
- );
+ String basename = build_context.build_paths[BuildPath_Output].basename;
+ String name = build_context.build_paths[BuildPath_Output].name;
+
+ bool use_temporary_directory = false;
+ if (USE_SEPARATE_MODULES && build_context.build_mode == BuildMode_Executable) {
+ // NOTE(bill): use a temporary directory
+ String dir = temporary_directory(permanent_allocator());
+ if (dir.len != 0) {
+ basename = dir;
+ use_temporary_directory = true;
+ }
+ }
+
+ gbString path = gb_string_make_length(heap_allocator(), basename.text, basename.len);
+ path = gb_string_appendc(path, "/");
+ path = gb_string_append_length(path, name.text, name.len);
if (m->file) {
char buf[32] = {};
isize n = gb_snprintf(buf, gb_size_of(buf), "-%u", m->file->id);
String suffix = make_string((u8 *)buf, n-1);
- path = concatenate_strings(permanent_allocator(), path, suffix);
+ path = gb_string_append_length(path, suffix.text, suffix.len);
} else if (m->pkg) {
- path = concatenate3_strings(permanent_allocator(), path, STR_LIT("-"), m->pkg->name);
+ path = gb_string_appendc(path, "-");
+ path = gb_string_append_length(path, m->pkg->name.text, m->pkg->name.len);
+ }
+
+ if (use_temporary_directory) {
+ path = gb_string_append_fmt(path, "-%p", m);
}
String ext = {};
@@ -2106,43 +2651,33 @@ gb_internal String lb_filepath_obj_for_module(lbModule *m) {
}
}
- return concatenate_strings(permanent_allocator(), path, ext);
-}
+ path = gb_string_append_length(path, ext.text, ext.len);
+
+ return make_string(cast(u8 *)path, gb_string_length(path));
-gb_internal WORKER_TASK_PROC(lb_llvm_module_verification_worker_proc) {
- char *llvm_error = nullptr;
- defer (LLVMDisposeMessage(llvm_error));
- lbModule *m = cast(lbModule *)data;
- if (LLVMVerifyModule(m->mod, LLVMReturnStatusAction, &llvm_error)) {
- gb_printf_err("LLVM Error:\n%s\n", llvm_error);
- if (build_context.keep_temp_files) {
- TIME_SECTION("LLVM Print Module to File");
- String filepath_ll = lb_filepath_ll_for_module(m);
- if (LLVMPrintModuleToFile(m->mod, cast(char const *)filepath_ll.text, &llvm_error)) {
- gb_printf_err("LLVM Error: %s\n", llvm_error);
- gb_exit(1);
- return false;
- }
- }
- gb_exit(1);
- return 1;
- }
- return 0;
}
gb_internal bool lb_llvm_module_verification(lbGenerator *gen, bool do_threading) {
- for (auto const &entry : gen->modules) {
- lbModule *m = entry.value;
- if (do_threading) {
+ if (LLVM_IGNORE_VERIFICATION) {
+ return true;
+ }
+
+ if (do_threading) {
+ for (auto const &entry : gen->modules) {
+ lbModule *m = entry.value;
thread_pool_add_task(lb_llvm_module_verification_worker_proc, m);
- } else {
+ }
+ thread_pool_wait();
+
+ } else {
+ for (auto const &entry : gen->modules) {
+ lbModule *m = entry.value;
if (lb_llvm_module_verification_worker_proc(m)) {
return false;
}
}
}
- thread_pool_wait();
return true;
}
@@ -2178,7 +2713,6 @@ gb_internal bool lb_llvm_object_generation(lbGenerator *gen, bool do_threading)
String filepath_ll = lb_filepath_ll_for_module(m);
String filepath_obj = lb_filepath_obj_for_module(m);
- // gb_printf_err("%.*s\n", LIT(filepath_obj));
array_add(&gen->output_object_paths, filepath_obj);
array_add(&gen->output_temp_paths, filepath_ll);
@@ -2209,7 +2743,7 @@ gb_internal bool lb_llvm_object_generation(lbGenerator *gen, bool do_threading)
if (LLVMTargetMachineEmitToFile(m->target_machine, m->mod, cast(char *)filepath_obj.text, code_gen_file_type, &llvm_error)) {
gb_printf_err("LLVM Error: %s\n", llvm_error);
- gb_exit(1);
+ exit_with_errors();
return false;
}
debugf("Generated File: %.*s\n", LIT(filepath_obj));
@@ -2363,12 +2897,7 @@ gb_internal lbProcedure *lb_create_main_procedure(lbModule *m, lbProcedure *star
}
- if (!m->debug_builder && LLVMVerifyFunction(p->value, LLVMReturnStatusAction)) {
- gb_printf_err("LLVM CODE GEN FAILED FOR PROCEDURE: %s\n", "main");
- LLVMDumpValue(p->value);
- gb_printf_err("\n\n\n\n");
- LLVMVerifyFunction(p->value, LLVMAbortProcessAction);
- }
+ lb_verify_function(m, p);
lb_run_function_pass_manager(default_function_pass_manager, p, lbFunctionPassManager_default);
return p;
@@ -2389,28 +2918,11 @@ gb_internal void lb_generate_procedure(lbModule *m, lbProcedure *p) {
lb_end_procedure(p);
// Add Flags
- if (p->body != nullptr) {
- if (p->name == "memcpy" || p->name == "memmove" ||
- p->name == "runtime.mem_copy" || p->name == "mem_copy_non_overlapping" ||
- string_starts_with(p->name, str_lit("llvm.memcpy")) ||
- string_starts_with(p->name, str_lit("llvm.memmove"))) {
- p->flags |= lbProcedureFlag_WithoutMemcpyPass;
- }
+ if (p->entity && p->entity->kind == Entity_Procedure && p->entity->Procedure.is_memcpy_like) {
+ p->flags |= lbProcedureFlag_WithoutMemcpyPass;
}
- if (!m->debug_builder && LLVMVerifyFunction(p->value, LLVMReturnStatusAction)) {
- char *llvm_error = nullptr;
-
- gb_printf_err("LLVM CODE GEN FAILED FOR PROCEDURE: %.*s\n", LIT(p->name));
- LLVMDumpValue(p->value);
- gb_printf_err("\n\n\n\n");
- String filepath_ll = lb_filepath_ll_for_module(m);
- if (LLVMPrintModuleToFile(m->mod, cast(char const *)filepath_ll.text, &llvm_error)) {
- gb_printf_err("LLVM Error: %s\n", llvm_error);
- }
- LLVMVerifyFunction(p->value, LLVMPrintMessageAction);
- gb_exit(1);
- }
+ lb_verify_function(m, p, true);
}
@@ -2486,42 +2998,29 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
LLVMCodeModel code_mode = LLVMCodeModelDefault;
if (is_arch_wasm()) {
code_mode = LLVMCodeModelJITDefault;
- } else if (build_context.metrics.os == TargetOs_freestanding) {
+ } else if (is_arch_x86() && build_context.metrics.os == TargetOs_freestanding) {
code_mode = LLVMCodeModelKernel;
}
- char const *host_cpu_name = LLVMGetHostCPUName();
- char const *llvm_cpu = "generic";
- char const *llvm_features = "";
- if (build_context.microarch.len != 0) {
- if (build_context.microarch == "native") {
- llvm_cpu = host_cpu_name;
- } else {
- llvm_cpu = alloc_cstring(permanent_allocator(), build_context.microarch);
- }
- if (gb_strcmp(llvm_cpu, host_cpu_name) == 0) {
- llvm_features = LLVMGetHostCPUFeatures();
- }
- } else if (build_context.metrics.arch == TargetArch_amd64) {
- // NOTE(bill): x86-64-v2 is more than enough for everyone
- //
- // x86-64: CMOV, CMPXCHG8B, FPU, FXSR, MMX, FXSR, SCE, SSE, SSE2
- // x86-64-v2: (close to Nehalem) CMPXCHG16B, LAHF-SAHF, POPCNT, SSE3, SSE4.1, SSE4.2, SSSE3
- // x86-64-v3: (close to Haswell) AVX, AVX2, BMI1, BMI2, F16C, FMA, LZCNT, MOVBE, XSAVE
- // x86-64-v4: AVX512F, AVX512BW, AVX512CD, AVX512DQ, AVX512VL
- if (ODIN_LLVM_MINIMUM_VERSION_12) {
- if (build_context.metrics.os == TargetOs_freestanding) {
- llvm_cpu = "x86-64";
- } else {
- llvm_cpu = "x86-64-v2";
- }
+ String llvm_cpu = get_final_microarchitecture();
+
+ gbString llvm_features = gb_string_make(temporary_allocator(), "");
+ String_Iterator it = {build_context.target_features_string, 0};
+ bool first = true;
+ for (;;) {
+ String str = string_split_iterator(&it, ',');
+ if (str == "") break;
+ if (!first) {
+ llvm_features = gb_string_appendc(llvm_features, ",");
}
- }
+ first = false;
- if (build_context.target_features_set.entries.count != 0) {
- llvm_features = target_features_set_to_cstring(permanent_allocator(), false);
+ llvm_features = gb_string_appendc(llvm_features, "+");
+ llvm_features = gb_string_append_length(llvm_features, str.text, str.len);
}
+ debugf("CPU: %.*s, Features: %s\n", LIT(llvm_cpu), llvm_features);
+
// GB_ASSERT_MSG(LLVMTargetHasAsmBackend(target));
LLVMCodeGenOptLevel code_gen_level = LLVMCodeGenLevelNone;
@@ -2548,10 +3047,16 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
switch (build_context.reloc_mode) {
case RelocMode_Default:
- if (build_context.metrics.os == TargetOs_openbsd) {
- // Always use PIC for OpenBSD: it defaults to PIE
+ if (build_context.metrics.os == TargetOs_openbsd || build_context.metrics.os == TargetOs_haiku) {
+ // Always use PIC for OpenBSD and Haiku: they default to PIE
+ reloc_mode = LLVMRelocPIC;
+ }
+
+ if (build_context.metrics.arch == TargetArch_riscv64) {
+ // NOTE(laytan): didn't seem to work without this.
reloc_mode = LLVMRelocPIC;
}
+
break;
case RelocMode_Static:
reloc_mode = LLVMRelocStatic;
@@ -2566,7 +3071,7 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
for (auto const &entry : gen->modules) {
LLVMTargetMachineRef target_machine = LLVMCreateTargetMachine(
- target, target_triple, llvm_cpu,
+ target, target_triple, (const char *)llvm_cpu.text,
llvm_features,
code_gen_level,
reloc_mode,
@@ -2574,6 +3079,13 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
lbModule *m = entry.value;
m->target_machine = target_machine;
LLVMSetModuleDataLayout(m->mod, LLVMCreateTargetDataLayout(target_machine));
+
+ #if LLVM_VERSION_MAJOR >= 18
+ if (build_context.fast_isel) {
+ LLVMSetTargetMachineFastISel(m->target_machine, true);
+ }
+ #endif
+
array_add(&target_machines, target_machine);
}
@@ -2637,17 +3149,19 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
{ // Add type info data
isize max_type_info_count = info->minimum_dependency_type_info_set.count+1;
- Type *t = alloc_type_array(t_type_info, max_type_info_count);
+ Type *t = alloc_type_array(t_type_info_ptr, max_type_info_count);
// IMPORTANT NOTE(bill): As LLVM does not have a union type, an array of unions cannot be initialized
// at compile time without cheating in some way. This means to emulate an array of unions is to use
// a giant packed struct of "corrected" data types.
- LLVMTypeRef internal_llvm_type = lb_setup_type_info_data_internal_type(m, max_type_info_count);
+ LLVMTypeRef internal_llvm_type = lb_type(m, t);
LLVMValueRef g = LLVMAddGlobal(m->mod, internal_llvm_type, LB_TYPE_INFO_DATA_NAME);
LLVMSetInitializer(g, LLVMConstNull(internal_llvm_type));
LLVMSetLinkage(g, USE_SEPARATE_MODULES ? LLVMExternalLinkage : LLVMInternalLinkage);
+ LLVMSetUnnamedAddress(g, LLVMGlobalUnnamedAddr);
+ LLVMSetGlobalConstant(g, true);
lbValue value = {};
value.value = g;
@@ -2656,15 +3170,11 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
lb_global_type_info_data_entity = alloc_entity_variable(nullptr, make_token_ident(LB_TYPE_INFO_DATA_NAME), t, EntityState_Resolved);
lb_add_entity(m, lb_global_type_info_data_entity, value);
- if (LB_USE_GIANT_PACKED_STRUCT) {
- LLVMSetLinkage(g, LLVMPrivateLinkage);
- LLVMSetUnnamedAddress(g, LLVMGlobalUnnamedAddr);
- LLVMSetGlobalConstant(g, /*true*/false);
- }
}
{ // Type info member buffer
// NOTE(bill): Removes need for heap allocation by making it global memory
isize count = 0;
+ isize offsets_extra = 0;
for (Type *t : m->info->type_info_types) {
isize index = lb_type_info_index(m->info, t, false);
@@ -2682,67 +3192,28 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
case Type_Tuple:
count += t->Tuple.variables.count;
break;
+ case Type_BitField:
+ count += t->BitField.fields.count;
+ // Twice is needed for the bit_offsets
+ offsets_extra += t->BitField.fields.count;
+ break;
}
}
- {
- char const *name = LB_TYPE_INFO_TYPES_NAME;
- Type *t = alloc_type_array(t_type_info_ptr, count);
- LLVMValueRef g = LLVMAddGlobal(m->mod, lb_type(m, t), name);
- LLVMSetInitializer(g, LLVMConstNull(lb_type(m, t)));
- LLVMSetLinkage(g, LLVMInternalLinkage);
- if (LB_USE_GIANT_PACKED_STRUCT) {
- lb_make_global_private_const(g);
- }
- lb_global_type_info_member_types = lb_addr({g, alloc_type_pointer(t)});
-
- }
- {
- char const *name = LB_TYPE_INFO_NAMES_NAME;
- Type *t = alloc_type_array(t_string, count);
- LLVMValueRef g = LLVMAddGlobal(m->mod, lb_type(m, t), name);
- LLVMSetInitializer(g, LLVMConstNull(lb_type(m, t)));
- LLVMSetLinkage(g, LLVMInternalLinkage);
- if (LB_USE_GIANT_PACKED_STRUCT) {
- lb_make_global_private_const(g);
- }
- lb_global_type_info_member_names = lb_addr({g, alloc_type_pointer(t)});
- }
- {
- char const *name = LB_TYPE_INFO_OFFSETS_NAME;
- Type *t = alloc_type_array(t_uintptr, count);
- LLVMValueRef g = LLVMAddGlobal(m->mod, lb_type(m, t), name);
- LLVMSetInitializer(g, LLVMConstNull(lb_type(m, t)));
- LLVMSetLinkage(g, LLVMInternalLinkage);
- if (LB_USE_GIANT_PACKED_STRUCT) {
- lb_make_global_private_const(g);
- }
- lb_global_type_info_member_offsets = lb_addr({g, alloc_type_pointer(t)});
- }
-
- {
- char const *name = LB_TYPE_INFO_USINGS_NAME;
- Type *t = alloc_type_array(t_bool, count);
+ auto const global_type_info_make = [](lbModule *m, char const *name, Type *elem_type, i64 count) -> lbAddr {
+ Type *t = alloc_type_array(elem_type, count);
LLVMValueRef g = LLVMAddGlobal(m->mod, lb_type(m, t), name);
LLVMSetInitializer(g, LLVMConstNull(lb_type(m, t)));
LLVMSetLinkage(g, LLVMInternalLinkage);
- if (LB_USE_GIANT_PACKED_STRUCT) {
- lb_make_global_private_const(g);
- }
- lb_global_type_info_member_usings = lb_addr({g, alloc_type_pointer(t)});
- }
+ lb_make_global_private_const(g);
+ return lb_addr({g, alloc_type_pointer(t)});
+ };
- {
- char const *name = LB_TYPE_INFO_TAGS_NAME;
- Type *t = alloc_type_array(t_string, count);
- LLVMValueRef g = LLVMAddGlobal(m->mod, lb_type(m, t), name);
- LLVMSetInitializer(g, LLVMConstNull(lb_type(m, t)));
- LLVMSetLinkage(g, LLVMInternalLinkage);
- if (LB_USE_GIANT_PACKED_STRUCT) {
- lb_make_global_private_const(g);
- }
- lb_global_type_info_member_tags = lb_addr({g, alloc_type_pointer(t)});
- }
+ lb_global_type_info_member_types = global_type_info_make(m, LB_TYPE_INFO_TYPES_NAME, t_type_info_ptr, count);
+ lb_global_type_info_member_names = global_type_info_make(m, LB_TYPE_INFO_NAMES_NAME, t_string, count);
+ lb_global_type_info_member_offsets = global_type_info_make(m, LB_TYPE_INFO_OFFSETS_NAME, t_uintptr, count+offsets_extra);
+ lb_global_type_info_member_usings = global_type_info_make(m, LB_TYPE_INFO_USINGS_NAME, t_bool, count);
+ lb_global_type_info_member_tags = global_type_info_make(m, LB_TYPE_INFO_TAGS_NAME, t_string, count);
}
}
@@ -2829,8 +3300,6 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
LLVMSetDLLStorageClass(g.value, LLVMDLLImportStorageClass);
LLVMSetExternallyInitialized(g.value, true);
lb_add_foreign_library_path(m, e->Variable.foreign_library);
-
- lb_set_wasm_import_attributes(g.value, e, name);
} else {
LLVMSetInitializer(g.value, LLVMConstNull(lb_type(m, e->type)));
}
@@ -2838,7 +3307,7 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
LLVMSetLinkage(g.value, LLVMDLLExportLinkage);
LLVMSetDLLStorageClass(g.value, LLVMDLLExportStorageClass);
} else if (!is_foreign) {
- LLVMSetLinkage(g.value, USE_SEPARATE_MODULES ? LLVMExternalLinkage : LLVMInternalLinkage);
+ LLVMSetLinkage(g.value, USE_SEPARATE_MODULES ? LLVMWeakAnyLinkage : LLVMInternalLinkage);
}
lb_set_linkage_from_entity_flags(m, g.value, e->flags);
@@ -2855,18 +3324,26 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
if (!is_type_any(e->type) && !is_type_union(e->type)) {
if (tav.mode != Addressing_Invalid) {
if (tav.value.kind != ExactValue_Invalid) {
+ bool is_rodata = e->kind == Entity_Variable && e->Variable.is_rodata;
ExactValue v = tav.value;
- lbValue init = lb_const_value(m, tav.type, v);
+ lbValue init = lb_const_value(m, tav.type, v, false, is_rodata);
LLVMSetInitializer(g.value, init.value);
var.is_initialized = true;
+ if (is_rodata) {
+ LLVMSetGlobalConstant(g.value, true);
+ }
}
}
}
if (!var.is_initialized && is_type_untyped_nil(tav.type)) {
var.is_initialized = true;
+ if (e->kind == Entity_Variable && e->Variable.is_rodata) {
+ LLVMSetGlobalConstant(g.value, true);
+ }
}
+ } else if (e->kind == Entity_Variable && e->Variable.is_rodata) {
+ LLVMSetGlobalConstant(g.value, true);
}
-
array_add(&global_variables, var);
lb_add_entity(m, e, g);
@@ -2903,12 +3380,11 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
}
}
- TIME_SECTION("LLVM Runtime Type Information Creation");
- gen->startup_type_info = lb_create_startup_type_info(default_module);
+ TIME_SECTION("LLVM Runtime Objective-C Names Creation");
gen->objc_names = lb_create_objc_names(default_module);
TIME_SECTION("LLVM Runtime Startup Creation (Global Variables & @(init))");
- gen->startup_runtime = lb_create_startup_runtime(default_module, gen->startup_type_info, gen->objc_names, global_variables);
+ gen->startup_runtime = lb_create_startup_runtime(default_module, gen->objc_names, global_variables);
TIME_SECTION("LLVM Runtime Cleanup Creation & @(fini)");
gen->cleanup_runtime = lb_create_cleanup_runtime(default_module);
@@ -2988,7 +3464,7 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
String filepath_ll = lb_filepath_ll_for_module(m);
if (LLVMPrintModuleToFile(m->mod, cast(char const *)filepath_ll.text, &llvm_error)) {
gb_printf_err("LLVM Error: %s\n", llvm_error);
- gb_exit(1);
+ exit_with_errors();
return false;
}
array_add(&gen->output_temp_paths, filepath_ll);
@@ -3002,7 +3478,22 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
TIME_SECTION("LLVM Add Foreign Library Paths");
lb_add_foreign_library_paths(gen);
- TIME_SECTION("LLVM Object Generation");
+ TIME_SECTION("LLVM Correct Entity Linkage");
+ lb_correct_entity_linkage(gen);
+
+ ////////////////////////////////////////////
+ for (auto const &entry: gen->modules) {
+ lbModule *m = entry.value;
+ if (!lb_is_module_empty(m)) {
+ gen->used_module_count += 1;
+ }
+ }
+
+ gbString label_object_generation = gb_string_make(heap_allocator(), "LLVM Object Generation");
+ if (gen->used_module_count > 1) {
+ label_object_generation = gb_string_append_fmt(label_object_generation, " (%td used modules)", gen->used_module_count);
+ }
+ TIME_SECTION_WITH_LEN(label_object_generation, gb_string_length(label_object_generation));
if (build_context.ignore_llvm_build) {
gb_printf_err("LLVM object generation has been ignored!\n");
@@ -3047,7 +3538,7 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
}
}
- gb_sort_array(gen->foreign_libraries.data, gen->foreign_libraries.count, foreign_library_cmp);
+ array_sort(gen->foreign_libraries, foreign_library_cmp);
return true;
}
diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp
index 5894dd38a..42d283a1e 100644
--- a/src/llvm_backend.hpp
+++ b/src/llvm_backend.hpp
@@ -1,13 +1,9 @@
#if defined(GB_SYSTEM_WINDOWS)
-#include "llvm-c/Core.h"
-#include "llvm-c/ExecutionEngine.h"
-#include "llvm-c/Target.h"
-#include "llvm-c/Analysis.h"
-#include "llvm-c/Object.h"
-#include "llvm-c/BitWriter.h"
-#include "llvm-c/DebugInfo.h"
-#include "llvm-c/Transforms/PassBuilder.h"
+#include <llvm-c/Config/llvm-config.h>
#else
+#include <llvm/Config/llvm-config.h>
+#endif
+
#include <llvm-c/Core.h>
#include <llvm-c/ExecutionEngine.h>
#include <llvm-c/Target.h>
@@ -26,7 +22,6 @@
#include <llvm-c/Transforms/Utils.h>
#include <llvm-c/Transforms/Vectorize.h>
#endif
-#endif
#if LLVM_VERSION_MAJOR < 11
#error "LLVM Version 11 is the minimum required"
@@ -62,6 +57,10 @@
#define LB_USE_NEW_PASS_SYSTEM 0
#endif
+#if LLVM_VERSION_MAJOR >= 19
+#define LLVMDIBuilderInsertDeclareAtEnd(...) LLVMDIBuilderInsertDeclareRecordAtEnd(__VA_ARGS__)
+#endif
+
gb_internal bool lb_use_new_pass_system(void) {
return LB_USE_NEW_PASS_SYSTEM;
}
@@ -80,10 +79,11 @@ enum lbAddrKind {
lbAddr_Context,
lbAddr_SoaVariable,
- lbAddr_RelativePointer,
lbAddr_Swizzle,
lbAddr_SwizzleLarge,
+
+ lbAddr_BitField,
};
struct lbAddr {
@@ -107,9 +107,6 @@ struct lbAddr {
Ast *node;
} index_set;
struct {
- bool deref;
- } relative;
- struct {
Type *type;
u8 count; // 2, 3, or 4 components
u8 indices[4];
@@ -118,6 +115,11 @@ struct lbAddr {
Type *type;
Slice<i32> indices;
} swizzle_large;
+ struct {
+ Type *type;
+ i64 bit_offset;
+ i64 bit_size;
+ } bitfield;
};
};
@@ -132,14 +134,15 @@ enum lbFunctionPassManagerKind {
lbFunctionPassManager_default,
lbFunctionPassManager_default_without_memcpy,
lbFunctionPassManager_none,
- lbFunctionPassManager_minimal,
- lbFunctionPassManager_size,
- lbFunctionPassManager_speed,
- lbFunctionPassManager_aggressive,
-
lbFunctionPassManager_COUNT
};
+struct lbPadType {
+ i64 padding;
+ i64 padding_align;
+ LLVMTypeRef type;
+};
+
struct lbModule {
LLVMModuleRef mod;
LLVMContextRef ctx;
@@ -150,6 +153,7 @@ struct lbModule {
CheckerInfo *info;
AstPackage *pkg; // possibly associated
AstFile *file; // possibly associated
+ char const *module_name;
PtrMap<Type *, LLVMTypeRef> types; // mutex: types_mutex
PtrMap<void *, lbStructFieldRemapping> struct_field_remapping; // Key: LLVMTypeRef or Type *, mutex: types_mutex
@@ -179,7 +183,8 @@ struct lbModule {
std::atomic<u32> nested_type_name_guid;
Array<lbProcedure *> procedures_to_generate;
- Array<Entity *> global_procedures_and_types_to_create;
+ Array<Entity *> global_procedures_to_create;
+ Array<Entity *> global_types_to_create;
lbProcedure *curr_procedure;
@@ -191,8 +196,6 @@ struct lbModule {
RecursiveMutex debug_values_mutex;
PtrMap<void *, LLVMMetadataRef> debug_values;
- Array<lbIncompleteDebugType> debug_incomplete_types;
-
StringMap<lbAddr> objc_classes;
StringMap<lbAddr> objc_selectors;
@@ -202,6 +205,15 @@ struct lbModule {
PtrMap<Ast *, lbAddr> exact_value_compound_literal_addr_map; // Key: Ast_CompoundLit
LLVMPassManagerRef function_pass_managers[lbFunctionPassManager_COUNT];
+
+ BlockingMutex pad_types_mutex;
+ Array<lbPadType> pad_types;
+};
+
+struct lbEntityCorrection {
+ lbModule * other_module;
+ Entity * e;
+ char const *cname;
};
struct lbGenerator : LinkerData {
@@ -217,10 +229,13 @@ struct lbGenerator : LinkerData {
std::atomic<u32> global_array_index;
std::atomic<u32> global_generated_index;
- lbProcedure *startup_type_info;
+ isize used_module_count;
+
lbProcedure *startup_runtime;
lbProcedure *cleanup_runtime;
lbProcedure *objc_names;
+
+ MPSCQueue<lbEntityCorrection> entities_to_correct_linkage;
};
@@ -299,6 +314,11 @@ enum lbProcedureFlag : u32 {
lbProcedureFlag_DebugAllocaCopy = 1<<1,
};
+struct lbVariadicReuseSlices {
+ Type *slice_type;
+ lbAddr slice_addr;
+};
+
struct lbProcedure {
u32 flags;
u16 state_flags;
@@ -339,6 +359,14 @@ struct lbProcedure {
bool in_multi_assignment;
Array<LLVMValueRef> raw_input_parameters;
+ bool uses_branch_location;
+ TokenPos branch_location_pos;
+ TokenPos curr_token_pos;
+
+ Array<lbVariadicReuseSlices> variadic_reuses;
+ lbAddr variadic_reuse_base_array_ptr;
+
+ LLVMValueRef temp_callee_return_struct_memory;
Ast *curr_stmt;
Array<Scope *> scope_stack;
@@ -365,7 +393,7 @@ struct lbProcedure {
gb_internal bool lb_init_generator(lbGenerator *gen, Checker *c);
-gb_internal String lb_mangle_name(lbModule *m, Entity *e);
+gb_internal String lb_mangle_name(Entity *e);
gb_internal String lb_get_entity_name(lbModule *m, Entity *e, String name = {});
gb_internal LLVMAttributeRef lb_create_enum_attribute(LLVMContextRef ctx, char const *name, u64 value=0);
@@ -383,7 +411,7 @@ gb_internal lbBlock *lb_create_block(lbProcedure *p, char const *name, bool appe
gb_internal lbValue lb_const_nil(lbModule *m, Type *type);
gb_internal lbValue lb_const_undef(lbModule *m, Type *type);
-gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_local=true);
+gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_local=true, bool is_rodata=false);
gb_internal lbValue lb_const_bool(lbModule *m, Type *type, bool value);
gb_internal lbValue lb_const_int(lbModule *m, Type *type, u64 value);
@@ -420,7 +448,8 @@ gb_internal lbValue lb_emit_matrix_ev(lbProcedure *p, lbValue s, isize row, isiz
gb_internal lbValue lb_emit_arith(lbProcedure *p, TokenKind op, lbValue lhs, lbValue rhs, Type *type);
gb_internal lbValue lb_emit_byte_swap(lbProcedure *p, lbValue value, Type *end_type);
-gb_internal void lb_emit_defer_stmts(lbProcedure *p, lbDeferExitKind kind, lbBlock *block);
+gb_internal void lb_emit_defer_stmts(lbProcedure *p, lbDeferExitKind kind, lbBlock *block, TokenPos pos);
+gb_internal void lb_emit_defer_stmts(lbProcedure *p, lbDeferExitKind kind, lbBlock *block, Ast *node);
gb_internal lbValue lb_emit_transmute(lbProcedure *p, lbValue value, Type *t);
gb_internal lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left, lbValue right);
gb_internal lbValue lb_emit_call(lbProcedure *p, lbValue value, Array<lbValue> const &args, ProcInlining inlining = ProcInlining_none);
@@ -476,7 +505,7 @@ gb_internal lbValue lb_emit_mul_add(lbProcedure *p, lbValue a, lbValue b, lbValu
gb_internal void lb_fill_slice(lbProcedure *p, lbAddr const &slice, lbValue base_elem, lbValue len);
-gb_internal lbValue lb_type_info(lbModule *m, Type *type);
+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_generate_anonymous_proc_lit(lbModule *m, String const &prefix_name, Ast *expr, lbProcedure *parent = nullptr);
@@ -499,12 +528,12 @@ gb_internal lbValue lb_dynamic_map_reserve(lbProcedure *p, lbValue const &map_pt
gb_internal lbValue lb_find_procedure_value_from_entity(lbModule *m, Entity *e);
gb_internal lbValue lb_find_value_from_entity(lbModule *m, Entity *e);
-gb_internal void lb_store_type_case_implicit(lbProcedure *p, Ast *clause, lbValue value);
+gb_internal void lb_store_type_case_implicit(lbProcedure *p, Ast *clause, lbValue value, bool is_default_case);
gb_internal lbAddr lb_store_range_stmt_val(lbProcedure *p, Ast *stmt_val, lbValue value);
gb_internal lbValue lb_emit_source_code_location_const(lbProcedure *p, String const &procedure, TokenPos const &pos);
gb_internal lbValue lb_const_source_code_location_const(lbModule *m, String const &procedure, TokenPos const &pos);
-gb_internal lbValue lb_handle_param_value(lbProcedure *p, Type *parameter_type, ParameterValue const &param_value, TokenPos const &pos);
+gb_internal lbValue lb_handle_param_value(lbProcedure *p, Type *parameter_type, ParameterValue const &param_value, TypeProc *procedure_type, Ast *call_expression);
gb_internal lbValue lb_equal_proc_for_type(lbModule *m, Type *type);
gb_internal lbValue lb_hasher_proc_for_type(lbModule *m, Type *type);
@@ -550,6 +579,7 @@ gb_internal LLVMValueRef lb_call_intrinsic(lbProcedure *p, const char *name, LLV
gb_internal void lb_mem_copy_overlapping(lbProcedure *p, lbValue dst, lbValue src, lbValue len, bool is_volatile=false);
gb_internal void lb_mem_copy_non_overlapping(lbProcedure *p, lbValue dst, lbValue src, lbValue len, bool is_volatile=false);
gb_internal LLVMValueRef lb_mem_zero_ptr_internal(lbProcedure *p, LLVMValueRef ptr, LLVMValueRef len, unsigned alignment, bool is_volatile);
+gb_internal LLVMValueRef lb_mem_zero_ptr_internal(lbProcedure *p, LLVMValueRef ptr, usize len, unsigned alignment, bool is_volatile);
gb_internal gb_inline i64 lb_max_zero_init_size(void) {
return cast(i64)(4*build_context.int_size);
@@ -560,7 +590,11 @@ gb_internal LLVMTypeRef OdinLLVMGetVectorElementType(LLVMTypeRef type);
gb_internal String lb_filepath_ll_for_module(lbModule *m);
+gb_internal LLVMTypeRef lb_type_internal_for_procedures_raw(lbModule *m, Type *type);
+gb_internal lbValue lb_emit_source_code_location_as_global_ptr(lbProcedure *p, String const &procedure, TokenPos const &pos);
+
+gb_internal LLVMMetadataRef lb_debug_location_from_token_pos(lbProcedure *p, TokenPos pos);
gb_internal LLVMTypeRef llvm_array_type(LLVMTypeRef ElementType, uint64_t ElementCount) {
#if LB_USE_NEW_PASS_SYSTEM
@@ -570,9 +604,12 @@ gb_internal LLVMTypeRef llvm_array_type(LLVMTypeRef ElementType, uint64_t Elemen
#endif
}
+
+gb_internal void lb_set_metadata_custom_u64(lbModule *m, LLVMValueRef v_ref, String name, u64 value);
+gb_internal u64 lb_get_metadata_custom_u64(lbModule *m, LLVMValueRef v_ref, String name);
+
#define LB_STARTUP_RUNTIME_PROC_NAME "__$startup_runtime"
#define LB_CLEANUP_RUNTIME_PROC_NAME "__$cleanup_runtime"
-#define LB_STARTUP_TYPE_INFO_PROC_NAME "__$startup_type_info"
#define LB_TYPE_INFO_DATA_NAME "__$type_info_data"
#define LB_TYPE_INFO_TYPES_NAME "__$type_info_types_data"
#define LB_TYPE_INFO_NAMES_NAME "__$type_info_names_data"
@@ -709,4 +746,6 @@ gb_global char const *llvm_linkage_strings[] = {
"linker private weak linkage"
};
-#define ODIN_METADATA_REQUIRE "odin-metadata-require", 21
+#define ODIN_METADATA_IS_PACKED str_lit("odin-is-packed")
+#define ODIN_METADATA_MIN_ALIGN str_lit("odin-min-align")
+#define ODIN_METADATA_MAX_ALIGN str_lit("odin-max-align")
diff --git a/src/llvm_backend_const.cpp b/src/llvm_backend_const.cpp
index 7584df3ee..754bbfca2 100644
--- a/src/llvm_backend_const.cpp
+++ b/src/llvm_backend_const.cpp
@@ -94,9 +94,6 @@ gb_internal LLVMValueRef llvm_const_cast(LLVMValueRef val, LLVMTypeRef dst) {
LLVMTypeKind kind = LLVMGetTypeKind(dst);
switch (kind) {
case LLVMPointerTypeKind:
- if (LB_USE_NEW_PASS_SYSTEM) {
- return val;
- }
return LLVMConstPointerCast(val, dst);
case LLVMStructTypeKind:
// GB_PANIC("%s -> %s", LLVMPrintValueToString(val), LLVMPrintTypeToString(dst));
@@ -157,7 +154,7 @@ gb_internal LLVMValueRef llvm_const_named_struct(lbModule *m, Type *t, LLVMValue
GB_ASSERT(value_count_ == bt->Struct.fields.count);
auto field_remapping = lb_get_struct_remapping(m, t);
- unsigned values_with_padding_count = LLVMCountStructElementTypes(struct_type);
+ unsigned values_with_padding_count = elem_count;
LLVMValueRef *values_with_padding = gb_alloc_array(permanent_allocator(), LLVMValueRef, values_with_padding_count);
for (unsigned i = 0; i < value_count; i++) {
@@ -287,11 +284,26 @@ gb_internal lbValue lb_expr_untyped_const_to_typed(lbModule *m, Ast *expr, Type
return lb_const_value(m, t, tv.value);
}
-gb_internal lbValue lb_const_source_code_location_const(lbModule *m, String const &procedure, TokenPos const &pos) {
+
+gb_internal lbValue lb_const_source_code_location_const(lbModule *m, String const &procedure_, TokenPos const &pos) {
+ String file = get_file_path_string(pos.file_id);
+ String procedure = procedure_;
+
+ i32 line = pos.line;
+ i32 column = pos.column;
+
+ if (build_context.obfuscate_source_code_locations) {
+ file = obfuscate_string(file, "F");
+ procedure = obfuscate_string(procedure, "P");
+
+ line = obfuscate_i32(line);
+ column = obfuscate_i32(column);
+ }
+
LLVMValueRef fields[4] = {};
- fields[0]/*file*/ = lb_find_or_add_entity_string(m, get_file_path_string(pos.file_id)).value;
- fields[1]/*line*/ = lb_const_int(m, t_i32, pos.line).value;
- fields[2]/*column*/ = lb_const_int(m, t_i32, pos.column).value;
+ fields[0]/*file*/ = lb_find_or_add_entity_string(m, file).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;
lbValue res = {};
@@ -326,6 +338,15 @@ gb_internal lbValue lb_emit_source_code_location_as_global_ptr(lbProcedure *p, S
return addr.addr;
}
+gb_internal lbValue lb_const_source_code_location_as_global_ptr(lbModule *m, String const &procedure, TokenPos const &pos) {
+ lbValue loc = lb_const_source_code_location_const(m, procedure, pos);
+ lbAddr addr = lb_add_global_generated(m, loc.type, loc, nullptr);
+ lb_make_global_private_const(addr);
+ return addr.addr;
+}
+
+
+
gb_internal lbValue lb_emit_source_code_location_as_global_ptr(lbProcedure *p, Ast *node) {
lbValue loc = lb_emit_source_code_location_const(p, node);
@@ -344,7 +365,11 @@ gb_internal lbValue lb_emit_source_code_location_as_global(lbProcedure *p, Ast *
-gb_internal LLVMValueRef lb_build_constant_array_values(lbModule *m, Type *type, Type *elem_type, isize count, LLVMValueRef *values, bool allow_local) {
+gb_internal LLVMValueRef lb_build_constant_array_values(lbModule *m, Type *type, Type *elem_type, isize count, LLVMValueRef *values, bool allow_local, bool is_rodata) {
+ if (allow_local) {
+ is_rodata = false;
+ }
+
bool is_local = allow_local && m->curr_procedure != nullptr;
bool is_const = true;
if (is_local) {
@@ -413,6 +438,8 @@ gb_internal LLVMValueRef lb_big_int_to_llvm(lbModule *m, Type *original_type, Bi
}
}
+ GB_ASSERT(!is_type_array(original_type));
+
LLVMValueRef value = LLVMConstIntOfArbitraryPrecision(lb_type(m, original_type), cast(unsigned)((sz+7)/8), cast(u64 *)rop);
if (big_int_is_neg(a)) {
value = LLVMConstNeg(value);
@@ -447,7 +474,11 @@ gb_internal bool lb_is_nested_possibly_constant(Type *ft, Selection const &sel,
return lb_is_elem_const(elem, ft);
}
-gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_local) {
+gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_local, bool is_rodata) {
+ if (allow_local) {
+ is_rodata = false;
+ }
+
LLVMContextRef ctx = m->ctx;
type = default_type(type);
@@ -505,7 +536,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
count = gb_max(cast(isize)cl->max_count, count);
Type *elem = base_type(type)->Slice.elem;
Type *t = alloc_type_array(elem, count);
- lbValue backing_array = lb_const_value(m, t, value, allow_local);
+ lbValue backing_array = lb_const_value(m, t, value, allow_local, is_rodata);
LLVMValueRef array_data = nullptr;
@@ -524,7 +555,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
LLVMValueRef ptr = LLVMBuildInBoundsGEP2(p->builder, llvm_type, array_data, indices, 2, "");
LLVMValueRef len = LLVMConstInt(lb_type(m, t_int), count, true);
- lbAddr slice = lb_add_local_generated(p, type, false);
+ lbAddr slice = lb_add_local_generated(p, original_type, false);
map_set(&m->exact_value_compound_literal_addr_map, value.value_compound, slice);
lb_fill_slice(p, slice, {ptr, alloc_type_pointer(elem)}, {len, t_int});
@@ -542,6 +573,10 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
array_data = LLVMAddGlobal(m->mod, lb_type(m, t), str);
LLVMSetInitializer(array_data, backing_array.value);
+ if (is_rodata) {
+ LLVMSetGlobalConstant(array_data, true);
+ }
+
lbValue g = {};
g.value = array_data;
g.type = t;
@@ -589,7 +624,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
}
// NOTE(bill, 2021-10-07): Allow for array programming value constants
Type *core_elem = core_array_type(type);
- return lb_const_value(m, core_elem, value, allow_local);
+ return lb_const_value(m, core_elem, value, allow_local, is_rodata);
} else if (is_type_u8_array(type) && value.kind == ExactValue_String) {
GB_ASSERT(type->Array.count == value.value_string.len);
LLVMValueRef data = LLVMConstStringInContext(ctx,
@@ -607,7 +642,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
Type *elem = type->Array.elem;
- lbValue single_elem = lb_const_value(m, elem, value, allow_local);
+ lbValue single_elem = lb_const_value(m, elem, value, allow_local, is_rodata);
LLVMValueRef *elems = gb_alloc_array(permanent_allocator(), LLVMValueRef, cast(isize)count);
for (i64 i = 0; i < count; i++) {
@@ -625,7 +660,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
Type *elem = type->Matrix.elem;
- lbValue single_elem = lb_const_value(m, elem, value, allow_local);
+ lbValue single_elem = lb_const_value(m, elem, value, allow_local, is_rodata);
single_elem.value = llvm_const_cast(single_elem.value, lb_type(m, elem));
i64 total_elem_count = matrix_type_total_internal_elems(type);
@@ -647,7 +682,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
i64 count = type->SimdVector.count;
Type *elem = type->SimdVector.elem;
- lbValue single_elem = lb_const_value(m, elem, value, allow_local);
+ lbValue single_elem = lb_const_value(m, elem, value, allow_local, is_rodata);
single_elem.value = llvm_const_cast(single_elem.value, lb_type(m, elem));
LLVMValueRef *elems = gb_alloc_array(permanent_allocator(), LLVMValueRef, count);
@@ -687,7 +722,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
}
case ExactValue_Integer:
- if (is_type_pointer(type) || is_type_multi_pointer(type)) {
+ if (is_type_pointer(type) || is_type_multi_pointer(type) || is_type_proc(type)) {
LLVMTypeRef t = lb_type(m, original_type);
LLVMValueRef i = lb_big_int_to_llvm(m, t_uintptr, &value.value_integer);
res.value = LLVMConstIntToPtr(i, t);
@@ -697,9 +732,21 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
return res;
case ExactValue_Float:
if (is_type_different_to_arch_endianness(type)) {
- u64 u = bit_cast<u64>(value.value_float);
- u = gb_endian_swap64(u);
- res.value = LLVMConstReal(lb_type(m, original_type), bit_cast<f64>(u));
+ if (type->Basic.kind == Basic_f32le || type->Basic.kind == Basic_f32be) {
+ f32 f = static_cast<float>(value.value_float);
+ u32 u = bit_cast<u32>(f);
+ u = gb_endian_swap32(u);
+ res.value = LLVMConstReal(lb_type(m, original_type), bit_cast<f32>(u));
+ } else if (type->Basic.kind == Basic_f16le || type->Basic.kind == Basic_f16be) {
+ f32 f = static_cast<float>(value.value_float);
+ u16 u = f32_to_f16(f);
+ u = gb_endian_swap16(u);
+ res.value = LLVMConstReal(lb_type(m, original_type), f16_to_f32(u));
+ } else {
+ u64 u = bit_cast<u64>(value.value_float);
+ u = gb_endian_swap64(u);
+ res.value = LLVMConstReal(lb_type(m, original_type), bit_cast<f64>(u));
+ }
} else {
res.value = LLVMConstReal(lb_type(m, original_type), value.value_float);
}
@@ -764,7 +811,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
case ExactValue_Compound:
if (is_type_slice(type)) {
- return lb_const_value(m, type, value, allow_local);
+ return lb_const_value(m, type, value, allow_local, is_rodata);
} else if (is_type_array(type)) {
ast_node(cl, CompoundLit, value.value_compound);
Type *elem_type = type->Array.elem;
@@ -798,7 +845,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
}
if (lo == i) {
TypeAndValue tav = fv->value->tav;
- LLVMValueRef val = lb_const_value(m, elem_type, tav.value, allow_local).value;
+ LLVMValueRef val = lb_const_value(m, elem_type, tav.value, allow_local, is_rodata).value;
for (i64 k = lo; k < hi; k++) {
values[value_index++] = val;
}
@@ -813,7 +860,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
i64 index = exact_value_to_i64(index_tav.value);
if (index == i) {
TypeAndValue tav = fv->value->tav;
- LLVMValueRef val = lb_const_value(m, elem_type, tav.value, allow_local).value;
+ LLVMValueRef val = lb_const_value(m, elem_type, tav.value, allow_local, is_rodata).value;
values[value_index++] = val;
found = true;
break;
@@ -826,7 +873,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
}
}
- res.value = lb_build_constant_array_values(m, type, elem_type, cast(isize)type->Array.count, values, allow_local);
+ res.value = lb_build_constant_array_values(m, type, elem_type, cast(isize)type->Array.count, values, allow_local, is_rodata);
return res;
} else {
GB_ASSERT_MSG(elem_count == type->Array.count, "%td != %td", elem_count, type->Array.count);
@@ -836,13 +883,13 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
for (isize i = 0; i < elem_count; i++) {
TypeAndValue tav = cl->elems[i]->tav;
GB_ASSERT(tav.mode != Addressing_Invalid);
- values[i] = lb_const_value(m, elem_type, tav.value, allow_local).value;
+ values[i] = lb_const_value(m, elem_type, tav.value, allow_local, is_rodata).value;
}
for (isize i = elem_count; i < type->Array.count; i++) {
values[i] = LLVMConstNull(lb_type(m, elem_type));
}
- res.value = lb_build_constant_array_values(m, type, elem_type, cast(isize)type->Array.count, values, allow_local);
+ res.value = lb_build_constant_array_values(m, type, elem_type, cast(isize)type->Array.count, values, allow_local, is_rodata);
return res;
}
} else if (is_type_enumerated_array(type)) {
@@ -882,7 +929,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
}
if (lo == i) {
TypeAndValue tav = fv->value->tav;
- LLVMValueRef val = lb_const_value(m, elem_type, tav.value, allow_local).value;
+ LLVMValueRef val = lb_const_value(m, elem_type, tav.value, allow_local, is_rodata).value;
for (i64 k = lo; k < hi; k++) {
values[value_index++] = val;
}
@@ -897,7 +944,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
i64 index = exact_value_to_i64(index_tav.value);
if (index == i) {
TypeAndValue tav = fv->value->tav;
- LLVMValueRef val = lb_const_value(m, elem_type, tav.value, allow_local).value;
+ LLVMValueRef val = lb_const_value(m, elem_type, tav.value, allow_local, is_rodata).value;
values[value_index++] = val;
found = true;
break;
@@ -910,7 +957,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
}
}
- res.value = lb_build_constant_array_values(m, type, elem_type, cast(isize)type->EnumeratedArray.count, values, allow_local);
+ res.value = lb_build_constant_array_values(m, type, elem_type, cast(isize)type->EnumeratedArray.count, values, allow_local, is_rodata);
return res;
} else {
GB_ASSERT_MSG(elem_count == type->EnumeratedArray.count, "%td != %td", elem_count, type->EnumeratedArray.count);
@@ -920,13 +967,13 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
for (isize i = 0; i < elem_count; i++) {
TypeAndValue tav = cl->elems[i]->tav;
GB_ASSERT(tav.mode != Addressing_Invalid);
- values[i] = lb_const_value(m, elem_type, tav.value, allow_local).value;
+ values[i] = lb_const_value(m, elem_type, tav.value, allow_local, is_rodata).value;
}
for (isize i = elem_count; i < type->EnumeratedArray.count; i++) {
values[i] = LLVMConstNull(lb_type(m, elem_type));
}
- res.value = lb_build_constant_array_values(m, type, elem_type, cast(isize)type->EnumeratedArray.count, values, allow_local);
+ res.value = lb_build_constant_array_values(m, type, elem_type, cast(isize)type->EnumeratedArray.count, values, allow_local, is_rodata);
return res;
}
} else if (is_type_simd_vector(type)) {
@@ -965,7 +1012,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
}
if (lo == i) {
TypeAndValue tav = fv->value->tav;
- LLVMValueRef val = lb_const_value(m, elem_type, tav.value, allow_local).value;
+ LLVMValueRef val = lb_const_value(m, elem_type, tav.value, allow_local, is_rodata).value;
for (i64 k = lo; k < hi; k++) {
values[value_index++] = val;
}
@@ -980,7 +1027,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
i64 index = exact_value_to_i64(index_tav.value);
if (index == i) {
TypeAndValue tav = fv->value->tav;
- LLVMValueRef val = lb_const_value(m, elem_type, tav.value, allow_local).value;
+ LLVMValueRef val = lb_const_value(m, elem_type, tav.value, allow_local, is_rodata).value;
values[value_index++] = val;
found = true;
break;
@@ -999,7 +1046,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
for (isize i = 0; i < elem_count; i++) {
TypeAndValue tav = cl->elems[i]->tav;
GB_ASSERT(tav.mode != Addressing_Invalid);
- values[i] = lb_const_value(m, elem_type, tav.value, allow_local).value;
+ values[i] = lb_const_value(m, elem_type, tav.value, allow_local, is_rodata).value;
}
LLVMTypeRef et = lb_type(m, elem_type);
@@ -1048,7 +1095,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
i32 index = field_remapping[f->Variable.field_index];
if (elem_type_can_be_constant(f->type)) {
if (sel.index.count == 1) {
- values[index] = lb_const_value(m, f->type, tav.value, allow_local).value;
+ values[index] = lb_const_value(m, f->type, tav.value, allow_local, is_rodata).value;
visited[index] = true;
} else {
if (!visited[index]) {
@@ -1090,7 +1137,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
}
}
if (is_constant) {
- LLVMValueRef elem_value = lb_const_value(m, tav.type, tav.value, allow_local).value;
+ LLVMValueRef elem_value = lb_const_value(m, tav.type, tav.value, allow_local, is_rodata).value;
if (LLVMIsConstant(elem_value)) {
values[index] = llvm_const_insert_value(m, values[index], elem_value, idx_list, idx_list_len);
} else {
@@ -1112,7 +1159,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
i32 index = field_remapping[f->Variable.field_index];
if (elem_type_can_be_constant(f->type)) {
- values[index] = lb_const_value(m, f->type, val, allow_local).value;
+ values[index] = lb_const_value(m, f->type, val, allow_local, is_rodata).value;
visited[index] = true;
}
}
@@ -1238,7 +1285,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
TypeAndValue tav = fv->value->tav;
- LLVMValueRef val = lb_const_value(m, elem_type, tav.value, allow_local).value;
+ LLVMValueRef val = lb_const_value(m, elem_type, tav.value, allow_local, is_rodata).value;
for (i64 k = lo; k < hi; k++) {
i64 offset = matrix_row_major_index_to_offset(type, k);
GB_ASSERT(values[offset] == nullptr);
@@ -1250,7 +1297,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
i64 index = exact_value_to_i64(index_tav.value);
GB_ASSERT(index < max_count);
TypeAndValue tav = fv->value->tav;
- LLVMValueRef val = lb_const_value(m, elem_type, tav.value, allow_local).value;
+ LLVMValueRef val = lb_const_value(m, elem_type, tav.value, allow_local, is_rodata).value;
i64 offset = matrix_row_major_index_to_offset(type, index);
GB_ASSERT(values[offset] == nullptr);
values[offset] = val;
@@ -1263,18 +1310,18 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
}
}
- res.value = lb_build_constant_array_values(m, type, elem_type, cast(isize)total_count, values, allow_local);
+ res.value = lb_build_constant_array_values(m, type, elem_type, cast(isize)total_count, values, allow_local, is_rodata);
return res;
} else {
GB_ASSERT_MSG(elem_count == max_count, "%td != %td", elem_count, max_count);
LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, cast(isize)total_count);
-
for_array(i, cl->elems) {
TypeAndValue tav = cl->elems[i]->tav;
GB_ASSERT(tav.mode != Addressing_Invalid);
- i64 offset = matrix_row_major_index_to_offset(type, i);
- values[offset] = lb_const_value(m, elem_type, tav.value, allow_local).value;
+ 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;
}
for (isize i = 0; i < total_count; i++) {
if (values[i] == nullptr) {
@@ -1282,7 +1329,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
}
}
- res.value = lb_build_constant_array_values(m, type, elem_type, cast(isize)total_count, values, allow_local);
+ res.value = lb_build_constant_array_values(m, type, elem_type, cast(isize)total_count, values, allow_local, is_rodata);
return res;
}
} else {
diff --git a/src/llvm_backend_debug.cpp b/src/llvm_backend_debug.cpp
index e053c5b40..464f7065c 100644
--- a/src/llvm_backend_debug.cpp
+++ b/src/llvm_backend_debug.cpp
@@ -46,6 +46,25 @@ gb_internal LLVMMetadataRef lb_debug_end_location_from_ast(lbProcedure *p, Ast *
return lb_debug_location_from_token_pos(p, ast_end_token(node).pos);
}
+gb_internal void lb_debug_file_line(lbModule *m, Ast *node, LLVMMetadataRef *file, unsigned *line) {
+ if (*file == nullptr) {
+ if (node) {
+ *file = lb_get_llvm_metadata(m, node->file());
+ *line = cast(unsigned)ast_token(node).pos.line;
+ }
+ }
+}
+
+gb_internal LLVMMetadataRef lb_debug_procedure_parameters(lbModule *m, Type *type) {
+ if (is_type_proc(type)) {
+ return lb_debug_type(m, t_rawptr);
+ }
+ if (type->kind == Type_Tuple && type->Tuple.variables.count == 1) {
+ return lb_debug_procedure_parameters(m, type->Tuple.variables[0]->type);
+ }
+ return lb_debug_type(m, type);
+}
+
gb_internal LLVMMetadataRef lb_debug_type_internal_proc(lbModule *m, Type *type) {
i64 size = type_size_of(type); // Check size
gb_unused(size);
@@ -63,13 +82,36 @@ gb_internal LLVMMetadataRef lb_debug_type_internal_proc(lbModule *m, Type *type)
parameter_count += 1;
}
}
- LLVMMetadataRef *parameters = gb_alloc_array(permanent_allocator(), LLVMMetadataRef, parameter_count);
- unsigned param_index = 0;
- if (type->Proc.result_count == 0) {
- parameters[param_index++] = nullptr;
- } else {
- parameters[param_index++] = lb_debug_type(m, type->Proc.results);
+ auto parameters = array_make<LLVMMetadataRef>(permanent_allocator(), 0, type->Proc.param_count+type->Proc.result_count+2);
+
+ array_add(&parameters, cast(LLVMMetadataRef)nullptr);
+
+ bool return_is_tuple = false;
+ if (type->Proc.result_count != 0) {
+ Type *single_ret = reduce_tuple_to_single_type(type->Proc.results);
+ if (is_type_proc(single_ret)) {
+ single_ret = t_rawptr;
+ }
+ if (is_type_tuple(single_ret) && is_calling_convention_odin(type->Proc.calling_convention)) {
+ LLVMTypeRef actual = lb_type_internal_for_procedures_raw(m, type);
+ actual = LLVMGetReturnType(actual);
+ if (actual == nullptr) {
+ // results were passed as a single pointer
+ parameters[0] = lb_debug_procedure_parameters(m, single_ret);
+ } else {
+ LLVMTypeRef possible = lb_type(m, type->Proc.results);
+ if (possible == actual) {
+ // results were returned directly
+ parameters[0] = lb_debug_procedure_parameters(m, single_ret);
+ } else {
+ // resulsts were returned separately
+ return_is_tuple = true;
+ }
+ }
+ } else {
+ parameters[0] = lb_debug_procedure_parameters(m, single_ret);
+ }
}
LLVMMetadataRef file = nullptr;
@@ -79,8 +121,22 @@ gb_internal LLVMMetadataRef lb_debug_type_internal_proc(lbModule *m, Type *type)
if (e->kind != Entity_Variable) {
continue;
}
- parameters[param_index] = lb_debug_type(m, e->type);
- param_index += 1;
+ array_add(&parameters, lb_debug_procedure_parameters(m, e->type));
+ }
+
+
+ if (return_is_tuple) {
+ Type *results = type->Proc.results;
+ GB_ASSERT(results != nullptr && results->kind == Type_Tuple);
+ isize count = results->Tuple.variables.count;
+ parameters[0] = lb_debug_procedure_parameters(m, results->Tuple.variables[count-1]->type);
+ for (isize i = 0; i < count-1; i++) {
+ array_add(&parameters, lb_debug_procedure_parameters(m, results->Tuple.variables[i]->type));
+ }
+ }
+
+ if (type->Proc.calling_convention == ProcCC_Odin) {
+ array_add(&parameters, lb_debug_type(m, t_context_ptr));
}
LLVMDIFlags flags = LLVMDIFlagZero;
@@ -88,7 +144,7 @@ gb_internal LLVMMetadataRef lb_debug_type_internal_proc(lbModule *m, Type *type)
flags = LLVMDIFlagNoReturn;
}
- return LLVMDIBuilderCreateSubroutineType(m->debug_builder, file, parameters, parameter_count, flags);
+ return LLVMDIBuilderCreateSubroutineType(m->debug_builder, file, parameters.data, cast(unsigned)parameters.count, flags);
}
gb_internal LLVMMetadataRef lb_debug_struct_field(lbModule *m, String const &name, Type *type, u64 offset_in_bits) {
@@ -114,6 +170,465 @@ gb_internal LLVMMetadataRef lb_debug_basic_struct(lbModule *m, String const &nam
return LLVMDIBuilderCreateStructType(m->debug_builder, scope, cast(char const *)name.text, name.len, file, 1, size_in_bits, align_in_bits, LLVMDIFlagZero, nullptr, elements, element_count, 0, nullptr, "", 0);
}
+gb_internal LLVMMetadataRef lb_debug_struct(lbModule *m, Type *type, Type *bt, String name, LLVMMetadataRef scope, LLVMMetadataRef file, unsigned line) {
+ GB_ASSERT(bt->kind == Type_Struct);
+
+ lb_debug_file_line(m, bt->Struct.node, &file, &line);
+
+ unsigned tag = DW_TAG_structure_type;
+ if (is_type_raw_union(bt)) {
+ tag = DW_TAG_union_type;
+ }
+
+ u64 size_in_bits = 8*type_size_of(bt);
+ u32 align_in_bits = 8*cast(u32)type_align_of(bt);
+
+ LLVMMetadataRef temp_forward_decl = LLVMDIBuilderCreateReplaceableCompositeType(
+ m->debug_builder, tag,
+ cast(char const *)name.text, cast(size_t)name.len,
+ scope, file, line, 0, size_in_bits, align_in_bits, LLVMDIFlagZero, "", 0
+ );
+
+ lb_set_llvm_metadata(m, type, temp_forward_decl);
+
+ type_set_offsets(bt);
+
+ unsigned element_count = cast(unsigned)(bt->Struct.fields.count);
+ LLVMMetadataRef *elements = gb_alloc_array(temporary_allocator(), LLVMMetadataRef, element_count);
+
+ LLVMMetadataRef member_scope = lb_get_llvm_metadata(m, bt->Struct.scope);
+
+ for_array(j, bt->Struct.fields) {
+ Entity *f = bt->Struct.fields[j];
+ String fname = f->token.string;
+
+ unsigned field_line = 0;
+ LLVMDIFlags field_flags = LLVMDIFlagZero;
+ GB_ASSERT(bt->Struct.offsets != nullptr);
+ u64 offset_in_bits = 8*cast(u64)bt->Struct.offsets[j];
+
+ elements[j] = LLVMDIBuilderCreateMemberType(
+ m->debug_builder,
+ member_scope,
+ cast(char const *)fname.text, cast(size_t)fname.len,
+ file, field_line,
+ 8*cast(u64)type_size_of(f->type), 8*cast(u32)type_align_of(f->type),
+ offset_in_bits,
+ field_flags,
+ lb_debug_type(m, f->type)
+ );
+ }
+
+ LLVMMetadataRef final_decl = nullptr;
+ if (tag == DW_TAG_union_type) {
+ final_decl = LLVMDIBuilderCreateUnionType(
+ m->debug_builder, scope,
+ cast(char const*)name.text, cast(size_t)name.len,
+ file, line,
+ size_in_bits, align_in_bits,
+ LLVMDIFlagZero,
+ elements, element_count,
+ 0,
+ "", 0
+ );
+ } else {
+ final_decl = LLVMDIBuilderCreateStructType(
+ m->debug_builder, scope,
+ cast(char const *)name.text, cast(size_t)name.len,
+ file, line,
+ size_in_bits, align_in_bits,
+ LLVMDIFlagZero,
+ nullptr,
+ elements, element_count,
+ 0,
+ nullptr,
+ "", 0
+ );
+ }
+
+ LLVMMetadataReplaceAllUsesWith(temp_forward_decl, final_decl);
+ lb_set_llvm_metadata(m, type, final_decl);
+ return final_decl;
+}
+
+gb_internal LLVMMetadataRef lb_debug_slice(lbModule *m, Type *type, String name, LLVMMetadataRef scope, LLVMMetadataRef file, unsigned line) {
+ Type *bt = base_type(type);
+ GB_ASSERT(bt->kind == Type_Slice);
+
+ unsigned const ptr_bits = cast(unsigned)(8*build_context.ptr_size);
+
+ u64 size_in_bits = 8*type_size_of(bt);
+ u32 align_in_bits = 8*cast(u32)type_align_of(bt);
+
+ LLVMMetadataRef temp_forward_decl = LLVMDIBuilderCreateReplaceableCompositeType(
+ m->debug_builder, DW_TAG_structure_type,
+ cast(char const *)name.text, cast(size_t)name.len,
+ scope, file, line, 0, size_in_bits, align_in_bits, LLVMDIFlagZero, "", 0
+ );
+
+ lb_set_llvm_metadata(m, type, temp_forward_decl);
+
+ unsigned element_count = 2;
+ LLVMMetadataRef elements[2];
+
+ // LLVMMetadataRef member_scope = lb_get_llvm_metadata(m, bt->Slice.scope);
+ LLVMMetadataRef member_scope = nullptr;
+
+ Type *elem_type = alloc_type_pointer(bt->Slice.elem);
+ elements[0] = LLVMDIBuilderCreateMemberType(
+ m->debug_builder, member_scope,
+ "data", 4,
+ file, line,
+ 8*cast(u64)type_size_of(elem_type), 8*cast(u32)type_align_of(elem_type),
+ 0,
+ LLVMDIFlagZero, lb_debug_type(m, elem_type)
+ );
+
+ elements[1] = LLVMDIBuilderCreateMemberType(
+ m->debug_builder, member_scope,
+ "len", 3,
+ file, line,
+ 8*cast(u64)type_size_of(t_int), 8*cast(u32)type_align_of(t_int),
+ ptr_bits,
+ LLVMDIFlagZero, lb_debug_type(m, t_int)
+ );
+
+ LLVMMetadataRef final_decl = LLVMDIBuilderCreateStructType(
+ m->debug_builder, scope,
+ cast(char const *)name.text, cast(size_t)name.len,
+ file, line,
+ size_in_bits, align_in_bits,
+ LLVMDIFlagZero,
+ nullptr,
+ elements, element_count,
+ 0,
+ nullptr,
+ "", 0
+ );
+
+ LLVMMetadataReplaceAllUsesWith(temp_forward_decl, final_decl);
+ lb_set_llvm_metadata(m, type, final_decl);
+ return final_decl;
+}
+
+gb_internal LLVMMetadataRef lb_debug_dynamic_array(lbModule *m, Type *type, String name, LLVMMetadataRef scope, LLVMMetadataRef file, unsigned line) {
+ Type *bt = base_type(type);
+ GB_ASSERT(bt->kind == Type_DynamicArray);
+
+ unsigned const ptr_bits = cast(unsigned)(8*build_context.ptr_size);
+ unsigned const int_bits = cast(unsigned)(8*build_context.int_size);
+
+ u64 size_in_bits = 8*type_size_of(bt);
+ u32 align_in_bits = 8*cast(u32)type_align_of(bt);
+
+ LLVMMetadataRef temp_forward_decl = LLVMDIBuilderCreateReplaceableCompositeType(
+ m->debug_builder, DW_TAG_structure_type,
+ cast(char const *)name.text, cast(size_t)name.len,
+ scope, file, line, 0, size_in_bits, align_in_bits, LLVMDIFlagZero, "", 0
+ );
+
+ lb_set_llvm_metadata(m, type, temp_forward_decl);
+
+ unsigned element_count = 4;
+ LLVMMetadataRef elements[4];
+
+ // LLVMMetadataRef member_scope = lb_get_llvm_metadata(m, bt->DynamicArray.scope);
+ LLVMMetadataRef member_scope = nullptr;
+
+ Type *elem_type = alloc_type_pointer(bt->DynamicArray.elem);
+ elements[0] = LLVMDIBuilderCreateMemberType(
+ m->debug_builder, member_scope,
+ "data", 4,
+ file, line,
+ 8*cast(u64)type_size_of(elem_type), 8*cast(u32)type_align_of(elem_type),
+ 0,
+ LLVMDIFlagZero, lb_debug_type(m, elem_type)
+ );
+
+ elements[1] = LLVMDIBuilderCreateMemberType(
+ m->debug_builder, member_scope,
+ "len", 3,
+ file, line,
+ 8*cast(u64)type_size_of(t_int), 8*cast(u32)type_align_of(t_int),
+ ptr_bits,
+ LLVMDIFlagZero, lb_debug_type(m, t_int)
+ );
+
+ elements[2] = LLVMDIBuilderCreateMemberType(
+ m->debug_builder, member_scope,
+ "cap", 3,
+ file, line,
+ 8*cast(u64)type_size_of(t_int), 8*cast(u32)type_align_of(t_int),
+ ptr_bits+int_bits,
+ LLVMDIFlagZero, lb_debug_type(m, t_int)
+ );
+
+ elements[3] = LLVMDIBuilderCreateMemberType(
+ m->debug_builder, member_scope,
+ "allocator", 9,
+ file, line,
+ 8*cast(u64)type_size_of(t_allocator), 8*cast(u32)type_align_of(t_allocator),
+ ptr_bits+int_bits+int_bits,
+ LLVMDIFlagZero, lb_debug_type(m, t_allocator)
+ );
+
+ LLVMMetadataRef final_decl = LLVMDIBuilderCreateStructType(
+ m->debug_builder, scope,
+ cast(char const *)name.text, cast(size_t)name.len,
+ file, line,
+ size_in_bits, align_in_bits,
+ LLVMDIFlagZero,
+ nullptr,
+ elements, element_count,
+ 0,
+ nullptr,
+ "", 0
+ );
+
+ LLVMMetadataReplaceAllUsesWith(temp_forward_decl, final_decl);
+ lb_set_llvm_metadata(m, type, final_decl);
+ return final_decl;
+}
+
+gb_internal LLVMMetadataRef lb_debug_union(lbModule *m, Type *type, String name, LLVMMetadataRef scope, LLVMMetadataRef file, unsigned line) {
+ Type *bt = base_type(type);
+ GB_ASSERT(bt->kind == Type_Union);
+
+ lb_debug_file_line(m, bt->Union.node, &file, &line);
+
+ u64 size_in_bits = 8*type_size_of(bt);
+ u32 align_in_bits = 8*cast(u32)type_align_of(bt);
+
+ LLVMMetadataRef temp_forward_decl = LLVMDIBuilderCreateReplaceableCompositeType(
+ m->debug_builder, DW_TAG_union_type,
+ cast(char const *)name.text, cast(size_t)name.len,
+ scope, file, line, 0, size_in_bits, align_in_bits, LLVMDIFlagZero, "", 0
+ );
+
+ lb_set_llvm_metadata(m, type, temp_forward_decl);
+
+ isize index_offset = 1;
+ if (is_type_union_maybe_pointer(bt)) {
+ index_offset = 0;
+ }
+
+ LLVMMetadataRef member_scope = lb_get_llvm_metadata(m, bt->Union.scope);
+ unsigned element_count = cast(unsigned)bt->Union.variants.count;
+ if (index_offset > 0) {
+ element_count += 1;
+ }
+
+ LLVMMetadataRef *elements = gb_alloc_array(temporary_allocator(), LLVMMetadataRef, element_count);
+
+ if (index_offset > 0) {
+ Type *tag_type = union_tag_type(bt);
+ u64 offset_in_bits = 8*cast(u64)bt->Union.variant_block_size;
+
+ elements[0] = LLVMDIBuilderCreateMemberType(
+ m->debug_builder, member_scope,
+ "tag", 3,
+ file, line,
+ 8*cast(u64)type_size_of(tag_type), 8*cast(u32)type_align_of(tag_type),
+ offset_in_bits,
+ LLVMDIFlagZero, lb_debug_type(m, tag_type)
+ );
+ }
+
+ for_array(j, bt->Union.variants) {
+ Type *variant = bt->Union.variants[j];
+
+ unsigned field_index = cast(unsigned)(index_offset+j);
+
+ char name[16] = {};
+ gb_snprintf(name, gb_size_of(name), "v%u", field_index);
+ isize name_len = gb_strlen(name);
+
+ elements[field_index] = LLVMDIBuilderCreateMemberType(
+ m->debug_builder, member_scope,
+ name, name_len,
+ file, line,
+ 8*cast(u64)type_size_of(variant), 8*cast(u32)type_align_of(variant),
+ 0,
+ LLVMDIFlagZero, lb_debug_type(m, variant)
+ );
+ }
+
+ LLVMMetadataRef final_decl = LLVMDIBuilderCreateUnionType(
+ m->debug_builder,
+ scope,
+ cast(char const *)name.text, cast(size_t)name.len,
+ file, line,
+ size_in_bits, align_in_bits,
+ LLVMDIFlagZero,
+ elements,
+ element_count,
+ 0,
+ "", 0
+ );
+
+ LLVMMetadataReplaceAllUsesWith(temp_forward_decl, final_decl);
+ lb_set_llvm_metadata(m, type, final_decl);
+ return final_decl;
+}
+
+gb_internal LLVMMetadataRef lb_debug_bitset(lbModule *m, Type *type, String name, LLVMMetadataRef scope, LLVMMetadataRef file, unsigned line) {
+ Type *bt = base_type(type);
+ GB_ASSERT(bt->kind == Type_BitSet);
+
+ lb_debug_file_line(m, bt->BitSet.node, &file, &line);
+
+ u64 size_in_bits = 8*type_size_of(bt);
+ u32 align_in_bits = 8*cast(u32)type_align_of(bt);
+
+ LLVMMetadataRef bit_set_field_type = lb_debug_type(m, t_bool);
+
+ unsigned element_count = 0;
+ LLVMMetadataRef *elements = nullptr;
+
+ Type *elem = base_type(bt->BitSet.elem);
+ if (elem->kind == Type_Enum) {
+ element_count = cast(unsigned)elem->Enum.fields.count;
+ elements = gb_alloc_array(temporary_allocator(), LLVMMetadataRef, element_count);
+
+ for_array(i, elem->Enum.fields) {
+ Entity *f = elem->Enum.fields[i];
+ GB_ASSERT(f->kind == Entity_Constant);
+ i64 val = exact_value_to_i64(f->Constant.value);
+ String field_name = f->token.string;
+ u64 offset_in_bits = cast(u64)(val - bt->BitSet.lower);
+ elements[i] = LLVMDIBuilderCreateBitFieldMemberType(
+ m->debug_builder,
+ scope,
+ cast(char const *)field_name.text, field_name.len,
+ file, line,
+ 1,
+ offset_in_bits,
+ 0,
+ LLVMDIFlagZero,
+ bit_set_field_type
+ );
+ }
+ } else {
+ char name[32] = {};
+
+ GB_ASSERT(is_type_integer(elem));
+ i64 count = bt->BitSet.upper - bt->BitSet.lower + 1;
+ GB_ASSERT(0 <= count);
+
+ element_count = cast(unsigned)count;
+ elements = gb_alloc_array(temporary_allocator(), LLVMMetadataRef, element_count);
+
+ for (unsigned i = 0; i < element_count; i++) {
+ u64 offset_in_bits = i;
+ i64 val = bt->BitSet.lower + cast(i64)i;
+ gb_snprintf(name, gb_count_of(name), "%lld", cast(long long)val);
+ elements[i] = LLVMDIBuilderCreateBitFieldMemberType(
+ m->debug_builder,
+ scope,
+ name, gb_strlen(name),
+ file, line,
+ 1,
+ offset_in_bits,
+ 0,
+ LLVMDIFlagZero,
+ bit_set_field_type
+ );
+ }
+ }
+
+ LLVMMetadataRef final_decl = LLVMDIBuilderCreateUnionType(
+ m->debug_builder,
+ scope,
+ cast(char const *)name.text, cast(size_t)name.len,
+ file, line,
+ size_in_bits, align_in_bits,
+ LLVMDIFlagZero,
+ elements,
+ element_count,
+ 0,
+ "", 0
+ );
+ lb_set_llvm_metadata(m, type, final_decl);
+ return final_decl;
+}
+
+gb_internal LLVMMetadataRef lb_debug_bitfield(lbModule *m, Type *type, String name, LLVMMetadataRef scope, LLVMMetadataRef file, unsigned line) {
+ Type *bt = base_type(type);
+ GB_ASSERT(bt->kind == Type_BitField);
+
+ lb_debug_file_line(m, bt->BitField.node, &file, &line);
+
+ 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);
+
+ 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,
+ cast(char const *)name.text, cast(size_t)name.len,
+ file, line,
+ size_in_bits, align_in_bits,
+ LLVMDIFlagZero,
+ nullptr,
+ elements, element_count,
+ 0,
+ nullptr,
+ "", 0
+ );
+ lb_set_llvm_metadata(m, type, final_decl);
+ return final_decl;
+}
+
+gb_internal LLVMMetadataRef lb_debug_enum(lbModule *m, Type *type, String name, LLVMMetadataRef scope, LLVMMetadataRef file, unsigned line) {
+ Type *bt = base_type(type);
+ GB_ASSERT(bt->kind == Type_Enum);
+
+ lb_debug_file_line(m, bt->Enum.node, &file, &line);
+
+ u64 size_in_bits = 8*type_size_of(bt);
+ u32 align_in_bits = 8*cast(u32)type_align_of(bt);
+
+ unsigned element_count = cast(unsigned)bt->Enum.fields.count;
+ LLVMMetadataRef *elements = gb_alloc_array(temporary_allocator(), LLVMMetadataRef, element_count);
+
+ Type *bt_enum = base_enum_type(bt);
+ LLVMBool is_unsigned = is_type_unsigned(bt_enum);
+ for (unsigned i = 0; i < element_count; i++) {
+ Entity *f = bt->Enum.fields[i];
+ GB_ASSERT(f->kind == Entity_Constant);
+ String enum_name = f->token.string;
+ i64 value = exact_value_to_i64(f->Constant.value);
+ elements[i] = LLVMDIBuilderCreateEnumerator(m->debug_builder, cast(char const *)enum_name.text, cast(size_t)enum_name.len, value, is_unsigned);
+ }
+
+ LLVMMetadataRef class_type = lb_debug_type(m, bt_enum);
+ LLVMMetadataRef final_decl = LLVMDIBuilderCreateEnumerationType(
+ m->debug_builder,
+ scope,
+ cast(char const *)name.text, cast(size_t)name.len,
+ file, line,
+ size_in_bits, align_in_bits,
+ elements, element_count,
+ class_type
+ );
+ lb_set_llvm_metadata(m, type, final_decl);
+ return final_decl;
+}
gb_internal LLVMMetadataRef lb_debug_type_basic_type(lbModule *m, String const &name, u64 size_in_bits, LLVMDWARFTypeEncoding encoding, LLVMDIFlags flags = LLVMDIFlagZero) {
LLVMMetadataRef basic_type = LLVMDIBuilderCreateBasicType(m->debug_builder, cast(char const *)name.text, name.len, size_in_bits, encoding, flags);
@@ -200,50 +715,50 @@ gb_internal LLVMMetadataRef lb_debug_type_internal(lbModule *m, Type *type) {
case Basic_complex32:
{
LLVMMetadataRef elements[2] = {};
- elements[0] = lb_debug_struct_field(m, str_lit("real"), t_f16, 0);
- elements[1] = lb_debug_struct_field(m, str_lit("imag"), t_f16, 4);
+ elements[0] = lb_debug_struct_field(m, str_lit("real"), t_f16, 0*16);
+ elements[1] = lb_debug_struct_field(m, str_lit("imag"), t_f16, 1*16);
return lb_debug_basic_struct(m, str_lit("complex32"), 64, 32, elements, gb_count_of(elements));
}
case Basic_complex64:
{
LLVMMetadataRef elements[2] = {};
- elements[0] = lb_debug_struct_field(m, str_lit("real"), t_f32, 0);
- elements[1] = lb_debug_struct_field(m, str_lit("imag"), t_f32, 4);
+ elements[0] = lb_debug_struct_field(m, str_lit("real"), t_f32, 0*32);
+ elements[1] = lb_debug_struct_field(m, str_lit("imag"), t_f32, 2*32);
return lb_debug_basic_struct(m, str_lit("complex64"), 64, 32, elements, gb_count_of(elements));
}
case Basic_complex128:
{
LLVMMetadataRef elements[2] = {};
- elements[0] = lb_debug_struct_field(m, str_lit("real"), t_f64, 0);
- elements[1] = lb_debug_struct_field(m, str_lit("imag"), t_f64, 8);
+ elements[0] = lb_debug_struct_field(m, str_lit("real"), t_f64, 0*64);
+ elements[1] = lb_debug_struct_field(m, str_lit("imag"), t_f64, 1*64);
return lb_debug_basic_struct(m, str_lit("complex128"), 128, 64, elements, gb_count_of(elements));
}
case Basic_quaternion64:
{
LLVMMetadataRef elements[4] = {};
- elements[0] = lb_debug_struct_field(m, str_lit("imag"), t_f16, 0);
- elements[1] = lb_debug_struct_field(m, str_lit("jmag"), t_f16, 4);
- elements[2] = lb_debug_struct_field(m, str_lit("kmag"), t_f16, 8);
- elements[3] = lb_debug_struct_field(m, str_lit("real"), t_f16, 12);
+ elements[0] = lb_debug_struct_field(m, str_lit("imag"), t_f16, 0*16);
+ elements[1] = lb_debug_struct_field(m, str_lit("jmag"), t_f16, 1*16);
+ elements[2] = lb_debug_struct_field(m, str_lit("kmag"), t_f16, 2*16);
+ elements[3] = lb_debug_struct_field(m, str_lit("real"), t_f16, 3*16);
return lb_debug_basic_struct(m, str_lit("quaternion64"), 128, 32, elements, gb_count_of(elements));
}
case Basic_quaternion128:
{
LLVMMetadataRef elements[4] = {};
- elements[0] = lb_debug_struct_field(m, str_lit("imag"), t_f32, 0);
- elements[1] = lb_debug_struct_field(m, str_lit("jmag"), t_f32, 4);
- elements[2] = lb_debug_struct_field(m, str_lit("kmag"), t_f32, 8);
- elements[3] = lb_debug_struct_field(m, str_lit("real"), t_f32, 12);
+ elements[0] = lb_debug_struct_field(m, str_lit("imag"), t_f32, 0*32);
+ elements[1] = lb_debug_struct_field(m, str_lit("jmag"), t_f32, 1*32);
+ elements[2] = lb_debug_struct_field(m, str_lit("kmag"), t_f32, 2*32);
+ elements[3] = lb_debug_struct_field(m, str_lit("real"), t_f32, 3*32);
return lb_debug_basic_struct(m, str_lit("quaternion128"), 128, 32, elements, gb_count_of(elements));
}
case Basic_quaternion256:
{
LLVMMetadataRef elements[4] = {};
- elements[0] = lb_debug_struct_field(m, str_lit("imag"), t_f64, 0);
- elements[1] = lb_debug_struct_field(m, str_lit("jmag"), t_f64, 8);
- elements[2] = lb_debug_struct_field(m, str_lit("kmag"), t_f64, 16);
- elements[3] = lb_debug_struct_field(m, str_lit("real"), t_f64, 24);
+ elements[0] = lb_debug_struct_field(m, str_lit("imag"), t_f64, 0*64);
+ elements[1] = lb_debug_struct_field(m, str_lit("jmag"), t_f64, 1*64);
+ elements[2] = lb_debug_struct_field(m, str_lit("kmag"), t_f64, 2*64);
+ elements[3] = lb_debug_struct_field(m, str_lit("real"), t_f64, 3*64);
return lb_debug_basic_struct(m, str_lit("quaternion256"), 256, 32, elements, gb_count_of(elements));
}
@@ -329,53 +844,21 @@ gb_internal LLVMMetadataRef lb_debug_type_internal(lbModule *m, Type *type) {
return LLVMDIBuilderCreateTypedef(m->debug_builder, array_type, name, gb_string_length(name), nullptr, 0, nullptr, cast(u32)(8*type_align_of(type)));
}
+ case Type_Map: {
+ init_map_internal_debug_types(type);
+ Type *bt = base_type(type->Map.debug_metadata_type);
+ GB_ASSERT(bt->kind == Type_Struct);
- case Type_Struct:
- case Type_Union:
- case Type_Slice:
- case Type_DynamicArray:
- case Type_Map:
- case Type_BitSet:
- {
- unsigned tag = DW_TAG_structure_type;
- if (is_type_raw_union(type) || is_type_union(type)) {
- tag = DW_TAG_union_type;
- }
- u64 size_in_bits = cast(u64)(8*type_size_of(type));
- u32 align_in_bits = cast(u32)(8*type_size_of(type));
- LLVMDIFlags flags = LLVMDIFlagZero;
-
- LLVMMetadataRef temp_forward_decl = LLVMDIBuilderCreateReplaceableCompositeType(
- m->debug_builder, tag, "", 0, nullptr, nullptr, 0, 0, size_in_bits, align_in_bits, flags, "", 0
- );
- lbIncompleteDebugType idt = {};
- idt.type = type;
- idt.metadata = temp_forward_decl;
-
- array_add(&m->debug_incomplete_types, idt);
- lb_set_llvm_metadata(m, type, temp_forward_decl);
- return temp_forward_decl;
- }
+ return lb_debug_struct(m, type, bt, make_string_c(type_to_string(type, temporary_allocator())), nullptr, nullptr, 0);
+ }
- case Type_Enum:
- {
- LLVMMetadataRef scope = nullptr;
- LLVMMetadataRef file = nullptr;
- unsigned line = 0;
- unsigned element_count = cast(unsigned)type->Enum.fields.count;
- LLVMMetadataRef *elements = gb_alloc_array(permanent_allocator(), LLVMMetadataRef, element_count);
- Type *bt = base_enum_type(type);
- LLVMBool is_unsigned = is_type_unsigned(bt);
- for (unsigned i = 0; i < element_count; i++) {
- Entity *f = type->Enum.fields[i];
- GB_ASSERT(f->kind == Entity_Constant);
- String name = f->token.string;
- i64 value = exact_value_to_i64(f->Constant.value);
- elements[i] = LLVMDIBuilderCreateEnumerator(m->debug_builder, cast(char const *)name.text, cast(size_t)name.len, value, is_unsigned);
- }
- LLVMMetadataRef class_type = lb_debug_type(m, bt);
- return LLVMDIBuilderCreateEnumerationType(m->debug_builder, scope, "", 0, file, line, 8*type_size_of(type), 8*cast(unsigned)type_align_of(type), elements, element_count, class_type);
- }
+ case Type_Struct: return lb_debug_struct( m, type, type, make_string_c(type_to_string(type, temporary_allocator())), nullptr, nullptr, 0);
+ case Type_Slice: return lb_debug_slice( m, type, make_string_c(type_to_string(type, temporary_allocator())), nullptr, nullptr, 0);
+ case Type_DynamicArray: return lb_debug_dynamic_array(m, type, make_string_c(type_to_string(type, temporary_allocator())), nullptr, nullptr, 0);
+ case Type_Union: return lb_debug_union( m, type, make_string_c(type_to_string(type, temporary_allocator())), nullptr, nullptr, 0);
+ case Type_BitSet: return lb_debug_bitset( m, type, make_string_c(type_to_string(type, temporary_allocator())), nullptr, nullptr, 0);
+ case Type_Enum: return lb_debug_enum( m, type, make_string_c(type_to_string(type, temporary_allocator())), nullptr, nullptr, 0);
+ case Type_BitField: return lb_debug_bitfield( m, type, make_string_c(type_to_string(type, temporary_allocator())), nullptr, nullptr, 0);
case Type_Tuple:
if (type->Tuple.variables.count == 1) {
@@ -437,17 +920,6 @@ gb_internal LLVMMetadataRef lb_debug_type_internal(lbModule *m, Type *type) {
elem, subscripts, gb_count_of(subscripts));
}
- case Type_RelativePointer: {
- LLVMMetadataRef base_integer = lb_debug_type(m, type->RelativePointer.base_integer);
- gbString name = type_to_string(type, temporary_allocator());
- return LLVMDIBuilderCreateTypedef(m->debug_builder, base_integer, name, gb_string_length(name), nullptr, 0, nullptr, cast(u32)(8*type_align_of(type)));
- }
- case Type_RelativeMultiPointer: {
- LLVMMetadataRef base_integer = lb_debug_type(m, type->RelativeMultiPointer.base_integer);
- gbString name = type_to_string(type, temporary_allocator());
- return LLVMDIBuilderCreateTypedef(m->debug_builder, base_integer, name, gb_string_length(name), nullptr, 0, nullptr, cast(u32)(8*type_align_of(type)));
- }
-
case Type_Matrix: {
LLVMMetadataRef subscripts[1] = {};
subscripts[0] = LLVMDIBuilderGetOrCreateSubrange(m->debug_builder,
@@ -503,7 +975,6 @@ gb_internal LLVMMetadataRef lb_debug_type(lbModule *m, Type *type) {
unsigned line = 0;
LLVMMetadataRef scope = nullptr;
-
if (type->Named.type_name != nullptr) {
Entity *e = type->Named.type_name;
scope = lb_get_base_scope_metadata(m, e->scope);
@@ -512,452 +983,50 @@ gb_internal LLVMMetadataRef lb_debug_type(lbModule *m, Type *type) {
}
line = cast(unsigned)e->token.pos.line;
}
- // TODO(bill): location data for Type_Named
- u64 size_in_bits = 8*type_size_of(type);
- u32 align_in_bits = 8*cast(u32)type_align_of(type);
String name = type->Named.name;
- char const *name_text = cast(char const *)name.text;
- size_t name_len = cast(size_t)name.len;
- unsigned tag = DW_TAG_structure_type;
- if (is_type_raw_union(type) || is_type_union(type)) {
- tag = DW_TAG_union_type;
+ if (type->Named.type_name && type->Named.type_name->pkg && type->Named.type_name->pkg->name.len != 0) {
+ name = concatenate3_strings(temporary_allocator(), type->Named.type_name->pkg->name, str_lit("."), type->Named.name);
}
- LLVMDIFlags flags = LLVMDIFlagZero;
Type *bt = base_type(type->Named.base);
- lbIncompleteDebugType idt = {};
- idt.type = type;
-
switch (bt->kind) {
- case Type_Enum:
- {
- unsigned line = 0;
- unsigned element_count = cast(unsigned)bt->Enum.fields.count;
- LLVMMetadataRef *elements = gb_alloc_array(permanent_allocator(), LLVMMetadataRef, element_count);
- Type *ct = base_enum_type(type);
- LLVMBool is_unsigned = is_type_unsigned(ct);
- for (unsigned i = 0; i < element_count; i++) {
- Entity *f = bt->Enum.fields[i];
- GB_ASSERT(f->kind == Entity_Constant);
- String name = f->token.string;
- i64 value = exact_value_to_i64(f->Constant.value);
- elements[i] = LLVMDIBuilderCreateEnumerator(m->debug_builder, cast(char const *)name.text, cast(size_t)name.len, value, is_unsigned);
- }
- LLVMMetadataRef class_type = lb_debug_type(m, ct);
- return LLVMDIBuilderCreateEnumerationType(m->debug_builder, scope, name_text, name_len, file, line, 8*type_size_of(type), 8*cast(unsigned)type_align_of(type), elements, element_count, class_type);
- }
-
-
- default:
- {
- LLVMMetadataRef debug_bt = lb_debug_type(m, bt);
- LLVMMetadataRef final_decl = LLVMDIBuilderCreateTypedef(m->debug_builder, debug_bt, name_text, name_len, file, line, scope, align_in_bits);
- lb_set_llvm_metadata(m, type, final_decl);
- return final_decl;
- }
+ default: {
+ u32 align_in_bits = 8*cast(u32)type_align_of(type);
+ LLVMMetadataRef debug_bt = lb_debug_type(m, bt);
+ LLVMMetadataRef final_decl = LLVMDIBuilderCreateTypedef(
+ m->debug_builder,
+ debug_bt,
+ cast(char const *)name.text, cast(size_t)name.len,
+ file, line, scope, align_in_bits
+ );
+ lb_set_llvm_metadata(m, type, final_decl);
+ return final_decl;
+ }
- case Type_Slice:
- case Type_DynamicArray:
- case Type_Map:
- case Type_Struct:
- case Type_Union:
- case Type_BitSet:
- {
- LLVMMetadataRef temp_forward_decl = LLVMDIBuilderCreateReplaceableCompositeType(
- m->debug_builder, tag, name_text, name_len, nullptr, nullptr, 0, 0, size_in_bits, align_in_bits, flags, "", 0
- );
- idt.metadata = temp_forward_decl;
-
- array_add(&m->debug_incomplete_types, idt);
- lb_set_llvm_metadata(m, type, temp_forward_decl);
-
- LLVMMetadataRef dummy = nullptr;
- switch (bt->kind) {
- case Type_Slice:
- dummy = lb_debug_type(m, bt->Slice.elem);
- dummy = lb_debug_type(m, alloc_type_pointer(bt->Slice.elem));
- dummy = lb_debug_type(m, t_int);
- break;
- case Type_DynamicArray:
- dummy = lb_debug_type(m, bt->DynamicArray.elem);
- dummy = lb_debug_type(m, alloc_type_pointer(bt->DynamicArray.elem));
- dummy = lb_debug_type(m, t_int);
- dummy = lb_debug_type(m, t_allocator);
- break;
- case Type_Map:
- dummy = lb_debug_type(m, bt->Map.key);
- dummy = lb_debug_type(m, bt->Map.value);
- dummy = lb_debug_type(m, t_int);
- dummy = lb_debug_type(m, t_allocator);
- dummy = lb_debug_type(m, t_uintptr);
- break;
- case Type_BitSet:
- if (bt->BitSet.elem) dummy = lb_debug_type(m, bt->BitSet.elem);
- if (bt->BitSet.underlying) dummy = lb_debug_type(m, bt->BitSet.underlying);
- break;
- }
+ case Type_Map: {
+ init_map_internal_debug_types(bt);
+ bt = base_type(bt->Map.debug_metadata_type);
+ GB_ASSERT(bt->kind == Type_Struct);
+ return lb_debug_struct(m, type, bt, name, scope, file, line);
+ }
- return temp_forward_decl;
- }
+ case Type_Struct: return lb_debug_struct(m, type, bt, name, scope, file, line);
+ case Type_Slice: return lb_debug_slice(m, type, name, scope, file, line);
+ case Type_DynamicArray: return lb_debug_dynamic_array(m, type, name, scope, file, line);
+ case Type_Union: return lb_debug_union(m, type, name, scope, file, line);
+ case Type_BitSet: return lb_debug_bitset(m, type, name, scope, file, line);
+ case Type_Enum: return lb_debug_enum(m, type, name, scope, file, line);
+ case Type_BitField: return lb_debug_bitfield(m, type, name, scope, file, line);
}
}
-
LLVMMetadataRef dt = lb_debug_type_internal(m, type);
lb_set_llvm_metadata(m, type, dt);
return dt;
}
-gb_internal void lb_debug_complete_types(lbModule *m) {
- unsigned const int_bits = cast(unsigned)(8*build_context.int_size);
-
- for_array(debug_incomplete_type_index, m->debug_incomplete_types) {
- TEMPORARY_ALLOCATOR_GUARD();
-
- auto const &idt = m->debug_incomplete_types[debug_incomplete_type_index];
- GB_ASSERT(idt.type != nullptr);
- GB_ASSERT(idt.metadata != nullptr);
-
- Type *t = idt.type;
- Type *bt = base_type(t);
-
- LLVMMetadataRef parent_scope = nullptr;
- LLVMMetadataRef file = nullptr;
- unsigned line_number = 0;
- u64 size_in_bits = 8*type_size_of(t);
- u32 align_in_bits = cast(u32)(8*type_align_of(t));
- LLVMDIFlags flags = LLVMDIFlagZero;
-
- LLVMMetadataRef derived_from = nullptr;
-
- LLVMMetadataRef *elements = nullptr;
- unsigned element_count = 0;
-
-
- unsigned runtime_lang = 0; // Objective-C runtime version
- char const *unique_id = "";
- LLVMMetadataRef vtable_holder = nullptr;
- size_t unique_id_len = 0;
-
-
- LLVMMetadataRef record_scope = nullptr;
-
- switch (bt->kind) {
- case Type_Slice:
- case Type_DynamicArray:
- case Type_Map:
- case Type_Struct:
- case Type_Union:
- case Type_BitSet: {
- bool is_union = is_type_raw_union(bt) || is_type_union(bt);
-
- String name = str_lit("<anonymous-struct>");
- if (t->kind == Type_Named) {
- name = t->Named.name;
- if (t->Named.type_name && t->Named.type_name->pkg && t->Named.type_name->pkg->name.len != 0) {
- name = concatenate3_strings(temporary_allocator(), t->Named.type_name->pkg->name, str_lit("."), t->Named.name);
- }
-
- LLVMMetadataRef file = nullptr;
- unsigned line = 0;
- LLVMMetadataRef file_scope = nullptr;
-
- if (t->Named.type_name != nullptr) {
- Entity *e = t->Named.type_name;
- file_scope = lb_get_llvm_metadata(m, e->scope);
- if (file_scope != nullptr) {
- file = LLVMDIScopeGetFile(file_scope);
- }
- line = cast(unsigned)e->token.pos.line;
- }
- // TODO(bill): location data for Type_Named
-
- } else {
- name = make_string_c(type_to_string(t, temporary_allocator()));
- }
-
-
-
- switch (bt->kind) {
- case Type_Slice:
- element_count = 2;
- elements = gb_alloc_array(temporary_allocator(), LLVMMetadataRef, element_count);
- #if defined(GB_SYSTEM_WINDOWS)
- elements[0] = lb_debug_struct_field(m, str_lit("data"), alloc_type_pointer(bt->Slice.elem), 0*int_bits);
- #else
- // FIX HACK TODO(bill): For some reason this causes a crash in *nix systems due to the reference counting
- // of the debug type information
- elements[0] = lb_debug_struct_field(m, str_lit("data"), t_rawptr, 0*int_bits);
- #endif
- elements[1] = lb_debug_struct_field(m, str_lit("len"), t_int, 1*int_bits);
- break;
- case Type_DynamicArray:
- element_count = 4;
- elements = gb_alloc_array(temporary_allocator(), LLVMMetadataRef, element_count);
- #if defined(GB_SYSTEM_WINDOWS)
- elements[0] = lb_debug_struct_field(m, str_lit("data"), alloc_type_pointer(bt->DynamicArray.elem), 0*int_bits);
- #else
- // FIX HACK TODO(bill): For some reason this causes a crash in *nix systems due to the reference counting
- // of the debug type information
- elements[0] = lb_debug_struct_field(m, str_lit("data"), t_rawptr, 0*int_bits);
- #endif
- elements[1] = lb_debug_struct_field(m, str_lit("len"), t_int, 1*int_bits);
- elements[2] = lb_debug_struct_field(m, str_lit("cap"), t_int, 2*int_bits);
- elements[3] = lb_debug_struct_field(m, str_lit("allocator"), t_allocator, 3*int_bits);
- break;
-
- case Type_Map:
- GB_ASSERT(t_raw_map != nullptr);
- bt = base_type(t_raw_map);
- /*fallthrough*/
- case Type_Struct:
- if (file == nullptr) {
- if (bt->Struct.node) {
- file = lb_get_llvm_metadata(m, bt->Struct.node->file());
- line_number = cast(unsigned)ast_token(bt->Struct.node).pos.line;
- }
- }
-
- type_set_offsets(bt);
- {
- isize element_offset = 0;
- record_scope = lb_get_llvm_metadata(m, bt->Struct.scope);
- switch (bt->Struct.soa_kind) {
- case StructSoa_Slice: element_offset = 1; break;
- case StructSoa_Dynamic: element_offset = 3; break;
- }
- element_count = cast(unsigned)(bt->Struct.fields.count + element_offset);
- elements = gb_alloc_array(temporary_allocator(), LLVMMetadataRef, element_count);
-
- isize field_size_bits = 8*type_size_of(bt) - element_offset*int_bits;
-
- switch (bt->Struct.soa_kind) {
- case StructSoa_Slice:
- elements[0] = LLVMDIBuilderCreateMemberType(
- m->debug_builder, record_scope,
- ".len", 4,
- file, 0,
- 8*cast(u64)type_size_of(t_int), 8*cast(u32)type_align_of(t_int),
- field_size_bits,
- LLVMDIFlagZero, lb_debug_type(m, t_int)
- );
- break;
- case StructSoa_Dynamic:
- elements[0] = LLVMDIBuilderCreateMemberType(
- m->debug_builder, record_scope,
- ".len", 4,
- file, 0,
- 8*cast(u64)type_size_of(t_int), 8*cast(u32)type_align_of(t_int),
- field_size_bits + 0*int_bits,
- LLVMDIFlagZero, lb_debug_type(m, t_int)
- );
- elements[1] = LLVMDIBuilderCreateMemberType(
- m->debug_builder, record_scope,
- ".cap", 4,
- file, 0,
- 8*cast(u64)type_size_of(t_int), 8*cast(u32)type_align_of(t_int),
- field_size_bits + 1*int_bits,
- LLVMDIFlagZero, lb_debug_type(m, t_int)
- );
- elements[2] = LLVMDIBuilderCreateMemberType(
- m->debug_builder, record_scope,
- ".allocator", 10,
- file, 0,
- 8*cast(u64)type_size_of(t_int), 8*cast(u32)type_align_of(t_int),
- field_size_bits + 2*int_bits,
- LLVMDIFlagZero, lb_debug_type(m, t_allocator)
- );
- break;
- }
-
- for_array(j, bt->Struct.fields) {
- Entity *f = bt->Struct.fields[j];
- String fname = f->token.string;
-
- unsigned field_line = 0;
- LLVMDIFlags field_flags = LLVMDIFlagZero;
- GB_ASSERT(bt->Struct.offsets != nullptr);
- u64 offset_in_bits = 8*cast(u64)bt->Struct.offsets[j];
-
- elements[element_offset+j] = LLVMDIBuilderCreateMemberType(
- m->debug_builder, record_scope,
- cast(char const *)fname.text, cast(size_t)fname.len,
- file, field_line,
- 8*cast(u64)type_size_of(f->type), 8*cast(u32)type_align_of(f->type),
- offset_in_bits,
- field_flags, lb_debug_type(m, f->type)
- );
- }
- }
- break;
- case Type_Union:
- {
- if (file == nullptr) {
- GB_ASSERT(bt->Union.node != nullptr);
- file = lb_get_llvm_metadata(m, bt->Union.node->file());
- line_number = cast(unsigned)ast_token(bt->Union.node).pos.line;
- }
-
- isize index_offset = 1;
- if (is_type_union_maybe_pointer(bt)) {
- index_offset = 0;
- }
- record_scope = lb_get_llvm_metadata(m, bt->Union.scope);
- element_count = cast(unsigned)bt->Union.variants.count;
- if (index_offset > 0) {
- element_count += 1;
- }
-
- elements = gb_alloc_array(temporary_allocator(), LLVMMetadataRef, element_count);
- if (index_offset > 0) {
- Type *tag_type = union_tag_type(bt);
- unsigned field_line = 0;
- u64 offset_in_bits = 8*cast(u64)bt->Union.variant_block_size;
- LLVMDIFlags field_flags = LLVMDIFlagZero;
-
- elements[0] = LLVMDIBuilderCreateMemberType(
- m->debug_builder, record_scope,
- "tag", 3,
- file, field_line,
- 8*cast(u64)type_size_of(tag_type), 8*cast(u32)type_align_of(tag_type),
- offset_in_bits,
- field_flags, lb_debug_type(m, tag_type)
- );
- }
-
- for_array(j, bt->Union.variants) {
- Type *variant = bt->Union.variants[j];
-
- unsigned field_index = cast(unsigned)(index_offset+j);
-
- char name[16] = {};
- gb_snprintf(name, gb_size_of(name), "v%u", field_index);
- isize name_len = gb_strlen(name);
-
- unsigned field_line = 0;
- LLVMDIFlags field_flags = LLVMDIFlagZero;
- u64 offset_in_bits = 0;
-
- elements[field_index] = LLVMDIBuilderCreateMemberType(
- m->debug_builder, record_scope,
- name, name_len,
- file, field_line,
- 8*cast(u64)type_size_of(variant), 8*cast(u32)type_align_of(variant),
- offset_in_bits,
- field_flags, lb_debug_type(m, variant)
- );
- }
- }
- break;
-
- case Type_BitSet:
- {
- if (file == nullptr) {
- GB_ASSERT(bt->BitSet.node != nullptr);
- file = lb_get_llvm_metadata(m, bt->BitSet.node->file());
- line_number = cast(unsigned)ast_token(bt->BitSet.node).pos.line;
- }
-
- LLVMMetadataRef bit_set_field_type = lb_debug_type(m, t_bool);
- LLVMMetadataRef scope = file;
-
- Type *elem = base_type(bt->BitSet.elem);
- if (elem->kind == Type_Enum) {
- element_count = cast(unsigned)elem->Enum.fields.count;
- elements = gb_alloc_array(temporary_allocator(), LLVMMetadataRef, element_count);
- for_array(i, elem->Enum.fields) {
- Entity *f = elem->Enum.fields[i];
- GB_ASSERT(f->kind == Entity_Constant);
- i64 val = exact_value_to_i64(f->Constant.value);
- String name = f->token.string;
- u64 offset_in_bits = cast(u64)(val - bt->BitSet.lower);
- elements[i] = LLVMDIBuilderCreateBitFieldMemberType(
- m->debug_builder,
- scope,
- cast(char const *)name.text, name.len,
- file, line_number,
- 1,
- offset_in_bits,
- 0,
- LLVMDIFlagZero,
- bit_set_field_type
- );
- }
- } else {
-
- char name[32] = {};
-
- GB_ASSERT(is_type_integer(elem));
- i64 count = bt->BitSet.upper - bt->BitSet.lower + 1;
- GB_ASSERT(0 <= count);
-
- element_count = cast(unsigned)count;
- elements = gb_alloc_array(temporary_allocator(), LLVMMetadataRef, element_count);
- for (unsigned i = 0; i < element_count; i++) {
- u64 offset_in_bits = i;
- i64 val = bt->BitSet.lower + cast(i64)i;
- gb_snprintf(name, gb_count_of(name), "%lld", cast(long long)val);
- elements[i] = LLVMDIBuilderCreateBitFieldMemberType(
- m->debug_builder,
- scope,
- name, gb_strlen(name),
- file, line_number,
- 1,
- offset_in_bits,
- 0,
- LLVMDIFlagZero,
- bit_set_field_type
- );
- }
- }
- }
- }
-
-
- LLVMMetadataRef final_metadata = nullptr;
- if (is_union) {
- final_metadata = LLVMDIBuilderCreateUnionType(
- m->debug_builder,
- parent_scope,
- cast(char const *)name.text, cast(size_t)name.len,
- file, line_number,
- size_in_bits, align_in_bits,
- flags,
- elements, element_count,
- runtime_lang,
- unique_id, unique_id_len
- );
- } else {
- final_metadata = LLVMDIBuilderCreateStructType(
- m->debug_builder,
- parent_scope,
- cast(char const *)name.text, cast(size_t)name.len,
- file, line_number,
- size_in_bits, align_in_bits,
- flags,
- derived_from,
- elements, element_count,
- runtime_lang,
- vtable_holder,
- unique_id, unique_id_len
- );
- }
-
- LLVMMetadataReplaceAllUsesWith(idt.metadata, final_metadata);
- lb_set_llvm_metadata(m, idt.type, final_metadata);
- } break;
- default:
- GB_PANIC("invalid incomplete debug type");
- break;
- }
- }
- array_clear(&m->debug_incomplete_types);
-}
-
-
-
gb_internal void lb_add_debug_local_variable(lbProcedure *p, LLVMValueRef ptr, Type *type, Token const &token) {
if (p->debug_info == nullptr) {
return;
@@ -1019,7 +1088,7 @@ gb_internal void lb_add_debug_local_variable(lbProcedure *p, LLVMValueRef ptr, T
LLVMDIBuilderInsertDeclareAtEnd(m->debug_builder, storage, var_info, llvm_expr, llvm_debug_loc, block);
}
-gb_internal void lb_add_debug_param_variable(lbProcedure *p, LLVMValueRef ptr, Type *type, Token const &token, unsigned arg_number, lbBlock *block, lbArgKind arg_kind) {
+gb_internal void lb_add_debug_param_variable(lbProcedure *p, LLVMValueRef ptr, Type *type, Token const &token, unsigned arg_number, lbBlock *block) {
if (p->debug_info == nullptr) {
return;
}
@@ -1080,15 +1149,7 @@ gb_internal void lb_add_debug_param_variable(lbProcedure *p, LLVMValueRef ptr, T
// NOTE(bill, 2022-02-01): For parameter values, you must insert them at the end of the decl block
// The reason is that if the parameter is at index 0 and a pointer, there is not such things as an
// instruction "before" it.
- switch (arg_kind) {
- case lbArg_Direct:
- LLVMDIBuilderInsertDbgValueAtEnd(m->debug_builder, storage, var_info, llvm_expr, llvm_debug_loc, block->block);
- break;
- case lbArg_Indirect:
- LLVMDIBuilderInsertDeclareAtEnd(m->debug_builder, storage, var_info, llvm_expr, llvm_debug_loc, block->block);
- break;
- }
-
+ LLVMDIBuilderInsertDeclareAtEnd(m->debug_builder, storage, var_info, llvm_expr, llvm_debug_loc, block->block);
}
@@ -1170,6 +1231,7 @@ gb_internal void add_debug_info_for_global_constant_from_entity(lbGenerator *gen
if (USE_SEPARATE_MODULES) {
m = lb_module_of_entity(gen, e);
}
+ GB_ASSERT(m != nullptr);
if (is_type_integer(e->type)) {
ExactValue const &value = e->Constant.value;
@@ -1227,4 +1289,4 @@ gb_internal void add_debug_info_for_global_constant_from_entity(lbGenerator *gen
add_debug_info_for_global_constant_internal_i64(m, e, dtype, v);
}
}
-} \ No newline at end of file
+}
diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp
index 8678a125c..df9dca801 100644
--- a/src/llvm_backend_expr.cpp
+++ b/src/llvm_backend_expr.cpp
@@ -130,12 +130,17 @@ gb_internal lbValue lb_emit_unary_arith(lbProcedure *p, TokenKind op, lbValue x,
LLVMTypeRef vector_type = nullptr;
if (op != Token_Not && lb_try_vector_cast(p->module, val, &vector_type)) {
LLVMValueRef vp = LLVMBuildPointerCast(p->builder, val.value, LLVMPointerType(vector_type, 0), "");
- LLVMValueRef v = LLVMBuildLoad2(p->builder, vector_type, vp, "");
+ LLVMValueRef v = OdinLLVMBuildLoad(p, vector_type, vp);
LLVMValueRef opv = nullptr;
switch (op) {
case Token_Xor:
opv = LLVMBuildNot(p->builder, v, "");
+ if (is_type_bit_set(elem_type)) {
+ ExactValue ev_mask = exact_bit_set_all_set_mask(elem_type);
+ lbValue mask = lb_const_value(p->module, elem_type, ev_mask);
+ opv = LLVMBuildAnd(p->builder, opv, mask.value, "");
+ }
break;
case Token_Sub:
if (is_type_float(elem_type)) {
@@ -176,8 +181,14 @@ gb_internal lbValue lb_emit_unary_arith(lbProcedure *p, TokenKind op, lbValue x,
if (op == Token_Xor) {
lbValue cmp = {};
- cmp.value = LLVMBuildNot(p->builder, x.value, "");
cmp.type = x.type;
+ if (is_type_bit_set(x.type)) {
+ ExactValue ev_mask = exact_bit_set_all_set_mask(x.type);
+ lbValue mask = lb_const_value(p->module, x.type, ev_mask);
+ cmp.value = LLVMBuildXor(p->builder, x.value, mask.value, "");
+ } else {
+ cmp.value = LLVMBuildNot(p->builder, x.value, "");
+ }
return lb_emit_conv(p, cmp, type);
}
@@ -296,12 +307,6 @@ gb_internal bool lb_try_direct_vector_arith(lbProcedure *p, TokenKind op, lbValu
GB_ASSERT(vector_type0 == vector_type1);
LLVMTypeRef vector_type = vector_type0;
- LLVMValueRef lhs_vp = LLVMBuildPointerCast(p->builder, lhs_ptr.value, LLVMPointerType(vector_type, 0), "");
- LLVMValueRef rhs_vp = LLVMBuildPointerCast(p->builder, rhs_ptr.value, LLVMPointerType(vector_type, 0), "");
- LLVMValueRef x = LLVMBuildLoad2(p->builder, vector_type, lhs_vp, "");
- LLVMValueRef y = LLVMBuildLoad2(p->builder, vector_type, rhs_vp, "");
- LLVMValueRef z = nullptr;
-
Type *integral_type = base_type(elem_type);
if (is_type_simd_vector(integral_type)) {
integral_type = core_array_type(integral_type);
@@ -311,8 +316,18 @@ gb_internal bool lb_try_direct_vector_arith(lbProcedure *p, TokenKind op, lbValu
case Token_Add: op = Token_Or; break;
case Token_Sub: op = Token_AndNot; break;
}
+ Type *u = bit_set_to_int(type);
+ if (is_type_array(u)) {
+ return false;
+ }
}
+ LLVMValueRef lhs_vp = LLVMBuildPointerCast(p->builder, lhs_ptr.value, LLVMPointerType(vector_type, 0), "");
+ LLVMValueRef rhs_vp = LLVMBuildPointerCast(p->builder, rhs_ptr.value, LLVMPointerType(vector_type, 0), "");
+ LLVMValueRef x = OdinLLVMBuildLoad(p, vector_type, lhs_vp);
+ LLVMValueRef y = OdinLLVMBuildLoad(p, vector_type, rhs_vp);
+ LLVMValueRef z = nullptr;
+
if (is_type_float(integral_type)) {
switch (op) {
case Token_Add:
@@ -504,6 +519,10 @@ gb_internal bool lb_is_matrix_simdable(Type *t) {
if ((mt->Matrix.row_count & 1) ^ (mt->Matrix.column_count & 1)) {
return false;
}
+ if (mt->Matrix.is_row_major) {
+ // TODO(bill): make #row_major matrices work with SIMD
+ return false;
+ }
if (elem->kind == Type_Basic) {
switch (elem->Basic.kind) {
@@ -532,15 +551,14 @@ gb_internal LLVMValueRef lb_matrix_to_vector(lbProcedure *p, lbValue matrix) {
Type *mt = base_type(matrix.type);
GB_ASSERT(mt->kind == Type_Matrix);
LLVMTypeRef elem_type = lb_type(p->module, mt->Matrix.elem);
-
+
unsigned total_count = cast(unsigned)matrix_type_total_internal_elems(mt);
LLVMTypeRef total_matrix_type = LLVMVectorType(elem_type, total_count);
-
+
#if 1
LLVMValueRef ptr = lb_address_from_load_or_generate_local(p, matrix).value;
LLVMValueRef matrix_vector_ptr = LLVMBuildPointerCast(p->builder, ptr, LLVMPointerType(total_matrix_type, 0), "");
- LLVMValueRef matrix_vector = LLVMBuildLoad2(p->builder, total_matrix_type, matrix_vector_ptr, "");
- LLVMSetAlignment(matrix_vector, cast(unsigned)type_align_of(mt));
+ LLVMValueRef matrix_vector = OdinLLVMBuildLoadAligned(p, total_matrix_type, matrix_vector_ptr, type_align_of(mt));
return matrix_vector;
#else
LLVMValueRef matrix_vector = LLVMBuildBitCast(p->builder, matrix.value, total_matrix_type, "");
@@ -684,39 +702,39 @@ gb_internal lbValue lb_emit_matrix_flatten(lbProcedure *p, lbValue m, Type *type
Type *mt = base_type(m.type);
GB_ASSERT(mt->kind == Type_Matrix);
- // TODO(bill): Determine why this fails on Windows sometimes
- if (false && lb_is_matrix_simdable(mt)) {
- LLVMValueRef vector = lb_matrix_to_trimmed_vector(p, m);
- return lb_matrix_cast_vector_to_type(p, vector, type);
- }
-
lbAddr res = lb_add_local_generated(p, type, true);
- i64 row_count = mt->Matrix.row_count;
- i64 column_count = mt->Matrix.column_count;
- TEMPORARY_ALLOCATOR_GUARD();
-
- auto srcs = array_make<lbValue>(temporary_allocator(), 0, row_count*column_count);
- auto dsts = array_make<lbValue>(temporary_allocator(), 0, row_count*column_count);
-
- for (i64 j = 0; j < column_count; j++) {
- for (i64 i = 0; i < row_count; i++) {
- lbValue src = lb_emit_matrix_ev(p, m, i, j);
- array_add(&srcs, src);
- }
- }
-
- for (i64 j = 0; j < column_count; j++) {
- for (i64 i = 0; i < row_count; i++) {
- lbValue dst = lb_emit_array_epi(p, res.addr, i + j*row_count);
- array_add(&dsts, dst);
- }
- }
-
- GB_ASSERT(srcs.count == dsts.count);
- for_array(i, srcs) {
- lb_emit_store(p, dsts[i], srcs[i]);
- }
+ GB_ASSERT(type_size_of(type) == type_size_of(m.type));
+
+ lbValue m_ptr = lb_address_from_load_or_generate_local(p, m);
+ lbValue n = lb_const_int(p->module, t_int, type_size_of(type));
+ lb_mem_copy_non_overlapping(p, res.addr, m_ptr, n);
+
+ // i64 row_count = mt->Matrix.row_count;
+ // i64 column_count = mt->Matrix.column_count;
+ // TEMPORARY_ALLOCATOR_GUARD();
+
+ // auto srcs = array_make<lbValue>(temporary_allocator(), 0, row_count*column_count);
+ // auto dsts = array_make<lbValue>(temporary_allocator(), 0, row_count*column_count);
+
+ // for (i64 j = 0; j < column_count; j++) {
+ // for (i64 i = 0; i < row_count; i++) {
+ // lbValue src = lb_emit_matrix_ev(p, m, i, j);
+ // array_add(&srcs, src);
+ // }
+ // }
+
+ // for (i64 j = 0; j < column_count; j++) {
+ // for (i64 i = 0; i < row_count; i++) {
+ // lbValue dst = lb_emit_array_epi(p, res.addr, i + j*row_count);
+ // array_add(&dsts, dst);
+ // }
+ // }
+
+ // GB_ASSERT(srcs.count == dsts.count);
+ // for_array(i, srcs) {
+ // lb_emit_store(p, dsts[i], srcs[i]);
+ // }
return lb_addr_load(p, res);
}
@@ -763,6 +781,7 @@ gb_internal lbValue lb_emit_matrix_mul(lbProcedure *p, lbValue lhs, lbValue rhs,
GB_ASSERT(is_type_matrix(yt));
GB_ASSERT(xt->Matrix.column_count == yt->Matrix.row_count);
GB_ASSERT(are_types_identical(xt->Matrix.elem, yt->Matrix.elem));
+ GB_ASSERT(xt->Matrix.is_row_major == yt->Matrix.is_row_major);
Type *elem = xt->Matrix.elem;
@@ -770,7 +789,7 @@ gb_internal lbValue lb_emit_matrix_mul(lbProcedure *p, lbValue lhs, lbValue rhs,
unsigned inner = cast(unsigned)xt->Matrix.column_count;
unsigned outer_columns = cast(unsigned)yt->Matrix.column_count;
- if (lb_is_matrix_simdable(xt)) {
+ if (!xt->Matrix.is_row_major && lb_is_matrix_simdable(xt)) {
unsigned x_stride = cast(unsigned)matrix_type_stride_in_elems(xt);
unsigned y_stride = cast(unsigned)matrix_type_stride_in_elems(yt);
@@ -812,7 +831,7 @@ gb_internal lbValue lb_emit_matrix_mul(lbProcedure *p, lbValue lhs, lbValue rhs,
return lb_addr_load(p, res);
}
- {
+ if (!xt->Matrix.is_row_major) {
lbAddr res = lb_add_local_generated(p, type, true);
auto inners = slice_make<lbValue[2]>(permanent_allocator(), inner);
@@ -836,6 +855,30 @@ gb_internal lbValue lb_emit_matrix_mul(lbProcedure *p, lbValue lhs, lbValue rhs,
}
return lb_addr_load(p, res);
+ } else {
+ lbAddr res = lb_add_local_generated(p, type, true);
+
+ auto inners = slice_make<lbValue[2]>(permanent_allocator(), inner);
+
+ for (unsigned i = 0; i < outer_rows; i++) {
+ for (unsigned j = 0; j < outer_columns; j++) {
+ lbValue dst = lb_emit_matrix_epi(p, res.addr, i, j);
+ for (unsigned k = 0; k < inner; k++) {
+ inners[k][0] = lb_emit_matrix_ev(p, lhs, i, k);
+ inners[k][1] = lb_emit_matrix_ev(p, rhs, k, j);
+ }
+
+ lbValue sum = lb_const_nil(p->module, elem);
+ for (unsigned k = 0; k < inner; k++) {
+ lbValue a = inners[k][0];
+ lbValue b = inners[k][1];
+ sum = lb_emit_mul_add(p, a, b, sum, elem);
+ }
+ lb_emit_store(p, dst, sum);
+ }
+ }
+
+ return lb_addr_load(p, res);
}
}
@@ -855,7 +898,7 @@ gb_internal lbValue lb_emit_matrix_mul_vector(lbProcedure *p, lbValue lhs, lbVal
Type *elem = mt->Matrix.elem;
- if (lb_is_matrix_simdable(mt)) {
+ if (!mt->Matrix.is_row_major && lb_is_matrix_simdable(mt)) {
unsigned stride = cast(unsigned)matrix_type_stride_in_elems(mt);
unsigned row_count = cast(unsigned)mt->Matrix.row_count;
@@ -924,7 +967,7 @@ gb_internal lbValue lb_emit_vector_mul_matrix(lbProcedure *p, lbValue lhs, lbVal
Type *elem = mt->Matrix.elem;
- if (lb_is_matrix_simdable(mt)) {
+ if (!mt->Matrix.is_row_major && lb_is_matrix_simdable(mt)) {
unsigned stride = cast(unsigned)matrix_type_stride_in_elems(mt);
unsigned row_count = cast(unsigned)mt->Matrix.row_count;
@@ -1104,12 +1147,21 @@ gb_internal lbValue lb_emit_arith(lbProcedure *p, TokenKind op, lbValue lhs, lbV
switch (op) {
case Token_Add:
- real = lb_emit_arith(p, Token_Add, a, c, ft);
- imag = lb_emit_arith(p, Token_Add, b, d, ft);
- break;
case Token_Sub:
- real = lb_emit_arith(p, Token_Sub, a, c, ft);
- imag = lb_emit_arith(p, Token_Sub, b, d, ft);
+ if (type_size_of(ft) == 2) {
+ a = lb_emit_conv(p, a, t_f32);
+ b = lb_emit_conv(p, b, t_f32);
+ c = lb_emit_conv(p, c, t_f32);
+ d = lb_emit_conv(p, d, t_f32);
+ real = lb_emit_arith(p, op, a, c, t_f32);
+ imag = lb_emit_arith(p, op, b, d, t_f32);
+
+ real = lb_emit_conv(p, real, ft);
+ imag = lb_emit_conv(p, imag, ft);
+ } else {
+ real = lb_emit_arith(p, op, a, c, ft);
+ imag = lb_emit_arith(p, op, b, d, ft);
+ }
break;
case Token_Mul: {
lbValue x = lb_emit_arith(p, Token_Mul, a, c, ft);
@@ -1133,6 +1185,11 @@ gb_internal lbValue lb_emit_arith(lbProcedure *p, TokenKind op, lbValue lhs, lbV
Type *ft = base_complex_elem_type(type);
if (op == Token_Add || op == Token_Sub) {
+ Type *immediate_type = ft;
+ if (type_size_of(ft) == 2) {
+ immediate_type = t_f32;
+ }
+
lbAddr res = lb_add_local_generated(p, type, false); // NOTE: initialized in full later
lbValue x0 = lb_emit_struct_ev(p, lhs, 0);
lbValue x1 = lb_emit_struct_ev(p, lhs, 1);
@@ -1144,15 +1201,39 @@ gb_internal lbValue lb_emit_arith(lbProcedure *p, TokenKind op, lbValue lhs, lbV
lbValue y2 = lb_emit_struct_ev(p, rhs, 2);
lbValue y3 = lb_emit_struct_ev(p, rhs, 3);
- lbValue z0 = lb_emit_arith(p, op, x0, y0, ft);
- lbValue z1 = lb_emit_arith(p, op, x1, y1, ft);
- lbValue z2 = lb_emit_arith(p, op, x2, y2, ft);
- lbValue z3 = lb_emit_arith(p, op, x3, y3, ft);
+ if (immediate_type != ft) {
+ x0 = lb_emit_conv(p, x0, immediate_type);
+ x1 = lb_emit_conv(p, x1, immediate_type);
+ x2 = lb_emit_conv(p, x2, immediate_type);
+ x3 = lb_emit_conv(p, x3, immediate_type);
+
+ y0 = lb_emit_conv(p, y0, immediate_type);
+ y1 = lb_emit_conv(p, y1, immediate_type);
+ y2 = lb_emit_conv(p, y2, immediate_type);
+ y3 = lb_emit_conv(p, y3, immediate_type);
+ }
+
+ lbValue z0 = lb_emit_arith(p, op, x0, y0, immediate_type);
+ lbValue z1 = lb_emit_arith(p, op, x1, y1, immediate_type);
+ lbValue z2 = lb_emit_arith(p, op, x2, y2, immediate_type);
+ lbValue z3 = lb_emit_arith(p, op, x3, y3, immediate_type);
+
+ lbValue d0 = lb_emit_struct_ep(p, res.addr, 0);
+ lbValue d1 = lb_emit_struct_ep(p, res.addr, 1);
+ lbValue d2 = lb_emit_struct_ep(p, res.addr, 2);
+ lbValue d3 = lb_emit_struct_ep(p, res.addr, 3);
+
+ if (immediate_type != ft) {
+ z0 = lb_emit_conv(p, z0, ft);
+ z1 = lb_emit_conv(p, z1, ft);
+ z2 = lb_emit_conv(p, z2, ft);
+ z3 = lb_emit_conv(p, z3, ft);
+ }
- lb_emit_store(p, lb_emit_struct_ep(p, res.addr, 0), z0);
- lb_emit_store(p, lb_emit_struct_ep(p, res.addr, 1), z1);
- lb_emit_store(p, lb_emit_struct_ep(p, res.addr, 2), z2);
- lb_emit_store(p, lb_emit_struct_ep(p, res.addr, 3), z3);
+ lb_emit_store(p, d0, z0);
+ lb_emit_store(p, d1, z1);
+ lb_emit_store(p, d2, z2);
+ lb_emit_store(p, d3, z3);
return lb_addr_load(p, res);
} else if (op == Token_Mul) {
@@ -1225,6 +1306,14 @@ handle_op:;
case Token_Add: op = Token_Or; break;
case Token_Sub: op = Token_AndNot; break;
}
+ Type *u = bit_set_to_int(type);
+ if (is_type_array(u)) {
+ lhs.type = u;
+ rhs.type = u;
+ res = lb_emit_arith(p, op, lhs, rhs, u);
+ res.type = type;
+ return res;
+ }
}
Type *integral_type = type;
@@ -1354,6 +1443,58 @@ gb_internal bool lb_is_empty_string_constant(Ast *expr) {
return false;
}
+gb_internal lbValue lb_build_binary_in(lbProcedure *p, lbValue left, lbValue right, TokenKind op) {
+ Type *rt = base_type(right.type);
+ if (is_type_pointer(rt)) {
+ right = lb_emit_load(p, right);
+ rt = base_type(type_deref(rt));
+ }
+
+ switch (rt->kind) {
+ case Type_Map:
+ {
+ lbValue map_ptr = lb_address_from_load_or_generate_local(p, right);
+ lbValue key = left;
+ lbValue ptr = lb_internal_dynamic_map_get_ptr(p, map_ptr, key);
+ if (op == Token_in) {
+ return lb_emit_conv(p, lb_emit_comp_against_nil(p, Token_NotEq, ptr), t_bool);
+ } else {
+ return lb_emit_conv(p, lb_emit_comp_against_nil(p, Token_CmpEq, ptr), t_bool);
+ }
+ }
+ break;
+ case Type_BitSet:
+ {
+ Type *key_type = rt->BitSet.elem;
+ GB_ASSERT(are_types_identical(left.type, key_type));
+
+ Type *it = bit_set_to_int(rt);
+
+ left = lb_emit_conv(p, left, it);
+ if (is_type_different_to_arch_endianness(it)) {
+ left = lb_emit_byte_swap(p, left, integer_endian_type_to_platform_type(it));
+ }
+
+ lbValue lower = lb_const_value(p->module, left.type, exact_value_i64(rt->BitSet.lower));
+ lbValue key = lb_emit_arith(p, Token_Sub, left, lower, left.type);
+ lbValue bit = lb_emit_arith(p, Token_Shl, lb_const_int(p->module, left.type, 1), key, left.type);
+ bit = lb_emit_conv(p, bit, it);
+
+ lbValue old_value = lb_emit_transmute(p, right, it);
+ lbValue new_value = lb_emit_arith(p, Token_And, old_value, bit, it);
+
+ if (op == Token_in) {
+ return lb_emit_conv(p, lb_emit_comp(p, Token_NotEq, new_value, lb_const_int(p->module, new_value.type, 0)), t_bool);
+ } else {
+ return lb_emit_conv(p, lb_emit_comp(p, Token_CmpEq, new_value, lb_const_int(p->module, new_value.type, 0)), t_bool);
+ }
+ }
+ break;
+ }
+ GB_PANIC("Invalid 'in' type");
+ return {};
+}
+
gb_internal lbValue lb_build_binary_expr(lbProcedure *p, Ast *expr) {
ast_node(be, BinaryExpr, expr);
@@ -1461,57 +1602,8 @@ gb_internal lbValue lb_build_binary_expr(lbProcedure *p, Ast *expr) {
{
lbValue left = lb_build_expr(p, be->left);
lbValue right = lb_build_expr(p, be->right);
- Type *rt = base_type(right.type);
- if (is_type_pointer(rt)) {
- right = lb_emit_load(p, right);
- rt = base_type(type_deref(rt));
- }
-
- switch (rt->kind) {
- case Type_Map:
- {
- lbValue map_ptr = lb_address_from_load_or_generate_local(p, right);
- lbValue key = left;
- lbValue ptr = lb_internal_dynamic_map_get_ptr(p, map_ptr, key);
- if (be->op.kind == Token_in) {
- return lb_emit_conv(p, lb_emit_comp_against_nil(p, Token_NotEq, ptr), t_bool);
- } else {
- return lb_emit_conv(p, lb_emit_comp_against_nil(p, Token_CmpEq, ptr), t_bool);
- }
- }
- break;
- case Type_BitSet:
- {
- Type *key_type = rt->BitSet.elem;
- GB_ASSERT(are_types_identical(left.type, key_type));
-
- Type *it = bit_set_to_int(rt);
- left = lb_emit_conv(p, left, it);
- if (is_type_different_to_arch_endianness(it)) {
- left = lb_emit_byte_swap(p, left, integer_endian_type_to_platform_type(it));
- }
-
- lbValue lower = lb_const_value(p->module, left.type, exact_value_i64(rt->BitSet.lower));
- lbValue key = lb_emit_arith(p, Token_Sub, left, lower, left.type);
- lbValue bit = lb_emit_arith(p, Token_Shl, lb_const_int(p->module, left.type, 1), key, left.type);
- bit = lb_emit_conv(p, bit, it);
-
- lbValue old_value = lb_emit_transmute(p, right, it);
- lbValue new_value = lb_emit_arith(p, Token_And, old_value, bit, it);
-
- if (be->op.kind == Token_in) {
- return lb_emit_conv(p, lb_emit_comp(p, Token_NotEq, new_value, lb_const_int(p->module, new_value.type, 0)), t_bool);
- } else {
- return lb_emit_conv(p, lb_emit_comp(p, Token_CmpEq, new_value, lb_const_int(p->module, new_value.type, 0)), t_bool);
- }
- }
- break;
- default:
- GB_PANIC("Invalid 'in' type");
- }
- break;
+ return lb_build_binary_in(p, left, right, be->op.kind);
}
- break;
default:
GB_PANIC("Invalid binary expression");
break;
@@ -1555,7 +1647,7 @@ gb_internal lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
lb_emit_store(p, a1, id);
return lb_addr_load(p, res);
} else if (dst->kind == Type_Basic) {
- if (src->Basic.kind == Basic_string && dst->Basic.kind == Basic_cstring) {
+ if (src->kind == Type_Basic && src->Basic.kind == Basic_string && dst->Basic.kind == Basic_cstring) {
String str = lb_get_const_string(m, value);
lbValue res = {};
res.type = t;
@@ -1848,13 +1940,40 @@ gb_internal lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
lbValue res_i128 = lb_emit_runtime_call(p, call, args);
return lb_emit_conv(p, res_i128, t);
}
+ i64 sz = type_size_of(src);
lbValue res = {};
res.type = t;
if (is_type_unsigned(dst)) {
- res.value = LLVMBuildFPToUI(p->builder, value.value, lb_type(m, t), "");
+ switch (sz) {
+ case 2:
+ case 4:
+ res.value = LLVMBuildFPToUI(p->builder, value.value, lb_type(m, t_u32), "");
+ res.value = LLVMBuildIntCast2(p->builder, res.value, lb_type(m, t), false, "");
+ break;
+ case 8:
+ res.value = LLVMBuildFPToUI(p->builder, value.value, lb_type(m, t_u64), "");
+ res.value = LLVMBuildIntCast2(p->builder, res.value, lb_type(m, t), false, "");
+ break;
+ default:
+ GB_PANIC("Unhandled float type");
+ break;
+ }
} else {
- res.value = LLVMBuildFPToSI(p->builder, value.value, lb_type(m, t), "");
+ switch (sz) {
+ case 2:
+ case 4:
+ res.value = LLVMBuildFPToSI(p->builder, value.value, lb_type(m, t_i32), "");
+ res.value = LLVMBuildIntCast2(p->builder, res.value, lb_type(m, t), true, "");
+ break;
+ case 8:
+ res.value = LLVMBuildFPToSI(p->builder, value.value, lb_type(m, t_i64), "");
+ res.value = LLVMBuildIntCast2(p->builder, res.value, lb_type(m, t), true, "");
+ break;
+ default:
+ GB_PANIC("Unhandled float type");
+ break;
+ }
}
return res;
}
@@ -1925,7 +2044,11 @@ gb_internal lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
} else if (is_type_integer(src_elem) && is_type_boolean(dst_elem)) {
LLVMValueRef i1vector = LLVMBuildICmp(p->builder, LLVMIntNE, value.value, LLVMConstNull(LLVMTypeOf(value.value)), "");
res.value = LLVMBuildIntCast2(p->builder, i1vector, lb_type(m, t), !is_type_unsigned(src_elem), "");
- } else {
+ } else if (is_type_pointer(src_elem) && is_type_integer(dst_elem)) {
+ res.value = LLVMBuildPtrToInt(p->builder, value.value, lb_type(m, t), "");
+ } else if (is_type_integer(src_elem) && is_type_pointer(dst_elem)) {
+ res.value = LLVMBuildIntToPtr(p->builder, value.value, lb_type(m, t), "");
+ }else {
GB_PANIC("Unhandled simd vector conversion: %s -> %s", type_to_string(src), type_to_string(dst));
}
return res;
@@ -1946,6 +2069,44 @@ gb_internal lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
}
}
+ // bit_field <-> backing type
+ if (is_type_bit_field(src)) {
+ if (are_types_identical(src->BitField.backing_type, dst)) {
+ lbValue res = {};
+ res.type = t;
+ res.value = value.value;
+ return res;
+ }
+ }
+ if (is_type_bit_field(dst)) {
+ if (are_types_identical(src, dst->BitField.backing_type)) {
+ lbValue res = {};
+ res.type = t;
+ res.value = value.value;
+ return res;
+ }
+ }
+
+ // bit_set <-> backing type
+ if (is_type_bit_set(src)) {
+ Type *backing = bit_set_to_int(src);
+ if (are_types_identical(backing, dst)) {
+ lbValue res = {};
+ res.type = t;
+ res.value = value.value;
+ return res;
+ }
+ }
+ if (is_type_bit_set(dst)) {
+ Type *backing = bit_set_to_int(dst);
+ if (are_types_identical(src, backing)) {
+ lbValue res = {};
+ res.type = t;
+ res.value = value.value;
+ return res;
+ }
+ }
+
// Pointer <-> uintptr
if (is_type_pointer(src) && is_type_uintptr(dst)) {
@@ -1974,22 +2135,59 @@ gb_internal lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
}
if (is_type_union(dst)) {
- for (Type *vt : dst->Union.variants) {
- if (are_types_identical(vt, src_type)) {
+ if (dst->Union.variants.count == 1) {
+ Type *vt = dst->Union.variants[0];
+ if (internal_check_is_assignable_to(src_type, vt)) {
+ value = lb_emit_conv(p, value, 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 (dst->Union.variants.count == 1) {
- Type *vt = dst->Union.variants[0];
- if (internal_check_is_assignable_to(src_type, vt)) {
- value = lb_emit_conv(p, value, vt);
+ for (Type *vt : dst->Union.variants) {
+ 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);
return lb_addr_load(p, parent);
}
}
+ ValidIndexAndScore *valids = gb_alloc_array(temporary_allocator(), ValidIndexAndScore, dst->Union.variants.count);
+ isize valid_count = 0;
+ isize first_success_index = -1;
+ for_array(i, dst->Union.variants) {
+ Type *vt = dst->Union.variants[i];
+ i64 score = 0;
+ if (internal_check_is_assignable_to(src_type, vt)) {
+ valids[valid_count].index = i;
+ valids[valid_count].score = score;
+ valid_count += 1;
+ if (first_success_index < 0) {
+ first_success_index = i;
+ }
+ }
+ }
+ if (valid_count > 1) {
+ gb_sort_array(valids, valid_count, valid_index_and_score_cmp);
+ i64 best_score = valids[0].score;
+ for (isize i = 1; i < valid_count; i++) {
+ auto v = valids[i];
+ if (best_score > v.score) {
+ valid_count = i;
+ break;
+ }
+ best_score = v.score;
+ }
+ first_success_index = valids[0].index;
+ }
+
+ if (valid_count == 1) {
+ Type *vt = dst->Union.variants[first_success_index];
+ value = lb_emit_conv(p, value, 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);
+ }
+
}
// NOTE(bill): This has to be done before 'Pointer <-> Pointer' as it's
@@ -2099,14 +2297,45 @@ gb_internal lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
if (is_type_array_like(dst)) {
Type *elem = base_array_type(dst);
+ isize index_count = cast(isize)get_array_type_count(dst);
+
+ isize inlineable = type_size_of(dst) <= build_context.max_simd_align;
lbValue e = lb_emit_conv(p, value, elem);
+ if (inlineable && lb_is_const(e)) {
+ lbAddr v = {};
+ if (e.value) {
+ TEMPORARY_ALLOCATOR_GUARD();
+ LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, index_count);
+ for (isize i = 0; i < index_count; i++) {
+ values[i] = e.value;
+ }
+ lbValue array_const_value = {};
+ array_const_value.type = t;
+ array_const_value.value = LLVMConstArray(lb_type(m, elem), values, cast(unsigned)index_count);
+ v = lb_add_global_generated(m, t, array_const_value);
+ } else {
+ v = lb_add_global_generated(m, t);
+ }
+
+ lb_make_global_private_const(v);
+ return lb_addr_load(p, v);
+ }
+
// NOTE(bill): Doesn't need to be zero because it will be initialized in the loops
lbAddr v = lb_add_local_generated(p, t, false);
- isize index_count = cast(isize)get_array_type_count(dst);
- for (isize i = 0; i < index_count; i++) {
- lbValue elem = lb_emit_array_epi(p, v.addr, i);
+ if (!inlineable) {
+ auto loop_data = lb_loop_start(p, index_count, t_int);
+
+ lbValue elem = lb_emit_array_ep(p, v.addr, loop_data.idx);
lb_emit_store(p, elem, e);
+
+ lb_loop_end(p, loop_data);
+ } else {
+ for (isize i = 0; i < index_count; i++) {
+ lbValue elem = lb_emit_array_epi(p, v.addr, i);
+ lb_emit_store(p, elem, e);
+ }
}
return lb_addr_load(p, v);
}
@@ -2134,12 +2363,23 @@ gb_internal lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
GB_ASSERT(src->kind == Type_Matrix);
lbAddr v = lb_add_local_generated(p, t, true);
- if (is_matrix_square(dst) && is_matrix_square(dst)) {
+ if (dst->Matrix.row_count == src->Matrix.row_count &&
+ dst->Matrix.column_count == src->Matrix.column_count) {
+ for (i64 j = 0; j < dst->Matrix.column_count; j++) {
+ for (i64 i = 0; i < dst->Matrix.row_count; i++) {
+ lbValue d = lb_emit_matrix_epi(p, v.addr, i, j);
+ lbValue s = lb_emit_matrix_ev(p, value, i, j);
+ s = lb_emit_conv(p, s, dst->Matrix.elem);
+ lb_emit_store(p, d, s);
+ }
+ }
+ } else if (is_matrix_square(dst) && is_matrix_square(dst)) {
for (i64 j = 0; j < dst->Matrix.column_count; j++) {
for (i64 i = 0; i < dst->Matrix.row_count; i++) {
if (i < src->Matrix.row_count && j < src->Matrix.column_count) {
lbValue d = lb_emit_matrix_epi(p, v.addr, i, j);
lbValue s = lb_emit_matrix_ev(p, value, i, j);
+ s = lb_emit_conv(p, s, dst->Matrix.elem);
lb_emit_store(p, d, s);
} else if (i == j) {
lbValue d = lb_emit_matrix_epi(p, v.addr, i, j);
@@ -2256,6 +2496,17 @@ gb_internal lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
return {};
}
+gb_internal lbValue lb_emit_c_vararg(lbProcedure *p, lbValue arg, Type *type) {
+ Type *core = core_type(type);
+ if (core->kind == Type_BitSet) {
+ core = core_type(bit_set_to_int(core));
+ arg = lb_emit_transmute(p, arg, core);
+ }
+
+ Type *promoted = c_vararg_promote_type(core);
+ return lb_emit_conv(p, arg, promoted);
+}
+
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));
lbValue left_ptr = lb_address_from_load_or_generate_local(p, left);
@@ -2314,9 +2565,26 @@ gb_internal lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left
if (are_types_identical(a, b)) {
// NOTE(bill): No need for a conversion
- } else if (lb_is_const(left) || lb_is_const_nil(left)) {
+ } else if ((lb_is_const(left) && !is_type_array(left.type)) || lb_is_const_nil(left)) {
+ // NOTE(karl): !is_type_array(left.type) is there to avoid lb_emit_conv
+ // trying to convert a constant array into a non-array. In that case we
+ // want the `else` branch to happen, so it can try to convert the
+ // non-array into an array instead.
+
+ if (lb_is_const_nil(left)) {
+ if (internal_check_is_assignable_to(right.type, left.type)) {
+ right = lb_emit_conv(p, right, left.type);
+ }
+ return lb_emit_comp_against_nil(p, op_kind, right);
+ }
left = lb_emit_conv(p, left, right.type);
- } else if (lb_is_const(right) || lb_is_const_nil(right)) {
+ } else if ((lb_is_const(right) && !is_type_array(right.type)) || lb_is_const_nil(right)) {
+ if (lb_is_const_nil(right)) {
+ if (internal_check_is_assignable_to(left.type, right.type)) {
+ left = lb_emit_conv(p, left, right.type);
+ }
+ return lb_emit_comp_against_nil(p, op_kind, left);
+ }
right = lb_emit_conv(p, right, left.type);
} else {
Type *lt = left.type;
@@ -2444,7 +2712,7 @@ gb_internal lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left
case Token_Lt: runtime_procedure = "cstring_lt"; break;
case Token_Gt: runtime_procedure = "cstring_gt"; break;
case Token_LtEq: runtime_procedure = "cstring_le"; break;
- case Token_GtEq: runtime_procedure = "cstring_gt"; break;
+ case Token_GtEq: runtime_procedure = "cstring_ge"; break;
}
GB_ASSERT(runtime_procedure != nullptr);
@@ -2467,7 +2735,7 @@ gb_internal lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left
case Token_Lt: runtime_procedure = "string_lt"; break;
case Token_Gt: runtime_procedure = "string_gt"; break;
case Token_LtEq: runtime_procedure = "string_le"; break;
- case Token_GtEq: runtime_procedure = "string_gt"; break;
+ case Token_GtEq: runtime_procedure = "string_ge"; break;
}
GB_ASSERT(runtime_procedure != nullptr);
@@ -2549,6 +2817,12 @@ gb_internal lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left
Type *it = bit_set_to_int(a);
lbValue lhs = lb_emit_transmute(p, left, it);
lbValue rhs = lb_emit_transmute(p, right, it);
+ if (is_type_different_to_arch_endianness(it)) {
+ it = integer_endian_type_to_platform_type(it);
+ lhs = lb_emit_byte_swap(p, lhs, it);
+ rhs = lb_emit_byte_swap(p, rhs, it);
+ }
+
lbValue res = lb_emit_arith(p, Token_And, lhs, rhs, it);
if (op_kind == Token_Lt || op_kind == Token_LtEq) {
@@ -2646,6 +2920,12 @@ gb_internal lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left
}
}
+ if (is_type_different_to_arch_endianness(left.type)) {
+ Type *pt = integer_endian_type_to_platform_type(left.type);
+ lhs = lb_emit_byte_swap(p, {lhs, pt}, pt).value;
+ rhs = lb_emit_byte_swap(p, {rhs, pt}, pt).value;
+ }
+
res.value = LLVMBuildICmp(p->builder, pred, lhs, rhs, "");
} else if (is_type_float(a)) {
LLVMRealPredicate pred = {};
@@ -2657,6 +2937,13 @@ gb_internal lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left
case Token_LtEq: pred = LLVMRealOLE; break;
case Token_NotEq: pred = LLVMRealONE; break;
}
+
+ if (is_type_different_to_arch_endianness(left.type)) {
+ Type *pt = integer_endian_type_to_platform_type(left.type);
+ left = lb_emit_byte_swap(p, left, pt);
+ right = lb_emit_byte_swap(p, right, pt);
+ }
+
res.value = LLVMBuildFCmp(p->builder, pred, left.value, right.value, "");
} else if (is_type_typeid(a)) {
LLVMIntPredicate pred = {};
@@ -2775,13 +3062,32 @@ gb_internal lbValue lb_emit_comp_against_nil(lbProcedure *p, TokenKind op_kind,
case Type_Pointer:
case Type_MultiPointer:
case Type_Proc:
- case Type_BitSet:
if (op_kind == Token_CmpEq) {
res.value = LLVMBuildIsNull(p->builder, x.value, "");
} else if (op_kind == Token_NotEq) {
res.value = LLVMBuildIsNotNull(p->builder, x.value, "");
}
return res;
+ case Type_BitSet:
+ {
+ Type *u = bit_set_to_int(bt);
+ if (is_type_array(u)) {
+ auto args = array_make<lbValue>(permanent_allocator(), 2);
+ lbValue lhs = lb_address_from_load_or_generate_local(p, x);
+ args[0] = lb_emit_conv(p, lhs, t_rawptr);
+ args[1] = lb_const_int(p->module, t_int, type_size_of(t));
+ lbValue val = lb_emit_runtime_call(p, "memory_compare_zero", args);
+ lbValue res = lb_emit_comp(p, op_kind, val, lb_const_int(p->module, t_int, 0));
+ return res;
+ } else {
+ if (op_kind == Token_CmpEq) {
+ res.value = LLVMBuildIsNull(p->builder, x.value, "");
+ } else if (op_kind == Token_NotEq) {
+ res.value = LLVMBuildIsNotNull(p->builder, x.value, "");
+ }
+ }
+ return res;
+ }
case Type_Slice:
{
@@ -2890,15 +3196,6 @@ gb_internal lbValue lb_emit_comp_against_nil(lbProcedure *p, TokenKind op_kind,
return {};
}
-gb_internal lbValue lb_make_soa_pointer(lbProcedure *p, Type *type, lbValue const &addr, lbValue const &index) {
- lbAddr v = lb_add_local_generated(p, type, false);
- lbValue ptr = lb_emit_struct_ep(p, v.addr, 0);
- lbValue idx = lb_emit_struct_ep(p, v.addr, 1);
- lb_emit_store(p, ptr, addr);
- lb_emit_store(p, idx, lb_emit_conv(p, index, t_int));
-
- return lb_addr_load(p, v);
-}
gb_internal lbValue lb_build_unary_and(lbProcedure *p, Ast *expr) {
ast_node(ue, UnaryExpr, expr);
@@ -2939,6 +3236,12 @@ gb_internal lbValue lb_build_unary_and(lbProcedure *p, Ast *expr) {
} else if (is_type_soa_pointer(tv.type)) {
ast_node(ie, IndexExpr, ue_expr);
lbValue addr = lb_build_addr_ptr(p, ie->expr);
+
+ if (is_type_pointer(type_deref(addr.type))) {
+ addr = lb_emit_load(p, addr);
+ }
+ GB_ASSERT(is_type_pointer(addr.type));
+
lbValue index = lb_build_expr(p, ie->index);
if (!build_context.no_bounds_check) {
@@ -3040,7 +3343,7 @@ gb_internal lbValue lb_build_unary_and(lbProcedure *p, Ast *expr) {
Type *dst_type = type;
- if ((p->state_flags & StateFlag_no_type_assert) == 0) {
+ if (!build_context.no_type_assert && (p->state_flags & StateFlag_no_type_assert) == 0) {
lbValue src_tag = {};
lbValue dst_tag = {};
if (is_type_union_maybe_pointer(src_type)) {
@@ -3080,7 +3383,7 @@ gb_internal lbValue lb_build_unary_and(lbProcedure *p, Ast *expr) {
v = lb_emit_load(p, v);
}
lbValue data_ptr = lb_emit_struct_ev(p, v, 0);
- if ((p->state_flags & StateFlag_no_type_assert) == 0) {
+ if (!build_context.no_type_assert && (p->state_flags & StateFlag_no_type_assert) == 0) {
GB_ASSERT(!build_context.no_rtti);
lbValue any_id = lb_emit_struct_ev(p, v, 1);
@@ -3176,13 +3479,8 @@ gb_internal lbValue lb_build_expr_internal(lbProcedure *p, Ast *expr) {
Type *type = type_of_expr(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) {
- // NOTE(bill): The commented out code below is just for debug purposes only
- // if (is_type_untyped(type)) {
- // gb_printf_err("%s %s : %s @ %p\n", token_pos_to_string(expr_pos), expr_to_string(expr), type_to_string(expr->tav.type), expr);
- // GB_PANIC("%s\n", type_to_string(tv.type));
- // }
+ if (tv.value.kind != ExactValue_Invalid) {
// NOTE(bill): Short on constant values
return lb_const_value(p->module, type, tv.value);
} else if (tv.mode == Addressing_Type) {
@@ -3192,13 +3490,25 @@ gb_internal lbValue lb_build_expr_internal(lbProcedure *p, Ast *expr) {
switch (expr->kind) {
case_ast_node(bl, BasicLit, expr);
+ if (type != nullptr && type->Named.name == "Error") {
+ Entity *e = type->Named.type_name;
+ if (e->pkg && e->pkg->name == "os") {
+ return lb_const_nil(p->module, type);
+ }
+ }
TokenPos pos = bl->token.pos;
- GB_PANIC("Non-constant basic literal %s - %.*s", token_pos_to_string(pos), LIT(token_strings[bl->token.kind]));
+ GB_PANIC("Non-constant basic literal %s - %.*s (%s)", token_pos_to_string(pos), LIT(token_strings[bl->token.kind]), type_to_string(type));
case_end;
case_ast_node(bd, BasicDirective, expr);
TokenPos pos = bd->token.pos;
- GB_PANIC("Non-constant basic literal %s - %.*s", token_pos_to_string(pos), LIT(bd->name.string));
+ String name = bd->name.string;
+ if (name == "branch_location") {
+ GB_ASSERT(p->uses_branch_location);
+ String proc_name = p->entity->token.string;
+ return lb_emit_source_code_location_as_global(p, proc_name, p->branch_location_pos);
+ }
+ GB_PANIC("Non-constant basic literal %s - %.*s", token_pos_to_string(pos), LIT(name));
case_end;
case_ast_node(i, Implicit, expr);
@@ -3364,7 +3674,7 @@ gb_internal lbValue lb_build_expr_internal(lbProcedure *p, Ast *expr) {
lb_emit_if(p, lb_emit_try_has_value(p, rhs), then, else_);
lb_start_block(p, else_);
- lb_emit_defer_stmts(p, lbDeferExit_Branch, block);
+ lb_emit_defer_stmts(p, lbDeferExit_Branch, block, expr);
lb_emit_jump(p, block);
lb_start_block(p, then);
@@ -3503,7 +3813,9 @@ gb_internal lbValue lb_get_using_variable(lbProcedure *p, Entity *e) {
lbValue v = {};
+ bool is_soa = false;
if (pv == nullptr && parent->flags & EntityFlag_SoaPtrField) {
+ is_soa = true;
// NOTE(bill): using SOA value (probably from for-in statement)
lbAddr parent_addr = lb_get_soa_variable_addr(p, parent);
v = lb_addr_get_ptr(p, parent_addr);
@@ -3514,7 +3826,7 @@ gb_internal lbValue lb_get_using_variable(lbProcedure *p, Entity *e) {
v = lb_build_addr_ptr(p, e->using_expr);
}
GB_ASSERT(v.value != nullptr);
- GB_ASSERT_MSG(parent->type == type_deref(v.type), "%s %s", type_to_string(parent->type), type_to_string(v.type));
+ GB_ASSERT_MSG(is_soa || parent->type == type_deref(v.type), "%s %s", type_to_string(parent->type), type_to_string(v.type));
lbValue ptr = lb_emit_deep_field_gep(p, v, sel);
if (parent->scope) {
if ((parent->scope->flags & (ScopeFlag_File|ScopeFlag_Pkg)) == 0) {
@@ -3578,7 +3890,7 @@ gb_internal lbAddr lb_build_array_swizzle_addr(lbProcedure *p, AstCallExpr *ce,
Type *type = base_type(lb_addr_type(addr));
GB_ASSERT(type->kind == Type_Array);
i64 count = type->Array.count;
- if (count <= 4) {
+ if (count <= 4 && index_count <= 4) {
u8 indices[4] = {};
u8 index_count = 0;
for (i32 i = 1; i < ce->args.count; i++) {
@@ -3651,7 +3963,7 @@ gb_internal void lb_build_addr_compound_lit_populate(lbProcedure *p, Slice<Ast *
Ast *elem = elems[i];
if (elem->kind == Ast_FieldValue) {
ast_node(fv, FieldValue, elem);
- if (lb_is_elem_const(fv->value, et)) {
+ if (bt->kind != Type_DynamicArray && lb_is_elem_const(fv->value, et)) {
continue;
}
if (is_ast_range(fv->field)) {
@@ -3775,27 +4087,39 @@ gb_internal lbAddr lb_build_addr_index_expr(lbProcedure *p, Ast *expr) {
if (ie->expr->tav.mode == Addressing_SoaVariable) {
// SOA Structures for slices/dynamic arrays
- GB_ASSERT(is_type_pointer(type_of_expr(ie->expr)));
+ GB_ASSERT_MSG(is_type_multi_pointer(type_of_expr(ie->expr)), "%s", type_to_string(type_of_expr(ie->expr)));
lbValue field = lb_build_expr(p, ie->expr);
lbValue index = lb_build_expr(p, ie->index);
-
if (!build_context.no_bounds_check) {
- // TODO HACK(bill): Clean up this hack to get the length for bounds checking
- // GB_ASSERT(LLVMIsALoadInst(field.value));
-
- // lbValue a = {};
- // a.value = LLVMGetOperand(field.value, 0);
- // a.type = alloc_type_pointer(field.type);
-
- // irInstr *b = &a->Instr;
- // GB_ASSERT(b->kind == irInstr_StructElementPtr);
- // lbValue base_struct = b->StructElementPtr.address;
+ Ast *se_expr = unparen_expr(ie->expr);
+ if (se_expr->kind == Ast_SelectorExpr) {
+ ast_node(se, SelectorExpr, se_expr);
+ lbValue len = {};
+
+ Type *type = base_type(type_deref(type_of_expr(se->expr)));
+ GB_ASSERT_MSG(is_type_soa_struct(type), "%s", type_to_string(type));
+ if (type->Struct.soa_kind == StructSoa_Fixed) {
+ len = lb_const_int(p->module, t_int, type->Struct.soa_count);
+ } else {
+ lbAddr *found = map_get(&p->selector_addr, se_expr);
+ if (found) {
+ lbAddr addr = *found;
+ lbValue parent = lb_addr_get_ptr(p, addr);
+ if (is_type_pointer(type_deref(parent.type))) {
+ parent = lb_emit_load(p, parent);
+ }
+ len = lb_soa_struct_len(p, parent);
+ }
+ }
- // GB_ASSERT(is_type_soa_struct(type_deref(ir_type(base_struct))));
- // lbValue len = ir_soa_struct_len(p, base_struct);
- // lb_emit_bounds_check(p, ast_token(ie->index), index, len);
+ if (len.value) {
+ lb_emit_bounds_check(p, ast_token(ie->index), index, len);
+ }
+ } else {
+ // TODO(bill): how do you even do bounds checking here?
+ }
}
lbValue val = lb_emit_ptr_offset(p, field, index);
return lb_addr(val);
@@ -3901,30 +4225,6 @@ gb_internal lbAddr lb_build_addr_index_expr(lbProcedure *p, Ast *expr) {
return lb_addr(v);
}
- case Type_RelativeMultiPointer: {
- lbAddr rel_ptr_addr = {};
- if (deref) {
- lbValue rel_ptr_ptr = lb_build_expr(p, ie->expr);
- rel_ptr_addr = lb_addr(rel_ptr_ptr);
- } else {
- rel_ptr_addr = lb_build_addr(p, ie->expr);
- }
- lbValue rel_ptr = lb_relative_pointer_to_pointer(p, rel_ptr_addr);
-
- lbValue index = lb_build_expr(p, ie->index);
- index = lb_emit_conv(p, index, t_int);
- lbValue v = {};
-
- Type *pointer_type = base_type(t->RelativeMultiPointer.pointer_type);
- GB_ASSERT(pointer_type->kind == Type_MultiPointer);
- Type *elem = pointer_type->MultiPointer.elem;
-
- LLVMValueRef indices[1] = {index.value};
- v.value = LLVMBuildGEP2(p->builder, lb_type(p->module, elem), rel_ptr.value, indices, 1, "");
- v.type = alloc_type_pointer(elem);
- return lb_addr(v);
- }
-
case Type_DynamicArray: {
lbValue dynamic_array = {};
dynamic_array = lb_build_expr(p, ie->expr);
@@ -3947,12 +4247,21 @@ gb_internal lbAddr lb_build_addr_index_expr(lbProcedure *p, Ast *expr) {
}
lbValue index = lb_build_expr(p, ie->index);
index = lb_emit_conv(p, index, t_int);
- lbValue elem = lb_emit_matrix_ep(p, matrix, lb_const_int(p->module, t_int, 0), index);
+
+ isize bounds_len = 0;
+ lbValue elem = {};
+ if (t->Matrix.is_row_major) {
+ bounds_len = t->Matrix.row_count;
+ elem = lb_emit_matrix_ep(p, matrix, index, lb_const_int(p->module, t_int, 0));
+ } else {
+ bounds_len = t->Matrix.column_count;
+ elem = lb_emit_matrix_ep(p, matrix, lb_const_int(p->module, t_int, 0), index);
+ }
elem = lb_emit_conv(p, elem, alloc_type_pointer(type_of_expr(expr)));
auto index_tv = type_and_value_of_expr(ie->index);
if (index_tv.mode != Addressing_Constant) {
- lbValue len = lb_const_int(p->module, t_int, t->Matrix.column_count);
+ lbValue len = lb_const_int(p->module, t_int, bounds_len);
lb_emit_bounds_check(p, ast_token(ie->index), index, len);
}
return lb_addr(elem);
@@ -4025,13 +4334,6 @@ gb_internal lbAddr lb_build_addr_slice_expr(lbProcedure *p, Ast *expr) {
return slice;
}
- case Type_RelativePointer:
- GB_PANIC("TODO(bill): Type_RelativePointer should be handled above already on the lb_addr_load");
- break;
- case Type_RelativeMultiPointer:
- GB_PANIC("TODO(bill): Type_RelativeMultiPointer should be handled above already on the lb_addr_load");
- break;
-
case Type_DynamicArray: {
Type *elem_type = type->DynamicArray.elem;
Type *slice_type = alloc_type_slice(elem_type);
@@ -4056,7 +4358,7 @@ gb_internal lbAddr lb_build_addr_slice_expr(lbProcedure *p, Ast *expr) {
if (se->high == nullptr) {
lbValue offset = base;
LLVMValueRef indices[1] = {low.value};
- offset.value = LLVMBuildGEP2(p->builder, lb_type(p->module, offset.type->MultiPointer.elem), offset.value, indices, 1, "");
+ offset.value = LLVMBuildGEP2(p->builder, lb_type(p->module, base_type(offset.type)->MultiPointer.elem), offset.value, indices, 1, "");
lb_addr_store(p, res, offset);
} else {
low = lb_emit_conv(p, low, t_int);
@@ -4065,7 +4367,7 @@ gb_internal lbAddr lb_build_addr_slice_expr(lbProcedure *p, Ast *expr) {
lb_emit_multi_pointer_slice_bounds_check(p, se->open, low, high);
LLVMValueRef indices[1] = {low.value};
- LLVMValueRef ptr = LLVMBuildGEP2(p->builder, lb_type(p->module, base.type->MultiPointer.elem), base.value, indices, 1, "");
+ LLVMValueRef ptr = LLVMBuildGEP2(p->builder, lb_type(p->module, base_type(base.type)->MultiPointer.elem), base.value, indices, 1, "");
LLVMValueRef len = LLVMBuildSub(p->builder, high.value, low.value, "");
LLVMValueRef gep0 = lb_emit_struct_ep(p, res.addr, 0).value;
@@ -4099,7 +4401,7 @@ gb_internal lbAddr lb_build_addr_slice_expr(lbProcedure *p, Ast *expr) {
}
case Type_Basic: {
- GB_ASSERT_MSG(type == t_string, "got %s", type_to_string(type));
+ 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;
@@ -4133,6 +4435,7 @@ gb_internal lbAddr lb_build_addr_slice_expr(lbProcedure *p, Ast *expr) {
lbValue field_dst = lb_emit_struct_ep(p, dst.addr, i);
lbValue field_src = lb_emit_struct_ep(p, lb_addr_get_ptr(p, addr), i);
field_src = lb_emit_array_ep(p, field_src, low);
+ field_src = lb_emit_conv(p, field_src, type_deref(field_dst.type));
lb_emit_store(p, field_dst, field_src);
}
@@ -4148,6 +4451,7 @@ gb_internal lbAddr lb_build_addr_slice_expr(lbProcedure *p, Ast *expr) {
lbValue field_dst = lb_emit_struct_ep(p, dst.addr, i);
lbValue field_src = lb_emit_struct_ev(p, base, i);
field_src = lb_emit_ptr_offset(p, field_src, low);
+ field_src = lb_emit_conv(p, field_src, type_deref(field_dst.type));
lb_emit_store(p, field_dst, field_src);
}
@@ -4162,6 +4466,7 @@ gb_internal lbAddr lb_build_addr_slice_expr(lbProcedure *p, Ast *expr) {
lbValue field_dst = lb_emit_struct_ep(p, dst.addr, i);
lbValue field_src = lb_emit_struct_ev(p, base, i);
field_src = lb_emit_ptr_offset(p, field_src, low);
+ field_src = lb_emit_conv(p, field_src, type_deref(field_dst.type));
lb_emit_store(p, field_dst, field_src);
}
@@ -4211,6 +4516,208 @@ gb_internal lbAddr lb_build_addr_compound_lit(lbProcedure *p, Ast *expr) {
switch (bt->kind) {
default: GB_PANIC("Unknown CompoundLit type: %s", type_to_string(type)); break;
+ case Type_BitField: {
+ TEMPORARY_ALLOCATOR_GUARD();
+
+ // Type *backing_type = core_type(bt->BitField.backing_type);
+
+ struct FieldData {
+ Type *field_type;
+ u64 bit_offset;
+ u64 bit_size;
+ };
+ auto values = array_make<lbValue>(temporary_allocator(), 0, cl->elems.count);
+ auto fields = array_make<FieldData>(temporary_allocator(), 0, cl->elems.count);
+
+ for (Ast *elem : cl->elems) {
+ ast_node(fv, FieldValue, elem);
+ String name = fv->field->Ident.token.string;
+ Selection sel = lookup_field(bt, name, false);
+ GB_ASSERT(sel.is_bit_field);
+ GB_ASSERT(!sel.indirect);
+ GB_ASSERT(sel.index.count == 1);
+ GB_ASSERT(sel.entity != nullptr);
+
+ i64 index = sel.index[0];
+ Entity *f = bt->BitField.fields[index];
+ GB_ASSERT(f == sel.entity);
+ i64 bit_offset = bt->BitField.bit_offsets[index];
+ i64 bit_size = bt->BitField.bit_sizes[index];
+ GB_ASSERT(bit_size > 0);
+
+ Type *field_type = sel.entity->type;
+ lbValue field_expr = lb_build_expr(p, fv->value);
+ field_expr = lb_emit_conv(p, field_expr, field_type);
+ array_add(&values, field_expr);
+ array_add(&fields, FieldData{field_type, cast(u64)bit_offset, cast(u64)bit_size});
+ }
+
+ // NOTE(bill): inline insertion sort should be good enough, right?
+ for (isize i = 1; i < values.count; i++) {
+ for (isize j = i;
+ j > 0 && fields[i].bit_offset < fields[j].bit_offset;
+ j--) {
+ auto vtmp = values[j];
+ values[j] = values[j-1];
+ values[j-1] = vtmp;
+
+ auto ftmp = fields[j];
+ fields[j] = fields[j-1];
+ fields[j-1] = ftmp;
+ }
+ }
+
+ bool any_fields_different_endian = false;
+ for (auto const &f : fields) {
+ if (is_type_different_to_arch_endianness(f.field_type)) {
+ // NOTE(bill): Just be slow for this, to be correct
+ any_fields_different_endian = true;
+ break;
+ }
+ }
+
+ if (!any_fields_different_endian &&
+ fields.count == bt->BitField.fields.count) {
+ // SINGLE INTEGER BACKING ONLY
+
+ Type *backing_type = core_type(bt->BitField.backing_type);
+ GB_ASSERT(is_type_integer(backing_type) ||
+ (is_type_array(backing_type) && is_type_integer(backing_type->Array.elem)));
+
+ // NOTE(bill): all fields are present
+ // this means no masking is necessary since on write, the bits will be overridden
+
+ lbValue dst_byte_ptr = lb_emit_conv(p, v.addr, t_u8_ptr);
+ u64 total_bit_size = cast(u64)(8*type_size_of(bt));
+
+ if (is_type_integer(backing_type)) {
+ LLVMTypeRef lit = lb_type(p->module, backing_type);
+
+ LLVMValueRef res = LLVMConstInt(lit, 0, false);
+
+ for (isize i = 0; i < fields.count; i++) {
+ auto const &f = fields[i];
+
+ LLVMValueRef mask = LLVMConstInt(lit, 1, false);
+ #if LLVM_VERSION_MAJOR >= 19
+ mask = LLVMBuildShl(p->builder, mask, LLVMConstInt(lit, f.bit_size, false), "");
+ #else
+ mask = LLVMConstShl(mask, LLVMConstInt(lit, f.bit_size, false));
+ #endif
+ mask = LLVMConstSub(mask, LLVMConstInt(lit, 1, false));
+
+ LLVMValueRef elem = values[i].value;
+ if (lb_sizeof(lit) < lb_sizeof(LLVMTypeOf(elem))) {
+ elem = LLVMBuildTrunc(p->builder, elem, lit, "");
+ } else {
+ elem = LLVMBuildZExt(p->builder, elem, lit, "");
+ }
+ elem = LLVMBuildAnd(p->builder, elem, mask, "");
+
+ elem = LLVMBuildShl(p->builder, elem, LLVMConstInt(lit, f.bit_offset, false), "");
+
+ res = LLVMBuildOr(p->builder, res, elem, "");
+ }
+
+ LLVMBuildStore(p->builder, res, v.addr.value);
+ } else if (is_type_array(backing_type)) {
+ // ARRAY OF INTEGER BACKING
+
+ i64 array_count = backing_type->Array.count;
+ LLVMTypeRef lit = lb_type(p->module, core_type(backing_type->Array.elem));
+ gb_unused(array_count);
+ gb_unused(lit);
+
+ LLVMValueRef *elems = gb_alloc_array(temporary_allocator(), LLVMValueRef, array_count);
+ for (i64 i = 0; i < array_count; i++) {
+ elems[i] = LLVMConstInt(lit, 0, false);
+ }
+
+ u64 elem_bit_size = cast(u64)(8*type_size_of(backing_type->Array.elem));
+ u64 curr_bit_offset = 0;
+ for (isize i = 0; i < fields.count; i++) {
+ auto const &f = fields[i];
+
+ LLVMValueRef val = values[i].value;
+ LLVMTypeRef vt = lb_type(p->module, values[i].type);
+ for (u64 bits_to_set = f.bit_size;
+ bits_to_set > 0;
+ /**/) {
+ i64 elem_idx = curr_bit_offset/elem_bit_size;
+ u64 elem_bit_offset = curr_bit_offset%elem_bit_size;
+
+ u64 mask_width = gb_min(bits_to_set, elem_bit_size-elem_bit_offset);
+ GB_ASSERT(mask_width > 0);
+ bits_to_set -= mask_width;
+
+ LLVMValueRef mask = LLVMConstInt(vt, 1, false);
+ #if LLVM_VERSION_MAJOR >= 19
+ mask = LLVMBuildShl(p->builder, mask, LLVMConstInt(vt, mask_width, false), "");
+ #else
+ mask = LLVMConstShl(mask, LLVMConstInt(vt, mask_width, false));
+ #endif
+ mask = LLVMConstSub(mask, LLVMConstInt(vt, 1, false));
+
+ LLVMValueRef to_set = LLVMBuildAnd(p->builder, val, mask, "");
+
+ if (elem_bit_offset != 0) {
+ to_set = LLVMBuildShl(p->builder, to_set, LLVMConstInt(vt, elem_bit_offset, false), "");
+ }
+ to_set = LLVMBuildTrunc(p->builder, to_set, lit, "");
+
+ if (LLVMIsNull(elems[elem_idx])) {
+ elems[elem_idx] = to_set; // don't even bother doing `0 | to_set`
+ } else {
+ elems[elem_idx] = LLVMBuildOr(p->builder, elems[elem_idx], to_set, "");
+ }
+
+ if (mask_width != 0) {
+ val = LLVMBuildLShr(p->builder, val, LLVMConstInt(vt, mask_width, false), "");
+ }
+ curr_bit_offset += mask_width;
+ }
+
+ GB_ASSERT(curr_bit_offset == f.bit_offset + f.bit_size);
+ }
+
+ for (i64 i = 0; i < array_count; i++) {
+ LLVMValueRef elem_ptr = LLVMBuildStructGEP2(p->builder, lb_type(p->module, backing_type), v.addr.value, cast(unsigned)i, "");
+ LLVMBuildStore(p->builder, elems[i], elem_ptr);
+ }
+ } else {
+ // SLOW STORAGE
+
+ for_array(i, fields) {
+ auto const &f = fields[i];
+
+ if ((f.bit_offset & 7) == 0) {
+ u64 unpacked_bit_size = cast(u64)(8*type_size_of(f.field_type));
+ u64 byte_size = (f.bit_size+7)/8;
+
+ if (f.bit_offset + unpacked_bit_size <= total_bit_size) {
+ byte_size = unpacked_bit_size/8;
+ }
+ lbValue dst = lb_emit_ptr_offset(p, dst_byte_ptr, lb_const_int(p->module, t_int, f.bit_offset/8));
+ lbValue src = lb_address_from_load_or_generate_local(p, values[i]);
+ lb_mem_copy_non_overlapping(p, dst, src, lb_const_int(p->module, t_uintptr, byte_size));
+ } else {
+ lbAddr dst = lb_addr_bit_field(v.addr, f.field_type, f.bit_offset, f.bit_size);
+ lb_addr_store(p, dst, values[i]);
+ }
+ }
+ }
+ } else {
+ // individual storing
+ for_array(i, values) {
+ auto const &f = fields[i];
+ lbAddr dst = lb_addr_bit_field(v.addr, f.field_type, f.bit_offset, f.bit_size);
+ lb_addr_store(p, dst, values[i]);
+ }
+ }
+
+ return v;
+ }
+
case Type_Struct: {
// TODO(bill): "constant" '#raw_union's are not initialized constantly at the moment.
// NOTE(bill): This is due to the layout of the unions when printed to LLVM-IR
@@ -4239,10 +4746,26 @@ gb_internal lbAddr lb_build_addr_compound_lit(lbProcedure *p, Ast *expr) {
if (lb_is_nested_possibly_constant(type, sel, elem)) {
continue;
}
- lbValue dst = lb_emit_deep_field_gep(p, comp_lit_ptr, sel);
field_expr = lb_build_expr(p, elem);
field_expr = lb_emit_conv(p, field_expr, sel.entity->type);
- lb_emit_store(p, dst, field_expr);
+ if (sel.is_bit_field) {
+ Selection sub_sel = trim_selection(sel);
+ lbValue trimmed_dst = lb_emit_deep_field_gep(p, comp_lit_ptr, sub_sel);
+ Type *bf = base_type(type_deref(trimmed_dst.type));
+ if (is_type_pointer(bf)) {
+ trimmed_dst = lb_emit_load(p, trimmed_dst);
+ bf = base_type(type_deref(trimmed_dst.type));
+ }
+ GB_ASSERT(bf->kind == Type_BitField);
+
+ isize idx = sel.index[sel.index.count-1];
+ lbAddr dst = lb_addr_bit_field(trimmed_dst, bf->BitField.fields[idx]->type, bf->BitField.bit_offsets[idx], bf->BitField.bit_sizes[idx]);
+ lb_addr_store(p, dst, field_expr);
+
+ } else {
+ lbValue dst = lb_emit_deep_field_gep(p, comp_lit_ptr, sel);
+ lb_emit_store(p, dst, field_expr);
+ }
continue;
}
@@ -4290,7 +4813,7 @@ gb_internal lbAddr lb_build_addr_compound_lit(lbProcedure *p, Ast *expr) {
if (cl->elems.count == 0) {
break;
}
- GB_ASSERT(!build_context.no_dynamic_literals);
+ GB_ASSERT(expr->file()->feature_flags & OptInFeatureFlag_DynamicLiterals);
lbValue err = lb_dynamic_map_reserve(p, v.addr, 2*cl->elems.count, pos);
gb_unused(err);
@@ -4379,7 +4902,7 @@ gb_internal lbAddr lb_build_addr_compound_lit(lbProcedure *p, Ast *expr) {
if (cl->elems.count == 0) {
break;
}
- GB_ASSERT(!build_context.no_dynamic_literals);
+ GB_ASSERT(expr->file()->feature_flags & OptInFeatureFlag_DynamicLiterals);
Type *et = bt->DynamicArray.elem;
lbValue size = lb_const_int(p->module, t_int, type_size_of(et));
@@ -4467,29 +4990,43 @@ gb_internal lbAddr lb_build_addr_compound_lit(lbProcedure *p, Ast *expr) {
case Type_BitSet: {
i64 sz = type_size_of(type);
if (cl->elems.count > 0 && sz > 0) {
- lb_addr_store(p, v, lb_const_value(p->module, type, exact_value_compound(expr)));
-
lbValue lower = lb_const_value(p->module, t_int, exact_value_i64(bt->BitSet.lower));
- for (Ast *elem : cl->elems) {
- GB_ASSERT(elem->kind != Ast_FieldValue);
- if (lb_is_elem_const(elem, et)) {
- continue;
+ Type *backing = bit_set_to_int(type);
+ if (is_type_array(backing)) {
+ GB_PANIC("TODO: bit_set [N]T");
+ Type *base_it = core_array_type(backing);
+ i64 bits_per_elem = 8*type_size_of(base_it);
+ gb_unused(bits_per_elem);
+ lbValue one = lb_const_value(p->module, t_i64, exact_value_i64(1));
+ for (Ast *elem : cl->elems) {
+ GB_ASSERT(elem->kind != Ast_FieldValue);
+ lbValue expr = lb_build_expr(p, elem);
+ GB_ASSERT(expr.type->kind != Type_Tuple);
+
+ lbValue e = lb_emit_conv(p, expr, t_i64);
+ e = lb_emit_arith(p, Token_Sub, e, lower, t_i64);
+ // lbValue idx = lb_emit_arith(p, Token_Div, e, bits_per_elem, t_i64);
+ // lbValue val = lb_emit_arith(p, Token_Div, e, bits_per_elem, t_i64);
}
-
- lbValue expr = lb_build_expr(p, elem);
- GB_ASSERT(expr.type->kind != Type_Tuple);
-
+ } else {
Type *it = bit_set_to_int(bt);
lbValue one = lb_const_value(p->module, it, exact_value_i64(1));
- lbValue e = lb_emit_conv(p, expr, it);
- e = lb_emit_arith(p, Token_Sub, e, lower, it);
- e = lb_emit_arith(p, Token_Shl, one, e, it);
-
- lbValue old_value = lb_emit_transmute(p, lb_addr_load(p, v), it);
- lbValue new_value = lb_emit_arith(p, Token_Or, old_value, e, it);
- new_value = lb_emit_transmute(p, new_value, type);
- lb_addr_store(p, v, new_value);
+ for (Ast *elem : cl->elems) {
+ GB_ASSERT(elem->kind != Ast_FieldValue);
+
+ lbValue expr = lb_build_expr(p, elem);
+ GB_ASSERT(expr.type->kind != Type_Tuple);
+
+ lbValue e = lb_emit_conv(p, expr, it);
+ e = lb_emit_arith(p, Token_Sub, e, lower, it);
+ e = lb_emit_arith(p, Token_Shl, one, e, it);
+
+ lbValue old_value = lb_emit_transmute(p, lb_addr_load(p, v), it);
+ lbValue new_value = lb_emit_arith(p, Token_Or, old_value, e, it);
+ new_value = lb_emit_transmute(p, new_value, type);
+ lb_addr_store(p, v, new_value);
+ }
}
}
break;
@@ -4591,15 +5128,17 @@ gb_internal lbAddr lb_build_addr_internal(lbProcedure *p, Ast *expr) {
if (tav.mode == Addressing_Type) { // Addressing_Type
Selection sel = lookup_field(tav.type, selector, true);
if (sel.pseudo_field) {
- GB_ASSERT(sel.entity->kind == Entity_Procedure);
- return lb_addr(lb_find_value_from_entity(p->module, sel.entity));
+ GB_ASSERT(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));
}
GB_PANIC("Unreachable %.*s", LIT(selector));
}
if (se->swizzle_count > 0) {
Type *array_type = base_type(type_deref(tav.type));
- GB_ASSERT(array_type->kind == Type_Array);
+ GB_ASSERT(array_type->kind == Type_Array || array_type->kind == Type_SimdVector);
u8 swizzle_count = se->swizzle_count;
u8 swizzle_indices_raw = se->swizzle_indices;
u8 swizzle_indices[4] = {};
@@ -4615,8 +5154,9 @@ gb_internal lbAddr lb_build_addr_internal(lbProcedure *p, Ast *expr) {
a = lb_addr_get_ptr(p, addr);
}
- GB_ASSERT(is_type_array(expr->tav.type));
- return lb_addr_swizzle(a, expr->tav.type, swizzle_count, swizzle_indices);
+ Type *type = type_deref(expr->tav.type);
+ GB_ASSERT(is_type_array(type) || is_type_simd_vector(type));
+ return lb_addr_swizzle(a, type, swizzle_count, swizzle_indices);
}
Selection sel = lookup_field(type, selector, false);
@@ -4628,6 +5168,33 @@ gb_internal lbAddr lb_build_addr_internal(lbProcedure *p, Ast *expr) {
return lb_addr(lb_find_value_from_entity(p->module, e));
}
+ if (sel.is_bit_field) {
+ lbAddr addr = lb_build_addr(p, se->expr);
+
+ Selection sub_sel = sel;
+ sub_sel.index.count -= 1;
+
+ lbValue ptr = lb_addr_get_ptr(p, addr);
+ if (sub_sel.index.count > 0) {
+ ptr = lb_emit_deep_field_gep(p, ptr, sub_sel);
+ }
+ if (is_type_pointer(type_deref(ptr.type))) {
+ ptr = lb_emit_load(p, ptr);
+ }
+
+ Type *bf_type = type_deref(ptr.type);
+ bf_type = base_type(bf_type);
+ GB_ASSERT(bf_type->kind == Type_BitField);
+
+ i32 index = sel.index[sel.index.count-1];
+
+ Entity *f = bf_type->BitField.fields[index];
+ u8 bit_size = bf_type->BitField.bit_sizes[index];
+ i64 bit_offset = bf_type->BitField.bit_offsets[index];
+
+ return lb_addr_bit_field(ptr, f->type, bit_offset, bit_size);
+ }
+
{
lbAddr addr = lb_build_addr(p, se->expr);
if (addr.kind == lbAddr_Map) {
@@ -4670,6 +5237,9 @@ gb_internal lbAddr lb_build_addr_internal(lbProcedure *p, Ast *expr) {
if (sub_sel.index.count > 0) {
item = lb_emit_deep_field_gep(p, item, sub_sel);
}
+ // make sure it's ^T and not [^]T
+ item.type = alloc_type_multi_pointer_to_pointer(item.type);
+
return lb_addr(item);
} else if (addr.kind == lbAddr_Swizzle) {
GB_ASSERT(sel.index.count > 0);
@@ -4681,6 +5251,11 @@ gb_internal lbAddr lb_build_addr_internal(lbProcedure *p, Ast *expr) {
sel.index[0] = addr.swizzle.indices[sel.index[0]];
}
+ Type *atype = type_deref(lb_addr_type(addr));
+ if (is_type_soa_struct(atype)) {
+ map_set(&p->selector_addr, expr, addr);
+ }
+
lbValue a = lb_addr_get_ptr(p, addr);
a = lb_emit_deep_field_gep(p, a, sel);
return lb_addr(a);
@@ -4770,11 +5345,7 @@ gb_internal lbAddr lb_build_addr_internal(lbProcedure *p, Ast *expr) {
case_ast_node(de, DerefExpr, expr);
Type *t = type_of_expr(de->expr);
- if (is_type_relative_pointer(t)) {
- lbAddr addr = lb_build_addr(p, de->expr);
- addr.relative.deref = true;
- return addr;
- } else if (is_type_soa_pointer(t)) {
+ if (is_type_soa_pointer(t)) {
lbValue value = lb_build_expr(p, de->expr);
lbValue ptr = lb_emit_struct_ev(p, value, 0);
lbValue idx = lb_emit_struct_ev(p, value, 1);
@@ -4886,6 +5457,54 @@ gb_internal lbAddr lb_build_addr_internal(lbProcedure *p, Ast *expr) {
lbValue ptr = lb_address_from_load_or_generate_local(p, lb_build_expr(p, expr));
return lb_addr(ptr);
case_end;
+
+
+ case_ast_node(be, OrBranchExpr, expr);
+ lbBlock *block = nullptr;
+
+ if (be->label != nullptr) {
+ lbBranchBlocks bb = lb_lookup_branch_blocks(p, be->label);
+ switch (be->token.kind) {
+ case Token_or_break: block = bb.break_; break;
+ case Token_or_continue: block = bb.continue_; break;
+ }
+ } else {
+ for (lbTargetList *t = p->target_list; t != nullptr && block == nullptr; t = t->prev) {
+ if (t->is_block) {
+ continue;
+ }
+
+ switch (be->token.kind) {
+ case Token_or_break: block = t->break_; break;
+ case Token_or_continue: block = t->continue_; break;
+ }
+ }
+ }
+
+ GB_ASSERT(block != nullptr);
+ TypeAndValue tv = expr->tav;
+
+ lbValue lhs = {};
+ lbValue rhs = {};
+ lb_emit_try_lhs_rhs(p, be->expr, tv, &lhs, &rhs);
+ Type *type = default_type(tv.type);
+ if (lhs.value) {
+ lhs = lb_emit_conv(p, lhs, type);
+ } else if (type != nullptr && type != t_invalid) {
+ lhs = lb_const_nil(p->module, type);
+ }
+
+ lbBlock *then = lb_create_block(p, "or_branch.then");
+ lbBlock *else_ = lb_create_block(p, "or_branch.else");
+
+ lb_emit_if(p, lb_emit_try_has_value(p, rhs), then, else_);
+ lb_start_block(p, else_);
+ lb_emit_defer_stmts(p, lbDeferExit_Branch, block, expr);
+ lb_emit_jump(p, block);
+ lb_start_block(p, then);
+
+ return lb_addr(lb_address_from_load_or_generate_local(p, lhs));
+ case_end;
}
TokenPos token_pos = ast_token(expr).pos;
diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp
index fdcf94f29..762256258 100644
--- a/src/llvm_backend_general.cpp
+++ b/src/llvm_backend_general.cpp
@@ -29,8 +29,9 @@ gb_internal void lb_init_module(lbModule *m, Checker *c) {
module_name = gb_string_appendc(module_name, "-builtin");
}
+ m->module_name = module_name ? module_name : "odin_package";
m->ctx = LLVMContextCreate();
- m->mod = LLVMModuleCreateWithNameInContext(module_name ? module_name : "odin_package", m->ctx);
+ m->mod = LLVMModuleCreateWithNameInContext(m->module_name, m->ctx);
// m->debug_builder = nullptr;
if (build_context.ODIN_DEBUG) {
enum {DEBUG_METADATA_VERSION = 3};
@@ -71,17 +72,17 @@ gb_internal void lb_init_module(lbModule *m, Checker *c) {
map_init(&m->hasher_procs);
map_init(&m->map_get_procs);
map_init(&m->map_set_procs);
- if (build_context.use_separate_modules) {
+ if (USE_SEPARATE_MODULES) {
array_init(&m->procedures_to_generate, a, 0, 1<<10);
map_init(&m->procedure_values, 1<<11);
} else {
array_init(&m->procedures_to_generate, a, 0, c->info.all_procedures.count);
map_init(&m->procedure_values, c->info.all_procedures.count*2);
}
- array_init(&m->global_procedures_and_types_to_create, a, 0, 1024);
+ array_init(&m->global_procedures_to_create, a, 0, 1024);
+ array_init(&m->global_types_to_create, a, 0, 1024);
array_init(&m->missing_procedures_to_check, a, 0, 16);
map_init(&m->debug_values);
- array_init(&m->debug_incomplete_types, a, 0, 1024);
string_map_init(&m->objc_classes);
string_map_init(&m->objc_selectors);
@@ -90,6 +91,9 @@ gb_internal void lb_init_module(lbModule *m, Checker *c) {
map_init(&m->map_cell_info_map, 0);
map_init(&m->exact_value_compound_literal_addr_map, 1024);
+ array_init(&m->pad_types, heap_allocator());
+
+
m->const_dummy_builder = LLVMCreateBuilderInContext(m->ctx);
}
@@ -107,6 +111,10 @@ gb_internal bool lb_init_generator(lbGenerator *gen, Checker *c) {
String init_fullpath = c->parser->init_fullpath;
linker_data_init(gen, &c->info, init_fullpath);
+ #if defined(GB_SYSTEM_OSX) && (LLVM_VERSION_MAJOR < 14)
+ linker_enable_system_library_linking(gen);
+ #endif
+
gen->info = &c->info;
map_init(&gen->modules, gen->info->packages.count*2);
@@ -114,15 +122,17 @@ gb_internal bool lb_init_generator(lbGenerator *gen, Checker *c) {
map_init(&gen->anonymous_proc_lits, 1024);
if (USE_SEPARATE_MODULES) {
+ bool module_per_file = build_context.module_per_file && build_context.optimization_level <= 0;
for (auto const &entry : gen->info->packages) {
AstPackage *pkg = entry.value;
- #if 1
auto m = gb_alloc_item(permanent_allocator(), lbModule);
m->pkg = pkg;
m->gen = gen;
map_set(&gen->modules, cast(void *)pkg, m);
lb_init_module(m, c);
- #else
+ if (!module_per_file) {
+ continue;
+ }
// NOTE(bill): Probably per file is not a good idea, so leave this for later
for (AstFile *file : pkg->files) {
auto m = gb_alloc_item(permanent_allocator(), lbModule);
@@ -132,21 +142,21 @@ gb_internal bool lb_init_generator(lbGenerator *gen, Checker *c) {
map_set(&gen->modules, cast(void *)file, m);
lb_init_module(m, c);
}
- #endif
}
}
gen->default_module.gen = gen;
- map_set(&gen->modules, cast(void *)nullptr, &gen->default_module);
+ map_set(&gen->modules, cast(void *)1, &gen->default_module);
lb_init_module(&gen->default_module, c);
-
for (auto const &entry : gen->modules) {
lbModule *m = entry.value;
LLVMContextRef ctx = LLVMGetModuleContext(m->mod);
map_set(&gen->modules_through_ctx, ctx, m);
}
+ mpsc_init(&gen->entities_to_correct_linkage, heap_allocator());
+
return true;
}
@@ -208,7 +218,7 @@ gb_internal void lb_loop_end(lbProcedure *p, lbLoopData const &data) {
gb_internal void lb_make_global_private_const(LLVMValueRef global_data) {
- LLVMSetLinkage(global_data, LLVMPrivateLinkage);
+ LLVMSetLinkage(global_data, LLVMLinkerPrivateLinkage);
LLVMSetUnnamedAddress(global_data, LLVMGlobalUnnamedAddr);
LLVMSetGlobalConstant(global_data, true);
}
@@ -383,12 +393,14 @@ gb_internal lbModule *lb_module_of_entity(lbGenerator *gen, Entity *e) {
if (e->file) {
found = map_get(&gen->modules, cast(void *)e->file);
if (found) {
+ GB_ASSERT(*found != nullptr);
return *found;
}
}
if (e->pkg) {
found = map_get(&gen->modules, cast(void *)e->pkg);
if (found) {
+ GB_ASSERT(*found != nullptr);
return *found;
}
}
@@ -397,14 +409,6 @@ gb_internal lbModule *lb_module_of_entity(lbGenerator *gen, Entity *e) {
gb_internal lbAddr lb_addr(lbValue addr) {
lbAddr v = {lbAddr_Default, addr};
- if (addr.type != nullptr && is_type_relative_pointer(type_deref(addr.type))) {
- GB_ASSERT(is_type_pointer(addr.type));
- v.kind = lbAddr_RelativePointer;
- } else if (addr.type != nullptr && is_type_relative_multi_pointer(type_deref(addr.type))) {
- GB_ASSERT(is_type_pointer(addr.type) ||
- is_type_multi_pointer(addr.type));
- v.kind = lbAddr_RelativePointer;
- }
return v;
}
@@ -430,7 +434,7 @@ gb_internal lbAddr lb_addr_soa_variable(lbValue addr, lbValue index, Ast *index_
}
gb_internal lbAddr lb_addr_swizzle(lbValue addr, Type *array_type, u8 swizzle_count, u8 swizzle_indices[4]) {
- GB_ASSERT(is_type_array(array_type));
+ GB_ASSERT(is_type_array(array_type) || is_type_simd_vector(array_type));
GB_ASSERT(1 < swizzle_count && swizzle_count <= 4);
lbAddr v = {lbAddr_Swizzle, addr};
v.swizzle.type = array_type;
@@ -447,6 +451,19 @@ gb_internal lbAddr lb_addr_swizzle_large(lbValue addr, Type *array_type, Slice<i
return v;
}
+gb_internal lbAddr lb_addr_bit_field(lbValue addr, Type *type, i64 bit_offset, i64 bit_size) {
+ GB_ASSERT(is_type_pointer(addr.type));
+ Type *mt = type_deref(addr.type);
+ GB_ASSERT_MSG(is_type_bit_field(mt), "%s", type_to_string(mt));
+
+ lbAddr v = {lbAddr_BitField, addr};
+ v.bitfield.type = type;
+ v.bitfield.bit_offset = bit_offset;
+ v.bitfield.bit_size = bit_size;
+ return v;
+}
+
+
gb_internal Type *lb_addr_type(lbAddr const &addr) {
if (addr.addr.value == nullptr) {
return nullptr;
@@ -476,43 +493,16 @@ gb_internal Type *lb_addr_type(lbAddr const &addr) {
return type_deref(addr.addr.type);
}
+gb_internal lbValue lb_make_soa_pointer(lbProcedure *p, Type *type, lbValue const &addr, lbValue const &index) {
+ lbAddr v = lb_add_local_generated(p, type, false);
+ lbValue ptr = lb_emit_struct_ep(p, v.addr, 0);
+ lbValue idx = lb_emit_struct_ep(p, v.addr, 1);
+ lb_emit_store(p, ptr, addr);
+ lb_emit_store(p, idx, lb_emit_conv(p, index, t_int));
-gb_internal lbValue lb_relative_pointer_to_pointer(lbProcedure *p, lbAddr const &addr) {
- GB_ASSERT(addr.kind == lbAddr_RelativePointer);
-
- Type *t = base_type(lb_addr_type(addr));
- GB_ASSERT(is_type_relative_pointer(t) || is_type_relative_multi_pointer(t));
-
- Type *pointer_type = nullptr;
- Type *base_integer = nullptr;
- if (t->kind == Type_RelativePointer) {
- pointer_type = t->RelativePointer.pointer_type;
- base_integer = t->RelativePointer.base_integer;
- } else if (t->kind == Type_RelativeMultiPointer) {
- pointer_type = t->RelativeMultiPointer.pointer_type;
- base_integer = t->RelativeMultiPointer.base_integer;
- }
-
- lbValue ptr = lb_emit_conv(p, addr.addr, t_uintptr);
- lbValue offset = lb_emit_conv(p, ptr, alloc_type_pointer(base_integer));
- offset = lb_emit_load(p, offset);
-
- if (!is_type_unsigned(base_integer)) {
- offset = lb_emit_conv(p, offset, t_i64);
- }
- offset = lb_emit_conv(p, offset, t_uintptr);
- lbValue absolute_ptr = lb_emit_arith(p, Token_Add, ptr, offset, t_uintptr);
- absolute_ptr = lb_emit_conv(p, absolute_ptr, pointer_type);
-
- lbValue cond = lb_emit_comp(p, Token_CmpEq, offset, lb_const_nil(p->module, base_integer));
-
- // NOTE(bill): nil check
- lbValue nil_ptr = lb_const_nil(p->module, pointer_type);
- lbValue final_ptr = lb_emit_select(p, cond, nil_ptr, absolute_ptr);
- return final_ptr;
+ return lb_addr_load(p, v);
}
-
gb_internal lbValue lb_addr_get_ptr(lbProcedure *p, lbAddr const &addr) {
if (addr.addr.value == nullptr) {
GB_PANIC("Illegal addr -> nullptr");
@@ -523,12 +513,13 @@ gb_internal lbValue lb_addr_get_ptr(lbProcedure *p, lbAddr const &addr) {
case lbAddr_Map:
return lb_internal_dynamic_map_get_ptr(p, addr.addr, addr.map.key);
- case lbAddr_RelativePointer:
- return lb_relative_pointer_to_pointer(p, addr);
-
case lbAddr_SoaVariable:
- // TODO(bill): FIX THIS HACK
- return lb_address_from_load(p, lb_addr_load(p, addr));
+ {
+ Type *soa_ptr_type = alloc_type_soa_pointer(lb_addr_type(addr));
+ return lb_address_from_load_or_generate_local(p, lb_make_soa_pointer(p, soa_ptr_type, addr.addr, addr.soa.index));
+ // TODO(bill): FIX THIS HACK
+ // return lb_address_from_load(p, lb_addr_load(p, addr));
+ }
case lbAddr_Context:
GB_PANIC("lbAddr_Context should be handled elsewhere");
@@ -546,9 +537,6 @@ gb_internal lbValue lb_addr_get_ptr(lbProcedure *p, lbAddr const &addr) {
gb_internal lbValue lb_build_addr_ptr(lbProcedure *p, Ast *expr) {
lbAddr addr = lb_build_addr(p, expr);
- if (addr.kind == lbAddr_RelativePointer) {
- return addr.addr;
- }
return lb_addr_get_ptr(p, addr);
}
@@ -684,7 +672,10 @@ gb_internal unsigned lb_try_get_alignment(LLVMValueRef addr_ptr, unsigned defaul
gb_internal bool lb_try_update_alignment(LLVMValueRef addr_ptr, unsigned alignment) {
if (LLVMIsAGlobalValue(addr_ptr) || LLVMIsAAllocaInst(addr_ptr) || LLVMIsALoadInst(addr_ptr)) {
if (LLVMGetAlignment(addr_ptr) < alignment) {
- if (LLVMIsAAllocaInst(addr_ptr) || LLVMIsAGlobalValue(addr_ptr)) {
+ if (LLVMIsAAllocaInst(addr_ptr)) {
+ LLVMSetAlignment(addr_ptr, alignment);
+ } else if (LLVMIsAGlobalValue(addr_ptr) && LLVMGetLinkage(addr_ptr) != LLVMExternalLinkage) {
+ // NOTE(laytan): setting alignment of an external global just changes the alignment we expect it to be.
LLVMSetAlignment(addr_ptr, alignment);
}
}
@@ -717,10 +708,7 @@ gb_internal bool lb_try_vector_cast(lbModule *m, lbValue ptr, LLVMTypeRef *vecto
LLVMValueRef addr_ptr = ptr.value;
if (LLVMIsAAllocaInst(addr_ptr) || LLVMIsAGlobalValue(addr_ptr)) {
- unsigned alignment = LLVMGetAlignment(addr_ptr);
- alignment = gb_max(alignment, vector_alignment);
- possible = true;
- LLVMSetAlignment(addr_ptr, alignment);
+ possible = lb_try_update_alignment(addr_ptr, vector_alignment);
} else if (LLVMIsALoadInst(addr_ptr)) {
unsigned alignment = LLVMGetAlignment(addr_ptr);
possible = alignment >= vector_alignment;
@@ -736,6 +724,47 @@ gb_internal bool lb_try_vector_cast(lbModule *m, lbValue ptr, LLVMTypeRef *vecto
return false;
}
+gb_internal LLVMValueRef OdinLLVMBuildLoad(lbProcedure *p, LLVMTypeRef type, LLVMValueRef value) {
+ LLVMValueRef result = LLVMBuildLoad2(p->builder, type, value, "");
+
+ // If it is not an instruction it isn't a GEP, so we don't need to track alignment in the metadata,
+ // which is not possible anyway (only LLVM instructions can have metadata).
+ if (LLVMIsAInstruction(value)) {
+ u64 is_packed = lb_get_metadata_custom_u64(p->module, value, ODIN_METADATA_IS_PACKED);
+ if (is_packed != 0) {
+ LLVMSetAlignment(result, 1);
+ }
+ u64 align = LLVMGetAlignment(result);
+ u64 align_min = lb_get_metadata_custom_u64(p->module, value, ODIN_METADATA_MIN_ALIGN);
+ u64 align_max = lb_get_metadata_custom_u64(p->module, value, ODIN_METADATA_MAX_ALIGN);
+ if (align_min != 0 && align < align_min) {
+ align = align_min;
+ }
+ if (align_max != 0 && align > align_max) {
+ align = align_max;
+ }
+ GB_ASSERT(align <= UINT_MAX);
+ LLVMSetAlignment(result, (unsigned int)align);
+ }
+
+ return result;
+}
+
+gb_internal LLVMValueRef OdinLLVMBuildLoadAligned(lbProcedure *p, LLVMTypeRef type, LLVMValueRef value, i64 alignment) {
+ LLVMValueRef result = LLVMBuildLoad2(p->builder, type, value, "");
+
+ LLVMSetAlignment(result, cast(unsigned)alignment);
+
+ if (LLVMIsAInstruction(value)) {
+ u64 is_packed = lb_get_metadata_custom_u64(p->module, value, ODIN_METADATA_IS_PACKED);
+ if (is_packed != 0) {
+ LLVMSetAlignment(result, 1);
+ }
+ }
+
+ return result;
+}
+
gb_internal void lb_addr_store(lbProcedure *p, lbAddr addr, lbValue value) {
if (addr.addr.value == nullptr) {
return;
@@ -751,48 +780,43 @@ gb_internal void lb_addr_store(lbProcedure *p, lbAddr addr, lbValue value) {
value.value = LLVMConstNull(lb_type(p->module, t));
}
- if (addr.kind == lbAddr_RelativePointer && addr.relative.deref) {
- addr = lb_addr(lb_address_from_load(p, lb_addr_load(p, addr)));
- }
-
- if (addr.kind == lbAddr_RelativePointer) {
- Type *rel_ptr = base_type(lb_addr_type(addr));
- GB_ASSERT(rel_ptr->kind == Type_RelativePointer ||
- rel_ptr->kind == Type_RelativeMultiPointer);
- Type *pointer_type = nullptr;
- Type *base_integer = nullptr;
-
- if (rel_ptr->kind == Type_RelativePointer) {
- pointer_type = rel_ptr->RelativePointer.pointer_type;
- base_integer = rel_ptr->RelativePointer.base_integer;
- } else if (rel_ptr->kind == Type_RelativeMultiPointer) {
- pointer_type = rel_ptr->RelativeMultiPointer.pointer_type;
- base_integer = rel_ptr->RelativeMultiPointer.base_integer;
- }
-
- value = lb_emit_conv(p, value, pointer_type);
-
- GB_ASSERT(is_type_pointer(addr.addr.type));
- lbValue ptr = lb_emit_conv(p, addr.addr, t_uintptr);
- lbValue val_ptr = lb_emit_conv(p, value, t_uintptr);
- lbValue offset = {};
- offset.value = LLVMBuildSub(p->builder, val_ptr.value, ptr.value, "");
- offset.type = t_uintptr;
+ if (addr.kind == lbAddr_BitField) {
+ lbValue dst = addr.addr;
+ if (is_type_endian_big(addr.bitfield.type)) {
+ i64 shift_amount = 8*type_size_of(value.type) - addr.bitfield.bit_size;
+ lbValue shifted_value = value;
+ shifted_value.value = LLVMBuildLShr(p->builder,
+ shifted_value.value,
+ LLVMConstInt(LLVMTypeOf(shifted_value.value), shift_amount, false), "");
+
+ lbValue src = lb_address_from_load_or_generate_local(p, shifted_value);
+
+ auto args = array_make<lbValue>(temporary_allocator(), 4);
+ args[0] = dst;
+ args[1] = src;
+ args[2] = lb_const_int(p->module, t_uintptr, addr.bitfield.bit_offset);
+ args[3] = lb_const_int(p->module, t_uintptr, addr.bitfield.bit_size);
+ lb_emit_runtime_call(p, "__write_bits", args);
+ } else if ((addr.bitfield.bit_offset % 8) == 0 &&
+ (addr.bitfield.bit_size % 8) == 0) {
+ lbValue src = lb_address_from_load_or_generate_local(p, value);
+
+ lbValue byte_offset = lb_const_int(p->module, t_uintptr, addr.bitfield.bit_offset/8);
+ lbValue byte_size = lb_const_int(p->module, t_uintptr, addr.bitfield.bit_size/8);
+ lbValue dst_offset = lb_emit_conv(p, dst, t_u8_ptr);
+ dst_offset = lb_emit_ptr_offset(p, dst_offset, byte_offset);
+ lb_mem_copy_non_overlapping(p, dst_offset, src, byte_size);
+ } else {
+ lbValue src = lb_address_from_load_or_generate_local(p, value);
- if (!is_type_unsigned(base_integer)) {
- offset = lb_emit_conv(p, offset, t_i64);
+ auto args = array_make<lbValue>(temporary_allocator(), 4);
+ args[0] = dst;
+ args[1] = src;
+ args[2] = lb_const_int(p->module, t_uintptr, addr.bitfield.bit_offset);
+ args[3] = lb_const_int(p->module, t_uintptr, addr.bitfield.bit_size);
+ lb_emit_runtime_call(p, "__write_bits", args);
}
- offset = lb_emit_conv(p, offset, base_integer);
-
- lbValue offset_ptr = lb_emit_conv(p, addr.addr, alloc_type_pointer(base_integer));
- offset = lb_emit_select(p,
- lb_emit_comp(p, Token_CmpEq, val_ptr, lb_const_nil(p->module, t_uintptr)),
- lb_const_nil(p->module, base_integer),
- offset
- );
- LLVMBuildStore(p->builder, offset.value, offset_ptr.value);
return;
-
} else if (addr.kind == lbAddr_Map) {
lb_internal_dynamic_map_set(p, addr.addr, addr.map.type, addr.map.key, value, p->curr_stmt);
return;
@@ -927,16 +951,6 @@ 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));
- // if (lb_is_const_or_global(value)) {
- // // NOTE(bill): Just bypass the actual storage and set the initializer
- // if (LLVMGetValueKind(addr.addr.value) == LLVMGlobalVariableValueKind) {
- // LLVMValueRef dst = addr.addr.value;
- // LLVMValueRef src = value.value;
- // LLVMSetInitializer(dst, src);
- // return;
- // }
- // }
-
lb_emit_store(p, addr.addr, value);
}
@@ -967,13 +981,15 @@ gb_internal void lb_emit_store(lbProcedure *p, lbValue ptr, lbValue value) {
return;
}
- Type *a = type_deref(ptr.type);
+ Type *a = type_deref(ptr.type, true);
if (LLVMIsNull(value.value)) {
LLVMTypeRef src_t = llvm_addr_type(p->module, ptr);
if (is_type_proc(a)) {
LLVMTypeRef rawptr_type = lb_type(p->module, t_rawptr);
LLVMTypeRef rawptr_ptr_type = LLVMPointerType(rawptr_type, 0);
LLVMBuildStore(p->builder, LLVMConstNull(rawptr_type), LLVMBuildBitCast(p->builder, ptr.value, rawptr_ptr_type, ""));
+ } else if (is_type_bit_set(a)) {
+ lb_mem_zero_ptr(p, ptr.value, a, 1);
} else if (lb_sizeof(src_t) <= lb_max_zero_init_size()) {
LLVMBuildStore(p->builder, LLVMConstNull(src_t), ptr.value);
} else {
@@ -1052,7 +1068,7 @@ gb_internal lbValue lb_emit_load(lbProcedure *p, lbValue value) {
Type *vt = base_type(value.type);
GB_ASSERT(vt->kind == Type_MultiPointer);
Type *t = vt->MultiPointer.elem;
- LLVMValueRef v = LLVMBuildLoad2(p->builder, lb_type(p->module, t), value.value, "");
+ LLVMValueRef v = OdinLLVMBuildLoad(p, lb_type(p->module, t), value.value);
return lbValue{v, t};
} else if (is_type_soa_pointer(value.type)) {
lbValue ptr = lb_emit_struct_ev(p, value, 0);
@@ -1061,56 +1077,94 @@ gb_internal lbValue lb_emit_load(lbProcedure *p, lbValue value) {
return lb_addr_load(p, addr);
}
- GB_ASSERT(is_type_pointer(value.type));
+ GB_ASSERT_MSG(is_type_pointer(value.type), "%s", type_to_string(value.type));
Type *t = type_deref(value.type);
- LLVMValueRef v = LLVMBuildLoad2(p->builder, lb_type(p->module, t), value.value, "");
+ LLVMValueRef v = OdinLLVMBuildLoad(p, lb_type(p->module, t), value.value);
+
return lbValue{v, t};
}
gb_internal lbValue lb_addr_load(lbProcedure *p, lbAddr const &addr) {
GB_ASSERT(addr.addr.value != nullptr);
+ if (addr.kind == lbAddr_BitField) {
+ Type *ct = core_type(addr.bitfield.type);
+ bool do_mask = false;
+ if (is_type_unsigned(ct) || is_type_boolean(ct)) {
+ // Mask
+ if (addr.bitfield.bit_size != 8*type_size_of(ct)) {
+ do_mask = true;
+ }
+ }
- if (addr.kind == lbAddr_RelativePointer) {
- Type *rel_ptr = base_type(lb_addr_type(addr));
- Type *base_integer = nullptr;
- Type *pointer_type = nullptr;
- GB_ASSERT(rel_ptr->kind == Type_RelativePointer ||
- rel_ptr->kind == Type_RelativeMultiPointer);
-
- if (rel_ptr->kind == Type_RelativePointer) {
- base_integer = rel_ptr->RelativePointer.base_integer;
- pointer_type = rel_ptr->RelativePointer.pointer_type;
- } else if (rel_ptr->kind == Type_RelativeMultiPointer) {
- base_integer = rel_ptr->RelativeMultiPointer.base_integer;
- pointer_type = rel_ptr->RelativeMultiPointer.pointer_type;
+ i64 total_bitfield_bit_size = 8*type_size_of(lb_addr_type(addr));
+ i64 dst_byte_size = type_size_of(addr.bitfield.type);
+ lbAddr dst = lb_add_local_generated(p, addr.bitfield.type, true);
+ lbValue src = addr.addr;
+
+ lbValue bit_offset = lb_const_int(p->module, t_uintptr, addr.bitfield.bit_offset);
+ lbValue bit_size = lb_const_int(p->module, t_uintptr, addr.bitfield.bit_size);
+ lbValue byte_offset = lb_const_int(p->module, t_uintptr, (addr.bitfield.bit_offset+7)/8);
+ lbValue byte_size = lb_const_int(p->module, t_uintptr, (addr.bitfield.bit_size+7)/8);
+
+ GB_ASSERT(type_size_of(addr.bitfield.type) >= ((addr.bitfield.bit_size+7)/8));
+
+ lbValue r = {};
+ if (is_type_endian_big(addr.bitfield.type)) {
+ auto args = array_make<lbValue>(temporary_allocator(), 4);
+ args[0] = dst.addr;
+ args[1] = src;
+ args[2] = bit_offset;
+ args[3] = bit_size;
+ lb_emit_runtime_call(p, "__read_bits", args);
+
+ LLVMValueRef shift_amount = LLVMConstInt(
+ lb_type(p->module, lb_addr_type(dst)),
+ 8*dst_byte_size - addr.bitfield.bit_size,
+ false
+ );
+ r = lb_addr_load(p, dst);
+ r.value = LLVMBuildShl(p->builder, r.value, shift_amount, "");
+ } else if ((addr.bitfield.bit_offset % 8) == 0) {
+ do_mask = 8*dst_byte_size != addr.bitfield.bit_size;
+
+ lbValue copy_size = byte_size;
+ lbValue src_offset = lb_emit_conv(p, src, t_u8_ptr);
+ src_offset = lb_emit_ptr_offset(p, src_offset, byte_offset);
+ if (addr.bitfield.bit_offset + 8*dst_byte_size <= total_bitfield_bit_size) {
+ copy_size = lb_const_int(p->module, t_uintptr, dst_byte_size);
+ }
+ lb_mem_copy_non_overlapping(p, dst.addr, src_offset, copy_size, false);
+ r = lb_addr_load(p, dst);
+ } else {
+ auto args = array_make<lbValue>(temporary_allocator(), 4);
+ args[0] = dst.addr;
+ args[1] = src;
+ args[2] = bit_offset;
+ args[3] = bit_size;
+ lb_emit_runtime_call(p, "__read_bits", args);
+ r = lb_addr_load(p, dst);
}
- lbValue ptr = lb_emit_conv(p, addr.addr, t_uintptr);
- lbValue offset = lb_emit_conv(p, ptr, alloc_type_pointer(base_integer));
- offset = lb_emit_load(p, offset);
+ Type *t = addr.bitfield.type;
+ if (do_mask) {
+ GB_ASSERT(addr.bitfield.bit_size <= 8*type_size_of(ct));
- if (!is_type_unsigned(base_integer)) {
- offset = lb_emit_conv(p, offset, t_i64);
+ lbValue mask = lb_const_int(p->module, t, (1ull<<cast(u64)addr.bitfield.bit_size)-1);
+ r = lb_emit_arith(p, Token_And, r, mask, t);
}
- offset = lb_emit_conv(p, offset, t_uintptr);
- lbValue absolute_ptr = lb_emit_arith(p, Token_Add, ptr, offset, t_uintptr);
- absolute_ptr = lb_emit_conv(p, absolute_ptr, pointer_type);
-
- lbValue cond = lb_emit_comp(p, Token_CmpEq, offset, lb_const_nil(p->module, base_integer));
-
- // NOTE(bill): nil check
- lbValue nil_ptr = lb_const_nil(p->module, pointer_type);
- lbValue final_ptr = {};
- final_ptr.type = absolute_ptr.type;
- final_ptr.value = LLVMBuildSelect(p->builder, cond.value, nil_ptr.value, absolute_ptr.value, "");
- if (rel_ptr->kind == Type_RelativeMultiPointer) {
- return final_ptr;
+ if (!is_type_unsigned(ct) && !is_type_boolean(ct)) {
+ // Sign extension
+ // m := 1<<(bit_size-1)
+ // r = (r XOR m) - m
+ lbValue m = lb_const_int(p->module, t, 1ull<<(addr.bitfield.bit_size-1));
+ r = lb_emit_arith(p, Token_Xor, r, m, t);
+ r = lb_emit_arith(p, Token_Sub, r, m, t);
}
- return lb_emit_load(p, final_ptr);
+ return r;
} else if (addr.kind == lbAddr_Map) {
Type *map_type = base_type(type_deref(addr.addr.type));
GB_ASSERT(map_type->kind == Type_Map);
@@ -1199,7 +1253,7 @@ gb_internal lbValue lb_addr_load(lbProcedure *p, lbAddr const &addr) {
for (isize i = 0; i < field_count; i++) {
Entity *field = t->Struct.fields[i];
Type *base_type = field->type;
- GB_ASSERT(base_type->kind == Type_Pointer);
+ GB_ASSERT(base_type->kind == Type_MultiPointer);
lbValue dst = lb_emit_struct_ep(p, res.addr, cast(i32)i);
lbValue src_ptr = lb_emit_struct_ep(p, addr.addr, cast(i32)i);
@@ -1213,6 +1267,30 @@ gb_internal lbValue lb_addr_load(lbProcedure *p, lbAddr const &addr) {
return lb_addr_load(p, res);
} else if (addr.kind == lbAddr_Swizzle) {
Type *array_type = base_type(addr.swizzle.type);
+ if (array_type->kind == Type_SimdVector) {
+ lbValue vec = lb_emit_load(p, addr.addr);
+ u8 index_count = addr.swizzle.count;
+ if (index_count == 0) {
+ return vec;
+ }
+
+ unsigned mask_len = cast(unsigned)index_count;
+ LLVMValueRef *mask_elems = gb_alloc_array(permanent_allocator(), LLVMValueRef, index_count);
+ for (isize i = 0; i < index_count; i++) {
+ mask_elems[i] = LLVMConstInt(lb_type(p->module, t_u32), addr.swizzle.indices[i], false);
+ }
+
+ LLVMValueRef mask = LLVMConstVector(mask_elems, mask_len);
+
+ LLVMValueRef v1 = vec.value;
+ LLVMValueRef v2 = vec.value;
+
+ lbValue res = {};
+ res.type = addr.swizzle.type;
+ res.value = LLVMBuildShuffleVector(p->builder, v1, v2, mask, "");
+ return res;
+ }
+
GB_ASSERT(array_type->kind == Type_Array);
unsigned res_align = cast(unsigned)type_align_of(addr.swizzle.type);
@@ -1234,10 +1312,8 @@ gb_internal lbValue lb_addr_load(lbProcedure *p, lbAddr const &addr) {
LLVMTypeRef vector_type = nullptr;
if (lb_try_vector_cast(p->module, addr.addr, &vector_type)) {
- LLVMSetAlignment(res.addr.value, cast(unsigned)lb_alignof(vector_type));
-
LLVMValueRef vp = LLVMBuildPointerCast(p->builder, addr.addr.value, LLVMPointerType(vector_type, 0), "");
- LLVMValueRef v = LLVMBuildLoad2(p->builder, vector_type, vp, "");
+ LLVMValueRef v = OdinLLVMBuildLoad(p, vector_type, vp);
LLVMValueRef scalars[4] = {};
for (u8 i = 0; i < addr.swizzle.count; i++) {
scalars[i] = LLVMConstInt(lb_type(p->module, t_u32), addr.swizzle.indices[i], false);
@@ -1245,6 +1321,8 @@ gb_internal lbValue lb_addr_load(lbProcedure *p, lbAddr const &addr) {
LLVMValueRef mask = LLVMConstVector(scalars, addr.swizzle.count);
LLVMValueRef sv = llvm_basic_shuffle(p, v, mask);
+ LLVMSetAlignment(res.addr.value, cast(unsigned)lb_alignof(LLVMTypeOf(sv)));
+
LLVMValueRef dst = LLVMBuildPointerCast(p->builder, ptr.value, LLVMPointerType(LLVMTypeOf(sv), 0), "");
LLVMBuildStore(p->builder, sv, dst);
} else {
@@ -1332,6 +1410,8 @@ gb_internal void lb_emit_store_union_variant(lbProcedure *p, lbValue parent, lbV
Type *pt = base_type(type_deref(parent.type));
GB_ASSERT(pt->kind == Type_Union);
if (pt->Union.kind == UnionType_shared_nil) {
+ GB_ASSERT(type_size_of(variant_type));
+
lbBlock *if_nil = lb_create_block(p, "shared_nil.if_nil");
lbBlock *if_not_nil = lb_create_block(p, "shared_nil.if_not_nil");
lbBlock *done = lb_create_block(p, "shared_nil.done");
@@ -1353,9 +1433,13 @@ gb_internal void lb_emit_store_union_variant(lbProcedure *p, lbValue parent, lbV
} else {
- lbValue underlying = lb_emit_conv(p, parent, alloc_type_pointer(variant_type));
-
- lb_emit_store(p, underlying, variant);
+ if (type_size_of(variant_type) == 0) {
+ unsigned alignment = 1;
+ lb_mem_zero_ptr_internal(p, parent.value, pt->Union.variant_block_size, alignment, false);
+ } else {
+ lbValue underlying = lb_emit_conv(p, parent, alloc_type_pointer(variant_type));
+ lb_emit_store(p, underlying, variant);
+ }
lb_emit_store_union_variant_tag(p, parent, variant_type);
}
}
@@ -1369,7 +1453,7 @@ gb_internal void lb_clone_struct_type(LLVMTypeRef dst, LLVMTypeRef src) {
LLVMStructSetBody(dst, fields, field_count, LLVMIsPackedStruct(src));
}
-gb_internal String lb_mangle_name(lbModule *m, Entity *e) {
+gb_internal String lb_mangle_name(Entity *e) {
String name = e->token.string;
AstPackage *pkg = e->pkg;
@@ -1434,9 +1518,11 @@ gb_internal String lb_set_nested_type_name_ir_mangled_name(Entity *e, lbProcedur
GB_ASSERT(scope->flags & ScopeFlag_Proc);
proc = scope->procedure_entity;
}
- GB_ASSERT(proc->kind == Entity_Procedure);
- if (proc->code_gen_procedure != nullptr) {
- p = proc->code_gen_procedure;
+ if (proc != nullptr) {
+ GB_ASSERT(proc->kind == Entity_Procedure);
+ if (proc->code_gen_procedure != nullptr) {
+ p = proc->code_gen_procedure;
+ }
}
}
@@ -1467,6 +1553,7 @@ gb_internal String lb_set_nested_type_name_ir_mangled_name(Entity *e, lbProcedur
}
gb_internal String lb_get_entity_name(lbModule *m, Entity *e, String default_name) {
+ GB_ASSERT(m != nullptr);
if (e != nullptr && e->kind == Entity_TypeName && e->TypeName.ir_mangled_name.len != 0) {
return e->TypeName.ir_mangled_name;
}
@@ -1498,7 +1585,7 @@ gb_internal String lb_get_entity_name(lbModule *m, Entity *e, String default_nam
}
if (!no_name_mangle) {
- name = lb_mangle_name(m, e);
+ name = lb_mangle_name(e);
}
if (name.len == 0) {
name = e->token.string;
@@ -1600,7 +1687,7 @@ gb_internal LLVMTypeRef lb_type_internal_for_procedures_raw(lbModule *m, Type *t
}
}
GB_ASSERT(param_index == param_count);
- lbFunctionType *ft = lb_get_abi_info(m->ctx, params, param_count, ret, ret != nullptr, return_is_tuple, type->Proc.calling_convention, type);
+ lbFunctionType *ft = lb_get_abi_info(m, params, param_count, ret, ret != nullptr, return_is_tuple, type->Proc.calling_convention, type);
{
for_array(j, ft->args) {
auto arg = ft->args[j];
@@ -1927,6 +2014,12 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
llvm_type = LLVMStructCreateNamed(ctx, name);
map_set(&m->types, type, llvm_type);
lb_clone_struct_type(llvm_type, lb_type(m, base));
+
+ if (base->kind == Type_Struct) {
+ map_set(&m->struct_field_remapping, cast(void *)llvm_type, lb_get_struct_remapping(m, base));
+ map_set(&m->struct_field_remapping, cast(void *)type, lb_get_struct_remapping(m, base));
+ }
+
return llvm_type;
}
}
@@ -1998,7 +2091,7 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
break;
case Type_Map:
- init_map_internal_types(type);
+ init_map_internal_debug_types(type);
GB_ASSERT(t_raw_map != nullptr);
return lb_type_internal(m, t_raw_map);
@@ -2038,16 +2131,19 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
array_add(&fields, padding_type);
}
- i64 padding_offset = 0;
+ i64 prev_offset = 0;
+ bool requires_packing = type->Struct.is_packed;
for (i32 field_index : struct_fields_index_by_increasing_offset(temporary_allocator(), type)) {
Entity *field = type->Struct.fields[field_index];
- i64 padding = type->Struct.offsets[field_index] - padding_offset;
+ i64 offset = type->Struct.offsets[field_index];
+ GB_ASSERT(offset >= prev_offset);
+ i64 padding = offset - prev_offset;
if (padding != 0) {
LLVMTypeRef padding_type = lb_type_padding_filler(m, padding, type_align_of(field->type));
array_add(&fields, padding_type);
}
-
+
field_remapping[field_index] = cast(i32)fields.count;
Type *field_type = field->type;
@@ -2057,15 +2153,16 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
field_type = t_rawptr;
}
+ // max_field_align might misalign items in a way that requires packing
+ // so check the alignment of all fields to see if packing is required.
+ requires_packing = requires_packing || ((offset % type_align_of(field_type)) != 0);
+
array_add(&fields, lb_type(m, field_type));
-
- if (!type->Struct.is_packed) {
- padding_offset = align_formula(padding_offset, type_align_of(field->type));
- }
- padding_offset += type_size_of(field->type);
+
+ prev_offset = offset + type_size_of(field->type);
}
- i64 end_padding = full_type_size-padding_offset;
+ i64 end_padding = full_type_size-prev_offset;
if (end_padding > 0) {
array_add(&fields, lb_type_padding_filler(m, end_padding, 1));
}
@@ -2074,7 +2171,7 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
GB_ASSERT(fields[i] != nullptr);
}
- LLVMTypeRef struct_type = LLVMStructTypeInContext(ctx, fields.data, cast(unsigned)fields.count, type->Struct.is_packed);
+ 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);
#if 0
@@ -2165,13 +2262,6 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
case Type_SimdVector:
return LLVMVectorType(lb_type(m, type->SimdVector.elem), cast(unsigned)type->SimdVector.count);
-
- case Type_RelativePointer:
- return lb_type_internal(m, type->RelativePointer.base_integer);
- case Type_RelativeMultiPointer:
- return lb_type_internal(m, type->RelativeMultiPointer.base_integer);
-
-
case Type_Matrix:
{
@@ -2206,7 +2296,9 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
}
return LLVMStructTypeInContext(ctx, fields, field_count, false);
}
-
+
+ case Type_BitField:
+ return lb_type_internal(m, type->BitField.backing_type);
}
GB_PANIC("Invalid type %s", type_to_string(type));
@@ -2338,6 +2430,15 @@ gb_internal LLVMAttributeRef lb_create_enum_attribute(LLVMContextRef ctx, char c
return LLVMCreateEnumAttribute(ctx, kind, value);
}
+gb_internal LLVMAttributeRef lb_create_string_attribute(LLVMContextRef ctx, String const &key, String const &value) {
+ LLVMAttributeRef attr = LLVMCreateStringAttribute(
+ ctx,
+ cast(char const *)key.text, cast(unsigned)key.len,
+ cast(char const *)value.text, cast(unsigned)value.len);
+ return attr;
+}
+
+
gb_internal void lb_add_proc_attribute_at_index(lbProcedure *p, isize index, char const *name, u64 value) {
LLVMAttributeRef attr = lb_create_enum_attribute(p->module->ctx, name, value);
GB_ASSERT(attr != nullptr);
@@ -2352,6 +2453,16 @@ gb_internal void lb_add_attribute_to_proc(lbModule *m, LLVMValueRef proc_value,
LLVMAddAttributeAtIndex(proc_value, LLVMAttributeIndex_FunctionIndex, lb_create_enum_attribute(m->ctx, name, value));
}
+gb_internal bool lb_proc_has_attribute(lbModule *m, LLVMValueRef proc_value, char const *name) {
+ LLVMAttributeRef ref = LLVMGetEnumAttributeAtIndex(proc_value, LLVMAttributeIndex_FunctionIndex, LLVMGetEnumAttributeKindForName(name, gb_strlen(name)));
+ return ref != nullptr;
+}
+
+gb_internal void lb_add_attribute_to_proc_with_string(lbModule *m, LLVMValueRef proc_value, String const &name, String const &value) {
+ LLVMAttributeRef attr = lb_create_string_attribute(m->ctx, name, value);
+ LLVMAddAttributeAtIndex(proc_value, LLVMAttributeIndex_FunctionIndex, attr);
+}
+
gb_internal void lb_add_edge(lbBlock *from, lbBlock *to) {
@@ -2497,7 +2608,7 @@ general_end:;
if (LLVMIsALoadInst(val) && (src_size >= 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 = LLVMBuildLoad2(p->builder, dst_type, val_ptr, "");
+ LLVMValueRef loaded_val = OdinLLVMBuildLoad(p, dst_type, val_ptr);
// LLVMSetAlignment(loaded_val, gb_min(src_align, dst_align));
@@ -2506,14 +2617,14 @@ general_end:;
GB_ASSERT(p->decl_block != p->curr_block);
i64 max_align = gb_max(lb_alignof(src_type), lb_alignof(dst_type));
- max_align = gb_max(max_align, 4);
+ max_align = gb_max(max_align, 16);
LLVMValueRef ptr = llvm_alloca(p, dst_type, max_align);
LLVMValueRef nptr = LLVMBuildPointerCast(p->builder, ptr, LLVMPointerType(src_type, 0), "");
LLVMBuildStore(p->builder, val, nptr);
- return LLVMBuildLoad2(p->builder, dst_type, ptr, "");
+ return OdinLLVMBuildLoad(p, dst_type, ptr);
}
}
@@ -2862,7 +2973,7 @@ gb_internal lbValue lb_find_value_from_entity(lbModule *m, Entity *e) {
if (e->code_gen_module != nullptr) {
other_module = e->code_gen_module;
} else {
- other_module = nullptr;
+ other_module = &m->gen->default_module;
}
is_external = other_module != m;
}
@@ -2880,8 +2991,6 @@ gb_internal lbValue lb_find_value_from_entity(lbModule *m, Entity *e) {
lb_set_entity_from_other_modules_linkage_correctly(other_module, e, name);
- // LLVMSetLinkage(other_g.value, LLVMExternalLinkage);
-
if (e->Variable.thread_local_model != "") {
LLVMSetThreadLocal(g.value, true);
@@ -2905,7 +3014,9 @@ gb_internal lbValue lb_find_value_from_entity(lbModule *m, Entity *e) {
return g;
}
}
- GB_PANIC("\n\tError in: %s, missing value '%.*s'\n", token_pos_to_string(e->token.pos), LIT(e->token.string));
+
+ GB_PANIC("\n\tError in: %s, missing value '%.*s' in module %s\n",
+ token_pos_to_string(e->token.pos), LIT(e->token.string), m->module_name);
return {};
}
diff --git a/src/llvm_backend_opt.cpp b/src/llvm_backend_opt.cpp
index 2e03b7974..7fe1359b4 100644
--- a/src/llvm_backend_opt.cpp
+++ b/src/llvm_backend_opt.cpp
@@ -380,6 +380,100 @@ gb_internal void lb_run_remove_dead_instruction_pass(lbProcedure *p) {
}
}
+gb_internal LLVMValueRef lb_run_instrumentation_pass_insert_call(lbProcedure *p, Entity *entity, LLVMBuilderRef dummy_builder, bool is_enter) {
+ lbModule *m = p->module;
+
+ if (p->debug_info != nullptr) {
+ TokenPos pos = {};
+ if (is_enter) {
+ pos = ast_token(p->body).pos;
+ } else {
+ pos = ast_end_token(p->body).pos;
+ }
+ LLVMSetCurrentDebugLocation2(dummy_builder, lb_debug_location_from_token_pos(p, pos));
+ }
+
+ lbValue cc = lb_find_procedure_value_from_entity(m, entity);
+
+ LLVMValueRef args[3] = {};
+ args[0] = LLVMConstPointerCast(p->value, lb_type(m, t_rawptr));
+
+ if (is_arch_wasm()) {
+ args[1] = LLVMConstPointerNull(lb_type(m, t_rawptr));
+ } else {
+ LLVMValueRef returnaddress_args[1] = {};
+
+ returnaddress_args[0] = LLVMConstInt(LLVMInt32TypeInContext(m->ctx), 0, false);
+
+ char const *instrinsic_name = "llvm.returnaddress";
+ unsigned id = LLVMLookupIntrinsicID(instrinsic_name, gb_strlen(instrinsic_name));
+ GB_ASSERT_MSG(id != 0, "Unable to find %s", instrinsic_name);
+ LLVMValueRef ip = LLVMGetIntrinsicDeclaration(m->mod, id, nullptr, 0);
+ LLVMTypeRef call_type = LLVMIntrinsicGetType(m->ctx, id, nullptr, 0);
+ args[1] = LLVMBuildCall2(dummy_builder, call_type, ip, returnaddress_args, gb_count_of(returnaddress_args), "");
+ }
+
+ Token name = {};
+ if (p->entity) {
+ name = p->entity->token;
+ }
+ args[2] = lb_emit_source_code_location_as_global_ptr(p, name.string, name.pos).value;
+
+ LLVMTypeRef fnp = lb_type_internal_for_procedures_raw(p->module, entity->type);
+ return LLVMBuildCall2(dummy_builder, fnp, cc.value, args, gb_count_of(args), "");
+}
+
+
+gb_internal void lb_run_instrumentation_pass(lbProcedure *p) {
+ lbModule *m = p->module;
+ Entity *enter = m->info->instrumentation_enter_entity;
+ Entity *exit = m->info->instrumentation_exit_entity;
+ if (enter == nullptr || exit == nullptr) {
+ return;
+ }
+ if (!(p->entity &&
+ p->entity->kind == Entity_Procedure &&
+ p->entity->Procedure.has_instrumentation)) {
+ return;
+ }
+
+#define LLVM_V_NAME(x) x, cast(unsigned)(gb_count_of(x)-1)
+
+ LLVMBuilderRef dummy_builder = LLVMCreateBuilderInContext(m->ctx);
+ defer (LLVMDisposeBuilder(dummy_builder));
+
+ LLVMBasicBlockRef entry_bb = p->entry_block->block;
+ LLVMPositionBuilder(dummy_builder, entry_bb, LLVMGetFirstInstruction(entry_bb));
+ lb_run_instrumentation_pass_insert_call(p, enter, dummy_builder, true);
+ LLVMRemoveStringAttributeAtIndex(p->value, LLVMAttributeIndex_FunctionIndex, LLVM_V_NAME("instrument-function-entry"));
+
+ unsigned bb_count = LLVMCountBasicBlocks(p->value);
+ LLVMBasicBlockRef *bbs = gb_alloc_array(temporary_allocator(), LLVMBasicBlockRef, bb_count);
+ LLVMGetBasicBlocks(p->value, bbs);
+ for (unsigned i = 0; i < bb_count; i++) {
+ LLVMBasicBlockRef bb = bbs[i];
+ LLVMValueRef terminator = LLVMGetBasicBlockTerminator(bb);
+ if (terminator == nullptr ||
+ !LLVMIsAReturnInst(terminator)) {
+ continue;
+ }
+
+ // TODO(bill): getTerminatingMustTailCall()
+ // If T is preceded by a musttail call, that's the real terminator.
+ // if (CallInst *CI = BB.getTerminatingMustTailCall())
+ // T = CI;
+
+
+ LLVMPositionBuilderBefore(dummy_builder, terminator);
+ lb_run_instrumentation_pass_insert_call(p, exit, dummy_builder, false);
+ }
+
+ LLVMRemoveStringAttributeAtIndex(p->value, LLVMAttributeIndex_FunctionIndex, LLVM_V_NAME("instrument-function-exit"));
+
+#undef LLVM_V_NAME
+}
+
+
gb_internal void lb_run_function_pass_manager(LLVMPassManagerRef fpm, lbProcedure *p, lbFunctionPassManagerKind pass_manager_kind) {
if (p == nullptr) {
@@ -391,6 +485,8 @@ gb_internal void lb_run_function_pass_manager(LLVMPassManagerRef fpm, lbProcedur
// are not removed
lb_run_remove_dead_instruction_pass(p);
+ lb_run_instrumentation_pass(p);
+
switch (pass_manager_kind) {
case lbFunctionPassManager_none:
return;
@@ -552,3 +648,5 @@ gb_internal void lb_run_remove_unused_globals_pass(lbModule *m) {
}
}
}
+
+
diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp
index f64cbd52a..7e44a0046 100644
--- a/src/llvm_backend_proc.cpp
+++ b/src/llvm_backend_proc.cpp
@@ -125,6 +125,10 @@ gb_internal lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool i
// map_init(&p->selector_addr, 0);
// map_init(&p->tuple_fix_map, 0);
+ if (p->entity != nullptr && p->entity->Procedure.uses_branch_location) {
+ p->uses_branch_location = true;
+ }
+
if (p->is_foreign) {
lb_add_foreign_library_path(p->module, entity->Procedure.foreign_library);
}
@@ -159,35 +163,41 @@ gb_internal lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool i
case ProcInlining_no_inline:
lb_add_attribute_to_proc(m, p->value, "noinline");
break;
+ default:
+ if (build_context.internal_no_inline) {
+ lb_add_attribute_to_proc(m, p->value, "noinline");
+ break;
+ }
}
switch (entity->Procedure.optimization_mode) {
case ProcedureOptimizationMode_None:
- break;
- case ProcedureOptimizationMode_Minimal:
lb_add_attribute_to_proc(m, p->value, "optnone");
lb_add_attribute_to_proc(m, p->value, "noinline");
break;
- case ProcedureOptimizationMode_Size:
- lb_add_attribute_to_proc(m, p->value, "optsize");
- break;
- case ProcedureOptimizationMode_Speed:
- // TODO(bill): handle this correctly
+ case ProcedureOptimizationMode_FavorSize:
lb_add_attribute_to_proc(m, p->value, "optsize");
break;
}
- if (!entity->Procedure.target_feature_disabled &&
- entity->Procedure.target_feature.len != 0) {
- auto features = split_by_comma(entity->Procedure.target_feature);
- for_array(i, features) {
- String feature = features[i];
- LLVMAttributeRef ref = LLVMCreateStringAttribute(
- m->ctx,
- cast(char const *)feature.text, cast(unsigned)feature.len,
- "", 0);
- LLVMAddAttributeAtIndex(p->value, LLVMAttributeIndex_FunctionIndex, ref);
+ if (pt->Proc.enable_target_feature.len != 0) {
+ gbString feature_str = gb_string_make(temporary_allocator(), "");
+
+ String_Iterator it = {pt->Proc.enable_target_feature, 0};
+ bool first = true;
+ for (;;) {
+ String str = string_split_iterator(&it, ',');
+ if (str == "") break;
+ if (!first) {
+ feature_str = gb_string_appendc(feature_str, ",");
+ }
+ first = false;
+
+ feature_str = gb_string_appendc(feature_str, "+");
+ feature_str = gb_string_append_length(feature_str, str.text, str.len);
}
+
+ lb_add_attribute_to_proc_with_string(m, p->value, make_string_c("target-features"), make_string_c(feature_str));
}
if (entity->flags & EntityFlag_Cold) {
@@ -226,7 +236,7 @@ gb_internal lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool i
if (p->is_foreign) {
- lb_set_wasm_import_attributes(p->value, entity, p->name);
+ lb_set_wasm_procedure_import_attributes(p->value, entity, p->name);
}
@@ -252,6 +262,11 @@ gb_internal lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool i
if (e->flags&EntityFlag_NoAlias) {
lb_add_proc_attribute_at_index(p, offset+parameter_index, "noalias");
}
+ if (e->flags&EntityFlag_NoCapture) {
+ if (is_type_internally_pointer_like(e->type)) {
+ lb_add_proc_attribute_at_index(p, offset+parameter_index, "nocapture");
+ }
+ }
parameter_index += 1;
}
}
@@ -329,6 +344,18 @@ gb_internal lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool i
}
}
+ if (p->body && entity->Procedure.has_instrumentation) {
+ Entity *instrumentation_enter = m->info->instrumentation_enter_entity;
+ Entity *instrumentation_exit = m->info->instrumentation_exit_entity;
+ if (instrumentation_enter && instrumentation_exit) {
+ String enter = lb_get_entity_name(m, instrumentation_enter);
+ String exit = lb_get_entity_name(m, instrumentation_exit);
+
+ lb_add_attribute_to_proc_with_string(m, p->value, make_string_c("instrument-function-entry"), enter);
+ lb_add_attribute_to_proc_with_string(m, p->value, make_string_c("instrument-function-exit"), exit);
+ }
+ }
+
lbValue proc_value = {p->value, p->type};
lb_add_entity(m, entity, proc_value);
lb_add_member(m, p->name, proc_value);
@@ -504,6 +531,7 @@ gb_internal void lb_begin_procedure_body(lbProcedure *p) {
lb_start_block(p, p->entry_block);
map_init(&p->direct_parameters);
+ p->variadic_reuses.allocator = heap_allocator();
GB_ASSERT(p->type != nullptr);
@@ -555,6 +583,8 @@ gb_internal void lb_begin_procedure_body(lbProcedure *p) {
p->raw_input_parameters = array_make<LLVMValueRef>(permanent_allocator(), raw_input_parameters_count);
LLVMGetParams(p->value, p->raw_input_parameters.data);
+ bool is_odin_cc = is_calling_convention_odin(ft->calling_convention);
+
unsigned param_index = 0;
for_array(i, params->variables) {
Entity *e = params->variables[i];
@@ -566,7 +596,10 @@ gb_internal void lb_begin_procedure_body(lbProcedure *p) {
defer (param_index += 1);
if (arg_type->kind == lbArg_Ignore) {
- continue;
+ // Even though it is an ignored argument, it might still be referenced in the
+ // body.
+ lbValue dummy = lb_add_local_generated(p, e->type, false).addr;
+ lb_add_entity(p->module, e, dummy);
} else if (arg_type->kind == lbArg_Direct) {
if (e->token.string.len != 0 && !is_blank_ident(e->token.string)) {
LLVMTypeRef param_type = lb_type(p->module, e->type);
@@ -582,24 +615,32 @@ gb_internal void lb_begin_procedure_body(lbProcedure *p) {
lbValue ptr = lb_address_from_load_or_generate_local(p, param);
GB_ASSERT(LLVMIsAAllocaInst(ptr.value));
lb_add_entity(p->module, e, ptr);
-
- lbBlock *block = p->decl_block;
- if (original_value != value) {
- block = p->curr_block;
- }
- LLVMValueRef debug_storage_value = value;
- if (original_value != value && LLVMIsALoadInst(value)) {
- debug_storage_value = LLVMGetOperand(value, 0);
- }
- lb_add_debug_param_variable(p, debug_storage_value, e->type, e->token, param_index+1, block, arg_type->kind);
+ lb_add_debug_param_variable(p, ptr.value, e->type, e->token, param_index+1, p->curr_block);
}
} else if (arg_type->kind == lbArg_Indirect) {
if (e->token.string.len != 0 && !is_blank_ident(e->token.string)) {
+ i64 sz = type_size_of(e->type);
+ bool do_callee_copy = false;
+
+ if (is_odin_cc) {
+ do_callee_copy = sz <= 16;
+ if (build_context.internal_by_value) {
+ do_callee_copy = true;
+ }
+ }
+
lbValue ptr = {};
ptr.value = LLVMGetParam(p->value, param_offset+param_index);
ptr.type = alloc_type_pointer(e->type);
+
+ if (do_callee_copy) {
+ lbValue new_ptr = lb_add_local_generated(p, e->type, false).addr;
+ lb_mem_copy_non_overlapping(p, new_ptr, ptr, lb_const_int(p->module, t_uint, sz));
+ ptr = new_ptr;
+ }
+
lb_add_entity(p->module, e, ptr);
- lb_add_debug_param_variable(p, ptr.value, e->type, e->token, param_index+1, p->decl_block, arg_type->kind);
+ lb_add_debug_param_variable(p, ptr.value, e->type, e->token, param_index+1, p->decl_block);
}
}
}
@@ -681,7 +722,9 @@ gb_internal void lb_begin_procedure_body(lbProcedure *p) {
}
if (e->Variable.param_value.kind != ParameterValue_Invalid) {
- lbValue c = lb_handle_param_value(p, e->type, e->Variable.param_value, e->token.pos);
+ GB_ASSERT(e->Variable.param_value.kind != ParameterValue_Location);
+ GB_ASSERT(e->Variable.param_value.kind != ParameterValue_Expression);
+ lbValue c = lb_handle_param_value(p, e->type, e->Variable.param_value, nullptr, nullptr);
lb_addr_store(p, res, c);
}
@@ -697,13 +740,12 @@ gb_internal void lb_begin_procedure_body(lbProcedure *p) {
lb_set_debug_position_to_procedure_begin(p);
if (p->debug_info != nullptr) {
if (p->context_stack.count != 0) {
+ lbBlock *prev_block = p->curr_block;
p->curr_block = p->decl_block;
lb_add_debug_context_variable(p, lb_find_or_generate_context_ptr(p));
+ p->curr_block = prev_block;
}
-
}
-
- lb_start_block(p, p->entry_block);
}
gb_internal void lb_end_procedure_body(lbProcedure *p) {
@@ -719,7 +761,7 @@ gb_internal void lb_end_procedure_body(lbProcedure *p) {
if (p->type->Proc.result_count == 0) {
instr = LLVMGetLastInstruction(p->curr_block->block);
if (!lb_is_instr_terminating(instr)) {
- lb_emit_defer_stmts(p, lbDeferExit_Return, nullptr);
+ lb_emit_defer_stmts(p, lbDeferExit_Return, nullptr, p->body);
lb_set_debug_position_to_procedure_end(p);
LLVMBuildRetVoid(p->builder);
}
@@ -1039,6 +1081,7 @@ gb_internal lbValue lb_emit_call(lbProcedure *p, lbValue value, Array<lbValue> c
Type *original_type = e->type;
lbArgType *arg = &ft->args[param_index];
if (arg->kind == lbArg_Ignore) {
+ param_index += 1;
continue;
}
@@ -1083,15 +1126,7 @@ gb_internal lbValue lb_emit_call(lbProcedure *p, lbValue value, Array<lbValue> c
ptr = lb_address_from_load_or_generate_local(p, x);
}
} else {
- if (LLVMIsConstant(x.value)) {
- // NOTE(bill): if the value is already constant, then just it as a global variable
- // and pass it by pointer
- lbAddr addr = lb_add_global_generated(p->module, original_type, x);
- lb_make_global_private_const(addr);
- ptr = addr.addr;
- } else {
- ptr = lb_copy_value_to_ptr(p, x, original_type, 16);
- }
+ ptr = lb_copy_value_to_ptr(p, x, original_type, 16);
}
array_add(&processed_args, ptr);
}
@@ -1105,10 +1140,6 @@ gb_internal lbValue lb_emit_call(lbProcedure *p, lbValue value, Array<lbValue> c
}
}
- if (inlining == ProcInlining_none) {
- inlining = p->inlining;
- }
-
Type *rt = reduce_tuple_to_single_type(results);
Type *original_rt = rt;
if (split_returns) {
@@ -1387,8 +1418,7 @@ gb_internal lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAn
return res;
case BuiltinProc_simd_min:
if (is_float) {
- LLVMValueRef cond = LLVMBuildFCmp(p->builder, LLVMRealOLT, arg0.value, arg1.value, "");
- res.value = LLVMBuildSelect(p->builder, cond, arg0.value, arg1.value, "");
+ return lb_emit_min(p, res.type, arg0, arg1);
} else {
LLVMValueRef cond = LLVMBuildICmp(p->builder, is_signed ? LLVMIntSLT : LLVMIntULT, arg0.value, arg1.value, "");
res.value = LLVMBuildSelect(p->builder, cond, arg0.value, arg1.value, "");
@@ -1396,8 +1426,7 @@ gb_internal lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAn
return res;
case BuiltinProc_simd_max:
if (is_float) {
- LLVMValueRef cond = LLVMBuildFCmp(p->builder, LLVMRealOGT, arg0.value, arg1.value, "");
- res.value = LLVMBuildSelect(p->builder, cond, arg0.value, arg1.value, "");
+ return lb_emit_max(p, res.type, arg0, arg1);
} else {
LLVMValueRef cond = LLVMBuildICmp(p->builder, is_signed ? LLVMIntSGT : LLVMIntUGT, arg0.value, arg1.value, "");
res.value = LLVMBuildSelect(p->builder, cond, arg0.value, arg1.value, "");
@@ -1519,6 +1548,23 @@ gb_internal lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAn
return res;
}
+ case BuiltinProc_simd_reduce_any:
+ case BuiltinProc_simd_reduce_all:
+ {
+ char const *name = nullptr;
+ switch (builtin_id) {
+ case BuiltinProc_simd_reduce_any: name = "llvm.vector.reduce.or"; break;
+ case BuiltinProc_simd_reduce_all: name = "llvm.vector.reduce.and"; break;
+ }
+
+ LLVMTypeRef types[1] = { lb_type(p->module, arg0.type) };
+ LLVMValueRef args[1] = { arg0.value };
+
+ res.value = lb_call_intrinsic(p, name, args, gb_count_of(args), types, gb_count_of(types));
+ return res;
+ }
+
+
case BuiltinProc_simd_shuffle:
{
Type *vt = arg0.type;
@@ -1621,13 +1667,13 @@ gb_internal lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAn
}
- case BuiltinProc_simd_add_sat:
- case BuiltinProc_simd_sub_sat:
+ case BuiltinProc_simd_saturating_add:
+ case BuiltinProc_simd_saturating_sub:
{
char const *name = nullptr;
switch (builtin_id) {
- case BuiltinProc_simd_add_sat: name = is_signed ? "llvm.sadd.sat" : "llvm.uadd.sat"; break;
- case BuiltinProc_simd_sub_sat: name = is_signed ? "llvm.ssub.sat" : "llvm.usub.sat"; break;
+ case BuiltinProc_simd_saturating_add: name = is_signed ? "llvm.sadd.sat" : "llvm.uadd.sat"; break;
+ case BuiltinProc_simd_saturating_sub: name = is_signed ? "llvm.ssub.sat" : "llvm.usub.sat"; break;
}
LLVMTypeRef types[1] = {lb_type(p->module, arg0.type)};
@@ -1663,6 +1709,85 @@ gb_internal lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAn
return res;
}
+
+ case BuiltinProc_simd_gather:
+ case BuiltinProc_simd_scatter:
+ case BuiltinProc_simd_masked_load:
+ case BuiltinProc_simd_masked_store:
+ case BuiltinProc_simd_masked_expand_load:
+ case BuiltinProc_simd_masked_compress_store:
+ {
+ LLVMValueRef ptr = arg0.value;
+ LLVMValueRef val = arg1.value;
+ LLVMValueRef mask = arg2.value;
+
+ unsigned count = cast(unsigned)get_array_type_count(arg1.type);
+
+ LLVMTypeRef mask_type = LLVMVectorType(LLVMInt1TypeInContext(p->module->ctx), count);
+ mask = LLVMBuildTrunc(p->builder, mask, mask_type, "");
+
+ char const *name = nullptr;
+ switch (builtin_id) {
+ case BuiltinProc_simd_gather: name = "llvm.masked.gather"; break;
+ case BuiltinProc_simd_scatter: name = "llvm.masked.scatter"; break;
+ case BuiltinProc_simd_masked_load: name = "llvm.masked.load"; break;
+ case BuiltinProc_simd_masked_store: name = "llvm.masked.store"; break;
+ case BuiltinProc_simd_masked_expand_load: name = "llvm.masked.expandload"; break;
+ case BuiltinProc_simd_masked_compress_store: name = "llvm.masked.compressstore"; break;
+ }
+ unsigned type_count = 2;
+ LLVMTypeRef types[2] = {
+ lb_type(p->module, arg1.type),
+ lb_type(p->module, arg0.type)
+ };
+
+ auto alignment = cast(unsigned long long)type_align_of(base_array_type(arg1.type));
+ LLVMValueRef align = LLVMConstInt(LLVMInt32TypeInContext(p->module->ctx), alignment, false);
+
+ unsigned arg_count = 4;
+ LLVMValueRef args[4] = {};
+ switch (builtin_id) {
+ case BuiltinProc_simd_masked_load:
+ types[1] = lb_type(p->module, t_rawptr);
+ /*fallthrough*/
+ case BuiltinProc_simd_gather:
+ args[0] = ptr;
+ args[1] = align;
+ args[2] = mask;
+ args[3] = val;
+ break;
+
+ case BuiltinProc_simd_masked_store:
+ types[1] = lb_type(p->module, t_rawptr);
+ /*fallthrough*/
+ case BuiltinProc_simd_scatter:
+ args[0] = val;
+ args[1] = ptr;
+ args[2] = align;
+ args[3] = mask;
+ break;
+
+ case BuiltinProc_simd_masked_expand_load:
+ arg_count = 3;
+ type_count = 1;
+ args[0] = ptr;
+ args[1] = mask;
+ args[2] = val;
+ break;
+
+ case BuiltinProc_simd_masked_compress_store:
+ arg_count = 3;
+ type_count = 1;
+ args[0] = val;
+ args[1] = ptr;
+ args[2] = mask;
+ break;
+ }
+
+ res.value = lb_call_intrinsic(p, name, args, arg_count, types, type_count);
+ return res;
+
+ }
}
GB_PANIC("Unhandled simd intrinsic: '%.*s'", LIT(builtin_procs[builtin_id].name));
@@ -1681,24 +1806,61 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
case BuiltinProc_DIRECTIVE: {
ast_node(bd, BasicDirective, ce->proc);
String name = bd->name.string;
- GB_ASSERT(name == "location");
- String procedure = p->entity->token.string;
- TokenPos pos = ast_token(ce->proc).pos;
- if (ce->args.count > 0) {
- Ast *ident = unselector_expr(ce->args[0]);
- GB_ASSERT(ident->kind == Ast_Ident);
- Entity *e = entity_of_node(ident);
- GB_ASSERT(e != nullptr);
-
- if (e->parent_proc_decl != nullptr && e->parent_proc_decl->entity != nullptr) {
- procedure = e->parent_proc_decl->entity->token.string;
- } else {
- procedure = str_lit("");
+ if (name == "location") {
+ String procedure = p->entity->token.string;
+ TokenPos pos = ast_token(ce->proc).pos;
+ if (ce->args.count > 0) {
+ Ast *ident = unselector_expr(ce->args[0]);
+ GB_ASSERT(ident->kind == Ast_Ident);
+ Entity *e = entity_of_node(ident);
+ GB_ASSERT(e != nullptr);
+
+ if (e->parent_proc_decl != nullptr && e->parent_proc_decl->entity != nullptr) {
+ procedure = e->parent_proc_decl->entity->token.string;
+ } else {
+ procedure = str_lit("");
+ }
+ pos = e->token.pos;
+
+ }
+ return lb_emit_source_code_location_as_global(p, procedure, pos);
+ } else if (name == "load_directory") {
+ lbModule *m = p->module;
+ TEMPORARY_ALLOCATOR_GUARD();
+ LoadDirectoryCache *cache = map_must_get(&m->info->load_directory_map, expr);
+ isize count = cache->files.count;
+
+ LLVMValueRef *elements = gb_alloc_array(temporary_allocator(), LLVMValueRef, count);
+ for_array(i, cache->files) {
+ LoadFileCache *file = cache->files[i];
+
+ String file_name = filename_without_directory(file->path);
+
+ LLVMValueRef values[2] = {};
+ values[0] = lb_const_string(m, file_name).value;
+ values[1] = lb_const_string(m, file->data).value;
+ LLVMValueRef element = llvm_const_named_struct(m, t_load_directory_file, values, gb_count_of(values));
+ elements[i] = element;
}
- pos = e->token.pos;
+ LLVMValueRef backing_array = llvm_const_array(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(m, array_type, {backing_array, array_type}, nullptr);
+ lb_make_global_private_const(backing_array_addr);
+
+ LLVMValueRef backing_array_ptr = backing_array_addr.addr.value;
+ backing_array_ptr = LLVMConstPointerCast(backing_array_ptr, lb_type(m, t_load_directory_file_ptr));
+
+ LLVMValueRef const_slice = llvm_const_slice_internal(m, backing_array_ptr, LLVMConstInt(lb_type(m, t_int), count, false));
+
+ lbAddr addr = lb_add_global_generated(p->module, tv.type, {const_slice, t_load_directory_file_slice}, nullptr);
+ lb_make_global_private_const(addr);
+
+ return lb_addr_load(p, addr);
+ } else {
+ GB_PANIC("UNKNOWN DIRECTIVE: %.*s", LIT(name));
}
- return lb_emit_source_code_location_as_global(p, procedure, pos);
}
case BuiltinProc_type_info_of: {
@@ -1706,7 +1868,7 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
TypeAndValue tav = type_and_value_of_expr(arg);
if (tav.mode == Addressing_Type) {
Type *t = default_type(type_of_expr(arg));
- return lb_type_info(p->module, t);
+ return lb_type_info(p, t);
}
GB_ASSERT(is_type_typeid(tav.type));
@@ -1826,24 +1988,41 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
}
case BuiltinProc_quaternion: {
- lbValue real = lb_build_expr(p, ce->args[0]);
- lbValue imag = lb_build_expr(p, ce->args[1]);
- lbValue jmag = lb_build_expr(p, ce->args[2]);
- lbValue kmag = lb_build_expr(p, ce->args[3]);
+ lbValue xyzw[4] = {};
+ for (i32 i = 0; i < 4; i++) {
+ ast_node(f, FieldValue, ce->args[i]);
+ GB_ASSERT(f->field->kind == Ast_Ident);
+ String name = f->field->Ident.token.string;
+ i32 index = -1;
+
+ // @QuaternionLayout
+ if (name == "x" || name == "imag") {
+ index = 0;
+ } else if (name == "y" || name == "jmag") {
+ index = 1;
+ } else if (name == "z" || name == "kmag") {
+ index = 2;
+ } else if (name == "w" || name == "real") {
+ index = 3;
+ }
+ GB_ASSERT(index >= 0);
+
+ xyzw[index] = lb_build_expr(p, f->value);
+ }
+
- // @QuaternionLayout
lbAddr dst_addr = lb_add_local_generated(p, tv.type, false);
lbValue dst = lb_addr_get_ptr(p, dst_addr);
Type *ft = base_complex_elem_type(tv.type);
- real = lb_emit_conv(p, real, ft);
- imag = lb_emit_conv(p, imag, ft);
- jmag = lb_emit_conv(p, jmag, ft);
- kmag = lb_emit_conv(p, kmag, ft);
- lb_emit_store(p, lb_emit_struct_ep(p, dst, 3), real);
- lb_emit_store(p, lb_emit_struct_ep(p, dst, 0), imag);
- lb_emit_store(p, lb_emit_struct_ep(p, dst, 1), jmag);
- lb_emit_store(p, lb_emit_struct_ep(p, dst, 2), kmag);
+ xyzw[0] = lb_emit_conv(p, xyzw[0], ft);
+ xyzw[1] = lb_emit_conv(p, xyzw[1], ft);
+ xyzw[2] = lb_emit_conv(p, xyzw[2], ft);
+ xyzw[3] = lb_emit_conv(p, xyzw[3], ft);
+ lb_emit_store(p, lb_emit_struct_ep(p, dst, 0), xyzw[0]);
+ lb_emit_store(p, lb_emit_struct_ep(p, dst, 1), xyzw[1]);
+ lb_emit_store(p, lb_emit_struct_ep(p, dst, 2), xyzw[2]);
+ lb_emit_store(p, lb_emit_struct_ep(p, dst, 3), xyzw[3]);
return lb_emit_load(p, dst);
}
@@ -2004,9 +2183,9 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
case BuiltinProc_clamp:
return lb_emit_clamp(p, type_of_expr(expr),
- lb_build_expr(p, ce->args[0]),
- lb_build_expr(p, ce->args[1]),
- lb_build_expr(p, ce->args[2]));
+ lb_build_expr(p, ce->args[0]),
+ lb_build_expr(p, ce->args[1]),
+ lb_build_expr(p, ce->args[2]));
case BuiltinProc_soa_zip:
@@ -2223,6 +2402,39 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
return res;
}
+ case BuiltinProc_saturating_add:
+ case BuiltinProc_saturating_sub:
+ {
+ Type *main_type = tv.type;
+ Type *type = main_type;
+
+ lbValue x = lb_build_expr(p, ce->args[0]);
+ lbValue y = lb_build_expr(p, ce->args[1]);
+ x = lb_emit_conv(p, x, type);
+ y = lb_emit_conv(p, y, type);
+
+ char const *name = nullptr;
+ if (is_type_unsigned(type)) {
+ switch (id) {
+ case BuiltinProc_saturating_add: name = "llvm.uadd.sat"; break;
+ case BuiltinProc_saturating_sub: name = "llvm.usub.sat"; break;
+ }
+ } else {
+ switch (id) {
+ case BuiltinProc_saturating_add: name = "llvm.sadd.sat"; break;
+ case BuiltinProc_saturating_sub: name = "llvm.ssub.sat"; break;
+ }
+ }
+ LLVMTypeRef types[1] = {lb_type(p->module, type)};
+
+ LLVMValueRef args[2] = { x.value, y.value };
+
+ lbValue res = {};
+ res.value = lb_call_intrinsic(p, name, args, gb_count_of(args), types, gb_count_of(types));
+ res.type = type;
+ return res;
+ }
+
case BuiltinProc_sqrt:
{
Type *type = tv.type;
@@ -2318,9 +2530,10 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
lbValue ptr0 = lb_emit_conv(p, lb_build_expr(p, ce->args[0]), t_uintptr);
lbValue ptr1 = lb_emit_conv(p, lb_build_expr(p, ce->args[1]), t_uintptr);
+ ptr0 = lb_emit_conv(p, ptr0, t_int);
+ ptr1 = lb_emit_conv(p, ptr1, t_int);
- lbValue diff = lb_emit_arith(p, Token_Sub, ptr0, ptr1, t_uintptr);
- diff = lb_emit_conv(p, diff, t_int);
+ lbValue diff = lb_emit_arith(p, Token_Sub, ptr0, ptr1, t_int);
return lb_emit_arith(p, Token_Quo, diff, lb_const_int(p->module, t_int, type_size_of(elem)), t_int);
}
@@ -2374,7 +2587,7 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
case BuiltinProc_atomic_load_explicit: {
lbValue dst = lb_build_expr(p, ce->args[0]);
- LLVMValueRef instr = LLVMBuildLoad2(p->builder, lb_type(p->module, type_deref(dst.type)), dst.value, "");
+ LLVMValueRef instr = OdinLLVMBuildLoad(p, lb_type(p->module, type_deref(dst.type)), dst.value);
switch (id) {
case BuiltinProc_non_temporal_load:
{
@@ -2427,8 +2640,7 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
if (is_type_simd_vector(t)) {
lbValue res = {};
res.type = t;
- res.value = LLVMBuildLoad2(p->builder, lb_type(p->module, t), src.value, "");
- LLVMSetAlignment(res.value, 1);
+ res.value = OdinLLVMBuildLoadAligned(p, lb_type(p->module, t), src.value, 1);
return res;
} else {
lbAddr dst = lb_add_local_generated(p, t, false);
@@ -2685,30 +2897,39 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
LLVMValueRef inline_asm = nullptr;
switch (build_context.metrics.arch) {
- case TargetArch_amd64:
+ case TargetArch_riscv64:
{
GB_ASSERT(arg_count <= 7);
- // FreeBSD additionally clobbers r8, r9, r10, but they
- // can also be used to pass in arguments, so this needs
- // to be handled in two parts.
- bool clobber_arg_regs[7] = {
- false, false, false, false, false, false, false
- };
- if (build_context.metrics.os == TargetOs_freebsd) {
- clobber_arg_regs[4] = true; // r10
- clobber_arg_regs[5] = true; // r8
- clobber_arg_regs[6] = true; // r9
+ char asm_string[] = "ecall";
+ gbString constraints = gb_string_make(heap_allocator(), "={a0}");
+ for (unsigned i = 0; i < arg_count; i++) {
+ constraints = gb_string_appendc(constraints, ",{");
+ static char const *regs[] = {
+ "a7",
+ "a0",
+ "a1",
+ "a2",
+ "a3",
+ "a4",
+ "a5",
+ "a6"
+ };
+ constraints = gb_string_appendc(constraints, regs[i]);
+ constraints = gb_string_appendc(constraints, "}");
}
+ inline_asm = llvm_get_inline_asm(func_type, make_string_c(asm_string), make_string_c(constraints));
+ }
+ break;
+ case TargetArch_amd64:
+ {
+ GB_ASSERT(arg_count <= 7);
+
char asm_string[] = "syscall";
gbString constraints = gb_string_make(heap_allocator(), "={rax}");
for (unsigned i = 0; i < arg_count; i++) {
- if (!clobber_arg_regs[i]) {
- constraints = gb_string_appendc(constraints, ",{");
- } else {
- constraints = gb_string_appendc(constraints, ",+{");
- }
+ constraints = gb_string_appendc(constraints, ",{");
static char const *regs[] = {
"rax",
"rdi",
@@ -2732,36 +2953,9 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
// Some but not all system calls will additionally
// clobber memory.
//
- // As a fix for CVE-2019-5595, FreeBSD started
- // clobbering R8, R9, and R10, instead of restoring
- // them. Additionally unlike Linux, instead of
- // returning negative errno, positive errno is
- // returned and CF is set.
- //
// TODO:
// * Figure out what Darwin does.
- // * Add some extra handling to propagate CF back
- // up to the caller on FreeBSD systems so that
- // the caller knows that the return value is
- // positive errno.
constraints = gb_string_appendc(constraints, ",~{rcx},~{r11},~{memory}");
- if (build_context.metrics.os == TargetOs_freebsd) {
- // Second half of dealing with FreeBSD's system
- // call semantics. Explicitly clobber the registers
- // that were not used to pass in arguments, and
- // then clobber RFLAGS.
- if (arg_count < 5) {
- constraints = gb_string_appendc(constraints, ",~{r10}");
- }
- if (arg_count < 6) {
- constraints = gb_string_appendc(constraints, ",~{r8}");
- }
- if (arg_count < 7) {
- constraints = gb_string_appendc(constraints, ",~{r9}");
- }
- constraints = gb_string_appendc(constraints, ",~{cc}");
- }
-
inline_asm = llvm_get_inline_asm(func_type, make_string_c(asm_string), make_string_c(constraints));
}
break;
@@ -2769,8 +2963,7 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
{
GB_ASSERT(arg_count <= 7);
- char asm_string_default[] = "int $$0x80";
- char *asm_string = asm_string_default;
+ char asm_string[] = "int $$0x80";
gbString constraints = gb_string_make(heap_allocator(), "={eax}");
for (unsigned i = 0; i < gb_min(arg_count, 6); i++) {
@@ -2782,16 +2975,11 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
"edx",
"esi",
"edi",
+ "ebp",
};
constraints = gb_string_appendc(constraints, regs[i]);
constraints = gb_string_appendc(constraints, "}");
}
- if (arg_count == 7) {
- char asm_string7[] = "push %[arg6]\npush %%ebp\nmov 4(%%esp), %%ebp\nint $0x80\npop %%ebp\nadd $4, %%esp";
- asm_string = asm_string7;
-
- constraints = gb_string_appendc(constraints, ",rm");
- }
inline_asm = llvm_get_inline_asm(func_type, make_string_c(asm_string), make_string_c(constraints));
}
@@ -2843,7 +3031,6 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
break;
case TargetArch_arm32:
{
- // TODO(bill): Check this is correct
GB_ASSERT(arg_count <= 7);
char asm_string[] = "svc #0";
@@ -2851,13 +3038,14 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
for (unsigned i = 0; i < arg_count; i++) {
constraints = gb_string_appendc(constraints, ",{");
static char const *regs[] = {
- "r8",
+ "r7",
"r0",
"r1",
"r2",
"r3",
"r4",
"r5",
+ "r6",
};
constraints = gb_string_appendc(constraints, regs[i]);
constraints = gb_string_appendc(constraints, "}");
@@ -2875,6 +3063,139 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
res.type = t_uintptr;
return res;
}
+ case BuiltinProc_syscall_bsd:
+ {
+ // This is a BSD-style syscall where errors are indicated by a high
+ // Carry Flag and a positive return value, allowing the kernel to
+ // return any value that fits into a machine word.
+ //
+ // This is unlike Linux, where errors are indicated by a negative
+ // return value, limiting what can be expressed in one result.
+ unsigned arg_count = cast(unsigned)ce->args.count;
+ LLVMValueRef *args = gb_alloc_array(permanent_allocator(), LLVMValueRef, arg_count);
+ for_array(i, ce->args) {
+ lbValue arg = lb_build_expr(p, ce->args[i]);
+ arg = lb_emit_conv(p, arg, t_uintptr);
+ args[i] = arg.value;
+ }
+
+ LLVMTypeRef llvm_uintptr = lb_type(p->module, t_uintptr);
+ LLVMTypeRef *llvm_arg_types = gb_alloc_array(permanent_allocator(), LLVMTypeRef, arg_count);
+ for (unsigned i = 0; i < arg_count; i++) {
+ llvm_arg_types[i] = llvm_uintptr;
+ }
+
+ LLVMTypeRef *results = gb_alloc_array(permanent_allocator(), LLVMTypeRef, 2);
+ results[0] = lb_type(p->module, t_uintptr);
+ results[1] = lb_type(p->module, t_bool);
+ LLVMTypeRef llvm_results = LLVMStructTypeInContext(p->module->ctx, results, 2, false);
+
+ LLVMTypeRef func_type = LLVMFunctionType(llvm_results, llvm_arg_types, arg_count, false);
+
+ LLVMValueRef inline_asm = nullptr;
+
+ switch (build_context.metrics.arch) {
+ case TargetArch_amd64:
+ {
+ GB_ASSERT(arg_count <= 7);
+
+ char asm_string[] = "syscall; setnb %cl";
+
+ // Using CL as an output; RCX doesn't need to get clobbered later.
+ gbString constraints = gb_string_make(heap_allocator(), "={rax},={cl}");
+ for (unsigned i = 0; i < arg_count; i++) {
+ constraints = gb_string_appendc(constraints, ",{");
+ static char const *regs[] = {
+ "rax",
+ "rdi",
+ "rsi",
+ "rdx",
+ "r10",
+ "r8",
+ "r9",
+ };
+ constraints = gb_string_appendc(constraints, regs[i]);
+ constraints = gb_string_appendc(constraints, "}");
+ }
+
+ // NOTE(Feoramund): If you're experiencing instability
+ // regarding syscalls during optimized builds, it is
+ // possible that the ABI has changed for your platform,
+ // or I've missed a register clobber.
+ //
+ // Documentation on this topic is sparse, but I was able to
+ // determine what registers were being clobbered by adding
+ // dummy values to them, setting a breakpoint after the
+ // syscall, and checking the state of the registers afterwards.
+ //
+ // Be advised that manually stepping through a debugger may
+ // cause the kernel to not return via sysret, which will
+ // preserve register state that normally would've been
+ // otherwise clobbered.
+ //
+ // It is also possible that some syscalls clobber different registers.
+
+ if (build_context.metrics.os == TargetOs_freebsd) {
+ // As a fix for CVE-2019-5595, FreeBSD started
+ // clobbering R8, R9, and R10, instead of restoring
+ // them.
+ //
+ // More info here:
+ //
+ // https://www.freebsd.org/security/advisories/FreeBSD-SA-19:01.syscall.asc
+ // https://github.com/freebsd/freebsd-src/blob/098dbd7ff7f3da9dda03802cdb2d8755f816eada/sys/amd64/amd64/exception.S#L605
+ // https://stackoverflow.com/q/66878250
+ constraints = gb_string_appendc(constraints, ",~{r8},~{r9},~{r10}");
+ }
+
+ // Both FreeBSD and NetBSD might clobber RDX.
+ //
+ // For NetBSD, it was clobbered during a call to sysctl.
+ //
+ // For FreeBSD, it's listed as "return value 2" in their
+ // AMD64 assembly, so there's no guarantee that it will persist.
+ constraints = gb_string_appendc(constraints, ",~{rdx},~{r11},~{cc},~{memory}");
+
+ inline_asm = llvm_get_inline_asm(func_type, make_string_c(asm_string), make_string_c(constraints));
+ }
+ break;
+ case TargetArch_arm64:
+ {
+ 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[] = {
+ "x8",
+ "x0",
+ "x1",
+ "x2",
+ "x3",
+ "x4",
+ "x5",
+ };
+ 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}");
+
+ inline_asm = llvm_get_inline_asm(func_type, make_string_c(asm_string), make_string_c(constraints));
+ }
+ break;
+ default:
+ GB_PANIC("Unsupported platform");
+ }
+
+ lbValue res = {};
+ res.value = LLVMBuildCall2(p->builder, func_type, inline_asm, args, arg_count, "");
+ res.type = make_optional_ok_type(t_uintptr, true);
+
+ return res;
+ }
case BuiltinProc_objc_send:
return lb_handle_objc_send(p, expr);
@@ -2991,9 +3312,6 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
case BuiltinProc_wasm_memory_atomic_wait32:
{
char const *name = "llvm.wasm.memory.atomic.wait32";
- LLVMTypeRef types[1] = {
- lb_type(p->module, t_u32),
- };
Type *t_u32_ptr = alloc_type_pointer(t_u32);
@@ -3004,26 +3322,24 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
lbValue res = {};
res.type = tv.type;
- res.value = lb_call_intrinsic(p, name, args, gb_count_of(args), types, gb_count_of(types));
+ res.value = lb_call_intrinsic(p, name, args, gb_count_of(args), nullptr, 0);
return res;
}
case BuiltinProc_wasm_memory_atomic_notify32:
{
char const *name = "llvm.wasm.memory.atomic.notify";
- LLVMTypeRef types[1] = {
- lb_type(p->module, t_u32),
- };
Type *t_u32_ptr = alloc_type_pointer(t_u32);
LLVMValueRef args[2] = {
- lb_emit_conv(p, lb_build_expr(p, ce->args[0]), t_u32_ptr).value,
- lb_emit_conv(p, lb_build_expr(p, ce->args[1]), t_u32).value };
+ lb_emit_conv(p, lb_build_expr(p, ce->args[0]), t_u32_ptr).value,
+ lb_emit_conv(p, lb_build_expr(p, ce->args[1]), t_u32).value
+ };
lbValue res = {};
res.type = tv.type;
- res.value = lb_call_intrinsic(p, name, args, gb_count_of(args), types, gb_count_of(types));
+ res.value = lb_call_intrinsic(p, name, args, gb_count_of(args), nullptr, 0);
return res;
}
@@ -3124,7 +3440,7 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
}
-gb_internal lbValue lb_handle_param_value(lbProcedure *p, Type *parameter_type, ParameterValue const &param_value, TokenPos const &pos) {
+gb_internal lbValue lb_handle_param_value(lbProcedure *p, Type *parameter_type, ParameterValue const &param_value, TypeProc *procedure_type, Ast* call_expression) {
switch (param_value.kind) {
case ParameterValue_Constant:
if (is_type_constant_type(parameter_type)) {
@@ -3150,8 +3466,60 @@ gb_internal lbValue lb_handle_param_value(lbProcedure *p, Type *parameter_type,
if (p->entity != nullptr) {
proc_name = p->entity->token.string;
}
+
+ ast_node(ce, CallExpr, call_expression);
+ TokenPos pos = ast_token(ce->proc).pos;
+
return lb_emit_source_code_location_as_global(p, proc_name, pos);
}
+ case ParameterValue_Expression:
+ {
+ Ast *orig = param_value.original_ast_expr;
+ if (orig->kind == Ast_BasicDirective) {
+ gbString expr = expr_to_string(call_expression, temporary_allocator());
+ return lb_const_string(p->module, make_string_c(expr));
+ }
+
+ isize param_idx = -1;
+ String param_str = {0};
+ {
+ Ast *call = unparen_expr(orig);
+ GB_ASSERT(call->kind == Ast_CallExpr);
+ ast_node(ce, CallExpr, call);
+ GB_ASSERT(ce->proc->kind == Ast_BasicDirective);
+ GB_ASSERT(ce->args.count == 1);
+ Ast *target = ce->args[0];
+ GB_ASSERT(target->kind == Ast_Ident);
+ String target_str = target->Ident.token.string;
+
+ param_idx = lookup_procedure_parameter(procedure_type, target_str);
+ param_str = target_str;
+ }
+ GB_ASSERT(param_idx >= 0);
+
+
+ Ast *target_expr = nullptr;
+ ast_node(ce, CallExpr, call_expression);
+
+ if (ce->split_args->positional.count > param_idx) {
+ target_expr = ce->split_args->positional[param_idx];
+ }
+
+ for_array(i, ce->split_args->named) {
+ Ast *arg = ce->split_args->named[i];
+ ast_node(fv, FieldValue, arg);
+ GB_ASSERT(fv->field->kind == Ast_Ident);
+ String name = fv->field->Ident.token.string;
+ if (name == param_str) {
+ target_expr = fv->value;
+ break;
+ }
+ }
+
+ gbString expr = expr_to_string(target_expr, temporary_allocator());
+ return lb_const_string(p->module, make_string_c(expr));
+ }
+
case ParameterValue_Value:
return lb_build_expr(p, param_value.ast_value);
}
@@ -3295,9 +3663,12 @@ gb_internal lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr) {
for (Ast *var_arg : variadic) {
lbValue arg = lb_build_expr(p, var_arg);
if (is_type_any(elem_type)) {
- array_add(&args, lb_emit_conv(p, arg, default_type(arg.type)));
+ if (is_type_untyped_nil(arg.type)) {
+ arg = lb_const_nil(p->module, t_rawptr);
+ }
+ array_add(&args, lb_emit_c_vararg(p, arg, arg.type));
} else {
- array_add(&args, lb_emit_conv(p, arg, elem_type));
+ array_add(&args, lb_emit_c_vararg(p, arg, elem_type));
}
}
break;
@@ -3316,17 +3687,67 @@ gb_internal lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr) {
}
isize slice_len = var_args.count;
if (slice_len > 0) {
- lbAddr slice = lb_add_local_generated(p, slice_type, true);
- lbAddr base_array = lb_add_local_generated(p, alloc_type_array(elem_type, slice_len), true);
+ lbAddr slice = {};
+
+ for (auto const &vr : p->variadic_reuses) {
+ if (are_types_identical(vr.slice_type, slice_type)) {
+ slice = vr.slice_addr;
+ break;
+ }
+ }
+
+ DeclInfo *d = decl_info_of_entity(p->entity);
+ if (d != nullptr && slice.addr.value == nullptr) {
+ for (auto const &vr : d->variadic_reuses) {
+ if (are_types_identical(vr.slice_type, slice_type)) {
+ #if LLVM_VERSION_MAJOR >= 13
+ // NOTE(bill): No point wasting even more memory, just reuse this stack variable too
+ if (p->variadic_reuses.count > 0) {
+ slice = p->variadic_reuses[0].slice_addr;
+ } else {
+ slice = lb_add_local_generated(p, slice_type, true);
+ }
+ // NOTE(bill): Change the underlying type to match the specific type
+ slice.addr.type = alloc_type_pointer(slice_type);
+ #else
+ slice = lb_add_local_generated(p, slice_type, true);
+ #endif
+ array_add(&p->variadic_reuses, lbVariadicReuseSlices{slice_type, slice});
+ break;
+ }
+ }
+ }
+
+ lbValue base_array_ptr = p->variadic_reuse_base_array_ptr.addr;
+ if (base_array_ptr.value == nullptr) {
+ if (d != nullptr) {
+ i64 max_bytes = d->variadic_reuse_max_bytes;
+ i64 max_align = gb_max(d->variadic_reuse_max_align, 16);
+ p->variadic_reuse_base_array_ptr = lb_add_local_generated(p, alloc_type_array(t_u8, max_bytes), true);
+ lb_try_update_alignment(p->variadic_reuse_base_array_ptr.addr, cast(unsigned)max_align);
+ base_array_ptr = p->variadic_reuse_base_array_ptr.addr;
+ } else {
+ base_array_ptr = lb_add_local_generated(p, alloc_type_array(elem_type, slice_len), true).addr;
+ }
+ }
+
+ if (slice.addr.value == nullptr) {
+ slice = lb_add_local_generated(p, slice_type, true);
+ }
+
+ GB_ASSERT(base_array_ptr.value != nullptr);
+ GB_ASSERT(slice.addr.value != nullptr);
+
+ base_array_ptr = lb_emit_conv(p, base_array_ptr, alloc_type_pointer(alloc_type_array(elem_type, slice_len)));
for (isize i = 0; i < var_args.count; i++) {
- lbValue addr = lb_emit_array_epi(p, base_array.addr, cast(i32)i);
+ lbValue addr = lb_emit_array_epi(p, base_array_ptr, cast(i32)i);
lbValue var_arg = var_args[i];
var_arg = lb_emit_conv(p, var_arg, elem_type);
lb_emit_store(p, addr, var_arg);
}
- lbValue base_elem = lb_emit_array_epi(p, base_array.addr, 0);
+ lbValue base_elem = lb_emit_array_epi(p, base_array_ptr, 0);
lbValue len = lb_const_int(p->module, t_int, slice_len);
lb_fill_slice(p, slice, base_elem, len);
@@ -3359,6 +3780,30 @@ gb_internal lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr) {
if (e->kind == Entity_TypeName) {
lbValue value = lb_const_nil(p->module, e->type);
args[param_index] = value;
+ } else if (is_c_vararg && pt->variadic && pt->variadic_index == param_index) {
+ GB_ASSERT(param_index == pt->param_count-1);
+ Type *slice_type = e->type;
+ GB_ASSERT(slice_type->kind == Type_Slice);
+ Type *elem_type = slice_type->Slice.elem;
+
+ if (fv->value->kind == Ast_CompoundLit) {
+ ast_node(literal, CompoundLit, fv->value);
+ for (Ast *var_arg : literal->elems) {
+ lbValue arg = lb_build_expr(p, var_arg);
+ if (is_type_any(elem_type)) {
+ if (is_type_untyped_nil(arg.type)) {
+ arg = lb_const_nil(p->module, t_rawptr);
+ }
+ array_add(&args, lb_emit_c_vararg(p, arg, arg.type));
+ } else {
+ array_add(&args, lb_emit_c_vararg(p, arg, elem_type));
+ }
+ }
+ } else {
+ lbValue value = lb_build_expr(p, fv->value);
+ GB_ASSERT(!is_type_tuple(value.type));
+ array_add(&args, lb_emit_c_vararg(p, value, value.type));
+ }
} else {
lbValue value = lb_build_expr(p, fv->value);
GB_ASSERT(!is_type_tuple(value.type));
@@ -3366,8 +3811,6 @@ gb_internal lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr) {
}
}
- TokenPos pos = ast_token(ce->proc).pos;
-
if (pt->params != nullptr) {
isize min_count = pt->params->Tuple.variables.count;
@@ -3385,13 +3828,13 @@ gb_internal lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr) {
}
lbValue arg = args[arg_index];
- if (arg.value == nullptr) {
+ if (arg.value == nullptr && arg.type == nullptr) {
switch (e->kind) {
case Entity_TypeName:
args[arg_index] = lb_const_nil(p->module, e->type);
break;
case Entity_Variable:
- args[arg_index] = lb_handle_param_value(p, e->type, e->Variable.param_value, pos);
+ args[arg_index] = lb_handle_param_value(p, e->type, e->Variable.param_value, pt, expr);
break;
case Entity_Constant:
diff --git a/src/llvm_backend_stmt.cpp b/src/llvm_backend_stmt.cpp
index 9d688be6a..a2f0d2f4a 100644
--- a/src/llvm_backend_stmt.cpp
+++ b/src/llvm_backend_stmt.cpp
@@ -201,13 +201,15 @@ gb_internal void lb_open_scope(lbProcedure *p, Scope *s) {
}
}
+ GB_ASSERT(s != nullptr);
+ p->curr_scope = s;
p->scope_index += 1;
array_add(&p->scope_stack, s);
}
-gb_internal void lb_close_scope(lbProcedure *p, lbDeferExitKind kind, lbBlock *block, bool pop_stack=true) {
- lb_emit_defer_stmts(p, kind, block);
+gb_internal void lb_close_scope(lbProcedure *p, lbDeferExitKind kind, lbBlock *block, Ast *node, bool pop_stack=true) {
+ lb_emit_defer_stmts(p, kind, block, node);
GB_ASSERT(p->scope_index > 0);
// NOTE(bill): Remove `context`s made in that scope
@@ -221,6 +223,10 @@ gb_internal void lb_close_scope(lbProcedure *p, lbDeferExitKind kind, lbBlock *b
}
+ if (p->curr_scope) {
+ p->curr_scope = p->curr_scope->parent;
+ }
+
p->scope_index -= 1;
array_pop(&p->scope_stack);
}
@@ -715,7 +721,7 @@ gb_internal void lb_build_range_interval(lbProcedure *p, AstBinaryExpr *node,
lb_build_stmt(p, rs->body);
- lb_close_scope(p, lbDeferExit_Default, nullptr);
+ lb_close_scope(p, lbDeferExit_Default, nullptr, node->left);
lb_pop_target_list(p);
if (check != nullptr) {
@@ -737,6 +743,22 @@ gb_internal void lb_build_range_interval(lbProcedure *p, AstBinaryExpr *node,
lb_start_block(p, done);
}
+gb_internal lbValue lb_enum_values_slice(lbProcedure *p, Type *enum_type, i64 *enum_count_) {
+ Type *t = enum_type;
+ GB_ASSERT(is_type_enum(t));
+ t = base_type(t);
+ GB_ASSERT(t->kind == Type_Enum);
+ i64 enum_count = t->Enum.fields.count;
+
+ if (enum_count_) *enum_count_ = enum_count;
+
+ lbValue ti = lb_type_info(p, t);
+ lbValue variant = lb_emit_struct_ep(p, ti, 4);
+ lbValue eti_ptr = lb_emit_conv(p, variant, t_type_info_enum_ptr);
+ lbValue values = lb_emit_load(p, lb_emit_struct_ep(p, eti_ptr, 2));
+ return values;
+}
+
gb_internal void lb_build_range_enum(lbProcedure *p, Type *enum_type, Type *val_type, lbValue *val_, lbValue *idx_, lbBlock **loop_, lbBlock **done_) {
lbModule *m = p->module;
@@ -744,15 +766,11 @@ gb_internal void lb_build_range_enum(lbProcedure *p, Type *enum_type, Type *val_
GB_ASSERT(is_type_enum(t));
t = base_type(t);
Type *core_elem = core_type(t);
- GB_ASSERT(t->kind == Type_Enum);
- i64 enum_count = t->Enum.fields.count;
- lbValue max_count = lb_const_int(m, t_int, enum_count);
+ i64 enum_count = 0;
- lbValue ti = lb_type_info(m, t);
- lbValue variant = lb_emit_struct_ep(p, ti, 4);
- lbValue eti_ptr = lb_emit_conv(p, variant, t_type_info_enum_ptr);
- lbValue values = lb_emit_load(p, lb_emit_struct_ep(p, eti_ptr, 2));
+ lbValue values = lb_enum_values_slice(p, enum_type, &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));
@@ -790,8 +808,19 @@ gb_internal void lb_build_range_enum(lbProcedure *p, Type *enum_type, Type *val_
if (done_) *done_ = done;
}
-gb_internal void lb_build_range_tuple(lbProcedure *p, Ast *expr, Type *val0_type, Type *val1_type,
- lbValue *val0_, lbValue *val1_, lbBlock **loop_, lbBlock **done_) {
+gb_internal void lb_build_range_tuple(lbProcedure *p, AstRangeStmt *rs, Scope *scope) {
+ Ast *expr = unparen_expr(rs->expr);
+
+ Type *expr_type = type_of_expr(expr);
+ Type *et = base_type(type_deref(expr_type));
+ GB_ASSERT(et->kind == Type_Tuple);
+
+ i32 value_count = cast(i32)et->Tuple.variables.count;
+
+ lbValue *values = gb_alloc_array(permanent_allocator(), lbValue, value_count);
+
+ lb_open_scope(p, scope);
+
lbBlock *loop = lb_create_block(p, "for.tuple.loop");
lb_emit_jump(p, loop);
lb_start_block(p, loop);
@@ -809,11 +838,26 @@ gb_internal void lb_build_range_tuple(lbProcedure *p, Ast *expr, Type *val0_type
lb_emit_if(p, cond, body, done);
lb_start_block(p, body);
+ for (i32 i = 0; i < value_count; i++) {
+ values[i] = lb_emit_tuple_ev(p, tuple_value, i);
+ }
- if (val0_) *val0_ = lb_emit_tuple_ev(p, tuple_value, 0);
- if (val1_) *val1_ = lb_emit_tuple_ev(p, tuple_value, 1);
- if (loop_) *loop_ = loop;
- if (done_) *done_ = done;
+ GB_ASSERT(rs->vals.count <= value_count);
+ for (isize i = 0; i < rs->vals.count; i++) {
+ Ast *val = rs->vals[i];
+ if (val != nullptr) {
+ lb_store_range_stmt_val(p, val, values[i]);
+ }
+ }
+
+ lb_push_target_list(p, rs->label, done, loop, nullptr);
+
+ lb_build_stmt(p, rs->body);
+
+ lb_close_scope(p, lbDeferExit_Default, nullptr, rs->body);
+ lb_pop_target_list(p);
+ lb_emit_jump(p, loop);
+ lb_start_block(p, done);
}
gb_internal void lb_build_range_stmt_struct_soa(lbProcedure *p, AstRangeStmt *rs, Scope *scope) {
@@ -932,7 +976,7 @@ gb_internal void lb_build_range_stmt_struct_soa(lbProcedure *p, AstRangeStmt *rs
lb_build_stmt(p, rs->body);
- lb_close_scope(p, lbDeferExit_Default, nullptr);
+ lb_close_scope(p, lbDeferExit_Default, nullptr, rs->body);
lb_pop_target_list(p);
lb_emit_jump(p, loop);
lb_start_block(p, done);
@@ -956,6 +1000,17 @@ gb_internal void lb_build_range_stmt(lbProcedure *p, AstRangeStmt *rs, Scope *sc
}
}
+ TypeAndValue tav = type_and_value_of_expr(expr);
+ if (tav.mode != Addressing_Type) {
+ Type *expr_type = type_of_expr(expr);
+ Type *et = base_type(type_deref(expr_type));
+ if (et->kind == Type_Tuple) {
+ lb_build_range_tuple(p, rs, scope);
+ return;
+ }
+ }
+
+
lb_open_scope(p, scope);
Ast *val0 = rs->vals.count > 0 ? lb_strip_and_prefix(rs->vals[0]) : nullptr;
@@ -974,7 +1029,6 @@ gb_internal void lb_build_range_stmt(lbProcedure *p, AstRangeStmt *rs, Scope *sc
lbBlock *loop = nullptr;
lbBlock *done = nullptr;
bool is_map = false;
- TypeAndValue tav = type_and_value_of_expr(expr);
if (tav.mode == Addressing_Type) {
lb_build_range_enum(p, type_deref(tav.type), val0_type, &val, &key, &loop, &done);
@@ -1050,8 +1104,75 @@ gb_internal void lb_build_range_stmt(lbProcedure *p, AstRangeStmt *rs, Scope *sc
break;
}
case Type_Tuple:
- lb_build_range_tuple(p, expr, val0_type, val1_type, &val, &key, &loop, &done);
+ GB_PANIC("Should be handled already");
+
+ case Type_BitSet: {
+ lbModule *m = p->module;
+
+ lbValue the_set = lb_build_expr(p, expr);
+ if (is_type_pointer(type_deref(the_set.type))) {
+ the_set = lb_emit_load(p, the_set);
+ }
+
+ 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));
+
+ lbValue max_count = lb_const_int(m, t_int, et->BitSet.upper);
+
+ loop = lb_create_block(p, "for.bit_set.range.loop");
+ lb_emit_jump(p, loop);
+ lb_start_block(p, loop);
+
+ 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");
+
+ 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);
+
+ val = lb_emit_conv(p, offset, elem);
+ lb_emit_increment(p, offset_.addr);
+
+ lbValue check = lb_build_binary_in(p, val, the_set, Token_in);
+ lb_emit_if(p, check, body, loop);
+ lb_start_block(p, body);
+ }
break;
+ }
default:
GB_PANIC("Cannot range over %s", type_to_string(expr_type));
break;
@@ -1071,7 +1192,7 @@ gb_internal void lb_build_range_stmt(lbProcedure *p, AstRangeStmt *rs, Scope *sc
lb_build_stmt(p, rs->body);
- lb_close_scope(p, lbDeferExit_Default, nullptr);
+ lb_close_scope(p, lbDeferExit_Default, nullptr, rs->body);
lb_pop_target_list(p);
lb_emit_jump(p, loop);
lb_start_block(p, done);
@@ -1242,7 +1363,7 @@ gb_internal void lb_build_unroll_range_stmt(lbProcedure *p, AstUnrollRangeStmt *
}
- lb_close_scope(p, lbDeferExit_Default, nullptr);
+ lb_close_scope(p, lbDeferExit_Default, nullptr, rs->body);
}
gb_internal bool lb_switch_stmt_can_be_trivial_jump_table(AstSwitchStmt *ss, bool *default_found_) {
@@ -1289,6 +1410,10 @@ gb_internal bool lb_switch_stmt_can_be_trivial_jump_table(AstSwitchStmt *ss, boo
}
+ if (is_typeid) {
+ return false;
+ }
+
return true;
}
@@ -1308,6 +1433,7 @@ gb_internal void lb_build_switch_stmt(lbProcedure *p, AstSwitchStmt *ss, Scope *
ast_node(body, BlockStmt, ss->body);
isize case_count = body->stmts.count;
+ Ast *default_clause = nullptr;
Slice<Ast *> default_stmts = {};
lbBlock *default_fall = nullptr;
lbBlock *default_block = nullptr;
@@ -1357,6 +1483,7 @@ gb_internal void lb_build_switch_stmt(lbProcedure *p, AstSwitchStmt *ss, Scope *
if (cc->list.count == 0) {
// default case
+ default_clause = clause;
default_stmts = cc->stmts;
default_fall = fall;
if (switch_instr == nullptr) {
@@ -1427,7 +1554,7 @@ gb_internal void lb_build_switch_stmt(lbProcedure *p, AstSwitchStmt *ss, Scope *
lb_push_target_list(p, ss->label, done, nullptr, fall);
lb_open_scope(p, body->scope);
lb_build_stmt_list(p, cc->stmts);
- lb_close_scope(p, lbDeferExit_Default, body);
+ lb_close_scope(p, lbDeferExit_Default, body, clause);
lb_pop_target_list(p);
lb_emit_jump(p, done);
@@ -1445,26 +1572,33 @@ gb_internal void lb_build_switch_stmt(lbProcedure *p, AstSwitchStmt *ss, Scope *
lb_push_target_list(p, ss->label, done, nullptr, default_fall);
lb_open_scope(p, default_block->scope);
lb_build_stmt_list(p, default_stmts);
- lb_close_scope(p, lbDeferExit_Default, default_block);
+ lb_close_scope(p, lbDeferExit_Default, default_block, default_clause);
lb_pop_target_list(p);
}
lb_emit_jump(p, done);
lb_start_block(p, done);
- lb_close_scope(p, lbDeferExit_Default, done);
+ lb_close_scope(p, lbDeferExit_Default, done, ss->body);
}
-gb_internal void lb_store_type_case_implicit(lbProcedure *p, Ast *clause, lbValue value) {
+gb_internal void lb_store_type_case_implicit(lbProcedure *p, Ast *clause, lbValue value, bool is_default_case) {
Entity *e = implicit_entity_of_node(clause);
GB_ASSERT(e != nullptr);
if (e->flags & EntityFlag_Value) {
// by value
- GB_ASSERT(are_types_identical(e->type, value.type));
- lbAddr x = lb_add_local(p, e->type, e, false);
- lb_addr_store(p, x, value);
+ if (are_types_identical(e->type, value.type)) {
+ lbAddr x = lb_add_local(p, e->type, e, false);
+ lb_addr_store(p, x, value);
+ } else {
+ GB_ASSERT_MSG(are_types_identical(e->type, type_deref(value.type)), "%s", type_to_string(value.type));
+ lbAddr x = lb_add_local(p, e->type, e, false);
+ lb_addr_store(p, x, lb_emit_load(p, value));
+ }
} else {
- // by reference
- GB_ASSERT(are_types_identical(e->type, type_deref(value.type)));
+ if (!is_default_case) {
+ Type *clause_type = e->type;
+ GB_ASSERT_MSG(are_types_identical(type_deref(clause_type), type_deref(value.type)), "%s %s", type_to_string(clause_type), type_to_string(value.type));
+ }
lb_add_entity(p->module, e, value);
}
}
@@ -1495,7 +1629,7 @@ gb_internal void lb_type_case_body(lbProcedure *p, Ast *label, Ast *clause, lbBl
lb_push_target_list(p, label, done, nullptr, nullptr);
lb_build_stmt_list(p, cc->stmts);
- lb_close_scope(p, lbDeferExit_Default, body);
+ lb_close_scope(p, lbDeferExit_Default, body, clause);
lb_pop_target_list(p);
lb_emit_jump(p, done);
@@ -1529,7 +1663,7 @@ gb_internal void lb_build_type_switch_stmt(lbProcedure *p, AstTypeSwitchStmt *ss
union_data = lb_emit_conv(p, parent_ptr, t_rawptr);
Type *union_type = type_deref(parent_ptr.type);
if (is_type_union_maybe_pointer(union_type)) {
- tag = lb_emit_conv(p, lb_emit_comp_against_nil(p, Token_NotEq, union_data), t_int);
+ tag = lb_emit_conv(p, lb_emit_comp_against_nil(p, Token_NotEq, parent_value), t_int);
} else if (union_tag_size(union_type) == 0) {
tag = {}; // there is no tag for a zero sized union
} else {
@@ -1619,10 +1753,17 @@ gb_internal void lb_build_type_switch_stmt(lbProcedure *p, AstTypeSwitchStmt *ss
for (Ast *clause : body->stmts) {
ast_node(cc, CaseClause, clause);
+
+ Entity *case_entity = implicit_entity_of_node(clause);
lb_open_scope(p, cc->scope);
+
if (cc->list.count == 0) {
lb_start_block(p, default_block);
- lb_store_type_case_implicit(p, clause, parent_value);
+ if (case_entity->flags & EntityFlag_Value) {
+ lb_store_type_case_implicit(p, clause, parent_value, true);
+ } else {
+ lb_store_type_case_implicit(p, clause, parent_ptr, true);
+ }
lb_type_case_body(p, ss->label, clause, p->curr_block, done);
continue;
}
@@ -1652,7 +1793,6 @@ gb_internal void lb_build_type_switch_stmt(lbProcedure *p, AstTypeSwitchStmt *ss
LLVMAddCase(switch_instr, on_val.value, body->block);
}
- Entity *case_entity = implicit_entity_of_node(clause);
lb_start_block(p, body);
@@ -1665,6 +1805,7 @@ gb_internal void lb_build_type_switch_stmt(lbProcedure *p, AstTypeSwitchStmt *ss
} else if (switch_kind == TypeSwitch_Any) {
data = lb_emit_load(p, lb_emit_struct_ep(p, parent_ptr, 0));
}
+ GB_ASSERT(is_type_pointer(data.type));
Type *ct = case_entity->type;
Type *ct_ptr = alloc_type_pointer(ct);
@@ -1688,7 +1829,7 @@ 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);
+ lb_store_type_case_implicit(p, clause, parent_value, false);
}
lb_type_case_body(p, ss->label, clause, body, done);
@@ -1696,7 +1837,7 @@ gb_internal void lb_build_type_switch_stmt(lbProcedure *p, AstTypeSwitchStmt *ss
lb_emit_jump(p, done);
lb_start_block(p, done);
- lb_close_scope(p, lbDeferExit_Default, done);
+ lb_close_scope(p, lbDeferExit_Default, done, ss->body);
}
@@ -1734,7 +1875,9 @@ gb_internal void lb_build_static_variables(lbProcedure *p, AstValueDecl *vd) {
LLVMSetInitializer(global, LLVMConstNull(lb_type(p->module, e->type)));
if (value.value != nullptr) {
LLVMSetInitializer(global, value.value);
- } else {
+ }
+ if (e->Variable.is_rodata) {
+ LLVMSetGlobalConstant(global, true);
}
if (e->Variable.thread_local_model != "") {
LLVMSetThreadLocal(global, true);
@@ -1818,7 +1961,7 @@ gb_internal void lb_build_assignment(lbProcedure *p, Array<lbAddr> &lvals, Slice
p->in_multi_assignment = prev_in_assignment;
}
-gb_internal void lb_build_return_stmt_internal(lbProcedure *p, lbValue res) {
+gb_internal void lb_build_return_stmt_internal(lbProcedure *p, lbValue res, TokenPos pos) {
lbFunctionType *ft = lb_get_function_type(p->module, p->type);
bool return_by_pointer = ft->ret.kind == lbArg_Indirect;
bool split_returns = ft->multiple_return_original_type != nullptr;
@@ -1841,40 +1984,72 @@ gb_internal void lb_build_return_stmt_internal(lbProcedure *p, lbValue res) {
LLVMBuildStore(p->builder, LLVMConstNull(p->abi_function_type->ret.type), p->return_ptr.addr.value);
}
- lb_emit_defer_stmts(p, lbDeferExit_Return, nullptr);
+ lb_emit_defer_stmts(p, lbDeferExit_Return, nullptr, pos);
- LLVMBuildRetVoid(p->builder);
+ // Check for terminator in the defer stmts
+ LLVMValueRef instr = LLVMGetLastInstruction(p->curr_block->block);
+ if (!lb_is_instr_terminating(instr)) {
+ LLVMBuildRetVoid(p->builder);
+ }
} else {
LLVMValueRef ret_val = res.value;
- ret_val = OdinLLVMBuildTransmute(p, ret_val, p->abi_function_type->ret.type);
- if (p->abi_function_type->ret.cast_type != nullptr) {
- ret_val = OdinLLVMBuildTransmute(p, ret_val, p->abi_function_type->ret.cast_type);
+ LLVMTypeRef ret_type = p->abi_function_type->ret.type;
+ if (LLVMTypeRef cast_type = p->abi_function_type->ret.cast_type) {
+ ret_type = cast_type;
+ }
+
+ if (LLVMGetTypeKind(ret_type) == LLVMStructTypeKind) {
+ LLVMTypeRef src_type = LLVMTypeOf(ret_val);
+
+ if (p->temp_callee_return_struct_memory == nullptr) {
+ i64 max_align = gb_max(lb_alignof(ret_type), lb_alignof(src_type));
+ p->temp_callee_return_struct_memory = llvm_alloca(p, ret_type, max_align);
+ }
+ // reuse the temp return value memory where possible
+ LLVMValueRef ptr = p->temp_callee_return_struct_memory;
+ LLVMValueRef nptr = LLVMBuildPointerCast(p->builder, ptr, LLVMPointerType(src_type, 0), "");
+ LLVMBuildStore(p->builder, ret_val, nptr);
+ ret_val = OdinLLVMBuildLoad(p, ret_type, ptr);
+ } else {
+ ret_val = OdinLLVMBuildTransmute(p, ret_val, ret_type);
}
- lb_emit_defer_stmts(p, lbDeferExit_Return, nullptr);
- LLVMBuildRet(p->builder, ret_val);
+ lb_emit_defer_stmts(p, lbDeferExit_Return, nullptr, pos);
+
+ // Check for terminator in the defer stmts
+ LLVMValueRef instr = LLVMGetLastInstruction(p->curr_block->block);
+ if (!lb_is_instr_terminating(instr)) {
+ LLVMBuildRet(p->builder, ret_val);
+ }
}
}
-gb_internal void lb_build_return_stmt(lbProcedure *p, Slice<Ast *> const &return_results) {
+gb_internal void lb_build_return_stmt(lbProcedure *p, Slice<Ast *> const &return_results, TokenPos pos) {
lb_ensure_abi_function_type(p->module, p);
+ isize return_count = p->type->Proc.result_count;
+
+ if (return_count == 0) {
+ // No return values
+
+ lb_emit_defer_stmts(p, lbDeferExit_Return, nullptr, pos);
+
+ // Check for terminator in the defer stmts
+ LLVMValueRef instr = LLVMGetLastInstruction(p->curr_block->block);
+ if (!lb_is_instr_terminating(instr)) {
+ LLVMBuildRetVoid(p->builder);
+ }
+ return;
+ }
+
lbValue res = {};
- TypeTuple *tuple = &p->type->Proc.results->Tuple;
- isize return_count = p->type->Proc.result_count;
+ TypeTuple *tuple = &p->type->Proc.results->Tuple;
isize res_count = return_results.count;
lbFunctionType *ft = lb_get_function_type(p->module, p->type);
bool return_by_pointer = ft->ret.kind == lbArg_Indirect;
- if (return_count == 0) {
- // No return values
-
- lb_emit_defer_stmts(p, lbDeferExit_Return, nullptr);
-
- LLVMBuildRetVoid(p->builder);
- return;
- } else if (return_count == 1) {
+ if (return_count == 1) {
Entity *e = tuple->variables[0];
if (res_count == 0) {
rw_mutex_shared_lock(&p->module->values_mutex);
@@ -1965,11 +2140,11 @@ gb_internal void lb_build_return_stmt(lbProcedure *p, Slice<Ast *> const &return
GB_ASSERT(result_values.count-1 == result_eps.count);
lb_addr_store(p, p->return_ptr, result_values[result_values.count-1]);
- lb_emit_defer_stmts(p, lbDeferExit_Return, nullptr);
+ lb_emit_defer_stmts(p, lbDeferExit_Return, nullptr, pos);
LLVMBuildRetVoid(p->builder);
return;
} else {
- return lb_build_return_stmt_internal(p, result_values[result_values.count-1]);
+ return lb_build_return_stmt_internal(p, result_values[result_values.count-1], pos);
}
} else {
@@ -1996,7 +2171,7 @@ gb_internal void lb_build_return_stmt(lbProcedure *p, Slice<Ast *> const &return
}
if (return_by_pointer) {
- lb_emit_defer_stmts(p, lbDeferExit_Return, nullptr);
+ lb_emit_defer_stmts(p, lbDeferExit_Return, nullptr, pos);
LLVMBuildRetVoid(p->builder);
return;
}
@@ -2004,14 +2179,24 @@ gb_internal void lb_build_return_stmt(lbProcedure *p, Slice<Ast *> const &return
res = lb_emit_load(p, res);
}
}
- lb_build_return_stmt_internal(p, res);
+ lb_build_return_stmt_internal(p, res, pos);
}
gb_internal void lb_build_if_stmt(lbProcedure *p, Ast *node) {
ast_node(is, IfStmt, node);
lb_open_scope(p, is->scope); // Scope #1
- defer (lb_close_scope(p, lbDeferExit_Default, nullptr));
+ defer (lb_close_scope(p, lbDeferExit_Default, nullptr, node));
+ lbBlock *then = lb_create_block(p, "if.then");
+ lbBlock *done = lb_create_block(p, "if.done");
+ lbBlock *else_ = done;
+ if (is->else_stmt != nullptr) {
+ else_ = lb_create_block(p, "if.else");
+ }
+ if (is->label != nullptr) {
+ lbTargetList *tl = lb_push_target_list(p, is->label, done, nullptr, nullptr);
+ tl->is_block = true;
+ }
if (is->init != nullptr) {
lbBlock *init = lb_create_block(p, "if.init");
lb_emit_jump(p, init);
@@ -2019,24 +2204,13 @@ gb_internal void lb_build_if_stmt(lbProcedure *p, Ast *node) {
lb_build_stmt(p, is->init);
}
- lbBlock *then = lb_create_block(p, "if.then");
- lbBlock *done = lb_create_block(p, "if.done");
- lbBlock *else_ = done;
- if (is->else_stmt != nullptr) {
- else_ = lb_create_block(p, "if.else");
- }
lbValue cond = lb_build_cond(p, is->cond, then, else_);
// Note `cond.value` only set for non-and/or conditions and const negs so that the `LLVMIsConstant()`
// and `LLVMConstIntGetZExtValue()` calls below will be valid and `LLVMInstructionEraseFromParent()`
// will target the correct (& only) branch statement
- if (is->label != nullptr) {
- lbTargetList *tl = lb_push_target_list(p, is->label, done, nullptr, nullptr);
- tl->is_block = true;
- }
-
- if (cond.value && LLVMIsConstant(cond.value)) {
+ if (cond.value && LLVMIsAConstantInt(cond.value)) {
// NOTE(bill): Do a compile time short circuit for when the condition is constantly known.
// This done manually rather than relying on the SSA passes because sometimes the SSA passes
// miss some even if they are constantly known, especially with few optimization passes.
@@ -2062,7 +2236,7 @@ gb_internal void lb_build_if_stmt(lbProcedure *p, Ast *node) {
lb_open_scope(p, scope_of_node(is->else_stmt));
lb_build_stmt(p, is->else_stmt);
- lb_close_scope(p, lbDeferExit_Default, nullptr);
+ lb_close_scope(p, lbDeferExit_Default, nullptr, is->else_stmt);
}
lb_emit_jump(p, done);
@@ -2079,7 +2253,7 @@ gb_internal void lb_build_if_stmt(lbProcedure *p, Ast *node) {
lb_open_scope(p, scope_of_node(is->else_stmt));
lb_build_stmt(p, is->else_stmt);
- lb_close_scope(p, lbDeferExit_Default, nullptr);
+ lb_close_scope(p, lbDeferExit_Default, nullptr, is->else_stmt);
lb_emit_jump(p, done);
}
@@ -2099,15 +2273,6 @@ gb_internal void lb_build_for_stmt(lbProcedure *p, Ast *node) {
if (p->debug_info != nullptr) {
LLVMSetCurrentDebugLocation2(p->builder, lb_debug_location_from_ast(p, node));
}
-
- 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);
- }
lbBlock *body = lb_create_block(p, "for.body");
lbBlock *done = lb_create_block(p, "for.done"); // NOTE(bill): Append later
lbBlock *loop = body;
@@ -2119,6 +2284,17 @@ gb_internal void lb_build_for_stmt(lbProcedure *p, Ast *node) {
post = lb_create_block(p, "for.post");
}
+ lb_push_target_list(p, fs->label, done, post, nullptr);
+
+ 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);
+ }
+
lb_emit_jump(p, loop);
lb_start_block(p, loop);
@@ -2131,7 +2307,6 @@ gb_internal void lb_build_for_stmt(lbProcedure *p, Ast *node) {
lb_start_block(p, body);
}
- lb_push_target_list(p, fs->label, done, post, nullptr);
lb_build_stmt(p, fs->body);
@@ -2149,7 +2324,7 @@ gb_internal void lb_build_for_stmt(lbProcedure *p, Ast *node) {
}
lb_start_block(p, done);
- lb_close_scope(p, lbDeferExit_Default, nullptr);
+ lb_close_scope(p, lbDeferExit_Default, nullptr, node);
}
gb_internal void lb_build_assign_stmt_array(lbProcedure *p, TokenKind op, lbAddr const &lhs, lbValue const &value) {
@@ -2415,7 +2590,7 @@ gb_internal void lb_build_stmt(lbProcedure *p, Ast *node) {
lb_open_scope(p, bs->scope);
lb_build_stmt_list(p, bs->stmts);
- lb_close_scope(p, lbDeferExit_Default, nullptr);
+ lb_close_scope(p, lbDeferExit_Default, nullptr, node);
if (done != nullptr) {
lb_emit_jump(p, done);
@@ -2529,7 +2704,7 @@ gb_internal void lb_build_stmt(lbProcedure *p, Ast *node) {
case_end;
case_ast_node(rs, ReturnStmt, node);
- lb_build_return_stmt(p, rs->results);
+ lb_build_return_stmt(p, rs->results, ast_token(node).pos);
case_end;
case_ast_node(is, IfStmt, node);
@@ -2582,7 +2757,7 @@ gb_internal void lb_build_stmt(lbProcedure *p, Ast *node) {
}
}
if (block != nullptr) {
- lb_emit_defer_stmts(p, lbDeferExit_Branch, block);
+ lb_emit_defer_stmts(p, lbDeferExit_Branch, block, node);
}
lb_emit_jump(p, block);
lb_start_block(p, lb_create_block(p, "unreachable"));
@@ -2622,7 +2797,13 @@ gb_internal void lb_build_defer_stmt(lbProcedure *p, lbDefer const &d) {
}
}
-gb_internal void lb_emit_defer_stmts(lbProcedure *p, lbDeferExitKind kind, lbBlock *block) {
+gb_internal void lb_emit_defer_stmts(lbProcedure *p, lbDeferExitKind kind, lbBlock *block, TokenPos pos) {
+ TokenPos prev_token_pos = p->branch_location_pos;
+ if (p->uses_branch_location) {
+ p->branch_location_pos = pos;
+ }
+ defer (p->branch_location_pos = prev_token_pos);
+
isize count = p->defer_stmts.count;
isize i = count;
while (i --> 0) {
@@ -2649,6 +2830,21 @@ gb_internal void lb_emit_defer_stmts(lbProcedure *p, lbDeferExitKind kind, lbBlo
}
}
+gb_internal void lb_emit_defer_stmts(lbProcedure *p, lbDeferExitKind kind, lbBlock *block, Ast *node) {
+ TokenPos pos = {};
+ if (node) {
+ if (node->kind == Ast_BlockStmt) {
+ pos = ast_end_token(node).pos;
+ } else if (node->kind == Ast_CaseClause) {
+ pos = ast_end_token(node).pos;
+ } else {
+ pos = ast_token(node).pos;
+ }
+ }
+ return lb_emit_defer_stmts(p, kind, block, pos);
+}
+
+
gb_internal void lb_add_defer_node(lbProcedure *p, isize scope_index, Ast *stmt) {
Type *pt = base_type(p->type);
GB_ASSERT(pt->kind == Type_Proc);
diff --git a/src/llvm_backend_type.cpp b/src/llvm_backend_type.cpp
index 02dad2a3a..6c12b37be 100644
--- a/src/llvm_backend_type.cpp
+++ b/src/llvm_backend_type.cpp
@@ -2,14 +2,19 @@ gb_internal isize lb_type_info_index(CheckerInfo *info, Type *type, bool err_on_
auto *set = &info->minimum_dependency_type_info_set;
isize index = type_info_index(info, type, err_on_not_found);
if (index >= 0) {
- auto *found = map_get(set, index);
+ auto *found = map_get(set, index+1);
if (found) {
GB_ASSERT(*found >= 0);
return *found + 1;
}
}
if (err_on_not_found) {
- GB_PANIC("NOT FOUND lb_type_info_index %s @ index %td", type_to_string(type), index);
+ gb_printf_err("NOT FOUND lb_type_info_index:\n\t%s\n\t@ index %td\n\tmax count: %u\nFound:\n", type_to_string(type), index, set->count);
+ for (auto const &entry : *set) {
+ isize type_info_index = entry.key;
+ gb_printf_err("\t%s\n", type_to_string(info->type_info_types[type_info_index]));
+ }
+ GB_PANIC("NOT FOUND");
}
return -1;
}
@@ -38,6 +43,8 @@ gb_internal u64 lb_typeid_kind(lbModule *m, Type *type, u64 id=0) {
if (flags & BasicFlag_Pointer) kind = Typeid_Pointer;
if (flags & BasicFlag_String) kind = Typeid_String;
if (flags & BasicFlag_Rune) kind = Typeid_Rune;
+
+ if (bt->Basic.kind == Basic_typeid) kind = Typeid_Type_Id;
} break;
case Type_Pointer: kind = Typeid_Pointer; break;
case Type_MultiPointer: kind = Typeid_Multi_Pointer; break;
@@ -54,9 +61,8 @@ gb_internal u64 lb_typeid_kind(lbModule *m, Type *type, u64 id=0) {
case Type_Proc: kind = Typeid_Procedure; break;
case Type_BitSet: kind = Typeid_Bit_Set; break;
case Type_SimdVector: kind = Typeid_Simd_Vector; break;
- case Type_RelativePointer: kind = Typeid_Relative_Pointer; break;
- case Type_RelativeMultiPointer: kind = Typeid_Relative_Multi_Pointer; break;
case Type_SoaPointer: kind = Typeid_SoaPointer; break;
+ case Type_BitField: kind = Typeid_Bit_Field; break;
}
return kind;
@@ -105,16 +111,19 @@ gb_internal lbValue lb_typeid(lbModule *m, Type *type) {
return res;
}
-gb_internal lbValue lb_type_info(lbModule *m, Type *type) {
+gb_internal lbValue lb_type_info(lbProcedure *p, Type *type) {
GB_ASSERT(!build_context.no_rtti);
type = default_type(type);
+ lbModule *m = p->module;
isize index = lb_type_info_index(m->info, type);
GB_ASSERT(index >= 0);
- lbValue data = lb_global_type_info_data_ptr(m);
- return lb_emit_array_epi(m, data, index);
+ lbValue global = lb_global_type_info_data_ptr(m);
+
+ lbValue ptr = lb_emit_array_epi(p, global, index);
+ return lb_emit_load(p, ptr);
}
gb_internal LLVMTypeRef lb_get_procedure_raw_type(lbModule *m, Type *type) {
@@ -175,16 +184,7 @@ gb_internal lbValue lb_type_info_member_tags_offset(lbModule *m, isize count, i6
return offset;
}
-// enum {LB_USE_GIANT_PACKED_STRUCT = LB_USE_NEW_PASS_SYSTEM};
-enum {LB_USE_GIANT_PACKED_STRUCT = 0};
-
-gb_internal LLVMTypeRef lb_setup_type_info_data_internal_type(lbModule *m, isize max_type_info_count) {
- if (!LB_USE_GIANT_PACKED_STRUCT) {
- Type *t = alloc_type_array(t_type_info, max_type_info_count);
- return lb_type(m, t);
- }
- CheckerInfo *info = m->gen->info;
-
+gb_internal LLVMTypeRef *lb_setup_modified_types_for_type_info(lbModule *m, isize max_type_info_count) {
LLVMTypeRef *element_types = gb_alloc_array(heap_allocator(), LLVMTypeRef, max_type_info_count);
defer (gb_free(heap_allocator(), element_types));
@@ -205,7 +205,7 @@ gb_internal LLVMTypeRef lb_setup_type_info_data_internal_type(lbModule *m, isize
stypes[1] = lb_type(m, tibt->Struct.fields[1]->type);
stypes[2] = lb_type(m, tibt->Struct.fields[2]->type);
isize variant_index = 0;
- if (build_context.int_size == 8) {
+ if (build_context.ptr_size == 8) {
stypes[3] = lb_type(m, t_i32); // padding
stypes[4] = lb_type(m, tibt->Struct.fields[3]->type);
variant_index = 5;
@@ -214,8 +214,8 @@ gb_internal LLVMTypeRef lb_setup_type_info_data_internal_type(lbModule *m, isize
variant_index = 4;
}
- LLVMTypeRef modified_types[32] = {};
- GB_ASSERT(gb_count_of(modified_types) >= ut->Union.variants.count);
+ LLVMTypeRef *modified_types = gb_alloc_array(heap_allocator(), LLVMTypeRef, Typeid__COUNT);
+ GB_ASSERT(Typeid__COUNT == ut->Union.variants.count);
modified_types[0] = element_types[0];
i64 tag_offset = ut->Union.variant_block_size;
@@ -237,40 +237,22 @@ gb_internal LLVMTypeRef lb_setup_type_info_data_internal_type(lbModule *m, isize
modified_types[i] = modified_type;
}
- for_array(type_info_type_index, info->type_info_types) {
- Type *t = info->type_info_types[type_info_type_index];
- if (t == nullptr || t == t_invalid) {
- continue;
- }
- isize entry_index = lb_type_info_index(info, t, false);
- if (entry_index <= 0) {
- continue;
- }
-
- if (entries_handled[entry_index]) {
- continue;
- }
- entries_handled[entry_index] = true;
-
-
- if (t->kind == Type_Named) {
- element_types[entry_index] = modified_types[0];
- } else {
- i64 variant_index = lb_typeid_kind(m, t);
- element_types[entry_index] = modified_types[variant_index];
- }
-
- GB_ASSERT(element_types[entry_index] != nullptr);
- }
-
- for_array(i, entries_handled) {
- GB_ASSERT(entries_handled[i]);
+ for (isize i = 0; i < Typeid__COUNT; i++) {
+ GB_ASSERT_MSG(modified_types[i] != nullptr, "%td", ut->Union.variants.count);
}
- return LLVMStructType(element_types, cast(unsigned)max_type_info_count, true);
+ return modified_types;
}
-gb_internal void lb_setup_type_info_data_giant_packed_struct(lbModule *m, i64 global_type_info_data_entity_count, lbProcedure *p) { // NOTE(bill): Setup type_info data
+gb_internal void lb_setup_type_info_data_giant_array(lbModule *m, i64 global_type_info_data_entity_count) { // NOTE(bill): Setup type_info data
+ auto const &ADD_GLOBAL_TYPE_INFO_ENTRY = [](lbModule *m, LLVMTypeRef type, isize index) -> LLVMValueRef {
+ char name[64] = {};
+ gb_snprintf(name, 63, "__$ti-%lld", cast(long long)index);
+ LLVMValueRef g = LLVMAddGlobal(m->mod, type, name);
+ lb_make_global_private_const(g);
+ return g;
+ };
+
CheckerInfo *info = m->info;
// Useful types
@@ -287,19 +269,49 @@ gb_internal void lb_setup_type_info_data_giant_packed_struct(lbModule *m, i64 gl
defer (gb_free(heap_allocator(), entries_handled.data));
entries_handled[0] = true;
- LLVMValueRef giant_struct = lb_global_type_info_data_ptr(m).value;
- LLVMTypeRef giant_struct_type = LLVMGlobalGetValueType(giant_struct);
- GB_ASSERT(LLVMGetTypeKind(giant_struct_type) == LLVMStructTypeKind);
-
LLVMValueRef *giant_const_values = gb_alloc_array(heap_allocator(), LLVMValueRef, global_type_info_data_entity_count);
defer (gb_free(heap_allocator(), giant_const_values));
- giant_const_values[0] = LLVMConstNull(LLVMStructGetTypeAtIndex(giant_struct_type, 0));
+ // zero value is just zero data
+ giant_const_values[0] = ADD_GLOBAL_TYPE_INFO_ENTRY(m, lb_type(m, t_type_info), 0);
+ LLVMSetInitializer(giant_const_values[0], LLVMConstNull(lb_type(m, t_type_info)));
+
+
+ LLVMTypeRef *modified_types = lb_setup_modified_types_for_type_info(m, global_type_info_data_entity_count);
+ defer (gb_free(heap_allocator(), modified_types));
+ for_array(type_info_type_index, info->type_info_types) {
+ Type *t = info->type_info_types[type_info_type_index];
+ if (t == nullptr || t == t_invalid) {
+ continue;
+ }
+
+ isize entry_index = lb_type_info_index(info, t, false);
+ if (entry_index <= 0) {
+ continue;
+ }
+
+ if (entries_handled[entry_index]) {
+ continue;
+ }
+ entries_handled[entry_index] = true;
+
+
+ LLVMTypeRef stype = nullptr;
+ if (t->kind == Type_Named) {
+ stype = modified_types[0];
+ } else {
+ stype = modified_types[lb_typeid_kind(m, t)];
+ }
+ giant_const_values[entry_index] = ADD_GLOBAL_TYPE_INFO_ENTRY(m, stype, entry_index);
+ }
+ for (isize i = 1; i < global_type_info_data_entity_count; i++) {
+ entries_handled[i] = false;
+ }
+
LLVMValueRef *small_const_values = gb_alloc_array(heap_allocator(), LLVMValueRef, 6);
defer (gb_free(heap_allocator(), small_const_values));
-
#define type_info_allocate_values(name) \
LLVMValueRef *name##_values = gb_alloc_array(heap_allocator(), LLVMValueRef, type_deref(name.addr.type)->Array.count); \
defer (gb_free(heap_allocator(), name##_values)); \
@@ -311,7 +323,7 @@ gb_internal void lb_setup_type_info_data_giant_packed_struct(lbModule *m, i64 gl
(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(elem, name##_values, at->Array.count)); \
})
type_info_allocate_values(lb_global_type_info_member_types);
@@ -321,27 +333,13 @@ gb_internal void lb_setup_type_info_data_giant_packed_struct(lbModule *m, i64 gl
type_info_allocate_values(lb_global_type_info_member_tags);
- i64 const type_info_struct_size = type_size_of(t_type_info);
- LLVMTypeRef llvm_u8 = lb_type(m, t_u8);
- LLVMTypeRef llvm_int = lb_type(m, t_int);
- // LLVMTypeRef llvm_type_info_ptr = lb_type(m, t_type_info_ptr);
-
auto const get_type_info_ptr = [&](lbModule *m, Type *type) -> LLVMValueRef {
type = default_type(type);
isize index = lb_type_info_index(m->info, type);
GB_ASSERT(index >= 0);
- u64 offset = cast(u64)(index * type_info_struct_size);
-
- LLVMValueRef indices[1] = {
- LLVMConstInt(llvm_int, offset, false)
- };
-
- // LLVMValueRef ptr = LLVMConstInBoundsGEP2(llvm_u8, giant_struct, indices, gb_count_of(indices));
- LLVMValueRef ptr = LLVMConstGEP2(llvm_u8, giant_struct, indices, gb_count_of(indices));
- return ptr;
- // return LLVMConstPointerCast(ptr, llvm_type_info_ptr);
+ return giant_const_values[index];
};
for_array(type_info_type_index, info->type_info_types) {
@@ -361,7 +359,12 @@ gb_internal void lb_setup_type_info_data_giant_packed_struct(lbModule *m, i64 gl
entries_handled[entry_index] = true;
- LLVMTypeRef stype = LLVMStructGetTypeAtIndex(giant_struct_type, cast(unsigned)entry_index);
+ LLVMTypeRef stype = nullptr;
+ if (t->kind == Type_Named) {
+ stype = modified_types[0];
+ } else {
+ stype = modified_types[lb_typeid_kind(m, t)];
+ }
i64 size = type_size_of(t);
i64 align = type_align_of(t);
@@ -371,12 +374,16 @@ gb_internal void lb_setup_type_info_data_giant_packed_struct(lbModule *m, i64 gl
lbValue type_info_flags = lb_const_int(m, t_type_info_flags, flags);
+ for (isize i = 0; i < 6; i++) {
+ small_const_values[i] = nullptr;
+ }
+
small_const_values[0] = LLVMConstInt(lb_type(m, t_int), size, true);
small_const_values[1] = LLVMConstInt(lb_type(m, t_int), align, true);
small_const_values[2] = type_info_flags.value;
unsigned variant_index = 0;
- if (build_context.int_size == 8) {
+ if (build_context.ptr_size == 8) {
small_const_values[3] = LLVMConstNull(LLVMStructGetTypeAtIndex(stype, 3));
small_const_values[4] = id.value;
variant_index = 5;
@@ -414,7 +421,7 @@ gb_internal void lb_setup_type_info_data_giant_packed_struct(lbModule *m, i64 gl
}
TokenPos pos = t->Named.type_name->token.pos;
- lbValue loc = lb_const_source_code_location_const(m, proc_name, pos);
+ lbValue loc = lb_const_source_code_location_as_global_ptr(m, proc_name, pos);
LLVMValueRef vals[4] = {
lb_const_string(m, t->Named.type_name->token.string).value,
@@ -803,32 +810,31 @@ gb_internal void lb_setup_type_info_data_giant_packed_struct(lbModule *m, i64 gl
case Type_Struct: {
tag_type = t_type_info_struct;
- LLVMValueRef vals[13] = {};
+ LLVMValueRef vals[11] = {};
{
- lbValue is_packed = lb_const_bool(m, t_bool, t->Struct.is_packed);
- lbValue is_raw_union = lb_const_bool(m, t_bool, t->Struct.is_raw_union);
- lbValue is_no_copy = lb_const_bool(m, t_bool, t->Struct.is_no_copy);
- lbValue is_custom_align = lb_const_bool(m, t_bool, t->Struct.custom_align != 0);
- vals[5] = is_packed.value;
- vals[6] = is_raw_union.value;
- vals[7] = is_no_copy.value;
- vals[8] = is_custom_align.value;
+ u8 flags = 0;
+ if (t->Struct.is_packed) flags |= 1<<0;
+ if (t->Struct.is_raw_union) flags |= 1<<1;
+ if (t->Struct.is_no_copy) flags |= 1<<2;
+ if (t->Struct.custom_align) flags |= 1<<3;
+
+ vals[6] = lb_const_int(m, t_u8, flags).value;
if (is_type_comparable(t) && !is_type_simple_compare(t)) {
- vals[9] = lb_equal_proc_for_type(m, t).value;
+ vals[10] = lb_equal_proc_for_type(m, t).value;
}
if (t->Struct.soa_kind != StructSoa_None) {
- Type *kind_type = get_struct_field_type(tag_type, 10);
+ Type *kind_type = get_struct_field_type(tag_type, 7);
lbValue soa_kind = lb_const_value(m, kind_type, exact_value_i64(t->Struct.soa_kind));
LLVMValueRef soa_type = get_type_info_ptr(m, t->Struct.soa_elem);
- lbValue soa_len = lb_const_int(m, t_int, t->Struct.soa_count);
+ lbValue soa_len = lb_const_int(m, t_i32, t->Struct.soa_count);
- vals[10] = soa_kind.value;
- vals[11] = soa_type;
- vals[12] = soa_len.value;
+ vals[7] = soa_kind.value;
+ vals[8] = soa_len.value;
+ vals[9] = soa_type;
}
}
@@ -851,7 +857,7 @@ gb_internal void lb_setup_type_info_data_giant_packed_struct(lbModule *m, i64 gl
Entity *f = t->Struct.fields[source_index];
i64 foffset = 0;
if (!t->Struct.is_raw_union) {
- GB_ASSERT(t->Struct.offsets != nullptr);
+ GB_ASSERT_MSG(t->Struct.offsets != nullptr, "%s", type_to_string(t));
GB_ASSERT(0 <= f->Variable.field_index && f->Variable.field_index < count);
foffset = t->Struct.offsets[source_index];
}
@@ -875,12 +881,13 @@ gb_internal void lb_setup_type_info_data_giant_packed_struct(lbModule *m, i64 gl
}
- lbValue cv = lb_const_int(m, t_int, count);
- vals[0] = llvm_const_slice(m, memory_types, cv);
- vals[1] = llvm_const_slice(m, memory_names, cv);
- vals[2] = llvm_const_slice(m, memory_offsets, cv);
- vals[3] = llvm_const_slice(m, memory_usings, cv);
- vals[4] = llvm_const_slice(m, memory_tags, cv);
+ lbValue cv = lb_const_int(m, t_i32, count);
+ vals[0] = memory_types.value;
+ vals[1] = memory_names.value;
+ vals[2] = memory_offsets.value;
+ vals[3] = memory_usings.value;
+ vals[4] = memory_tags.value;
+ vals[5] = cv.value;
}
for (isize i = 0; i < gb_count_of(vals); i++) {
if (vals[i] == nullptr) {
@@ -894,7 +901,7 @@ gb_internal void lb_setup_type_info_data_giant_packed_struct(lbModule *m, i64 gl
case Type_Map: {
tag_type = t_type_info_map;
- init_map_internal_types(t);
+ init_map_internal_debug_types(t);
LLVMValueRef vals[3] = {
get_type_info_ptr(m, t->Map.key),
@@ -941,46 +948,87 @@ gb_internal void lb_setup_type_info_data_giant_packed_struct(lbModule *m, i64 gl
}
break;
- case Type_RelativePointer:
- {
- tag_type = t_type_info_relative_pointer;
- LLVMValueRef vals[2] = {
- get_type_info_ptr(m, t->RelativePointer.pointer_type),
- get_type_info_ptr(m, t->RelativePointer.base_integer),
- };
-
- variant_value = llvm_const_named_struct(m, tag_type, vals, gb_count_of(vals));
- }
- break;
-
- case Type_RelativeMultiPointer:
- {
- tag_type = t_type_info_relative_multi_pointer;
- LLVMValueRef vals[2] = {
- get_type_info_ptr(m, t->RelativeMultiPointer.pointer_type),
- get_type_info_ptr(m, t->RelativeMultiPointer.base_integer),
- };
-
- variant_value = llvm_const_named_struct(m, tag_type, vals, gb_count_of(vals));
- }
- break;
-
case Type_Matrix:
{
tag_type = t_type_info_matrix;
i64 ez = type_size_of(t->Matrix.elem);
- LLVMValueRef vals[5] = {
+ LLVMValueRef vals[6] = {
get_type_info_ptr(m, t->Matrix.elem),
lb_const_int(m, t_int, ez).value,
lb_const_int(m, t_int, matrix_type_stride_in_elems(t)).value,
lb_const_int(m, t_int, t->Matrix.row_count).value,
lb_const_int(m, t_int, t->Matrix.column_count).value,
+ lb_const_int(m, t_u8, cast(u8)t->Matrix.is_row_major).value,
};
variant_value = llvm_const_named_struct(m, tag_type, vals, gb_count_of(vals));
}
break;
+
+ case Type_BitField:
+ {
+ tag_type = t_type_info_bit_field;
+
+ LLVMValueRef vals[7] = {};
+ vals[0] = get_type_info_ptr(m, t->BitField.backing_type);
+ isize count = t->BitField.fields.count;
+ if (count > 0) {
+ i64 names_offset = 0;
+ i64 types_offset = 0;
+ i64 bit_sizes_offset = 0;
+ i64 bit_offsets_offset = 0;
+ i64 tags_offset = 0;
+ lbValue memory_names = lb_type_info_member_names_offset (m, count, &names_offset);
+ lbValue memory_types = lb_type_info_member_types_offset (m, count, &types_offset);
+ lbValue memory_bit_sizes = lb_type_info_member_offsets_offset(m, count, &bit_sizes_offset);
+ lbValue memory_bit_offsets = lb_type_info_member_offsets_offset(m, count, &bit_offsets_offset);
+ lbValue memory_tags = lb_type_info_member_tags_offset (m, count, &tags_offset);
+
+ u64 bit_offset = 0;
+ for (isize source_index = 0; source_index < count; source_index++) {
+ Entity *f = t->BitField.fields[source_index];
+ u64 bit_size = cast(u64)t->BitField.bit_sizes[source_index];
+
+ lbValue index = lb_const_int(m, t_int, source_index);
+ if (f->token.string.len > 0) {
+ lb_global_type_info_member_names_values[names_offset+source_index] = lb_const_string(m, f->token.string).value;
+ }
+
+ lb_global_type_info_member_types_values[types_offset+source_index] = get_type_info_ptr(m, f->type);
+
+ lb_global_type_info_member_offsets_values[bit_sizes_offset+source_index] = lb_const_int(m, t_uintptr, bit_size).value;
+ lb_global_type_info_member_offsets_values[bit_offsets_offset+source_index] = lb_const_int(m, t_uintptr, bit_offset).value;
+
+ if (t->BitField.tags) {
+ String tag = t->BitField.tags[source_index];
+ if (tag.len > 0) {
+ lb_global_type_info_member_tags_values[tags_offset+source_index] = lb_const_string(m, tag).value;
+ }
+ }
+
+ bit_offset += bit_size;
+ }
+
+ lbValue cv = lb_const_int(m, t_int, count);
+ vals[1] = memory_names.value;
+ vals[2] = memory_types.value;
+ vals[3] = memory_bit_sizes.value;
+ vals[4] = memory_bit_offsets.value;
+ vals[5] = memory_tags.value;
+ vals[6] = cv.value;
+ }
+
+
+ for (isize i = 0; i < gb_count_of(vals); i++) {
+ if (vals[i] == nullptr) {
+ vals[i] = LLVMConstNull(lb_type(m, get_struct_field_type(tag_type, i)));
+ }
+ }
+
+ variant_value = llvm_const_named_struct(m, tag_type, vals, gb_count_of(vals));
+ break;
+ }
}
@@ -989,6 +1037,7 @@ gb_internal void lb_setup_type_info_data_giant_packed_struct(lbModule *m, i64 gl
if (tag_type != nullptr) {
tag_index = union_variant_index(ut, tag_type);
}
+ GB_ASSERT(tag_index <= Typeid__COUNT);
LLVMValueRef full_variant_values[3] = {};
@@ -1019,788 +1068,47 @@ gb_internal void lb_setup_type_info_data_giant_packed_struct(lbModule *m, i64 gl
small_const_values[variant_index] = full_variant_value;
- giant_const_values[entry_index] = LLVMConstNamedStruct(stype, small_const_values, variant_index+1);
+ LLVMSetInitializer(giant_const_values[entry_index], LLVMConstNamedStruct(stype, small_const_values, variant_index+1));
+ }
+ for (isize i = 0; i < global_type_info_data_entity_count; i++) {
+ giant_const_values[i] = LLVMConstPointerCast(giant_const_values[i], lb_type(m, t_type_info_ptr));
}
- LLVMValueRef giant_const = LLVMConstNamedStruct(giant_struct_type, giant_const_values, cast(unsigned)global_type_info_data_entity_count);
- LLVMSetInitializer(giant_struct, giant_const);
+
+ LLVMValueRef giant_const = LLVMConstArray(lb_type(m, t_type_info_ptr), giant_const_values, cast(unsigned)global_type_info_data_entity_count);
+ LLVMValueRef giant_array = lb_global_type_info_data_ptr(m).value;
+ LLVMSetInitializer(giant_array, giant_const);
+ lb_make_global_private_const(giant_array);
}
-gb_internal void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info data
+gb_internal void lb_setup_type_info_data(lbModule *m) { // NOTE(bill): Setup type_info data
if (build_context.no_rtti) {
return;
}
- lbModule *m = p->module;
- CheckerInfo *info = m->info;
i64 global_type_info_data_entity_count = 0;
- {
- // NOTE(bill): Set the type_table slice with the global backing array
- lbValue global_type_table = lb_find_runtime_value(m, str_lit("type_table"));
- Type *type = base_type(lb_global_type_info_data_entity->type);
- GB_ASSERT(type->kind == Type_Array);
- global_type_info_data_entity_count = type->Array.count;
-
- LLVMValueRef data = lb_global_type_info_data_ptr(m).value;
- data = LLVMConstPointerCast(data, lb_type(m, alloc_type_pointer(type->Array.elem)));
- LLVMValueRef len = LLVMConstInt(lb_type(m, t_int), type->Array.count, true);
- Type *t = type_deref(global_type_table.type);
- GB_ASSERT(is_type_slice(t));
- LLVMValueRef slice = llvm_const_slice_internal(m, data, len);
-
- LLVMSetInitializer(global_type_table.value, slice);
- }
-
- if (LB_USE_GIANT_PACKED_STRUCT) {
- lb_setup_type_info_data_giant_packed_struct(m, global_type_info_data_entity_count, p);
- return;
- }
-
- // Useful types
- Entity *type_info_flags_entity = find_core_entity(info->checker, str_lit("Type_Info_Flags"));
- Type *t_type_info_flags = type_info_flags_entity->type;
-
-
- auto entries_handled = slice_make<bool>(heap_allocator(), cast(isize)global_type_info_data_entity_count);
- defer (gb_free(heap_allocator(), entries_handled.data));
- entries_handled[0] = true;
-
- for_array(type_info_type_index, info->type_info_types) {
- Type *t = info->type_info_types[type_info_type_index];
- if (t == nullptr || t == t_invalid) {
- continue;
- }
-
- isize entry_index = lb_type_info_index(info, t, false);
- if (entry_index <= 0) {
- continue;
- }
- if (entries_handled[entry_index]) {
- continue;
- }
- entries_handled[entry_index] = true;
-
- lbValue global_data_ptr = lb_global_type_info_data_ptr(m);
- lbValue tag = {};
- lbValue ti_ptr = lb_emit_array_epi(p, global_data_ptr, cast(i32)entry_index);
-
- i64 size = type_size_of(t);
- i64 align = type_align_of(t);
- u32 flags = type_info_flags_of_type(t);
- lbValue id = lb_typeid(m, t);
- GB_ASSERT_MSG(align != 0, "%lld %s", align, type_to_string(t));
-
- lbValue type_info_flags = lb_const_int(p->module, t_type_info_flags, flags);
-
- lbValue size_ptr = lb_emit_struct_ep(p, ti_ptr, 0);
- lbValue align_ptr = lb_emit_struct_ep(p, ti_ptr, 1);
- lbValue flags_ptr = lb_emit_struct_ep(p, ti_ptr, 2);
- lbValue id_ptr = lb_emit_struct_ep(p, ti_ptr, 3);
-
- lb_emit_store(p, size_ptr, lb_const_int(m, t_int, size));
- lb_emit_store(p, align_ptr, lb_const_int(m, t_int, align));
- lb_emit_store(p, flags_ptr, type_info_flags);
- lb_emit_store(p, id_ptr, id);
-
- lbValue variant_ptr = lb_emit_struct_ep(p, ti_ptr, 4);
+ // NOTE(bill): Set the type_table slice with the global backing array
+ lbValue global_type_table = lb_find_runtime_value(m, str_lit("type_table"));
+ Type *type = base_type(lb_global_type_info_data_entity->type);
+ GB_ASSERT(type->kind == Type_Array);
+ global_type_info_data_entity_count = type->Array.count;
- switch (t->kind) {
- case Type_Named: {
- tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_named_ptr);
-
- LLVMValueRef pkg_name = nullptr;
- if (t->Named.type_name->pkg) {
- pkg_name = lb_const_string(m, t->Named.type_name->pkg->name).value;
- } else {
- pkg_name = LLVMConstNull(lb_type(m, t_string));
- }
-
- 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;
- }
- }
- TokenPos pos = t->Named.type_name->token.pos;
-
- lbValue loc = lb_emit_source_code_location_const(p, proc_name, pos);
-
- LLVMValueRef vals[4] = {
- lb_const_string(p->module, t->Named.type_name->token.string).value,
- lb_type_info(m, t->Named.base).value,
- pkg_name,
- loc.value
- };
-
- lbValue res = {};
- res.type = type_deref(tag.type);
- res.value = llvm_const_named_struct(m, res.type, vals, gb_count_of(vals));
- lb_emit_store(p, tag, res);
- break;
- }
-
- case Type_Basic:
- switch (t->Basic.kind) {
- case Basic_bool:
- case Basic_b8:
- case Basic_b16:
- case Basic_b32:
- case Basic_b64:
- tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_boolean_ptr);
- break;
-
- case Basic_i8:
- case Basic_u8:
- case Basic_i16:
- case Basic_u16:
- case Basic_i32:
- case Basic_u32:
- case Basic_i64:
- case Basic_u64:
- case Basic_i128:
- case Basic_u128:
-
- case Basic_i16le:
- case Basic_u16le:
- case Basic_i32le:
- case Basic_u32le:
- case Basic_i64le:
- case Basic_u64le:
- case Basic_i128le:
- case Basic_u128le:
- case Basic_i16be:
- case Basic_u16be:
- case Basic_i32be:
- case Basic_u32be:
- case Basic_i64be:
- case Basic_u64be:
- case Basic_i128be:
- case Basic_u128be:
-
- case Basic_int:
- case Basic_uint:
- case Basic_uintptr: {
- tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_integer_ptr);
-
- lbValue is_signed = lb_const_bool(m, t_bool, (t->Basic.flags & BasicFlag_Unsigned) == 0);
- // NOTE(bill): This is matches the runtime layout
- u8 endianness_value = 0;
- if (t->Basic.flags & BasicFlag_EndianLittle) {
- endianness_value = 1;
- } else if (t->Basic.flags & BasicFlag_EndianBig) {
- endianness_value = 2;
- }
- lbValue endianness = lb_const_int(m, t_u8, endianness_value);
-
- LLVMValueRef vals[2] = {
- is_signed.value,
- endianness.value,
- };
-
- lbValue res = {};
- res.type = type_deref(tag.type);
- res.value = llvm_const_named_struct(m, res.type, vals, gb_count_of(vals));
- lb_emit_store(p, tag, res);
- break;
- }
-
- case Basic_rune:
- tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_rune_ptr);
- break;
-
- case Basic_f16:
- case Basic_f32:
- case Basic_f64:
- case Basic_f16le:
- case Basic_f32le:
- case Basic_f64le:
- case Basic_f16be:
- case Basic_f32be:
- case Basic_f64be:
- {
- tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_float_ptr);
-
- // NOTE(bill): This is matches the runtime layout
- u8 endianness_value = 0;
- if (t->Basic.flags & BasicFlag_EndianLittle) {
- endianness_value = 1;
- } else if (t->Basic.flags & BasicFlag_EndianBig) {
- endianness_value = 2;
- }
- lbValue endianness = lb_const_int(m, t_u8, endianness_value);
-
- LLVMValueRef vals[1] = {
- endianness.value,
- };
-
- lbValue res = {};
- res.type = type_deref(tag.type);
- res.value = llvm_const_named_struct(m, res.type, vals, gb_count_of(vals));
- lb_emit_store(p, tag, res);
- }
- break;
-
- case Basic_complex32:
- case Basic_complex64:
- case Basic_complex128:
- tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_complex_ptr);
- break;
-
- case Basic_quaternion64:
- case Basic_quaternion128:
- case Basic_quaternion256:
- tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_quaternion_ptr);
- break;
-
- case Basic_rawptr:
- tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_pointer_ptr);
- break;
-
- case Basic_string:
- tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_string_ptr);
- break;
-
- case Basic_cstring:
- {
- tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_string_ptr);
- LLVMValueRef vals[1] = {
- lb_const_bool(m, t_bool, true).value,
- };
-
- lbValue res = {};
- res.type = type_deref(tag.type);
- res.value = llvm_const_named_struct(m, res.type, vals, gb_count_of(vals));
- lb_emit_store(p, tag, res);
- }
- break;
-
- case Basic_any:
- tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_any_ptr);
- break;
-
- case Basic_typeid:
- tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_typeid_ptr);
- break;
- }
- break;
-
- case Type_Pointer: {
- tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_pointer_ptr);
- lbValue gep = lb_type_info(m, t->Pointer.elem);
-
- LLVMValueRef vals[1] = {
- gep.value,
- };
-
- lbValue res = {};
- res.type = type_deref(tag.type);
- res.value = llvm_const_named_struct(m, res.type, vals, gb_count_of(vals));
- lb_emit_store(p, tag, res);
- break;
- }
- case Type_MultiPointer: {
- tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_multi_pointer_ptr);
- lbValue gep = lb_type_info(m, t->MultiPointer.elem);
-
- LLVMValueRef vals[1] = {
- gep.value,
- };
-
- lbValue res = {};
- res.type = type_deref(tag.type);
- res.value = llvm_const_named_struct(m, res.type, vals, gb_count_of(vals));
- lb_emit_store(p, tag, res);
- break;
- }
- case Type_SoaPointer: {
- tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_soa_pointer_ptr);
- lbValue gep = lb_type_info(m, t->SoaPointer.elem);
-
- LLVMValueRef vals[1] = {
- gep.value,
- };
-
- lbValue res = {};
- res.type = type_deref(tag.type);
- res.value = llvm_const_named_struct(m, res.type, vals, gb_count_of(vals));
- lb_emit_store(p, tag, res);
- break;
- }
- case Type_Array: {
- tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_array_ptr);
- i64 ez = type_size_of(t->Array.elem);
-
- LLVMValueRef vals[3] = {
- lb_type_info(m, t->Array.elem).value,
- lb_const_int(m, t_int, ez).value,
- lb_const_int(m, t_int, t->Array.count).value,
- };
-
- lbValue res = {};
- res.type = type_deref(tag.type);
- res.value = llvm_const_named_struct(m, res.type, vals, gb_count_of(vals));
- lb_emit_store(p, tag, res);
- break;
- }
- case Type_EnumeratedArray: {
- tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_enumerated_array_ptr);
-
- LLVMValueRef vals[7] = {
- lb_type_info(m, t->EnumeratedArray.elem).value,
- lb_type_info(m, t->EnumeratedArray.index).value,
- lb_const_int(m, t_int, type_size_of(t->EnumeratedArray.elem)).value,
- lb_const_int(m, t_int, t->EnumeratedArray.count).value,
-
- // Unions
- LLVMConstNull(lb_type(m, t_type_info_enum_value)),
- LLVMConstNull(lb_type(m, t_type_info_enum_value)),
-
- lb_const_bool(m, t_bool, t->EnumeratedArray.is_sparse).value,
- };
-
- lbValue res = {};
- res.type = type_deref(tag.type);
- res.value = llvm_const_named_struct(m, res.type, vals, gb_count_of(vals));
- lb_emit_store(p, tag, res);
-
- // NOTE(bill): Union assignment
- lbValue min_value = lb_emit_struct_ep(p, tag, 4);
- lbValue max_value = lb_emit_struct_ep(p, tag, 5);
-
- lbValue min_v = lb_const_value(m, t_i64, *t->EnumeratedArray.min_value);
- lbValue max_v = lb_const_value(m, t_i64, *t->EnumeratedArray.max_value);
-
- lb_emit_store(p, min_value, min_v);
- lb_emit_store(p, max_value, max_v);
- break;
- }
- case Type_DynamicArray: {
- tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_dynamic_array_ptr);
-
- LLVMValueRef vals[2] = {
- lb_type_info(m, t->DynamicArray.elem).value,
- lb_const_int(m, t_int, type_size_of(t->DynamicArray.elem)).value,
- };
-
- lbValue res = {};
- res.type = type_deref(tag.type);
- res.value = llvm_const_named_struct(m, res.type, vals, gb_count_of(vals));
- lb_emit_store(p, tag, res);
- break;
- }
- case Type_Slice: {
- tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_slice_ptr);
-
- LLVMValueRef vals[2] = {
- lb_type_info(m, t->Slice.elem).value,
- lb_const_int(m, t_int, type_size_of(t->Slice.elem)).value,
- };
-
- lbValue res = {};
- res.type = type_deref(tag.type);
- res.value = llvm_const_named_struct(m, res.type, vals, gb_count_of(vals));
- lb_emit_store(p, tag, res);
- break;
- }
- case Type_Proc: {
- tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_procedure_ptr);
-
- LLVMValueRef params = LLVMConstNull(lb_type(m, t_type_info_ptr));
- LLVMValueRef results = LLVMConstNull(lb_type(m, t_type_info_ptr));
- if (t->Proc.params != nullptr) {
- params = lb_type_info(m, t->Proc.params).value;
- }
- if (t->Proc.results != nullptr) {
- results = lb_type_info(m, t->Proc.results).value;
- }
-
- LLVMValueRef vals[4] = {
- params,
- results,
- lb_const_bool(m, t_bool, t->Proc.variadic).value,
- lb_const_int(m, t_u8, t->Proc.calling_convention).value,
- };
-
- lbValue res = {};
- res.type = type_deref(tag.type);
- res.value = llvm_const_named_struct(m, res.type, vals, gb_count_of(vals));
- lb_emit_store(p, tag, res);
- break;
- }
- case Type_Tuple: {
- tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_parameters_ptr);
-
- lbValue memory_types = lb_type_info_member_types_offset(m, t->Tuple.variables.count);
- lbValue memory_names = lb_type_info_member_names_offset(m, t->Tuple.variables.count);
-
-
- for_array(i, t->Tuple.variables) {
- // NOTE(bill): offset is not used for tuples
- Entity *f = t->Tuple.variables[i];
-
- lbValue index = lb_const_int(m, t_int, i);
- lbValue type_info = lb_emit_ptr_offset(p, memory_types, index);
-
- // TODO(bill): Make this constant if possible, 'lb_const_store' does not work
- lb_emit_store(p, type_info, lb_type_info(m, f->type));
- if (f->token.string.len > 0) {
- lbValue name = lb_emit_ptr_offset(p, memory_names, index);
- lb_emit_store(p, name, lb_const_string(m, f->token.string));
- }
- }
-
- lbValue count = lb_const_int(m, t_int, t->Tuple.variables.count);
-
- LLVMValueRef types_slice = llvm_const_slice(m, memory_types, count);
- LLVMValueRef names_slice = llvm_const_slice(m, memory_names, count);
-
- LLVMValueRef vals[2] = {
- types_slice,
- names_slice,
- };
-
- lbValue res = {};
- res.type = type_deref(tag.type);
- res.value = llvm_const_named_struct(m, res.type, vals, gb_count_of(vals));
- lb_emit_store(p, tag, res);
-
- break;
- }
-
- case Type_Enum:
- tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_enum_ptr);
-
- {
- GB_ASSERT(t->Enum.base_type != nullptr);
- // GB_ASSERT_MSG(type_size_of(t_type_info_enum_value) == 16, "%lld == 16", cast(long long)type_size_of(t_type_info_enum_value));
-
-
- LLVMValueRef vals[3] = {};
- vals[0] = lb_type_info(m, t->Enum.base_type).value;
- if (t->Enum.fields.count > 0) {
- auto fields = t->Enum.fields;
- lbValue name_array = lb_generate_global_array(m, t_string, fields.count,
- str_lit("$enum_names"), cast(i64)entry_index);
- lbValue value_array = lb_generate_global_array(m, t_type_info_enum_value, fields.count,
- str_lit("$enum_values"), cast(i64)entry_index);
-
-
- LLVMValueRef *name_values = gb_alloc_array(temporary_allocator(), LLVMValueRef, fields.count);
- LLVMValueRef *value_values = gb_alloc_array(temporary_allocator(), LLVMValueRef, fields.count);
-
- GB_ASSERT(is_type_integer(t->Enum.base_type));
-
- for_array(i, fields) {
- name_values[i] = lb_const_string(m, fields[i]->token.string).value;
- 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);
- LLVMSetInitializer(name_array.value, name_init);
- LLVMSetInitializer(value_array.value, value_init);
- LLVMSetGlobalConstant(name_array.value, true);
- LLVMSetGlobalConstant(value_array.value, true);
-
- lbValue v_count = lb_const_int(m, t_int, fields.count);
-
- vals[1] = llvm_const_slice(m, lb_array_elem(p, name_array), v_count);
- vals[2] = llvm_const_slice(m, lb_array_elem(p, value_array), v_count);
- } else {
- vals[1] = LLVMConstNull(lb_type(m, base_type(t_type_info_enum)->Struct.fields[1]->type));
- vals[2] = LLVMConstNull(lb_type(m, base_type(t_type_info_enum)->Struct.fields[2]->type));
- }
-
-
- lbValue res = {};
- res.type = type_deref(tag.type);
- res.value = llvm_const_named_struct(m, res.type, vals, gb_count_of(vals));
- lb_emit_store(p, tag, res);
- }
- break;
-
- case Type_Union: {
- tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_union_ptr);
-
- {
- LLVMValueRef vals[7] = {};
-
- isize variant_count = gb_max(0, t->Union.variants.count);
- lbValue memory_types = lb_type_info_member_types_offset(m, variant_count);
-
- // NOTE(bill): Zeroth is nil so ignore it
- for (isize variant_index = 0; variant_index < variant_count; variant_index++) {
- Type *vt = t->Union.variants[variant_index];
- lbValue tip = lb_type_info(m, vt);
-
- lbValue index = lb_const_int(m, t_int, variant_index);
- lbValue type_info = lb_emit_ptr_offset(p, memory_types, index);
- lb_emit_store(p, type_info, lb_type_info(m, vt));
- }
-
- lbValue count = lb_const_int(m, t_int, variant_count);
- vals[0] = llvm_const_slice(m, memory_types, count);
-
- i64 tag_size = union_tag_size(t);
- if (tag_size > 0) {
- i64 tag_offset = align_formula(t->Union.variant_block_size, tag_size);
- vals[1] = lb_const_int(m, t_uintptr, tag_offset).value;
- vals[2] = lb_type_info(m, union_tag_type(t)).value;
- } else {
- vals[1] = lb_const_int(m, t_uintptr, 0).value;
- vals[2] = LLVMConstNull(lb_type(m, t_type_info_ptr));
- }
-
- if (is_type_comparable(t) && !is_type_simple_compare(t)) {
- vals[3] = lb_equal_proc_for_type(m, t).value;
- }
-
- vals[4] = lb_const_bool(m, t_bool, t->Union.custom_align != 0).value;
- vals[5] = lb_const_bool(m, t_bool, t->Union.kind == UnionType_no_nil).value;
- vals[6] = lb_const_bool(m, t_bool, t->Union.kind == UnionType_shared_nil).value;
-
- for (isize i = 0; i < gb_count_of(vals); i++) {
- if (vals[i] == nullptr) {
- vals[i] = LLVMConstNull(lb_type(m, get_struct_field_type(tag.type, i)));
- }
- }
-
- lbValue res = {};
- res.type = type_deref(tag.type);
- res.value = llvm_const_named_struct(m, res.type, vals, gb_count_of(vals));
- lb_emit_store(p, tag, res);
- }
-
- break;
- }
-
- case Type_Struct: {
- tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_struct_ptr);
-
- LLVMValueRef vals[13] = {};
-
-
- {
- lbValue is_packed = lb_const_bool(m, t_bool, t->Struct.is_packed);
- lbValue is_raw_union = lb_const_bool(m, t_bool, t->Struct.is_raw_union);
- lbValue is_no_copy = lb_const_bool(m, t_bool, t->Struct.is_no_copy);
- lbValue is_custom_align = lb_const_bool(m, t_bool, t->Struct.custom_align != 0);
- vals[5] = is_packed.value;
- vals[6] = is_raw_union.value;
- vals[7] = is_no_copy.value;
- vals[8] = is_custom_align.value;
- if (is_type_comparable(t) && !is_type_simple_compare(t)) {
- vals[9] = lb_equal_proc_for_type(m, t).value;
- }
-
-
- if (t->Struct.soa_kind != StructSoa_None) {
- lbValue kind = lb_emit_struct_ep(p, tag, 10);
- Type *kind_type = type_deref(kind.type);
-
- lbValue soa_kind = lb_const_value(m, kind_type, exact_value_i64(t->Struct.soa_kind));
- lbValue soa_type = lb_type_info(m, t->Struct.soa_elem);
- lbValue soa_len = lb_const_int(m, t_int, t->Struct.soa_count);
-
- vals[10] = soa_kind.value;
- vals[11] = soa_type.value;
- vals[12] = soa_len.value;
- }
- }
-
- isize count = t->Struct.fields.count;
- if (count > 0) {
- lbValue memory_types = lb_type_info_member_types_offset (m, count);
- lbValue memory_names = lb_type_info_member_names_offset (m, count);
- lbValue memory_offsets = lb_type_info_member_offsets_offset(m, count);
- lbValue memory_usings = lb_type_info_member_usings_offset (m, count);
- lbValue memory_tags = lb_type_info_member_tags_offset (m, count);
-
- type_set_offsets(t); // NOTE(bill): Just incase the offsets have not been set yet
- for (isize source_index = 0; source_index < count; source_index++) {
- Entity *f = t->Struct.fields[source_index];
- lbValue tip = lb_type_info(m, f->type);
- i64 foffset = 0;
- if (!t->Struct.is_raw_union) {
- GB_ASSERT(t->Struct.offsets != nullptr);
- GB_ASSERT(0 <= f->Variable.field_index && f->Variable.field_index < count);
- foffset = t->Struct.offsets[source_index];
- }
- GB_ASSERT(f->kind == Entity_Variable && f->flags & EntityFlag_Field);
-
- lbValue index = lb_const_int(m, t_int, source_index);
- lbValue type_info = lb_emit_ptr_offset(p, memory_types, index);
- lbValue offset = lb_emit_ptr_offset(p, memory_offsets, index);
- lbValue is_using = lb_emit_ptr_offset(p, memory_usings, index);
-
- lb_emit_store(p, type_info, lb_type_info(m, f->type));
- if (f->token.string.len > 0) {
- lbValue name = lb_emit_ptr_offset(p, memory_names, index);
- lb_emit_store(p, name, lb_const_string(m, f->token.string));
- }
- lb_emit_store(p, offset, lb_const_int(m, t_uintptr, foffset));
- lb_emit_store(p, is_using, lb_const_bool(m, t_bool, (f->flags&EntityFlag_Using) != 0));
-
- if (t->Struct.tags != nullptr) {
- String tag_string = t->Struct.tags[source_index];
- if (tag_string.len > 0) {
- lbValue tag_ptr = lb_emit_ptr_offset(p, memory_tags, index);
- lb_emit_store(p, tag_ptr, lb_const_string(m, tag_string));
- }
- }
-
- }
-
- lbValue cv = lb_const_int(m, t_int, count);
- vals[0] = llvm_const_slice(m, memory_types, cv);
- vals[1] = llvm_const_slice(m, memory_names, cv);
- vals[2] = llvm_const_slice(m, memory_offsets, cv);
- vals[3] = llvm_const_slice(m, memory_usings, cv);
- vals[4] = llvm_const_slice(m, memory_tags, cv);
- }
- for (isize i = 0; i < gb_count_of(vals); i++) {
- if (vals[i] == nullptr) {
- vals[i] = LLVMConstNull(lb_type(m, get_struct_field_type(tag.type, i)));
- }
- }
-
- lbValue res = {};
- res.type = type_deref(tag.type);
- res.value = llvm_const_named_struct(m, res.type, vals, gb_count_of(vals));
- lb_emit_store(p, tag, res);
-
- break;
- }
-
- case Type_Map: {
- tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_map_ptr);
- init_map_internal_types(t);
-
- LLVMValueRef vals[3] = {
- lb_type_info(m, t->Map.key).value,
- lb_type_info(m, t->Map.value).value,
- lb_gen_map_info_ptr(p->module, t).value
- };
-
- lbValue res = {};
- res.type = type_deref(tag.type);
- res.value = llvm_const_named_struct(m, res.type, vals, gb_count_of(vals));
- lb_emit_store(p, tag, res);
- break;
- }
-
- case Type_BitSet:
- {
- tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_bit_set_ptr);
-
- GB_ASSERT(is_type_typed(t->BitSet.elem));
-
-
- LLVMValueRef vals[4] = {
- lb_type_info(m, t->BitSet.elem).value,
- LLVMConstNull(lb_type(m, t_type_info_ptr)),
- lb_const_int(m, t_i64, t->BitSet.lower).value,
- lb_const_int(m, t_i64, t->BitSet.upper).value,
- };
- if (t->BitSet.underlying != nullptr) {
- vals[1] =lb_type_info(m, t->BitSet.underlying).value;
- }
-
- lbValue res = {};
- res.type = type_deref(tag.type);
- res.value = llvm_const_named_struct(m, res.type, vals, gb_count_of(vals));
- lb_emit_store(p, tag, res);
- }
- break;
-
- case Type_SimdVector:
- {
- tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_simd_vector_ptr);
-
- LLVMValueRef vals[3] = {};
-
- vals[0] = lb_type_info(m, t->SimdVector.elem).value;
- vals[1] = lb_const_int(m, t_int, type_size_of(t->SimdVector.elem)).value;
- vals[2] = lb_const_int(m, t_int, t->SimdVector.count).value;
-
- lbValue res = {};
- res.type = type_deref(tag.type);
- res.value = llvm_const_named_struct(m, res.type, vals, gb_count_of(vals));
- lb_emit_store(p, tag, res);
- }
- break;
-
- case Type_RelativePointer:
- {
- tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_relative_pointer_ptr);
- LLVMValueRef vals[2] = {
- lb_type_info(m, t->RelativePointer.pointer_type).value,
- lb_type_info(m, t->RelativePointer.base_integer).value,
- };
-
- lbValue res = {};
- res.type = type_deref(tag.type);
- res.value = llvm_const_named_struct(m, res.type, vals, gb_count_of(vals));
- lb_emit_store(p, tag, res);
- }
- break;
-
- case Type_RelativeMultiPointer:
- {
- tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_relative_multi_pointer_ptr);
- LLVMValueRef vals[2] = {
- lb_type_info(m, t->RelativeMultiPointer.pointer_type).value,
- lb_type_info(m, t->RelativeMultiPointer.base_integer).value,
- };
-
- lbValue res = {};
- res.type = type_deref(tag.type);
- res.value = llvm_const_named_struct(m, res.type, vals, gb_count_of(vals));
- lb_emit_store(p, tag, res);
- }
- break;
-
- case Type_Matrix:
- {
- tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_matrix_ptr);
- i64 ez = type_size_of(t->Matrix.elem);
-
- LLVMValueRef vals[5] = {
- lb_type_info(m, t->Matrix.elem).value,
- lb_const_int(m, t_int, ez).value,
- lb_const_int(m, t_int, matrix_type_stride_in_elems(t)).value,
- lb_const_int(m, t_int, t->Matrix.row_count).value,
- lb_const_int(m, t_int, t->Matrix.column_count).value,
- };
+ if (true) {
+ lb_setup_type_info_data_giant_array(m, global_type_info_data_entity_count);
+ }
- lbValue res = {};
- res.type = type_deref(tag.type);
- res.value = llvm_const_named_struct(m, res.type, vals, gb_count_of(vals));
- lb_emit_store(p, tag, res);
- }
- break;
- }
+ LLVMValueRef data = lb_global_type_info_data_ptr(m).value;
+ data = LLVMConstPointerCast(data, lb_type(m, alloc_type_pointer(type->Array.elem)));
+ LLVMValueRef len = LLVMConstInt(lb_type(m, t_int), type->Array.count, true);
+ Type *t = type_deref(global_type_table.type);
+ GB_ASSERT(is_type_slice(t));
+ LLVMValueRef slice = llvm_const_slice_internal(m, data, len);
+ LLVMSetInitializer(global_type_table.value, slice);
- if (tag.value != nullptr) {
- Type *tag_type = type_deref(tag.type);
- GB_ASSERT(is_type_named(tag_type));
- // lb_emit_store_union_variant(p, variant_ptr, lb_emit_load(p, tag), tag_type);
- lb_emit_store_union_variant_tag(p, variant_ptr, tag_type);
- } else {
- if (t != t_llvm_bool) {
- GB_PANIC("Unhandled Type_Info variant: %s", type_to_string(t));
- }
- }
- }
-
- for_array(i, entries_handled) {
- if (!entries_handled[i]) {
- GB_PANIC("UNHANDLED ENTRY %td (%td)", i, entries_handled.count);
- }
- }
+ // force it to be constant
+ LLVMSetGlobalConstant(global_type_table.value, true);
}
diff --git a/src/llvm_backend_utility.cpp b/src/llvm_backend_utility.cpp
index d8dbfd736..7b7c9d6e9 100644
--- a/src/llvm_backend_utility.cpp
+++ b/src/llvm_backend_utility.cpp
@@ -57,6 +57,33 @@ gb_internal lbValue lb_correct_endianness(lbProcedure *p, lbValue value) {
return value;
}
+
+gb_internal void lb_set_metadata_custom_u64(lbModule *m, LLVMValueRef v_ref, String name, u64 value) {
+ unsigned md_id = LLVMGetMDKindIDInContext(m->ctx, cast(char const *)name.text, cast(unsigned)name.len);
+ LLVMMetadataRef md = LLVMValueAsMetadata(LLVMConstInt(lb_type(m, t_u64), value, false));
+ LLVMValueRef node = LLVMMetadataAsValue(m->ctx, LLVMMDNodeInContext2(m->ctx, &md, 1));
+ LLVMSetMetadata(v_ref, md_id, node);
+}
+gb_internal u64 lb_get_metadata_custom_u64(lbModule *m, LLVMValueRef v_ref, String name) {
+ unsigned md_id = LLVMGetMDKindIDInContext(m->ctx, cast(char const *)name.text, cast(unsigned)name.len);
+ LLVMValueRef v_md = LLVMGetMetadata(v_ref, md_id);
+ if (v_md == nullptr) {
+ return 0;
+ }
+ unsigned node_count = LLVMGetMDNodeNumOperands(v_md);
+ if (node_count == 0) {
+ return 0;
+ }
+ GB_ASSERT(node_count == 1);
+ LLVMValueRef value = nullptr;
+ LLVMGetMDNodeOperands(v_md, &value);
+ return LLVMConstIntGetZExtValue(value);
+}
+
+gb_internal LLVMValueRef lb_mem_zero_ptr_internal(lbProcedure *p, LLVMValueRef ptr, usize len, unsigned alignment, bool is_volatile) {
+ return lb_mem_zero_ptr_internal(p, ptr, LLVMConstInt(lb_type(p->module, t_uint), len, false), alignment, is_volatile);
+}
+
gb_internal LLVMValueRef lb_mem_zero_ptr_internal(lbProcedure *p, LLVMValueRef ptr, LLVMValueRef len, unsigned alignment, bool is_volatile) {
bool is_inlinable = false;
@@ -79,27 +106,13 @@ gb_internal LLVMValueRef lb_mem_zero_ptr_internal(lbProcedure *p, LLVMValueRef p
lb_type(p->module, t_rawptr),
lb_type(p->module, t_int)
};
- if (true || is_inlinable) {
+ LLVMValueRef args[4] = {};
+ args[0] = LLVMBuildPointerCast(p->builder, ptr, types[0], "");
+ args[1] = LLVMConstInt(LLVMInt8TypeInContext(p->module->ctx), 0, false);
+ args[2] = LLVMBuildIntCast2(p->builder, len, types[1], /*signed*/false, "");
+ args[3] = LLVMConstInt(LLVMInt1TypeInContext(p->module->ctx), is_volatile, false);
- LLVMValueRef args[4] = {};
- args[0] = LLVMBuildPointerCast(p->builder, ptr, types[0], "");
- args[1] = LLVMConstInt(LLVMInt8TypeInContext(p->module->ctx), 0, false);
- args[2] = LLVMBuildIntCast2(p->builder, len, types[1], /*signed*/false, "");
- args[3] = LLVMConstInt(LLVMInt1TypeInContext(p->module->ctx), is_volatile, false);
-
- return lb_call_intrinsic(p, name, args, gb_count_of(args), types, gb_count_of(types));
- } else {
- lbValue pr = lb_lookup_runtime_procedure(p->module, str_lit("memset"));
-
- LLVMValueRef args[3] = {};
- args[0] = LLVMBuildPointerCast(p->builder, ptr, types[0], "");
- args[1] = LLVMConstInt(LLVMInt32TypeInContext(p->module->ctx), 0, false);
- args[2] = LLVMBuildIntCast2(p->builder, len, types[1], /*signed*/false, "");
-
- // We always get the function pointer type rather than the function and there is apparently no way around that?
- LLVMTypeRef type = lb_type_internal_for_procedures_raw(p->module, pr.type);
- return LLVMBuildCall2(p->builder, type, pr.value, args, gb_count_of(args), "");
- }
+ return lb_call_intrinsic(p, name, args, gb_count_of(args), types, gb_count_of(types));
}
@@ -107,13 +120,18 @@ gb_internal void lb_mem_zero_ptr(lbProcedure *p, LLVMValueRef ptr, Type *type, u
LLVMTypeRef llvm_type = lb_type(p->module, type);
LLVMTypeKind kind = LLVMGetTypeKind(llvm_type);
-
+ i64 sz = type_size_of(type);
switch (kind) {
case LLVMStructTypeKind:
case LLVMArrayTypeKind:
- {
+ if (is_type_tuple(type)) {
+ // NOTE(bill): even though this should be safe, to keep ASAN happy, do not zero the implicit padding at the end
+ GB_ASSERT(type->kind == Type_Tuple);
+ i64 n = type->Tuple.variables.count-1;
+ i64 end_offset = type->Tuple.offsets[n] + type_size_of(type->Tuple.variables[n]->type);
+ lb_mem_zero_ptr_internal(p, ptr, lb_const_int(p->module, t_int, end_offset).value, alignment, false);
+ } else {
// NOTE(bill): Enforce zeroing through memset to make sure padding is zeroed too
- i32 sz = cast(i32)type_size_of(type);
lb_mem_zero_ptr_internal(p, ptr, lb_const_int(p->module, t_int, sz).value, alignment, false);
}
break;
@@ -134,11 +152,33 @@ gb_internal lbValue lb_emit_select(lbProcedure *p, lbValue cond, lbValue x, lbVa
gb_internal lbValue lb_emit_min(lbProcedure *p, Type *t, lbValue x, lbValue y) {
x = lb_emit_conv(p, x, t);
y = lb_emit_conv(p, y, t);
+ bool use_llvm_intrinsic = !is_arch_wasm() && (is_type_float(t) || (is_type_simd_vector(t) && is_type_float(base_array_type(t))));
+ if (use_llvm_intrinsic) {
+ LLVMValueRef args[2] = {x.value, y.value};
+ LLVMTypeRef types[1] = {lb_type(p->module, t)};
+
+ // NOTE(bill): f either operand is a NaN, returns NaN. Otherwise returns the lesser of the two arguments.
+ // -0.0 is considered to be less than +0.0 for this intrinsic.
+ // These semantics are specified by IEEE 754-2008.
+ LLVMValueRef v = lb_call_intrinsic(p, "llvm.minnum", args, gb_count_of(args), types, gb_count_of(types));
+ return {v, t};
+ }
return lb_emit_select(p, lb_emit_comp(p, Token_Lt, x, y), x, y);
}
gb_internal lbValue lb_emit_max(lbProcedure *p, Type *t, lbValue x, lbValue y) {
x = lb_emit_conv(p, x, t);
y = lb_emit_conv(p, y, t);
+ bool use_llvm_intrinsic = !is_arch_wasm() && (is_type_float(t) || (is_type_simd_vector(t) && is_type_float(base_array_type(t))));
+ if (use_llvm_intrinsic) {
+ LLVMValueRef args[2] = {x.value, y.value};
+ LLVMTypeRef types[1] = {lb_type(p->module, t)};
+
+ // NOTE(bill): If either operand is a NaN, returns NaN. Otherwise returns the greater of the two arguments.
+ // -0.0 is considered to be less than +0.0 for this intrinsic.
+ // These semantics are specified by IEEE 754-2008.
+ LLVMValueRef v = lb_call_intrinsic(p, "llvm.maxnum", args, gb_count_of(args), types, gb_count_of(types));
+ return {v, t};
+ }
return lb_emit_select(p, lb_emit_comp(p, Token_Gt, x, y), x, y);
}
@@ -231,13 +271,13 @@ gb_internal lbValue lb_emit_transmute(lbProcedure *p, lbValue value, Type *t) {
if (is_type_simd_vector(src) && is_type_simd_vector(dst)) {
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)) {
+ } else if (is_type_array_like(src) && (is_type_simd_vector(dst) || is_type_integer_128bit(dst))) {
unsigned align = cast(unsigned)gb_max(type_align_of(src), type_align_of(dst));
lbValue ptr = lb_address_from_load_or_generate_local(p, value);
if (lb_try_update_alignment(ptr, align)) {
LLVMTypeRef result_type = lb_type(p->module, t);
res.value = LLVMBuildPointerCast(p->builder, ptr.value, LLVMPointerType(result_type, 0), "");
- res.value = LLVMBuildLoad2(p->builder, result_type, res.value, "");
+ res.value = OdinLLVMBuildLoad(p, result_type, res.value);
return res;
}
lbAddr addr = lb_add_local_generated(p, t, false);
@@ -296,6 +336,7 @@ gb_internal lbValue lb_soa_zip(lbProcedure *p, AstCallExpr *ce, TypeAndValue con
lbAddr res = lb_add_local_generated(p, tv.type, true);
for_array(i, slices) {
lbValue src = lb_slice_elem(p, slices[i]);
+ src = lb_emit_conv(p, src, alloc_type_pointer_to_multi_pointer(src.type));
lbValue dst = lb_emit_struct_ep(p, res.addr, cast(i32)i);
lb_emit_store(p, dst, src);
}
@@ -435,8 +476,8 @@ gb_internal lbValue lb_emit_or_else(lbProcedure *p, Ast *arg, Ast *else_expr, Ty
}
}
-gb_internal void lb_build_return_stmt(lbProcedure *p, Slice<Ast *> const &return_results);
-gb_internal void lb_build_return_stmt_internal(lbProcedure *p, lbValue res);
+gb_internal void lb_build_return_stmt(lbProcedure *p, Slice<Ast *> const &return_results, TokenPos pos);
+gb_internal void lb_build_return_stmt_internal(lbProcedure *p, lbValue res, TokenPos pos);
gb_internal lbValue lb_emit_or_return(lbProcedure *p, Ast *arg, TypeAndValue const &tv) {
lbValue lhs = {};
@@ -465,10 +506,10 @@ gb_internal lbValue lb_emit_or_return(lbProcedure *p, Ast *arg, TypeAndValue con
lbValue found = map_must_get(&p->module->values, end_entity);
lb_emit_store(p, found, rhs);
- lb_build_return_stmt(p, {});
+ lb_build_return_stmt(p, {}, ast_token(arg).pos);
} else {
GB_ASSERT(tuple->variables.count == 1);
- lb_build_return_stmt_internal(p, rhs);
+ lb_build_return_stmt_internal(p, rhs, ast_token(arg).pos);
}
}
lb_start_block(p, continue_block);
@@ -716,29 +757,31 @@ gb_internal lbValue lb_emit_union_cast(lbProcedure *p, lbValue value, Type *type
lb_start_block(p, end_block);
if (!is_tuple) {
- GB_ASSERT((p->state_flags & StateFlag_no_type_assert) == 0);
- // NOTE(bill): Panic on invalid conversion
- Type *dst_type = tuple->Tuple.variables[0]->type;
-
- isize arg_count = 7;
- if (build_context.no_rtti) {
- arg_count = 4;
- }
+ if (!build_context.no_type_assert) {
+ GB_ASSERT((p->state_flags & StateFlag_no_type_assert) == 0);
+ // NOTE(bill): Panic on invalid conversion
+ Type *dst_type = tuple->Tuple.variables[0]->type;
+
+ isize arg_count = 7;
+ if (build_context.no_rtti) {
+ arg_count = 4;
+ }
- lbValue ok = lb_emit_load(p, lb_emit_struct_ep(p, v.addr, 1));
- auto args = array_make<lbValue>(permanent_allocator(), arg_count);
- args[0] = ok;
+ lbValue ok = lb_emit_load(p, lb_emit_struct_ep(p, v.addr, 1));
+ auto args = array_make<lbValue>(permanent_allocator(), arg_count);
+ args[0] = ok;
- args[1] = lb_const_string(m, get_file_path_string(pos.file_id));
- args[2] = lb_const_int(m, t_i32, pos.line);
- args[3] = lb_const_int(m, t_i32, pos.column);
+ args[1] = lb_const_string(m, get_file_path_string(pos.file_id));
+ args[2] = lb_const_int(m, t_i32, pos.line);
+ args[3] = lb_const_int(m, t_i32, pos.column);
- if (!build_context.no_rtti) {
- args[4] = lb_typeid(m, src_type);
- args[5] = lb_typeid(m, dst_type);
- args[6] = lb_emit_conv(p, value_, t_rawptr);
+ if (!build_context.no_rtti) {
+ args[4] = lb_typeid(m, src_type);
+ args[5] = lb_typeid(m, dst_type);
+ args[6] = lb_emit_conv(p, value_, t_rawptr);
+ }
+ lb_emit_runtime_call(p, "type_assertion_check2", args);
}
- lb_emit_runtime_call(p, "type_assertion_check2", args);
return lb_emit_load(p, lb_emit_struct_ep(p, v.addr, 0));
}
@@ -794,25 +837,27 @@ gb_internal lbAddr lb_emit_any_cast_addr(lbProcedure *p, lbValue value, Type *ty
if (!is_tuple) {
// NOTE(bill): Panic on invalid conversion
- lbValue ok = lb_emit_load(p, lb_emit_struct_ep(p, v.addr, 1));
+ if (!build_context.no_type_assert) {
+ lbValue ok = lb_emit_load(p, lb_emit_struct_ep(p, v.addr, 1));
- isize arg_count = 7;
- if (build_context.no_rtti) {
- arg_count = 4;
- }
- auto args = array_make<lbValue>(permanent_allocator(), arg_count);
- args[0] = ok;
+ isize arg_count = 7;
+ if (build_context.no_rtti) {
+ arg_count = 4;
+ }
+ auto args = array_make<lbValue>(permanent_allocator(), arg_count);
+ args[0] = ok;
- args[1] = lb_const_string(m, get_file_path_string(pos.file_id));
- args[2] = lb_const_int(m, t_i32, pos.line);
- args[3] = lb_const_int(m, t_i32, pos.column);
+ args[1] = lb_const_string(m, get_file_path_string(pos.file_id));
+ args[2] = lb_const_int(m, t_i32, pos.line);
+ args[3] = lb_const_int(m, t_i32, pos.column);
- if (!build_context.no_rtti) {
- args[4] = any_typeid;
- args[5] = dst_typeid;
- args[6] = lb_emit_struct_ev(p, value, 0);
+ if (!build_context.no_rtti) {
+ args[4] = any_typeid;
+ args[5] = dst_typeid;
+ args[6] = lb_emit_struct_ev(p, value, 0);
+ }
+ lb_emit_runtime_call(p, "type_assertion_check2", args);
}
- lb_emit_runtime_call(p, "type_assertion_check2", args);
return lb_addr(lb_emit_struct_ep(p, v.addr, 0));
}
@@ -966,6 +1011,21 @@ gb_internal i32 lb_convert_struct_index(lbModule *m, Type *t, i32 index) {
}
gb_internal LLVMTypeRef lb_type_padding_filler(lbModule *m, i64 padding, i64 padding_align) {
+ MUTEX_GUARD(&m->pad_types_mutex);
+ if (padding % padding_align == 0) {
+ for (auto pd : m->pad_types) {
+ if (pd.padding == padding && pd.padding_align == padding_align) {
+ return pd.type;
+ }
+ }
+ } else {
+ for (auto pd : m->pad_types) {
+ if (pd.padding == padding && pd.padding_align == 1) {
+ return pd.type;
+ }
+ }
+ }
+
// NOTE(bill): limit to `[N x u64]` to prevent ABI issues
padding_align = gb_clamp(padding_align, 1, 8);
if (padding % padding_align == 0) {
@@ -979,13 +1039,19 @@ gb_internal LLVMTypeRef lb_type_padding_filler(lbModule *m, i64 padding, i64 pad
}
GB_ASSERT_MSG(elem != nullptr, "Invalid lb_type_padding_filler padding and padding_align: %lld", padding_align);
+
+ LLVMTypeRef type = nullptr;
if (len != 1) {
- return llvm_array_type(elem, len);
+ type = llvm_array_type(elem, len);
} else {
- return elem;
+ type = elem;
}
+ array_add(&m->pad_types, lbPadType{padding, padding_align, type});
+ return type;
} else {
- return llvm_array_type(lb_type(m, t_u8), padding);
+ LLVMTypeRef type = llvm_array_type(lb_type(m, t_u8), padding);
+ array_add(&m->pad_types, lbPadType{padding, 1, type});
+ return type;
}
}
@@ -1065,10 +1131,6 @@ gb_internal lbValue lb_emit_struct_ep(lbProcedure *p, lbValue s, i32 index) {
Type *t = base_type(type_deref(s.type));
Type *result_type = nullptr;
- if (is_type_relative_pointer(t)) {
- s = lb_addr_get_ptr(p, lb_addr(s));
- }
-
if (is_type_struct(t)) {
result_type = get_struct_field_type(t, index);
} else if (is_type_union(t)) {
@@ -1113,7 +1175,7 @@ gb_internal lbValue lb_emit_struct_ep(lbProcedure *p, lbValue s, i32 index) {
case 3: result_type = t_allocator; break;
}
} else if (is_type_map(t)) {
- init_map_internal_types(t);
+ init_map_internal_debug_types(t);
Type *itp = alloc_type_pointer(t_raw_map);
s = lb_emit_transmute(p, s, itp);
@@ -1135,7 +1197,28 @@ gb_internal lbValue lb_emit_struct_ep(lbProcedure *p, lbValue s, i32 index) {
GB_ASSERT_MSG(result_type != nullptr, "%s %d", type_to_string(t), index);
- return lb_emit_struct_ep_internal(p, s, index, result_type);
+ lbValue gep = lb_emit_struct_ep_internal(p, s, index, result_type);
+
+ Type *bt = base_type(t);
+ if (bt->kind == Type_Struct) {
+ if (bt->Struct.is_packed) {
+ lb_set_metadata_custom_u64(p->module, gep.value, ODIN_METADATA_IS_PACKED, 1);
+ GB_ASSERT(lb_get_metadata_custom_u64(p->module, gep.value, ODIN_METADATA_IS_PACKED) == 1);
+ }
+ u64 align_max = bt->Struct.custom_max_field_align;
+ u64 align_min = bt->Struct.custom_min_field_align;
+ GB_ASSERT(align_min == 0 || align_max == 0 || align_min <= align_max);
+ if (align_max) {
+ lb_set_metadata_custom_u64(p->module, gep.value, ODIN_METADATA_MAX_ALIGN, align_max);
+ GB_ASSERT(lb_get_metadata_custom_u64(p->module, gep.value, ODIN_METADATA_MAX_ALIGN) == align_max);
+ }
+ if (align_min) {
+ lb_set_metadata_custom_u64(p->module, gep.value, ODIN_METADATA_MIN_ALIGN, align_min);
+ GB_ASSERT(lb_get_metadata_custom_u64(p->module, gep.value, ODIN_METADATA_MIN_ALIGN) == align_min);
+ }
+ }
+
+ return gep;
}
gb_internal lbValue lb_emit_tuple_ev(lbProcedure *p, lbValue value, i32 index) {
@@ -1252,7 +1335,7 @@ gb_internal lbValue lb_emit_struct_ev(lbProcedure *p, lbValue s, i32 index) {
case Type_Map:
{
- init_map_internal_types(t);
+ init_map_internal_debug_types(t);
switch (index) {
case 0: result_type = get_struct_field_type(t_raw_map, 0); break;
case 1: result_type = get_struct_field_type(t_raw_map, 1); break;
@@ -1320,6 +1403,8 @@ gb_internal lbValue lb_emit_deep_field_gep(lbProcedure *p, lbValue e, Selection
} else {
e = lb_emit_ptr_offset(p, lb_emit_load(p, arr), index);
}
+ e.type = alloc_type_multi_pointer_to_pointer(e.type);
+
} else if (is_type_quaternion(type)) {
e = lb_emit_struct_ep(p, e, index);
} else if (is_type_raw_union(type)) {
@@ -1342,7 +1427,7 @@ gb_internal lbValue lb_emit_deep_field_gep(lbProcedure *p, lbValue e, Selection
if (index == 0) {
type = t_rawptr;
} else if (index == 1) {
- type = t_type_info_ptr;
+ type = t_typeid;
}
e = lb_emit_struct_ep(p, e, index);
break;
@@ -1364,8 +1449,6 @@ gb_internal lbValue lb_emit_deep_field_gep(lbProcedure *p, lbValue e, Selection
e = lb_emit_array_epi(p, e, index);
} else if (type->kind == Type_Map) {
e = lb_emit_struct_ep(p, e, index);
- } else if (type->kind == Type_RelativePointer) {
- e = lb_emit_struct_ep(p, e, index);
} else {
GB_PANIC("un-gep-able type %s", type_to_string(type));
}
@@ -1452,14 +1535,16 @@ gb_internal lbValue lb_emit_matrix_epi(lbProcedure *p, lbValue s, isize row, isi
Type *t = s.type;
GB_ASSERT(is_type_pointer(t));
Type *mt = base_type(type_deref(t));
- if (column == 0) {
- GB_ASSERT_MSG(is_type_matrix(mt) || is_type_array_like(mt), "%s", type_to_string(mt));
- return lb_emit_epi(p, s, row);
- } else if (row == 0 && is_type_array_like(mt)) {
- return lb_emit_epi(p, s, column);
+
+ if (!mt->Matrix.is_row_major) {
+ if (column == 0) {
+ GB_ASSERT_MSG(is_type_matrix(mt) || is_type_array_like(mt), "%s", type_to_string(mt));
+ return lb_emit_epi(p, s, row);
+ } else if (row == 0 && is_type_array_like(mt)) {
+ return lb_emit_epi(p, s, column);
+ }
}
-
GB_ASSERT_MSG(is_type_matrix(mt), "%s", type_to_string(mt));
isize offset = matrix_indices_to_offset(mt, row, column);
@@ -1479,7 +1564,13 @@ gb_internal lbValue lb_emit_matrix_ep(lbProcedure *p, lbValue s, lbValue row, lb
row = lb_emit_conv(p, row, t_int);
column = lb_emit_conv(p, column, t_int);
- LLVMValueRef index = LLVMBuildAdd(p->builder, row.value, LLVMBuildMul(p->builder, column.value, stride_elems, ""), "");
+ LLVMValueRef index = nullptr;
+
+ if (mt->Matrix.is_row_major) {
+ index = LLVMBuildAdd(p->builder, column.value, LLVMBuildMul(p->builder, row.value, stride_elems, ""), "");
+ } else {
+ index = LLVMBuildAdd(p->builder, row.value, LLVMBuildMul(p->builder, column.value, stride_elems, ""), "");
+ }
LLVMValueRef indices[2] = {
LLVMConstInt(lb_type(p->module, t_int), 0, false),
@@ -1507,19 +1598,26 @@ gb_internal lbValue lb_emit_matrix_ev(lbProcedure *p, lbValue s, isize row, isiz
return lb_emit_load(p, ptr);
}
-
gb_internal void lb_fill_slice(lbProcedure *p, lbAddr const &slice, lbValue base_elem, lbValue len) {
Type *t = lb_addr_type(slice);
GB_ASSERT(is_type_slice(t));
lbValue ptr = lb_addr_get_ptr(p, slice);
- lb_emit_store(p, lb_emit_struct_ep(p, ptr, 0), base_elem);
+ lbValue data = lb_emit_struct_ep(p, ptr, 0);
+ if (are_types_identical(type_deref(base_elem.type, true), type_deref(type_deref(data.type), true))) {
+ base_elem = lb_emit_conv(p, base_elem, type_deref(data.type));
+ }
+ lb_emit_store(p, data, base_elem);
lb_emit_store(p, lb_emit_struct_ep(p, ptr, 1), len);
}
gb_internal void lb_fill_string(lbProcedure *p, lbAddr const &string, lbValue base_elem, lbValue len) {
Type *t = lb_addr_type(string);
GB_ASSERT(is_type_string(t));
lbValue ptr = lb_addr_get_ptr(p, string);
- lb_emit_store(p, lb_emit_struct_ep(p, ptr, 0), base_elem);
+ lbValue data = lb_emit_struct_ep(p, ptr, 0);
+ if (are_types_identical(type_deref(base_elem.type, true), type_deref(type_deref(data.type), true))) {
+ base_elem = lb_emit_conv(p, base_elem, type_deref(data.type));
+ }
+ lb_emit_store(p, data, base_elem);
lb_emit_store(p, lb_emit_struct_ep(p, ptr, 1), len);
}
@@ -1687,7 +1785,8 @@ gb_internal lbValue lb_emit_mul_add(lbProcedure *p, lbValue a, lbValue b, lbValu
if (is_possible) {
switch (build_context.metrics.arch) {
case TargetArch_amd64:
- if (type_size_of(t) == 2) {
+ // NOTE: using the intrinsic when not supported causes slow codegen (See #2928).
+ if (type_size_of(t) == 2 || !check_target_feature_is_enabled(str_lit("fma"), nullptr)) {
is_possible = false;
}
break;
@@ -1955,7 +2054,7 @@ gb_internal LLVMValueRef llvm_get_inline_asm(LLVMTypeRef func_type, String const
}
-gb_internal void lb_set_wasm_import_attributes(LLVMValueRef value, Entity *entity, String import_name) {
+gb_internal void lb_set_wasm_procedure_import_attributes(LLVMValueRef value, Entity *entity, String import_name) {
if (!is_arch_wasm()) {
return;
}
@@ -1966,7 +2065,11 @@ gb_internal void lb_set_wasm_import_attributes(LLVMValueRef value, Entity *entit
GB_ASSERT(foreign_library->LibraryName.paths.count == 1);
module_name = foreign_library->LibraryName.paths[0];
-
+
+ if (string_ends_with(module_name, str_lit(".o"))) {
+ return;
+ }
+
if (string_starts_with(import_name, module_name)) {
import_name = substring(import_name, module_name.len+WASM_MODULE_NAME_SEPARATOR.len, import_name.len);
}
@@ -2162,8 +2265,8 @@ gb_internal LLVMAtomicOrdering llvm_atomic_ordering_from_odin(ExactValue const &
GB_ASSERT(value.kind == ExactValue_Integer);
i64 v = exact_value_to_i64(value);
switch (v) {
- case OdinAtomicMemoryOrder_relaxed: return LLVMAtomicOrderingUnordered;
- case OdinAtomicMemoryOrder_consume: return LLVMAtomicOrderingMonotonic;
+ case OdinAtomicMemoryOrder_relaxed: return LLVMAtomicOrderingMonotonic;
+ case OdinAtomicMemoryOrder_consume: return LLVMAtomicOrderingAcquire;
case OdinAtomicMemoryOrder_acquire: return LLVMAtomicOrderingAcquire;
case OdinAtomicMemoryOrder_release: return LLVMAtomicOrderingRelease;
case OdinAtomicMemoryOrder_acq_rel: return LLVMAtomicOrderingAcquireRelease;
diff --git a/src/main.cpp b/src/main.cpp
index 79c2b3561..41c7170f6 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -61,6 +61,7 @@ gb_global Timings global_timings = {0};
#include "llvm-c/Types.h"
#else
#include <llvm-c/Types.h>
+#include <signal.h>
#endif
#include "parser.hpp"
@@ -70,6 +71,8 @@ gb_global Timings global_timings = {0};
#include "checker.cpp"
#include "docs.cpp"
+#include "cached.cpp"
+
#include "linker.cpp"
#if defined(GB_SYSTEM_WINDOWS) && defined(ODIN_TILDE_BACKEND)
@@ -86,27 +89,46 @@ gb_global Timings global_timings = {0};
#if defined(GB_SYSTEM_OSX)
#include <llvm/Config/llvm-config.h>
- #if LLVM_VERSION_MAJOR < 11
- #error LLVM Version 11+ is required => "brew install llvm@11"
- #endif
- #if (LLVM_VERSION_MAJOR > 14 && LLVM_VERSION_MAJOR < 17) || LLVM_VERSION_MAJOR > 17
- #error LLVM Version 11..=14 or =17 is required => "brew install llvm@14"
+ #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
-gb_internal i32 system_exec_command_line_app(char const *name, char const *fmt, ...) {
+gb_internal i32 system_exec_command_line_app_internal(bool exit_on_err, char const *name, char const *fmt, va_list va) {
isize const cmd_cap = 64<<20; // 64 MiB should be more than enough
char *cmd_line = gb_alloc_array(gb_heap_allocator(), char, cmd_cap);
isize cmd_len = 0;
- va_list va;
i32 exit_code = 0;
- va_start(va, fmt);
cmd_len = gb_snprintf_va(cmd_line, cmd_cap-1, fmt, va);
- va_end(va);
+
+ if (build_context.print_linker_flags) {
+ // NOTE(bill): remove the first argument (the executable) from the executable list
+ // and then print it for the "linker flags"
+ while (*cmd_line && gb_char_is_space(*cmd_line)) {
+ cmd_line++;
+ }
+ if (*cmd_line == '\"') for (cmd_line++; *cmd_line; cmd_line++) {
+ if (*cmd_line == '\\') {
+ cmd_line++;
+ if (*cmd_line == '\"') {
+ cmd_line++;
+ }
+ } else if (*cmd_line == '\"') {
+ cmd_line++;
+ break;
+ }
+ }
+ while (*cmd_line && gb_char_is_space(*cmd_line)) {
+ cmd_line++;
+ }
+
+ fprintf(stdout, "%s\n", cmd_line);
+ return exit_code;
+ }
#if defined(GB_SYSTEM_WINDOWS)
STARTUPINFOW start_info = {gb_size_of(STARTUPINFOW)};
@@ -146,18 +168,68 @@ gb_internal i32 system_exec_command_line_app(char const *name, char const *fmt,
gb_printf_err("%s\n\n", cmd_line);
}
exit_code = system(cmd_line);
+ if (exit_on_err && WIFSIGNALED(exit_code)) {
+ raise(WTERMSIG(exit_code));
+ }
if (WIFEXITED(exit_code)) {
exit_code = WEXITSTATUS(exit_code);
}
#endif
- if (exit_code) {
+ if (exit_on_err && exit_code) {
exit(exit_code);
}
return exit_code;
}
+gb_internal i32 system_exec_command_line_app(char const *name, char const *fmt, ...) {
+ va_list va;
+ va_start(va, fmt);
+ i32 exit_code = system_exec_command_line_app_internal(/* exit_on_err= */ false, name, fmt, va);
+ va_end(va);
+ return exit_code;
+}
+
+gb_internal void system_must_exec_command_line_app(char const *name, char const *fmt, ...) {
+ va_list va;
+ va_start(va, fmt);
+ system_exec_command_line_app_internal(/* exit_on_err= */ true, name, fmt, va);
+ va_end(va);
+}
+
+#if defined(GB_SYSTEM_WINDOWS)
+#define popen _popen
+#define pclose _pclose
+#endif
+
+gb_internal bool system_exec_command_line_app_output(char const *command, gbString *output) {
+ GB_ASSERT(output);
+
+ u8 buffer[256];
+ FILE *stream;
+ stream = popen(command, "r");
+ if (!stream) {
+ return false;
+ }
+ defer (pclose(stream));
+
+ while (!feof(stream)) {
+ size_t n = fread(buffer, 1, 255, stream);
+ *output = gb_string_append_length(*output, buffer, n);
+
+ if (ferror(stream)) {
+ return false;
+ }
+ }
+
+ if (build_context.show_system_calls) {
+ gb_printf_err("[SYSTEM CALL OUTPUT] %s -> %s\n", command, *output);
+ }
+
+ return true;
+}
+
gb_internal Array<String> setup_args(int argc, char const **argv) {
gbAllocator a = heap_allocator();
@@ -198,20 +270,26 @@ gb_internal void print_usage_line(i32 indent, char const *fmt, ...) {
gb_printf("\n");
}
-gb_internal void usage(String argv0) {
- print_usage_line(0, "%.*s is a tool for managing Odin source code", LIT(argv0));
+gb_internal void usage(String argv0, String argv1 = {}) {
+ if (argv1 == "run.") {
+ print_usage_line(0, "Did you mean 'odin run .'?");
+ } else if (argv1 == "build.") {
+ print_usage_line(0, "Did you mean 'odin build .'?");
+ }
+ print_usage_line(0, "%.*s is a tool for managing Odin source code.", LIT(argv0));
print_usage_line(0, "Usage:");
print_usage_line(1, "%.*s command [arguments]", LIT(argv0));
print_usage_line(0, "Commands:");
- print_usage_line(1, "build compile 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 parse, and type check a directory of .odin files");
- print_usage_line(1, "strip-semicolon parse, type check, and remove unneeded semicolons from the entire program");
- print_usage_line(1, "test build and runs procedures with the attribute @(test) in the initial package");
- print_usage_line(1, "doc generate documentation on a directory of .odin files");
- print_usage_line(1, "version print version");
- print_usage_line(1, "report print information useful to reporting a bug");
+ 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, "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, "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.");
print_usage_line(0, "");
print_usage_line(0, "For further details on a command, invoke command help:");
print_usage_line(1, "e.g. `odin build -help` or `odin help build`");
@@ -231,6 +309,8 @@ enum BuildFlagKind {
BuildFlag_ShowMoreTimings,
BuildFlag_ExportTimings,
BuildFlag_ExportTimingsFile,
+ BuildFlag_ExportDependencies,
+ BuildFlag_ExportDependenciesFile,
BuildFlag_ShowSystemCalls,
BuildFlag_ThreadCount,
BuildFlag_KeepTempFiles,
@@ -242,41 +322,56 @@ enum BuildFlagKind {
BuildFlag_Debug,
BuildFlag_DisableAssert,
BuildFlag_NoBoundsCheck,
+ BuildFlag_NoTypeAssert,
BuildFlag_NoDynamicLiterals,
BuildFlag_NoCRT,
+ BuildFlag_NoRPath,
BuildFlag_NoEntryPoint,
BuildFlag_UseLLD,
+ BuildFlag_UseRADLink,
+ BuildFlag_Linker,
BuildFlag_UseSeparateModules,
BuildFlag_NoThreadedChecker,
BuildFlag_ShowDebugMessages,
+ BuildFlag_ShowDefineables,
+ BuildFlag_ExportDefineables,
+
BuildFlag_Vet,
BuildFlag_VetShadowing,
BuildFlag_VetUnused,
+ BuildFlag_VetUnusedImports,
+ BuildFlag_VetUnusedVariables,
+ BuildFlag_VetUnusedProcedures,
BuildFlag_VetUsingStmt,
BuildFlag_VetUsingParam,
BuildFlag_VetStyle,
BuildFlag_VetSemicolon,
+ BuildFlag_VetCast,
+ BuildFlag_VetTabs,
+ BuildFlag_VetPackages,
+ BuildFlag_CustomAttribute,
BuildFlag_IgnoreUnknownAttributes,
BuildFlag_ExtraLinkerFlags,
BuildFlag_ExtraAssemblerFlags,
BuildFlag_Microarch,
BuildFlag_TargetFeatures,
+ BuildFlag_StrictTargetFeatures,
BuildFlag_MinimumOSVersion,
BuildFlag_NoThreadLocal,
BuildFlag_RelocMode,
BuildFlag_DisableRedZone,
- BuildFlag_TestName,
-
BuildFlag_DisallowDo,
BuildFlag_DefaultToNilAllocator,
+ BuildFlag_DefaultToPanicAllocator,
BuildFlag_StrictStyle,
BuildFlag_ForeignErrorProcedures,
BuildFlag_NoRTTI,
BuildFlag_DynamicMapCalls,
+ BuildFlag_ObfuscateSourceCodeLocations,
BuildFlag_Compact,
BuildFlag_GlobalDefinitions,
@@ -290,12 +385,23 @@ enum BuildFlagKind {
BuildFlag_WarningsAsErrors,
BuildFlag_TerseErrors,
BuildFlag_VerboseErrors,
+ BuildFlag_JsonErrors,
BuildFlag_ErrorPosStyle,
BuildFlag_MaxErrorCount,
+ BuildFlag_MinLinkLibs,
+
+ BuildFlag_PrintLinkerFlags,
+
// internal use only
+ BuildFlag_InternalFastISel,
BuildFlag_InternalIgnoreLazy,
BuildFlag_InternalIgnoreLLVMBuild,
+ BuildFlag_InternalIgnorePanic,
+ BuildFlag_InternalModulePerFile,
+ BuildFlag_InternalCached,
+ BuildFlag_InternalNoInline,
+ BuildFlag_InternalByValue,
BuildFlag_Tilde,
@@ -308,7 +414,6 @@ enum BuildFlagKind {
BuildFlag_Subsystem,
#endif
-
BuildFlag_COUNT,
};
@@ -328,12 +433,12 @@ struct BuildFlag {
String name;
BuildFlagParamKind param_kind;
u32 command_support;
- bool allow_mulitple;
+ bool allow_multiple;
};
-gb_internal void add_flag(Array<BuildFlag> *build_flags, BuildFlagKind kind, String name, BuildFlagParamKind param_kind, u32 command_support, bool allow_mulitple=false) {
- BuildFlag flag = {kind, name, param_kind, command_support, allow_mulitple};
+gb_internal void add_flag(Array<BuildFlag> *build_flags, BuildFlagKind kind, String name, BuildFlagParamKind param_kind, u32 command_support, bool allow_multiple=false) {
+ BuildFlag flag = {kind, name, param_kind, command_support, allow_multiple};
array_add(build_flags, flag);
}
@@ -409,12 +514,14 @@ gb_internal bool parse_build_flags(Array<String> args) {
auto build_flags = array_make<BuildFlag>(heap_allocator(), 0, BuildFlag_COUNT);
add_flag(&build_flags, BuildFlag_Help, str_lit("help"), BuildFlagParam_None, Command_all);
add_flag(&build_flags, BuildFlag_SingleFile, str_lit("file"), BuildFlagParam_None, Command__does_build | Command__does_check);
- add_flag(&build_flags, BuildFlag_OutFile, str_lit("out"), BuildFlagParam_String, Command__does_build | Command_test);
+ add_flag(&build_flags, BuildFlag_OutFile, str_lit("out"), BuildFlagParam_String, Command__does_build | Command_test | Command_doc);
add_flag(&build_flags, BuildFlag_OptimizationMode, str_lit("o"), BuildFlagParam_String, Command__does_build);
add_flag(&build_flags, BuildFlag_ShowTimings, str_lit("show-timings"), BuildFlagParam_None, Command__does_check);
add_flag(&build_flags, BuildFlag_ShowMoreTimings, str_lit("show-more-timings"), BuildFlagParam_None, Command__does_check);
add_flag(&build_flags, BuildFlag_ExportTimings, str_lit("export-timings"), BuildFlagParam_String, Command__does_check);
add_flag(&build_flags, BuildFlag_ExportTimingsFile, str_lit("export-timings-file"), BuildFlagParam_String, Command__does_check);
+ add_flag(&build_flags, BuildFlag_ExportDependencies, str_lit("export-dependencies"), BuildFlagParam_String, Command__does_build);
+ add_flag(&build_flags, BuildFlag_ExportDependenciesFile, str_lit("export-dependencies-file"), BuildFlagParam_String, Command__does_build);
add_flag(&build_flags, BuildFlag_ShowUnused, str_lit("show-unused"), BuildFlagParam_None, Command_check);
add_flag(&build_flags, BuildFlag_ShowUnusedWithLocation, str_lit("show-unused-with-location"), BuildFlagParam_None, Command_check);
add_flag(&build_flags, BuildFlag_ShowSystemCalls, str_lit("show-system-calls"), BuildFlagParam_None, Command_all);
@@ -428,37 +535,51 @@ gb_internal bool parse_build_flags(Array<String> args) {
add_flag(&build_flags, BuildFlag_Debug, str_lit("debug"), BuildFlagParam_None, Command__does_check);
add_flag(&build_flags, BuildFlag_DisableAssert, str_lit("disable-assert"), BuildFlagParam_None, Command__does_check);
add_flag(&build_flags, BuildFlag_NoBoundsCheck, str_lit("no-bounds-check"), BuildFlagParam_None, Command__does_check);
+ 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_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);
add_flag(&build_flags, BuildFlag_UseLLD, str_lit("lld"), BuildFlagParam_None, Command__does_build);
+ add_flag(&build_flags, BuildFlag_UseRADLink, str_lit("radlink"), BuildFlagParam_None, Command__does_build);
+ add_flag(&build_flags, BuildFlag_Linker, str_lit("linker"), BuildFlagParam_String, Command__does_build);
add_flag(&build_flags, BuildFlag_UseSeparateModules, str_lit("use-separate-modules"), BuildFlagParam_None, Command__does_build);
add_flag(&build_flags, BuildFlag_NoThreadedChecker, str_lit("no-threaded-checker"), BuildFlagParam_None, Command__does_check);
add_flag(&build_flags, BuildFlag_ShowDebugMessages, str_lit("show-debug-messages"), BuildFlagParam_None, Command_all);
+ add_flag(&build_flags, BuildFlag_ShowDefineables, str_lit("show-defineables"), BuildFlagParam_None, Command__does_check);
+ add_flag(&build_flags, BuildFlag_ExportDefineables, str_lit("export-defineables"), BuildFlagParam_String, Command__does_check);
+
add_flag(&build_flags, BuildFlag_Vet, str_lit("vet"), BuildFlagParam_None, Command__does_check);
add_flag(&build_flags, BuildFlag_VetUnused, str_lit("vet-unused"), BuildFlagParam_None, Command__does_check);
+ add_flag(&build_flags, BuildFlag_VetUnusedVariables, str_lit("vet-unused-variables"), BuildFlagParam_None, Command__does_check);
+ add_flag(&build_flags, BuildFlag_VetUnusedProcedures, str_lit("vet-unused-procedures"), BuildFlagParam_None, Command__does_check);
+ add_flag(&build_flags, BuildFlag_VetUnusedImports, str_lit("vet-unused-imports"), BuildFlagParam_None, Command__does_check);
add_flag(&build_flags, BuildFlag_VetShadowing, str_lit("vet-shadowing"), BuildFlagParam_None, Command__does_check);
add_flag(&build_flags, BuildFlag_VetUsingStmt, str_lit("vet-using-stmt"), BuildFlagParam_None, Command__does_check);
add_flag(&build_flags, BuildFlag_VetUsingParam, str_lit("vet-using-param"), BuildFlagParam_None, Command__does_check);
add_flag(&build_flags, BuildFlag_VetStyle, str_lit("vet-style"), BuildFlagParam_None, Command__does_check);
add_flag(&build_flags, BuildFlag_VetSemicolon, str_lit("vet-semicolon"), BuildFlagParam_None, Command__does_check);
+ add_flag(&build_flags, BuildFlag_VetCast, str_lit("vet-cast"), BuildFlagParam_None, Command__does_check);
+ add_flag(&build_flags, BuildFlag_VetTabs, str_lit("vet-tabs"), BuildFlagParam_None, Command__does_check);
+ add_flag(&build_flags, BuildFlag_VetPackages, str_lit("vet-packages"), BuildFlagParam_String, Command__does_check);
+ add_flag(&build_flags, BuildFlag_CustomAttribute, str_lit("custom-attribute"), BuildFlagParam_String, Command__does_check, true);
add_flag(&build_flags, BuildFlag_IgnoreUnknownAttributes, str_lit("ignore-unknown-attributes"), BuildFlagParam_None, Command__does_check);
add_flag(&build_flags, BuildFlag_ExtraLinkerFlags, str_lit("extra-linker-flags"), BuildFlagParam_String, Command__does_build);
add_flag(&build_flags, BuildFlag_ExtraAssemblerFlags, str_lit("extra-assembler-flags"), BuildFlagParam_String, Command__does_build);
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_RelocMode, str_lit("reloc-mode"), BuildFlagParam_String, Command__does_build);
add_flag(&build_flags, BuildFlag_DisableRedZone, str_lit("disable-red-zone"), BuildFlagParam_None, Command__does_build);
- add_flag(&build_flags, BuildFlag_TestName, str_lit("test-name"), BuildFlagParam_String, Command_test);
-
add_flag(&build_flags, BuildFlag_DisallowDo, str_lit("disallow-do"), BuildFlagParam_None, Command__does_check);
add_flag(&build_flags, BuildFlag_DefaultToNilAllocator, str_lit("default-to-nil-allocator"), BuildFlagParam_None, Command__does_check);
+ add_flag(&build_flags, BuildFlag_DefaultToPanicAllocator, str_lit("default-to-panic-allocator"),BuildFlagParam_None, Command__does_check);
add_flag(&build_flags, BuildFlag_StrictStyle, str_lit("strict-style"), BuildFlagParam_None, Command__does_check);
add_flag(&build_flags, BuildFlag_ForeignErrorProcedures, str_lit("foreign-error-procedures"), BuildFlagParam_None, Command__does_check);
@@ -467,19 +588,32 @@ 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_Short, str_lit("short"), BuildFlagParam_None, Command_doc);
- add_flag(&build_flags, BuildFlag_AllPackages, str_lit("all-packages"), 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_DocFormat, str_lit("doc-format"), BuildFlagParam_None, Command_doc);
add_flag(&build_flags, BuildFlag_IgnoreWarnings, str_lit("ignore-warnings"), BuildFlagParam_None, Command_all);
add_flag(&build_flags, BuildFlag_WarningsAsErrors, str_lit("warnings-as-errors"), BuildFlagParam_None, Command_all);
add_flag(&build_flags, BuildFlag_TerseErrors, str_lit("terse-errors"), BuildFlagParam_None, Command_all);
add_flag(&build_flags, BuildFlag_VerboseErrors, str_lit("verbose-errors"), BuildFlagParam_None, Command_all);
+ add_flag(&build_flags, BuildFlag_JsonErrors, str_lit("json-errors"), BuildFlagParam_None, Command_all);
add_flag(&build_flags, BuildFlag_ErrorPosStyle, str_lit("error-pos-style"), BuildFlagParam_String, Command_all);
add_flag(&build_flags, BuildFlag_MaxErrorCount, str_lit("max-error-count"), BuildFlagParam_Integer, Command_all);
+ add_flag(&build_flags, BuildFlag_MinLinkLibs, str_lit("min-link-libs"), BuildFlagParam_None, Command__does_build);
+
+ add_flag(&build_flags, BuildFlag_PrintLinkerFlags, str_lit("print-linker-flags"), BuildFlagParam_None, Command_build);
+
+ add_flag(&build_flags, BuildFlag_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);
+ add_flag(&build_flags, BuildFlag_InternalIgnorePanic, str_lit("internal-ignore-panic"), BuildFlagParam_None, Command_all);
+ add_flag(&build_flags, BuildFlag_InternalModulePerFile, str_lit("internal-module-per-file"), BuildFlagParam_None, Command_all);
+ add_flag(&build_flags, BuildFlag_InternalCached, str_lit("internal-cached"), BuildFlagParam_None, Command_all);
+ add_flag(&build_flags, BuildFlag_InternalNoInline, str_lit("internal-no-inline"), BuildFlagParam_None, Command_all);
+ add_flag(&build_flags, BuildFlag_InternalByValue, str_lit("internal-by-value"), BuildFlagParam_None, Command_all);
#if ALLOW_TILDE
add_flag(&build_flags, BuildFlag_Tilde, str_lit("tilde"), BuildFlagParam_None, Command__does_build);
@@ -487,6 +621,7 @@ gb_internal bool parse_build_flags(Array<String> args) {
add_flag(&build_flags, BuildFlag_Sanitize, str_lit("sanitize"), BuildFlagParam_String, Command__does_build, true);
+
#if defined(GB_SYSTEM_WINDOWS)
add_flag(&build_flags, BuildFlag_IgnoreVsSearch, str_lit("ignore-vs-search"), BuildFlagParam_None, Command__does_build);
add_flag(&build_flags, BuildFlag_ResourceFile, str_lit("resource"), BuildFlagParam_String, Command__does_build);
@@ -731,6 +866,53 @@ gb_internal bool parse_build_flags(Array<String> args) {
break;
}
+ case BuildFlag_ExportDependencies: {
+ GB_ASSERT(value.kind == ExactValue_String);
+
+ if (value.value_string == "make") {
+ build_context.export_dependencies_format = DependenciesExportMake;
+ } else if (value.value_string == "json") {
+ build_context.export_dependencies_format = DependenciesExportJson;
+ } else {
+ gb_printf_err("Invalid export format for -export-dependencies:<string>, got %.*s\n", LIT(value.value_string));
+ gb_printf_err("Valid export formats:\n");
+ gb_printf_err("\tmake\n");
+ gb_printf_err("\tjson\n");
+ bad_flags = true;
+ }
+ break;
+ }
+ case BuildFlag_ExportDependenciesFile: {
+ GB_ASSERT(value.kind == ExactValue_String);
+
+ String export_path = string_trim_whitespace(value.value_string);
+ if (is_build_flag_path_valid(export_path)) {
+ build_context.export_dependencies_file = path_to_full_path(heap_allocator(), export_path);
+ } else {
+ gb_printf_err("Invalid -export-dependencies path, got %.*s\n", LIT(export_path));
+ bad_flags = true;
+ }
+
+ break;
+ }
+ case BuildFlag_ShowDefineables: {
+ GB_ASSERT(value.kind == ExactValue_Invalid);
+ build_context.show_defineables = true;
+ break;
+ }
+ case BuildFlag_ExportDefineables: {
+ GB_ASSERT(value.kind == ExactValue_String);
+
+ String export_path = string_trim_whitespace(value.value_string);
+ if (is_build_flag_path_valid(export_path)) {
+ build_context.export_defineables_file = path_to_full_path(heap_allocator(), export_path);
+ } else {
+ gb_printf_err("Invalid -export-defineables path, got %.*s\n", LIT(export_path));
+ bad_flags = true;
+ }
+
+ break;
+ }
case BuildFlag_ShowSystemCalls: {
GB_ASSERT(value.kind == ExactValue_Invalid);
build_context.show_system_calls = true;
@@ -802,9 +984,10 @@ gb_internal bool parse_build_flags(Array<String> args) {
}
gbAllocator a = heap_allocator();
- String fullpath = path_to_fullpath(a, path);
- if (!path_is_directory(fullpath)) {
- gb_printf_err("Library collection '%.*s' path must be a directory, got '%.*s'\n", LIT(name), LIT(fullpath));
+ bool path_ok = false;
+ String fullpath = path_to_fullpath(a, path, &path_ok);
+ if (!path_ok || !path_is_directory(fullpath)) {
+ gb_printf_err("Library collection '%.*s' path must be a directory, got '%.*s'\n", LIT(name), LIT(path_ok ? fullpath : path));
gb_free(a, fullpath.text);
bad_flags = true;
break;
@@ -883,27 +1066,29 @@ gb_internal bool parse_build_flags(Array<String> args) {
}
if (!found) {
- struct DistanceAndTargetIndex {
- isize distance;
- isize target_index;
- };
+ if (str != "?") {
+ struct DistanceAndTargetIndex {
+ isize distance;
+ isize target_index;
+ };
- DistanceAndTargetIndex distances[gb_count_of(named_targets)] = {};
- for (isize i = 0; i < gb_count_of(named_targets); i++) {
- distances[i].target_index = i;
- distances[i].distance = levenstein_distance_case_insensitive(str, named_targets[i].name);
- }
- gb_sort_array(distances, gb_count_of(distances), gb_isize_cmp(gb_offset_of(DistanceAndTargetIndex, distance)));
+ DistanceAndTargetIndex distances[gb_count_of(named_targets)] = {};
+ for (isize i = 0; i < gb_count_of(named_targets); i++) {
+ distances[i].target_index = i;
+ distances[i].distance = levenstein_distance_case_insensitive(str, named_targets[i].name);
+ }
+ gb_sort_array(distances, gb_count_of(distances), gb_isize_cmp(gb_offset_of(DistanceAndTargetIndex, distance)));
- gb_printf_err("Unknown target '%.*s'\n", LIT(str));
+ gb_printf_err("Unknown target '%.*s'\n", LIT(str));
- if (distances[0].distance <= MAX_SMALLEST_DID_YOU_MEAN_DISTANCE) {
- gb_printf_err("Did you mean:\n");
- for (isize i = 0; i < gb_count_of(named_targets); i++) {
- if (distances[i].distance > MAX_SMALLEST_DID_YOU_MEAN_DISTANCE) {
- break;
+ if (distances[0].distance <= MAX_SMALLEST_DID_YOU_MEAN_DISTANCE) {
+ gb_printf_err("Did you mean:\n");
+ for (isize i = 0; i < gb_count_of(named_targets); i++) {
+ if (distances[i].distance > MAX_SMALLEST_DID_YOU_MEAN_DISTANCE) {
+ break;
+ }
+ gb_printf_err("\t%.*s\n", LIT(named_targets[distances[i].target_index].name));
}
- gb_printf_err("\t%.*s\n", LIT(named_targets[distances[i].target_index].name));
}
}
gb_printf_err("All supported targets:\n");
@@ -966,20 +1151,27 @@ gb_internal bool parse_build_flags(Array<String> args) {
build_context.build_mode = BuildMode_DynamicLibrary;
} else if (str == "obj" || str == "object") {
build_context.build_mode = BuildMode_Object;
+ } else if (str == "static" || str == "lib") {
+ build_context.build_mode = BuildMode_StaticLibrary;
} else if (str == "exe") {
build_context.build_mode = BuildMode_Executable;
} else if (str == "asm" || str == "assembly" || str == "assembler") {
build_context.build_mode = BuildMode_Assembly;
} else if (str == "llvm" || str == "llvm-ir") {
build_context.build_mode = BuildMode_LLVM_IR;
+ } else if (str == "test") {
+ build_context.build_mode = BuildMode_Executable;
+ build_context.command_kind = Command_test;
} else {
gb_printf_err("Unknown build mode '%.*s'\n", LIT(str));
gb_printf_err("Valid build modes:\n");
gb_printf_err("\tdll, shared, dynamic\n");
+ gb_printf_err("\tlib, static\n");
gb_printf_err("\tobj, object\n");
gb_printf_err("\texe\n");
gb_printf_err("\tasm, assembly, assembler\n");
gb_printf_err("\tllvm, llvm-ir\n");
+ gb_printf_err("\ttest\n");
bad_flags = true;
break;
}
@@ -996,12 +1188,18 @@ gb_internal bool parse_build_flags(Array<String> args) {
case BuildFlag_NoBoundsCheck:
build_context.no_bounds_check = true;
break;
+ case BuildFlag_NoTypeAssert:
+ build_context.no_type_assert = true;
+ break;
case BuildFlag_NoDynamicLiterals:
- build_context.no_dynamic_literals = true;
+ gb_printf_err("Warning: Use of -no-dynamic-literals is now redundant\n");
break;
case BuildFlag_NoCRT:
build_context.no_crt = true;
break;
+ case BuildFlag_NoRPath:
+ build_context.no_rpath = true;
+ break;
case BuildFlag_NoEntryPoint:
build_context.no_entry_point = true;
break;
@@ -1009,15 +1207,44 @@ gb_internal bool parse_build_flags(Array<String> args) {
build_context.no_thread_local = true;
break;
case BuildFlag_UseLLD:
- build_context.use_lld = true;
+ gb_printf_err("Warning: Use of -lld has been deprecated in favour of -linker:lld\n");
+ build_context.linker_choice = Linker_lld;
+ break;
+ case BuildFlag_UseRADLink:
+ gb_printf_err("Warning: Use of -lld has been deprecated in favour of -linker:radlink\n");
+ build_context.linker_choice = Linker_radlink;
break;
+ case BuildFlag_Linker:
+ {
+ GB_ASSERT(value.kind == ExactValue_String);
+ LinkerChoice linker_choice = Linker_Invalid;
+
+ for (i32 i = 0; i < Linker_COUNT; i++) {
+ if (linker_choices[i] == value.value_string) {
+ linker_choice = cast(LinkerChoice)i;
+ break;
+ }
+ }
+
+ if (linker_choice == Linker_Invalid) {
+ gb_printf_err("Invalid option for -linker:<string>. Expected one of the following\n");
+ for (i32 i = 0; i < Linker_COUNT; i++) {
+ gb_printf_err("\t%.*s\n", LIT(linker_choices[i]));
+ }
+ bad_flags = true;
+ } else {
+ build_context.linker_choice = linker_choice;
+ }
+ }
+ break;
+
+
case BuildFlag_UseSeparateModules:
build_context.use_separate_modules = true;
break;
- case BuildFlag_NoThreadedChecker: {
+ case BuildFlag_NoThreadedChecker:
build_context.no_threaded_checker = true;
break;
- }
case BuildFlag_ShowDebugMessages:
build_context.show_debug_messages = true;
break;
@@ -1025,12 +1252,69 @@ gb_internal bool parse_build_flags(Array<String> args) {
build_context.vet_flags |= VetFlag_All;
break;
- case BuildFlag_VetUnused: build_context.vet_flags |= VetFlag_Unused; break;
- case BuildFlag_VetShadowing: build_context.vet_flags |= VetFlag_Shadowing; break;
- case BuildFlag_VetUsingStmt: build_context.vet_flags |= VetFlag_UsingStmt; break;
- case BuildFlag_VetUsingParam: build_context.vet_flags |= VetFlag_UsingParam; break;
- case BuildFlag_VetStyle: build_context.vet_flags |= VetFlag_Style; break;
- case BuildFlag_VetSemicolon: build_context.vet_flags |= VetFlag_Semicolon; break;
+ case BuildFlag_VetUnusedVariables: build_context.vet_flags |= VetFlag_UnusedVariables; break;
+ case BuildFlag_VetUnusedImports: build_context.vet_flags |= VetFlag_UnusedImports; break;
+ case BuildFlag_VetUnused: build_context.vet_flags |= VetFlag_Unused; break;
+ case BuildFlag_VetShadowing: build_context.vet_flags |= VetFlag_Shadowing; break;
+ case BuildFlag_VetUsingStmt: build_context.vet_flags |= VetFlag_UsingStmt; break;
+ case BuildFlag_VetUsingParam: build_context.vet_flags |= VetFlag_UsingParam; break;
+ case BuildFlag_VetStyle: build_context.vet_flags |= VetFlag_Style; break;
+ case BuildFlag_VetSemicolon: build_context.vet_flags |= VetFlag_Semicolon; break;
+ case BuildFlag_VetCast: build_context.vet_flags |= VetFlag_Cast; break;
+ case BuildFlag_VetTabs: build_context.vet_flags |= VetFlag_Tabs; break;
+ case BuildFlag_VetUnusedProcedures:
+ build_context.vet_flags |= VetFlag_UnusedProcedures;
+ if (!set_flags[BuildFlag_VetPackages]) {
+ gb_printf_err("-%.*s must be used with -vet-packages\n", LIT(name));
+ bad_flags = true;
+ }
+ break;
+
+ case BuildFlag_VetPackages:
+ {
+ GB_ASSERT(value.kind == ExactValue_String);
+ String val = value.value_string;
+ String_Iterator it = {val, 0};
+ for (;;) {
+ String pkg = string_split_iterator(&it, ',');
+ if (pkg.len == 0) {
+ break;
+ }
+
+ pkg = string_trim_whitespace(pkg);
+ if (!string_is_valid_identifier(pkg)) {
+ gb_printf_err("-%.*s '%.*s' must be a valid identifier\n", LIT(name), LIT(pkg));
+ bad_flags = true;
+ continue;
+ }
+
+ string_set_add(&build_context.vet_packages, pkg);
+ }
+ }
+ break;
+
+ case BuildFlag_CustomAttribute:
+ {
+ GB_ASSERT(value.kind == ExactValue_String);
+ String val = value.value_string;
+ String_Iterator it = {val, 0};
+ for (;;) {
+ String attr = string_split_iterator(&it, ',');
+ if (attr.len == 0) {
+ break;
+ }
+
+ attr = string_trim_whitespace(attr);
+ if (!string_is_valid_identifier(attr)) {
+ gb_printf_err("-%.*s '%.*s' must be a valid identifier\n", LIT(name), LIT(attr));
+ bad_flags = true;
+ continue;
+ }
+
+ string_set_add(&build_context.custom_attributes, attr);
+ }
+ }
+ break;
case BuildFlag_IgnoreUnknownAttributes:
build_context.ignore_unknown_attributes = true;
@@ -1055,9 +1339,13 @@ gb_internal bool parse_build_flags(Array<String> args) {
string_to_lower(&build_context.target_features_string);
break;
}
+ case BuildFlag_StrictTargetFeatures:
+ build_context.strict_target_features = true;
+ break;
case BuildFlag_MinimumOSVersion: {
GB_ASSERT(value.kind == ExactValue_String);
build_context.minimum_os_version_string = value.value_string;
+ build_context.minimum_os_version_string_given = true;
break;
}
case BuildFlag_RelocMode: {
@@ -1085,21 +1373,6 @@ gb_internal bool parse_build_flags(Array<String> args) {
case BuildFlag_DisableRedZone:
build_context.disable_red_zone = true;
break;
- case BuildFlag_TestName: {
- GB_ASSERT(value.kind == ExactValue_String);
- {
- String name = value.value_string;
- if (!string_is_valid_identifier(name)) {
- gb_printf_err("Test name '%.*s' must be a valid identifier\n", LIT(name));
- bad_flags = true;
- break;
- }
- string_set_add(&build_context.test_names, name);
-
- // NOTE(bill): Allow for multiple -test-name
- continue;
- }
- }
case BuildFlag_DisallowDo:
build_context.disallow_do = true;
break;
@@ -1113,9 +1386,26 @@ gb_internal bool parse_build_flags(Array<String> args) {
case BuildFlag_DynamicMapCalls:
build_context.dynamic_map_calls = true;
break;
+
+ case BuildFlag_ObfuscateSourceCodeLocations:
+ build_context.obfuscate_source_code_locations = true;
+ break;
+
case BuildFlag_DefaultToNilAllocator:
+ if (build_context.ODIN_DEFAULT_TO_PANIC_ALLOCATOR) {
+ gb_printf_err("'-default-to-panic-allocator' cannot be used with '-default-to-nil-allocator'\n");
+ bad_flags = true;
+ }
build_context.ODIN_DEFAULT_TO_NIL_ALLOCATOR = true;
break;
+ case BuildFlag_DefaultToPanicAllocator:
+ if (build_context.ODIN_DEFAULT_TO_NIL_ALLOCATOR) {
+ gb_printf_err("'-default-to-nil-allocator' cannot be used with '-default-to-panic-allocator'\n");
+ bad_flags = true;
+ }
+ build_context.ODIN_DEFAULT_TO_PANIC_ALLOCATOR = true;
+ break;
+
case BuildFlag_ForeignErrorProcedures:
build_context.ODIN_FOREIGN_ERROR_PROCEDURES = true;
break;
@@ -1127,6 +1417,7 @@ gb_internal bool parse_build_flags(Array<String> args) {
break;
case BuildFlag_AllPackages:
build_context.cmd_doc_flags |= CmdDocFlag_AllPackages;
+ build_context.test_all_packages = true;
break;
case BuildFlag_DocFormat:
build_context.cmd_doc_flags |= CmdDocFlag_DocFormat;
@@ -1152,10 +1443,16 @@ gb_internal bool parse_build_flags(Array<String> args) {
case BuildFlag_TerseErrors:
build_context.hide_error_line = true;
+ build_context.terse_errors = true;
break;
case BuildFlag_VerboseErrors:
- gb_printf_err("-verbose-errors is not the default, -terse-errors can now disable it\n");
+ gb_printf_err("-verbose-errors is now the default, -terse-errors can disable it\n");
build_context.hide_error_line = false;
+ build_context.terse_errors = false;
+ break;
+
+ case BuildFlag_JsonErrors:
+ build_context.json_errors = true;
break;
case BuildFlag_ErrorPosStyle:
@@ -1182,12 +1479,41 @@ gb_internal bool parse_build_flags(Array<String> args) {
break;
}
+ case BuildFlag_MinLinkLibs:
+ build_context.min_link_libs = true;
+ break;
+
+ case BuildFlag_PrintLinkerFlags:
+ build_context.print_linker_flags = true;
+ break;
+
+ case BuildFlag_InternalFastISel:
+ build_context.fast_isel = true;
+ break;
case BuildFlag_InternalIgnoreLazy:
build_context.ignore_lazy = true;
break;
case BuildFlag_InternalIgnoreLLVMBuild:
build_context.ignore_llvm_build = true;
break;
+ case BuildFlag_InternalIgnorePanic:
+ build_context.ignore_panic = true;
+ break;
+ case BuildFlag_InternalModulePerFile:
+ build_context.module_per_file = true;
+ build_context.use_separate_modules = true;
+ break;
+ case BuildFlag_InternalCached:
+ build_context.cached = true;
+ build_context.use_separate_modules = true;
+ break;
+ case BuildFlag_InternalNoInline:
+ build_context.internal_no_inline = true;
+ break;
+ case BuildFlag_InternalByValue:
+ build_context.internal_by_value = true;
+ break;
+
case BuildFlag_Tilde:
build_context.tilde_backend = true;
break;
@@ -1207,6 +1533,7 @@ gb_internal bool parse_build_flags(Array<String> args) {
}
break;
+
#if defined(GB_SYSTEM_WINDOWS)
case BuildFlag_IgnoreVsSearch: {
GB_ASSERT(value.kind == ExactValue_Invalid);
@@ -1218,8 +1545,9 @@ gb_internal bool parse_build_flags(Array<String> args) {
String path = value.value_string;
path = string_trim_whitespace(path);
if (is_build_flag_path_valid(path)) {
- if(!string_ends_with(path, str_lit(".rc"))) {
- gb_printf_err("Invalid -resource path %.*s, missing .rc\n", LIT(path));
+ bool is_resource = string_ends_with(path, str_lit(".rc")) || string_ends_with(path, str_lit(".res"));
+ if(!is_resource) {
+ gb_printf_err("Invalid -resource path %.*s, missing .rc or .res file\n", LIT(path));
bad_flags = true;
break;
} else if (!gb_file_exists((const char *)path.text)) {
@@ -1260,16 +1588,43 @@ gb_internal bool parse_build_flags(Array<String> args) {
}
case BuildFlag_Subsystem: {
+ // TODO(Jeroen): Parse optional "[,major[.minor]]"
+
GB_ASSERT(value.kind == ExactValue_String);
String subsystem = value.value_string;
- if (str_eq_ignore_case(subsystem, str_lit("console"))) {
- build_context.use_subsystem_windows = false;
- } else if (str_eq_ignore_case(subsystem, str_lit("window"))) {
- build_context.use_subsystem_windows = true;
- } else if (str_eq_ignore_case(subsystem, str_lit("windows"))) {
- build_context.use_subsystem_windows = true;
- } else {
- gb_printf_err("Invalid -subsystem string, got %.*s, expected either 'console' or 'windows'\n", LIT(subsystem));
+ bool subsystem_found = false;
+ for (int i = 0; i < Windows_Subsystem_COUNT; i++) {
+ if (str_eq_ignore_case(subsystem, windows_subsystem_names[i])) {
+ build_context.ODIN_WINDOWS_SUBSYSTEM = windows_subsystem_names[i];
+ subsystem_found = true;
+ break;
+ }
+ }
+
+ // WINDOW is a hidden alias for WINDOWS. Check it.
+ String subsystem_windows_alias = str_lit("WINDOW");
+ if (!subsystem_found && str_eq_ignore_case(subsystem, subsystem_windows_alias)) {
+ build_context.ODIN_WINDOWS_SUBSYSTEM = windows_subsystem_names[Windows_Subsystem_WINDOWS];
+ subsystem_found = true;
+ break;
+ }
+
+ if (!subsystem_found) {
+ gb_printf_err("Invalid -subsystem string, got %.*s. Expected one of:\n", LIT(subsystem));
+ gb_printf_err("\t");
+ for (int i = 0; i < Windows_Subsystem_COUNT; i++) {
+ if (i > 0) {
+ gb_printf_err(", ");
+ }
+ gb_printf_err("%.*s", LIT(windows_subsystem_names[i]));
+ if (i == Windows_Subsystem_CONSOLE) {
+ gb_printf_err(" (default)");
+ }
+ if (i == Windows_Subsystem_WINDOWS) {
+ gb_printf_err(" (or WINDOW)");
+ }
+ }
+ gb_printf_err("\n");
bad_flags = true;
}
break;
@@ -1279,7 +1634,7 @@ gb_internal bool parse_build_flags(Array<String> args) {
}
}
- if (!bf.allow_mulitple) {
+ if (!bf.allow_multiple) {
set_flags[bf.kind] = ok;
}
}
@@ -1320,6 +1675,16 @@ gb_internal bool parse_build_flags(Array<String> args) {
gb_printf_err("`-export-timings:<format>` requires `-show-timings` or `-show-more-timings` to be present\n");
bad_flags = true;
}
+
+
+ if (build_context.export_dependencies_format != DependenciesExportUnspecified && build_context.print_linker_flags) {
+ gb_printf_err("-export-dependencies cannot be used with -print-linker-flags\n");
+ bad_flags = true;
+ } else if (build_context.show_timings && build_context.print_linker_flags) {
+ gb_printf_err("-show-timings/-show-more-timings cannot be used with -print-linker-flags\n");
+ bad_flags = true;
+ }
+
return !bad_flags;
}
@@ -1345,7 +1710,7 @@ gb_internal void timings_export_all(Timings *t, Checker *c, bool timings_are_fin
gbFileError err = gb_file_open_mode(&f, gbFileMode_Write, fileName);
if (err != gbFileError_None) {
gb_printf_err("Failed to export timings to: %s\n", fileName);
- gb_exit(1);
+ exit_with_errors();
return;
} else {
gb_printf("\nExporting timings to '%s'... ", fileName);
@@ -1418,6 +1783,129 @@ gb_internal void timings_export_all(Timings *t, Checker *c, bool timings_are_fin
gb_printf("Done.\n");
}
+gb_internal void check_defines(BuildContext *bc, Checker *c) {
+ for (auto const &entry : bc->defined_values) {
+ String name = make_string_c(entry.key);
+ ExactValue value = entry.value;
+ GB_ASSERT(value.kind != ExactValue_Invalid);
+
+ bool found = false;
+ for_array(i, c->info.defineables) {
+ Defineable *def = &c->info.defineables[i];
+ if (def->name == name) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ ERROR_BLOCK();
+ warning(nullptr, "given -define:%.*s is unused in the project", LIT(name));
+ error_line("\tSuggestion: use the -show-defineables flag for an overview of the possible defines\n");
+ }
+ }
+}
+
+gb_internal void temp_alloc_defineable_strings(Checker *c) {
+ for_array(i, c->info.defineables) {
+ Defineable *def = &c->info.defineables[i];
+ def->default_value_str = make_string_c(write_exact_value_to_string(gb_string_make(temporary_allocator(), ""), def->default_value));
+ def->pos_str = make_string_c(token_pos_to_string(def->pos));
+ }
+}
+
+gb_internal GB_COMPARE_PROC(defineables_cmp) {
+ Defineable *x = (Defineable *)a;
+ Defineable *y = (Defineable *)b;
+
+ int cmp = 0;
+
+ String x_file = get_file_path_string(x->pos.file_id);
+ String y_file = get_file_path_string(y->pos.file_id);
+ cmp = string_compare(x_file, y_file);
+ if (cmp) {
+ return cmp;
+ }
+
+ return i32_cmp(x->pos.offset, y->pos.offset);
+}
+
+gb_internal void sort_defineables_and_remove_duplicates(Checker *c) {
+ if (c->info.defineables.count == 0) {
+ return;
+ }
+ gb_sort_array(c->info.defineables.data, c->info.defineables.count, defineables_cmp);
+
+ Defineable prev = c->info.defineables[0];
+ for (isize i = 1; i < c->info.defineables.count; ) {
+ Defineable curr = c->info.defineables[i];
+ if (prev.pos == curr.pos) {
+ array_ordered_remove(&c->info.defineables, i);
+ continue;
+ }
+ prev = curr;
+ i++;
+ }
+}
+
+gb_internal void export_defineables(Checker *c, String path) {
+ gbFile f = {};
+ gbFileError err = gb_file_open_mode(&f, gbFileMode_Write, (char *)path.text);
+ if (err != gbFileError_None) {
+ gb_printf_err("Failed to export defineables to: %.*s\n", LIT(path));
+ gb_exit(1);
+ return;
+ } else {
+ gb_printf("Exporting defineables to '%.*s'...\n", LIT(path));
+ }
+ defer (gb_file_close(&f));
+
+ gbString docs = gb_string_make(heap_allocator(), "");
+ defer (gb_string_free(docs));
+
+ gb_fprintf(&f, "Defineable,Default Value,Docs,Location\n");
+ for_array(i, c->info.defineables) {
+ Defineable *def = &c->info.defineables[i];
+
+ gb_string_clear(docs);
+ if (def->docs) {
+ docs = gb_string_appendc(docs, "\"");
+ for (Token const &token : def->docs->list) {
+ for (isize i = 0; i < token.string.len; i++) {
+ u8 c = token.string.text[i];
+ if (c == '"') {
+ docs = gb_string_appendc(docs, "\"\"");
+ } else {
+ docs = gb_string_append_length(docs, &c, 1);
+ }
+ }
+ }
+ docs = gb_string_appendc(docs, "\"");
+ }
+
+ gb_fprintf(&f,"%.*s,%.*s,%s,%.*s\n", LIT(def->name), LIT(def->default_value_str), docs, LIT(def->pos_str));
+ }
+}
+
+gb_internal void show_defineables(Checker *c) {
+ for_array(i, c->info.defineables) {
+ Defineable *def = &c->info.defineables[i];
+ if (has_ansi_terminal_colours()) {
+ gb_printf("\x1b[0;90m");
+ }
+ printf("%.*s\n", LIT(def->pos_str));
+ if (def->docs) {
+ for (Token const &token : def->docs->list) {
+ gb_printf("%.*s\n", LIT(token.string));
+ }
+ }
+ if (has_ansi_terminal_colours()) {
+ gb_printf("\x1b[0m");
+ }
+ gb_printf("%.*s :: %.*s\n\n", LIT(def->name), LIT(def->default_value_str));
+ }
+}
+
gb_internal void show_timings(Checker *c, Timings *t) {
Parser *p = c->parser;
isize lines = p->total_line_count;
@@ -1546,11 +2034,123 @@ gb_internal void show_timings(Checker *c, Timings *t) {
}
}
+gb_internal GB_COMPARE_PROC(file_path_cmp) {
+ AstFile *x = *(AstFile **)a;
+ AstFile *y = *(AstFile **)b;
+ return string_compare(x->fullpath, y->fullpath);
+}
+
+gb_internal void export_dependencies(Checker *c) {
+ GB_ASSERT(build_context.export_dependencies_format != DependenciesExportUnspecified);
+
+ if (build_context.export_dependencies_file.len <= 0) {
+ gb_printf_err("No dependency file specified with `-export-dependencies-file`\n");
+ exit_with_errors();
+ return;
+ }
+
+ Parser *p = c->parser;
+
+ gbFile f = {};
+ char * fileName = (char *)build_context.export_dependencies_file.text;
+ gbFileError err = gb_file_open_mode(&f, gbFileMode_Write, fileName);
+ if (err != gbFileError_None) {
+ gb_printf_err("Failed to export dependencies to: %s\n", fileName);
+ exit_with_errors();
+ return;
+ }
+ defer (gb_file_close(&f));
+
+
+ auto files = array_make<AstFile *>(heap_allocator());
+ for (AstPackage *pkg : p->packages) {
+ for (AstFile *f : pkg->files) {
+ array_add(&files, f);
+ }
+ }
+ array_sort(files, file_path_cmp);
+
+
+ auto load_files = array_make<LoadFileCache *>(heap_allocator());
+ for (auto const &entry : c->info.load_file_cache) {
+ auto *cache = entry.value;
+ if (!cache || !cache->exists) {
+ continue;
+ }
+ array_add(&load_files, cache);
+ }
+ array_sort(files, file_cache_sort_cmp);
+
+ if (build_context.export_dependencies_format == DependenciesExportMake) {
+ String exe_name = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_Output]);
+ defer (gb_free(heap_allocator(), exe_name.text));
+
+ gb_fprintf(&f, "%.*s:", LIT(exe_name));
+
+ isize current_line_length = exe_name.len + 1;
+
+ for_array(i, files) {
+ AstFile *file = files[i];
+ /* Arbitrary line break value. Maybe make this better? */
+ if (current_line_length >= 80-2) {
+ gb_file_write(&f, " \\\n ", 4);
+ current_line_length = 1;
+ }
+
+ gb_file_write(&f, " ", 1);
+ current_line_length++;
+
+ for (isize k = 0; k < file->fullpath.len; k++) {
+ char part = file->fullpath.text[k];
+ if (part == ' ') {
+ gb_file_write(&f, "\\", 1);
+ current_line_length++;
+ }
+ gb_file_write(&f, &part, 1);
+ current_line_length++;
+ }
+ }
+
+ gb_fprintf(&f, "\n");
+ } else if (build_context.export_dependencies_format == DependenciesExportJson) {
+ gb_fprintf(&f, "{\n");
+
+ gb_fprintf(&f, "\t\"source_files\": [\n");
+
+ for_array(i, files) {
+ AstFile *file = files[i];
+ gb_fprintf(&f, "\t\t\"%.*s\"", LIT(file->fullpath));
+ if (i+1 < files.count) {
+ gb_fprintf(&f, ",");
+ }
+ gb_fprintf(&f, "\n");
+ }
+
+ gb_fprintf(&f, "\t],\n");
+
+ gb_fprintf(&f, "\t\"load_files\": [\n");
+
+ for_array(i, load_files) {
+ LoadFileCache *cache = load_files[i];
+ gb_fprintf(&f, "\t\t\"%.*s\"", LIT(cache->path));
+ if (i+1 < load_files.count) {
+ gb_fprintf(&f, ",");
+ }
+ gb_fprintf(&f, "\n");
+ }
+
+ gb_fprintf(&f, "\t]\n");
+
+ gb_fprintf(&f, "}\n");
+ }
+}
+
gb_internal void remove_temp_files(lbGenerator *gen) {
if (build_context.keep_temp_files) return;
switch (build_context.build_mode) {
case BuildMode_Executable:
+ case BuildMode_StaticLibrary:
case BuildMode_DynamicLibrary:
break;
@@ -1569,6 +2169,7 @@ gb_internal void remove_temp_files(lbGenerator *gen) {
if (!build_context.keep_object_files) {
switch (build_context.build_mode) {
case BuildMode_Executable:
+ case BuildMode_StaticLibrary:
case BuildMode_DynamicLibrary:
for (String const &path : gen->output_object_paths) {
gb_file_remove(cast(char const *)path.text);
@@ -1579,46 +2180,53 @@ gb_internal void remove_temp_files(lbGenerator *gen) {
}
-gb_internal void print_show_help(String const arg0, String const &command) {
- print_usage_line(0, "%.*s is a tool for managing Odin source code", LIT(arg0));
+gb_internal void print_show_help(String const arg0, String command, String optional_flag = {}) {
+ 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, ""));
+
if (command == "build") {
- print_usage_line(1, "build Compile directory of .odin files as an executable.");
+ 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.");
print_usage_line(2, "Examples:");
- print_usage_line(3, "odin build . # Build package in current directory");
- print_usage_line(3, "odin build <dir> # Build package in <dir>");
- print_usage_line(3, "odin build filename.odin -file # Build single-file package, must contain entry point.");
+ print_usage_line(3, "odin build . Builds package in current directory.");
+ 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_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:");
- print_usage_line(3, "odin run . # Build and run package in current directory");
- print_usage_line(3, "odin run <dir> # Build and run package in <dir>");
- print_usage_line(3, "odin run filename.odin -file # Build and run single-file package, must contain entry point.");
+ print_usage_line(3, "odin run . Builds and runs package in current directory.");
+ 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_line(1, "check Parse and type check directory of .odin files");
+ 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 check package in current directory");
- print_usage_line(3, "odin check <dir> # Type check package in <dir>");
- print_usage_line(3, "odin check filename.odin -file # Type check single-file package, must contain entry point.");
+ 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_line(1, "test Build and runs procedures with the attribute @(test) in the initial package");
+ print_usage_line(1, "test Builds and runs procedures with the attribute @(test) in the initial package.");
} else if (command == "doc") {
- print_usage_line(1, "doc generate documentation from a directory of .odin files");
+ print_usage_line(1, "doc Generates documentation from a directory of .odin files.");
print_usage_line(2, "Examples:");
- print_usage_line(3, "odin doc . # Generate documentation on package in current directory");
- print_usage_line(3, "odin doc <dir> # Generate documentation on package in <dir>");
- print_usage_line(3, "odin doc filename.odin -file # Generate documentation on single-file package.");
+ 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_line(1, "version print version");
+ print_usage_line(1, "version Prints version.");
} else if (command == "strip-semicolon") {
print_usage_line(1, "strip-semicolon");
- print_usage_line(2, "Parse and type check .odin file(s) and then remove unneeded semicolons from the entire project");
+ print_usage_line(2, "Parses and type checks .odin file(s) and then removes unneeded semicolons from the entire project.");
}
bool doc = command == "doc";
@@ -1629,356 +2237,560 @@ gb_internal void print_show_help(String const arg0, String const &command) {
bool check_only = command == "check" || strip_semicolon;
bool check = run_or_build || check_only;
+ if (command == "help") {
+ doc = true;
+ build = true;
+ run_or_build = true;
+ test_only = true;
+ strip_semicolon = true;
+ check_only = true;
+ check = true;
+ }
+
print_usage_line(0, "");
print_usage_line(1, "Flags");
print_usage_line(0, "");
- if (check) {
- print_usage_line(1, "-file");
- print_usage_line(2, "Tells `%.*s %.*s` to treat the given file as a self-contained package.", LIT(arg0), LIT(command));
- print_usage_line(2, "This means that `<dir>/a.odin` won't have access to `<dir>/b.odin`'s contents.");
+
+
+ auto const print_flag = [&optional_flag](char const *flag) -> bool {
+ if (optional_flag.len != 0) {
+ String f = make_string_c(flag);
+ isize i = string_index_byte(f, ':');
+ if (i >= 0) {
+ f.len = i;
+ }
+ if (optional_flag != f) {
+ return false;
+ }
+ }
print_usage_line(0, "");
- }
+ print_usage_line(1, flag);
+ return true;
+ };
+
if (doc) {
- print_usage_line(1, "-short");
- print_usage_line(2, "Show shortened documentation for the packages");
- print_usage_line(0, "");
+ if (print_flag("-all-packages")) {
+ print_usage_line(2, "Generates documentation for all packages used in the current project.");
+ }
+ }
+ if (test_only) {
+ if (print_flag("-all-packages")) {
+ print_usage_line(2, "Tests all packages imported into the given initial package.");
+ }
+ }
- print_usage_line(1, "-all-packages");
- print_usage_line(2, "Generates documentation for all packages used in the current project");
- print_usage_line(0, "");
+ if (build) {
+ 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(1, "-doc-format");
- print_usage_line(2, "Generates documentation as the .odin-doc format (useful for external tooling)");
- print_usage_line(0, "");
+ if (check) {
+ if (print_flag("-collection:<name>=<filepath>")) {
+ 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\"");
+ }
+
+ 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");
+ }
}
if (run_or_build) {
- print_usage_line(1, "-out:<filepath>");
- print_usage_line(2, "Set the file name of the outputted executable");
- print_usage_line(2, "Example: -out:foo.exe");
- print_usage_line(0, "");
-
- print_usage_line(1, "-o:<string>");
- print_usage_line(2, "Set the optimization mode for compilation");
- if (LB_USE_NEW_PASS_SYSTEM) {
- print_usage_line(2, "Accepted values: none, minimal, size, speed, aggressive");
- } else {
- print_usage_line(2, "Accepted values: none, minimal, size, speed");
+ if (print_flag("-debug")) {
+ print_usage_line(2, "Enables debug information, and defines the global constant ODIN_DEBUG to be 'true'.");
}
- print_usage_line(2, "Example: -o:speed");
- print_usage_line(2, "The default is -o:minimal");
- print_usage_line(0, "");
}
if (check) {
- print_usage_line(1, "-show-timings");
- print_usage_line(2, "Shows basic overview of the timings of different stages within the compiler in milliseconds");
- print_usage_line(0, "");
-
- print_usage_line(1, "-show-more-timings");
- print_usage_line(2, "Shows an advanced overview of the timings of different stages within the compiler in milliseconds");
- print_usage_line(0, "");
+ if (print_flag("-default-to-nil-allocator")) {
+ print_usage_line(2, "Sets the default allocator to be the nil_allocator, an allocator which does nothing.");
+ }
- print_usage_line(1, "-show-system-calls");
- print_usage_line(2, "Prints the whole command and arguments for calls to external tools like linker and assembler");
- print_usage_line(0, "");
+ if (print_flag("-define:<name>=<value>")) {
+ 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(1, "-export-timings:<format>");
- print_usage_line(2, "Export 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 Export compile time stats to JSON");
- print_usage_line(3, "-export-timings:csv Export compile time stats to CSV");
- print_usage_line(0, "");
+ if (run_or_build) {
+ if (print_flag("-disable-assert")) {
+ print_usage_line(2, "Disables the code generation of the built-in run-time 'assert' procedure, and defines the global constant ODIN_DISABLE_ASSERT to be 'true'.");
+ }
- print_usage_line(1, "-export-timings-file:<filename>");
- print_usage_line(2, "Specify the filename for `-export-timings`");
- print_usage_line(2, "Example: -export-timings-file:timings.json");
- print_usage_line(0, "");
+ if (print_flag("-disable-red-zone")) {
+ print_usage_line(2, "Disables red zone on a supported freestanding target.");
+ }
+ }
- print_usage_line(1, "-thread-count:<integer>");
- print_usage_line(2, "Override the number of threads the compiler will use to compile with");
- print_usage_line(2, "Example: -thread-count:2");
- print_usage_line(0, "");
+ if (check) {
+ if (print_flag("-disallow-do")) {
+ print_usage_line(2, "Disallows the 'do' keyword in the project.");
+ }
}
- if (check_only) {
- print_usage_line(1, "-show-unused");
- print_usage_line(2, "Shows unused package declarations within the current project");
- print_usage_line(0, "");
- print_usage_line(1, "-show-unused-with-location");
- print_usage_line(2, "Shows unused package declarations within the current project with the declarations source location");
- print_usage_line(0, "");
+ if (doc) {
+ if (print_flag("-doc-format")) {
+ print_usage_line(2, "Generates documentation as the .odin-doc format (useful for external tooling).");
+ }
}
if (run_or_build) {
- print_usage_line(1, "-keep-temp-files");
- print_usage_line(2, "Keeps the temporary files generated during compilation");
- print_usage_line(0, "");
- } else if (strip_semicolon) {
- print_usage_line(1, "-keep-temp-files");
- print_usage_line(2, "Keeps the temporary files generated during stripping the unneeded semicolons from files");
- print_usage_line(0, "");
+ if (print_flag("-dynamic-map-calls")) {
+ print_usage_line(2, "Uses dynamic map calls to minimize code generation at the cost of runtime execution.");
+ }
}
if (check) {
- print_usage_line(1, "-collection:<name>=<filepath>");
- 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(0, "");
+ 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(1, "-define:<name>=<value>");
- 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, "To use: #config(SPAM, default_value)");
- print_usage_line(0, "");
+ if (print_flag("-export-defineables:<filename>")) {
+ print_usage_line(2, "Exports an overview of all the #config/#defined usages in CSV format to the given file path.");
+ print_usage_line(2, "Example: -export-defineables:defineables.csv");
+ }
+
+ 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");
+ }
+
+ if (print_flag("-export-dependencies-file:<filename>")) {
+ print_usage_line(2, "Specifies the filename for `-export-dependencies`.");
+ print_usage_line(2, "Example: -export-dependencies-file:dependencies.d");
+ }
+
+ 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.");
+ }
+
+ if (print_flag("-export-timings-file:<filename>")) {
+ print_usage_line(2, "Specifies the filename for `-export-timings`.");
+ print_usage_line(2, "Example: -export-timings-file:timings.json");
+ }
}
- if (build) {
- print_usage_line(1, "-build-mode:<mode>");
- print_usage_line(2, "Sets the build mode");
- print_usage_line(2, "Available options:");
- print_usage_line(3, "-build-mode:exe Build as an executable");
- print_usage_line(3, "-build-mode:dll Build as a dynamically linked library");
- print_usage_line(3, "-build-mode:shared Build as a dynamically linked library");
- print_usage_line(3, "-build-mode:obj Build as an object file");
- print_usage_line(3, "-build-mode:object Build as an object file");
- print_usage_line(3, "-build-mode:assembly Build as an assembly file");
- print_usage_line(3, "-build-mode:assembler Build as an assembly file");
- print_usage_line(3, "-build-mode:asm Build as an assembly file");
- print_usage_line(3, "-build-mode:llvm-ir Build as an LLVM IR file");
- print_usage_line(3, "-build-mode:llvm Build as an LLVM IR file");
- print_usage_line(0, "");
+ if (run_or_build) {
+ if (print_flag("-extra-assembler-flags:<string>")) {
+ print_usage_line(2, "Adds extra assembler specific flags in a string.");
+ }
+
+ if (print_flag("-extra-linker-flags:<string>")) {
+ print_usage_line(2, "Adds extra linker specific flags in a string.");
+ }
}
if (check) {
- print_usage_line(1, "-target:<string>");
- print_usage_line(2, "Sets the target for the executable to be built in");
- print_usage_line(0, "");
+ if (print_flag("-file")) {
+ print_usage_line(2, "Tells `%.*s %.*s` to treat the given file as a self-contained package.", LIT(arg0), LIT(command));
+ print_usage_line(2, "This means that `<dir>/a.odin` won't have access to `<dir>/b.odin`'s contents.");
+ }
+
+ if (print_flag("-foreign-error-procedures")) {
+ print_usage_line(2, "States that the error procedures used in the runtime are defined in a separate translation unit.");
+ }
+
+ if (print_flag("-ignore-unknown-attributes")) {
+ print_usage_line(2, "Ignores unknown attributes.");
+ print_usage_line(2, "This can be used with metaprogramming tools.");
+ }
}
if (run_or_build) {
- print_usage_line(1, "-debug");
- print_usage_line(2, "Enabled debug information, and defines the global constant ODIN_DEBUG to be 'true'");
- print_usage_line(0, "");
+ #if defined(GB_SYSTEM_WINDOWS)
+ if (print_flag("-ignore-vs-search")) {
+ print_usage_line(2, "[Windows only]");
+ print_usage_line(2, "Ignores the Visual Studio search for library paths.");
+ }
+ #endif
+ }
- print_usage_line(1, "-disable-assert");
- print_usage_line(2, "Disable the code generation of the built-in run-time 'assert' procedure, and defines the global constant ODIN_DISABLE_ASSERT to be 'true'");
- print_usage_line(0, "");
+ if (check) {
+ if (print_flag("-ignore-warnings")) {
+ print_usage_line(2, "Ignores warning messages.");
+ }
- print_usage_line(1, "-no-bounds-check");
- print_usage_line(2, "Disables bounds checking program wide");
- print_usage_line(0, "");
+ if (print_flag("-json-errors")) {
+ print_usage_line(2, "Prints the error messages as json to stderr.");
+ }
+ }
- print_usage_line(1, "-no-crt");
- print_usage_line(2, "Disables automatic linking with the C Run Time");
- print_usage_line(0, "");
+ if (run_or_build) {
+ if (print_flag("-keep-temp-files")) {
+ print_usage_line(2, "Keeps the temporary files generated during compilation.");
+ }
+ } else if (strip_semicolon) {
+ if (print_flag("-keep-temp-files")) {
+ print_usage_line(2, "Keeps the temporary files generated during stripping the unneeded semicolons from files.");
+ }
+ }
- print_usage_line(1, "-no-thread-local");
- print_usage_line(2, "Ignore @thread_local attribute, effectively treating the program as if it is single-threaded");
- print_usage_line(0, "");
+ if (run_or_build) {
+ if (print_flag("-linker:<string>")) {
+ print_usage_line(2, "Specify the linker to use.");
+ print_usage_line(2, "Choices:");
+ for (i32 i = 0; i < Linker_COUNT; i++) {
+ print_usage_line(3, "%.*s", LIT(linker_choices[i]));
+ }
+ }
- print_usage_line(1, "-lld");
- print_usage_line(2, "Use the LLD linker rather than the default");
- print_usage_line(0, "");
+ if (print_flag("-lld")) {
+ print_usage_line(2, "Uses the LLD linker rather than the default.");
+ }
+ }
- print_usage_line(1, "-use-separate-modules");
- print_usage_line(1, "[EXPERIMENTAL]");
- print_usage_line(2, "The backend generates multiple build units which are then linked together");
- print_usage_line(2, "Normally, a single build unit is generated for a standard project");
- print_usage_line(0, "");
+ if (check) {
+ if (print_flag("-max-error-count:<integer>")) {
+ print_usage_line(2, "Sets the maximum number of errors that can be displayed before the compiler terminates.");
+ print_usage_line(2, "Must be an integer >0.");
+ print_usage_line(2, "If not set, the default max error count is %d.", DEFAULT_MAX_ERROR_COLLECTOR_COUNT);
+ }
+ }
+ if (run_or_build) {
+ 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");
+ }
}
if (check) {
- print_usage_line(1, "-no-threaded-checker");
- print_usage_line(2, "Disabled multithreading in the semantic checker stage");
- print_usage_line(0, "");
+ if (print_flag("-min-link-libs")) {
+ print_usage_line(2, "If set, the number of linked libraries will be minimized to prevent duplications.");
+ print_usage_line(2, "This is useful for so called \"dumb\" linkers compared to \"smart\" linkers.");
+ }
}
- if (check) {
- print_usage_line(1, "-vet");
- print_usage_line(2, "Do extra checks on the code");
- print_usage_line(2, "Extra checks include:");
- print_usage_line(2, "-vet-unused");
- print_usage_line(2, "-vet-shadowing");
- print_usage_line(2, "-vet-using-stmt");
- print_usage_line(0, "");
+ if (run_or_build) {
+ 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(1, "-vet-unused");
- print_usage_line(2, "Checks for unused declarations");
- print_usage_line(0, "");
+ if (print_flag("-no-bounds-check")) {
+ print_usage_line(2, "Disables bounds checking program wide.");
+ }
- print_usage_line(1, "-vet-shadowing");
- print_usage_line(2, "Checks for variable shadowing within procedures");
- print_usage_line(0, "");
+ if (print_flag("-no-crt")) {
+ print_usage_line(2, "Disables automatic linking with the C Run Time.");
+ }
+ }
- print_usage_line(1, "-vet-using-stmt");
- print_usage_line(2, "Checks for the use of 'using' as a statement");
- print_usage_line(2, "'using' is considered bad practice outside of immediate refactoring");
- print_usage_line(0, "");
+ if (check && command != "test") {
+ if (print_flag("-no-entry-point")) {
+ print_usage_line(2, "Removes default requirement of an entry point (e.g. main procedure).");
+ }
+ }
- print_usage_line(1, "-vet-using-param");
- print_usage_line(2, "Checks for the use of 'using' on procedure parameters");
- print_usage_line(2, "'using' is considered bad practice outside of immediate refactoring");
- print_usage_line(0, "");
+ if (run_or_build) {
+ if (print_flag("-no-rpath")) {
+ print_usage_line(2, "Disables automatic addition of an rpath linked to the executable directory.");
+ }
- print_usage_line(1, "-vet-style");
- 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, "Does not err on unneeded tokens (unlike -strict-style)");
- print_usage_line(0, "");
+ if (print_flag("-no-thread-local")) {
+ print_usage_line(2, "Ignores @thread_local attribute, effectively treating the program as if it is single-threaded.");
+ }
- print_usage_line(1, "-vet-semicolon");
- print_usage_line(2, "Errs on unneeded semicolons");
- print_usage_line(0, "");
+ if (print_flag("-no-threaded-checker")) {
+ print_usage_line(2, "Disables multithreading in the semantic checker stage.");
+ }
+
+ if (print_flag("-no-type-assert")) {
+ print_usage_line(2, "Disables type assertion checking program wide.");
+ }
}
- if (check) {
- print_usage_line(1, "-ignore-unknown-attributes");
- print_usage_line(2, "Ignores unknown attributes");
- print_usage_line(2, "This can be used with metaprogramming tools");
- print_usage_line(0, "");
+ if (run_or_build) {
+ 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");
+ if (LB_USE_NEW_PASS_SYSTEM) {
+ print_usage_line(3, "-o:aggressive (use this with caution)");
+ }
+ print_usage_line(2, "The default is -o:minimal.");
+ }
- if (command != "test") {
- print_usage_line(1, "-no-entry-point");
- print_usage_line(2, "Removes default requirement of an entry point (e.g. main procedure)");
- print_usage_line(0, "");
+
+ 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("-out:<filepath>")) {
+ print_usage_line(2, "Sets the file name of the outputted executable.");
+ print_usage_line(2, "Example: -out:foo.exe");
}
}
- if (test_only) {
- print_usage_line(1, "-test-name:<string>");
- print_usage_line(2, "Run specific test only by name");
- print_usage_line(0, "");
+ 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, "The extension can be optionally included; the resulting file will always have an extension of '.odin-doc'.");
+ print_usage_line(2, "Example: -out:foo");
+ }
}
if (run_or_build) {
- print_usage_line(1, "-minimum-os-version:<string>");
- print_usage_line(2, "Sets the minimum OS version targeted by the application");
- print_usage_line(2, "e.g. -minimum-os-version:12.0.0");
- print_usage_line(2, "(Only used when target is Darwin)");
- print_usage_line(0, "");
+ #if defined(GB_SYSTEM_WINDOWS)
+ if (print_flag("-pdb-name:<filepath>")) {
+ print_usage_line(2, "[Windows only]");
+ print_usage_line(2, "Defines the generated PDB name when -debug is enabled.");
+ print_usage_line(2, "Example: -pdb-name:different.pdb");
+ }
+ #endif
+ }
- print_usage_line(1, "-extra-linker-flags:<string>");
- print_usage_line(2, "Adds extra linker specific flags in a string");
- print_usage_line(0, "");
+ if (build) {
+ if (print_flag("-print-linker-flags")) {
+ print_usage_line(2, "Prints the all of the flags/arguments that will be passed to the linker.");
+ }
+ }
- print_usage_line(1, "-extra-assembler-flags:<string>");
- print_usage_line(2, "Adds extra assembler specific flags in a string");
- print_usage_line(0, "");
+ if (run_or_build) {
+ if (print_flag("-radlink")) {
+ print_usage_line(2, "Uses the RAD linker rather than the default.");
+ }
- print_usage_line(1, "-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(0, "");
+ 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(1, "-reloc-mode:<string>");
- print_usage_line(2, "Specifies the reloc mode");
- print_usage_line(2, "Options:");
- print_usage_line(3, "default");
- print_usage_line(3, "static");
- print_usage_line(3, "pic");
- print_usage_line(3, "dynamic-no-pic");
- print_usage_line(0, "");
+ #if defined(GB_SYSTEM_WINDOWS)
+ if (print_flag("-resource:<filepath>")) {
+ print_usage_line(2, "[Windows only]");
+ print_usage_line(2, "Defines the resource file for the executable.");
+ print_usage_line(2, "Example: -resource:path/to/file.rc");
+ print_usage_line(2, "or: -resource:path/to/file.res for a precompiled one.");
+ }
+ #endif
- print_usage_line(1, "-disable-red-zone");
- print_usage_line(2, "Disable red zone on a supported freestanding target");
- print_usage_line(0, "");
+ 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(1, "-dynamic-map-calls");
- print_usage_line(2, "Use dynamic map calls to minimize code generation at the cost of runtime execution");
- print_usage_line(0, "");
+ if (doc) {
+ if (print_flag("-short")) {
+ print_usage_line(2, "Shows shortened documentation for the packages.");
+ }
}
if (check) {
- print_usage_line(1, "-disallow-do");
- print_usage_line(2, "Disallows the 'do' keyword in the project");
- print_usage_line(0, "");
+ if (print_flag("-show-defineables")) {
+ print_usage_line(2, "Shows an overview of all the #config/#defined usages in the project.");
+ }
- print_usage_line(1, "-default-to-nil-allocator");
- print_usage_line(2, "Sets the default allocator to be the nil_allocator, an allocator which does nothing");
- print_usage_line(0, "");
+ if (print_flag("-show-system-calls")) {
+ print_usage_line(2, "Prints the whole command and arguments for calls to external tools like linker and assembler.");
+ }
- print_usage_line(1, "-strict-style");
- 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(0, "");
+ if (print_flag("-show-timings")) {
+ print_usage_line(2, "Shows basic overview of the timings of different stages within the compiler in milliseconds.");
+ }
- print_usage_line(1, "-ignore-warnings");
- print_usage_line(2, "Ignores warning messages");
- print_usage_line(0, "");
+ if (print_flag("-show-more-timings")) {
+ print_usage_line(2, "Shows an advanced overview of the timings of different stages within the compiler in milliseconds.");
+ }
+ }
- print_usage_line(1, "-warnings-as-errors");
- print_usage_line(2, "Treats warning messages as error messages");
- print_usage_line(0, "");
+ if (check_only) {
+ if (print_flag("-show-unused")) {
+ print_usage_line(2, "Shows unused package declarations within the current project.");
+ }
+ if (print_flag("-show-unused-with-location")) {
+ print_usage_line(2, "Shows unused package declarations within the current project with the declarations source location.");
+ }
+ }
- print_usage_line(1, "-terse-errors");
- print_usage_line(2, "Prints a terse error message without showing the code on that line and the location in that line");
- print_usage_line(0, "");
+ if (check) {
+ if (print_flag("-strict-style")) {
+ print_usage_line(2, "This enforces parts of same style as the Odin compiler, prefer '-vet-style -vet-semicolon' if you do not want to match it exactly.");
+ print_usage_line(2, "");
+ 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 'case' labels are not in the same column as the associated 'switch' token.");
+ }
+ }
- print_usage_line(1, "-error-pos-style:<string>");
- print_usage_line(2, "Options are 'unix', 'odin' and 'default' (odin)");
- print_usage_line(2, "'odin' file/path(45:3)");
- print_usage_line(2, "'unix' file/path:45:3:");
- print_usage_line(0, "");
+ if (run_or_build) {
+ if (print_flag("-strict-target-features")) {
+ print_usage_line(2, "Makes @(enable_target_features=\"...\") behave the same way as @(require_target_features=\"...\").");
+ print_usage_line(2, "This enforces that all generated code uses features supported by the combination of -target, -microarch, and -target-features.");
+ }
+ #if defined(GB_SYSTEM_WINDOWS)
+ if (print_flag("-subsystem:<option>")) {
+ 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");
+ }
+ #endif
- print_usage_line(1, "-max-error-count:<integer>");
- print_usage_line(2, "Set the maximum number of errors that can be displayed before the compiler terminates");
- print_usage_line(2, "Must be an integer >0");
- print_usage_line(2, "If not set, the default max error count is %d", DEFAULT_MAX_ERROR_COLLECTOR_COUNT);
- print_usage_line(0, "");
+ 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(1, "-foreign-error-procedures");
- print_usage_line(2, "States that the error procedues used in the runtime are defined in a separate translation unit");
- print_usage_line(0, "");
+ if (check) {
+ if (print_flag("-target:<string>")) {
+ print_usage_line(2, "Sets the target for the executable to be built in.");
+ }
+ if (print_flag("-terse-errors")) {
+ print_usage_line(2, "Prints a terse error message without showing the code on that line and the location in that line.");
+ }
+
+ if (print_flag("-thread-count:<integer>")) {
+ print_usage_line(2, "Overrides the number of threads the compiler will use to compile with.");
+ print_usage_line(2, "Example: -thread-count:2");
+ }
}
if (run_or_build) {
- print_usage_line(1, "-sanitize:<string>");
- print_usage_line(1, "Enables sanitization analysis");
- print_usage_line(1, "Options are 'address', 'memory', and 'thread'");
- print_usage_line(1, "NOTE: This flag can be used multiple times");
- print_usage_line(0, "");
+ if (print_flag("-use-separate-modules")) {
+ print_usage_line(2, "The backend generates multiple build units which are then linked together.");
+ print_usage_line(2, "Normally, a single build unit is generated for a standard project.");
+ print_usage_line(2, "This is the default behaviour on Windows for '-o:none' and '-o:minimal' builds.");
+ }
}
- if (run_or_build) {
- #if defined(GB_SYSTEM_WINDOWS)
- print_usage_line(1, "-ignore-vs-search");
- print_usage_line(2, "[Windows only]");
- print_usage_line(2, "Ignores the Visual Studio search for library paths");
- print_usage_line(0, "");
- print_usage_line(1, "-resource:<filepath>");
- print_usage_line(2, "[Windows only]");
- print_usage_line(2, "Defines the resource file for the executable");
- print_usage_line(2, "Example: -resource:path/to/file.rc");
- print_usage_line(0, "");
+ if (check) {
+ 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(1, "-pdb-name:<filepath>");
- print_usage_line(2, "[Windows only]");
- print_usage_line(2, "Defines the generated PDB name when -debug is enabled");
- print_usage_line(2, "Example: -pdb-name:different.pdb");
- print_usage_line(0, "");
+ if (print_flag("-vet-cast")) {
+ print_usage_line(2, "Errs on casting a value to its own type or using `transmute` rather than `cast`.");
+ }
- print_usage_line(1, "-subsystem:<option>");
- 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, "console");
- print_usage_line(3, "windows");
- print_usage_line(0, "");
+ if (print_flag("-vet-packages:<comma-separated-strings>")) {
+ print_usage_line(2, "Sets which packages by name will be vetted.");
+ print_usage_line(2, "Files with specific +vet tags will not be ignored if they are not in the packages set.");
+ }
- #endif
+ if (print_flag("-vet-semicolon")) {
+ print_usage_line(2, "Errs on unneeded semicolons.");
+ }
+
+
+ if (print_flag("-vet-shadowing")) {
+ print_usage_line(2, "Checks for variable shadowing within procedures.");
+ }
+
+ if (print_flag("-vet-style")) {
+ 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, "Does not err on unneeded tokens (unlike -strict-style).");
+ }
+
+
+ if (print_flag("-vet-tabs")) {
+ print_usage_line(2, "Errs when the use of tabs has not been used for indentation.");
+ }
+
+
+ if (print_flag("-vet-unused")) {
+ print_usage_line(2, "Checks for unused declarations (variables and imports).");
+ }
+
+ if (print_flag("-vet-unused-imports")) {
+ print_usage_line(2, "Checks for unused import declarations.");
+ }
+
+ if (print_flag("-vet-unused-procedures")) {
+ print_usage_line(2, "Checks for unused procedures.");
+ print_usage_line(2, "Must be used with -vet-packages or specified on a per file with +vet tags.");
+ }
+
+ if (print_flag("-vet-unused-variables")) {
+ print_usage_line(2, "Checks for unused variable declarations.");
+ }
+
+
+ if (print_flag("-vet-using-param")) {
+ print_usage_line(2, "Checks for the use of 'using' on procedure parameters.");
+ print_usage_line(2, "'using' is considered bad practice outside of immediate refactoring.");
+ }
+
+ if (print_flag("-vet-using-stmt")) {
+ print_usage_line(2, "Checks for the use of 'using' as a statement.");
+ print_usage_line(2, "'using' is considered bad practice outside of immediate refactoring.");
+ }
+ }
+
+ if (check) {
+ if (print_flag("-warnings-as-errors")) {
+ print_usage_line(2, "Treats warning messages as error messages.");
+ }
}
}
@@ -2027,7 +2839,7 @@ gb_internal void print_show_unused(Checker *c) {
array_add(&unused, e);
}
- gb_sort_array(unused.data, unused.count, cmp_entities_for_printing);
+ array_sort(unused, cmp_entities_for_printing);
print_usage_line(0, "Unused Package Declarations");
@@ -2328,13 +3140,22 @@ int main(int arg_count, char const **arg_ptr) {
TIME_SECTION("init default library collections");
array_init(&library_collections, heap_allocator());
// NOTE(bill): 'core' cannot be (re)defined by the user
- add_library_collection(str_lit("core"), get_fullpath_relative(heap_allocator(), odin_root_dir(), str_lit("core")));
- add_library_collection(str_lit("vendor"), get_fullpath_relative(heap_allocator(), odin_root_dir(), str_lit("vendor")));
+
+ auto const &add_collection = [](String const &name) {
+ bool ok = false;
+ add_library_collection(name, get_fullpath_relative(heap_allocator(), odin_root_dir(), name, &ok));
+ if (!ok) {
+ compiler_error("Cannot find the library collection '%.*s'. Is the ODIN_ROOT set up correctly?", LIT(name));
+ }
+ };
+
+ add_collection(str_lit("base"));
+ add_collection(str_lit("core"));
+ add_collection(str_lit("vendor"));
TIME_SECTION("init args");
map_init(&build_context.defined_values);
build_context.extra_packages.allocator = heap_allocator();
- string_set_init(&build_context.test_names);
Array<String> args = setup_args(arg_count, arg_ptr);
@@ -2357,16 +3178,26 @@ int main(int arg_count, char const **arg_ptr) {
Array<String> run_args = array_make<String>(heap_allocator(), 0, arg_count);
defer (array_free(&run_args));
+ isize run_args_start_idx = -1;
for_array(i, args) {
if (args[i] == "--") {
- last_non_run_arg = i;
- }
- if (i <= last_non_run_arg) {
- continue;
+ run_args_start_idx = i;
+ break;
}
- array_add(&run_args, args[i]);
}
+ if (run_args_start_idx != -1) {
+ last_non_run_arg = run_args_start_idx;
+ if (run_args_start_idx == 2) {
+ // missing src path on argv[2], invocation: odin [run|test] --
+ usage(args[0]);
+ return 1;
+ }
+
+ for(isize i = run_args_start_idx+1; i < args.count; ++i) {
+ array_add(&run_args, args[i]);
+ }
+ }
args = array_slice(args, 0, last_non_run_arg);
run_args_string = string_join_and_quote(heap_allocator(), run_args);
@@ -2448,11 +3279,20 @@ int main(int arg_count, char const **arg_ptr) {
usage(args[0]);
return 1;
} else {
- print_show_help(args[0], args[2]);
+ print_show_help(args[0], args[1], args[2]);
return 0;
}
+ } else if (command == "root") {
+ gb_printf("%.*s", LIT(odin_root_dir()));
+ return 0;
+ } else if (command == "clear-cache") {
+ return try_clear_cache() ? 0 : 1;
} else {
- usage(args[0]);
+ String argv1 = {};
+ if (args.count > 1) {
+ argv1 = args[1];
+ }
+ usage(args[0], argv1);
return 1;
}
@@ -2491,12 +3331,19 @@ int main(int arg_count, char const **arg_ptr) {
gb_printf_err("Expected either a directory or a .odin file, got '%.*s'\n", LIT(init_filename));
return 1;
}
+ if (!gb_file_exists(cast(const char*)init_filename.text)) {
+ gb_printf_err("The file '%.*s' was not found.\n", LIT(init_filename));
+ return 1;
+ }
}
}
}
build_context.command = command;
+ string_set_init(&build_context.custom_attributes);
+ string_set_init(&build_context.vet_packages);
+
if (!parse_build_flags(args)) {
return 1;
}
@@ -2509,7 +3356,7 @@ int main(int arg_count, char const **arg_ptr) {
// NOTE(bill): add 'shared' directory if it is not already set
if (!find_library_collection_path(str_lit("shared"), nullptr)) {
add_library_collection(str_lit("shared"),
- get_fullpath_relative(heap_allocator(), odin_root_dir(), str_lit("shared")));
+ get_fullpath_relative(heap_allocator(), odin_root_dir(), str_lit("shared"), nullptr));
}
init_build_context(selected_target_metrics ? selected_target_metrics->metrics : nullptr, selected_subtarget);
@@ -2518,12 +3365,132 @@ int main(int arg_count, char const **arg_ptr) {
// return 1;
// }
+ // Check chosen microarchitecture. If not found or ?, print list.
+ bool print_microarch_list = true;
+ if (build_context.microarch.len == 0 || build_context.microarch == str_lit("native")) {
+ // Autodetect, no need to print list.
+ print_microarch_list = false;
+ } else {
+ String march_list = target_microarch_list[build_context.metrics.arch];
+ String_Iterator it = {march_list, 0};
+ for (;;) {
+ String str = string_split_iterator(&it, ',');
+ if (str == "") break;
+ if (str == build_context.microarch) {
+ // Found matching microarch
+ print_microarch_list = false;
+ break;
+ }
+ }
+ }
+
// Set and check build paths...
if (!init_build_paths(init_filename)) {
return 1;
}
+ String default_march = get_default_microarchitecture();
+ if (print_microarch_list) {
+ if (build_context.microarch != "?") {
+ gb_printf("Unknown microarchitecture '%.*s'.\n", LIT(build_context.microarch));
+ }
+ gb_printf("Possible -microarch values for target %.*s are:\n", LIT(target_arch_names[build_context.metrics.arch]));
+ gb_printf("\n");
+
+ String march_list = target_microarch_list[build_context.metrics.arch];
+ String_Iterator it = {march_list, 0};
+
+ for (;;) {
+ String str = string_split_iterator(&it, ',');
+ if (str == "") break;
+ if (str == default_march) {
+ gb_printf("\t%.*s (default)\n", LIT(str));
+ } else {
+ gb_printf("\t%.*s\n", LIT(str));
+ }
+ }
+ return 0;
+ }
+
+ String march = get_final_microarchitecture();
+ String default_features = get_default_features();
+ {
+ String_Iterator it = {default_features, 0};
+ for (;;) {
+ String str = string_split_iterator(&it, ',');
+ if (str == "") break;
+ string_set_add(&build_context.target_features_set, str);
+ }
+ }
+
+ #if defined(GB_CPU_X86)
+ // We've detected that the CPU doesn't support popcnt, or another reason to use `-microarch:native`,
+ // and that no custom microarch was chosen.
+ if (should_use_march_native() && march == get_default_microarchitecture()) {
+ if (command == "run" || command == "test") {
+ gb_printf_err("Error: Try using '-microarch:native' as Odin defaults to %.*s (close to Nehalem), and your CPU seems to be older.\n", LIT(march));
+ gb_exit(1);
+ } else if (command == "build") {
+ gb_printf("Suggestion: Try using '-microarch:native' as Odin defaults to %.*s (close to Nehalem), and your CPU seems to be older.\n", LIT(march));
+ }
+ }
+ #endif
+
+ if (build_context.target_features_string.len != 0) {
+ String_Iterator target_it = {build_context.target_features_string, 0};
+ for (;;) {
+ String item = string_split_iterator(&target_it, ',');
+ if (item == "") break;
+
+ String invalid;
+ if (!check_target_feature_is_valid_for_target_arch(item, &invalid) && item != str_lit("help")) {
+ if (item != str_lit("?")) {
+ gb_printf_err("Unkown target feature '%.*s'.\n", LIT(invalid));
+ }
+ gb_printf("Possible -target-features for target %.*s are:\n", LIT(target_arch_names[build_context.metrics.arch]));
+ gb_printf("\n");
+
+ String feature_list = target_features_list[build_context.metrics.arch];
+ String_Iterator it = {feature_list, 0};
+ for (;;) {
+ String str = string_split_iterator(&it, ',');
+ if (str == "") break;
+ if (check_single_target_feature_is_valid(default_features, str)) {
+ if (has_ansi_terminal_colours()) {
+ gb_printf("\t%.*s\x1b[38;5;244m (implied by target microarch %.*s)\x1b[0m\n", LIT(str), LIT(march));
+ } else {
+ gb_printf("\t%.*s (implied by current microarch %.*s)\n", LIT(str), LIT(march));
+ }
+ } else {
+ gb_printf("\t%.*s\n", LIT(str));
+ }
+ }
+
+ return 1;
+ }
+
+ string_set_add(&build_context.target_features_set, item);
+ }
+ }
+
+ // NOTE(laytan): on riscv64 we want to enforce some features.
+ if (build_context.metrics.arch == TargetArch_riscv64) {
+ String disabled;
+ if (!check_target_feature_is_enabled(str_lit("64bit,f,d,m"), &disabled)) { // 64bit, floats, doubles, integer multiplication.
+ gb_printf_err("missing required target feature: \"%.*s\", enable it by setting a different -microarch or explicitly adding it through -target-features\n", LIT(disabled));
+ gb_exit(1);
+ }
+
+ // NOTE(laytan): some weird errors on LLVM 14 that LLVM 17 fixes.
+ if (LLVM_VERSION_MAJOR < 17) {
+ gb_printf_err("Invalid LLVM version %s, RISC-V targets require at least LLVM 17\n", LLVM_VERSION_STRING);
+ gb_exit(1);
+ }
+ }
+
if (build_context.show_debug_messages) {
+ debugf("Selected microarch: %.*s\n", LIT(march));
+ debugf("Default microarch features: %.*s\n", LIT(default_features));
for_array(i, build_context.build_paths) {
String build_path = path_to_string(heap_allocator(), build_context.build_paths[i]);
debugf("build_paths[%ld]: %.*s\n", i, LIT(build_path));
@@ -2540,6 +3507,7 @@ int main(int arg_count, char const **arg_ptr) {
Parser *parser = gb_alloc_item(permanent_allocator(), Parser);
Checker *checker = gb_alloc_item(permanent_allocator(), Checker);
+ bool failed_to_cache_parsing = false;
MAIN_TIME_SECTION("parse files");
@@ -2551,23 +3519,50 @@ int main(int arg_count, char const **arg_ptr) {
// TODO(jeroen): Remove the `init_filename` param.
// Let's put that on `build_context.build_paths[0]` instead.
if (parse_packages(parser, init_filename) != ParseFile_None) {
- return 1;
+ GB_ASSERT_MSG(any_errors(), "parse_packages failed but no error was reported.");
+ // We depend on the next conditional block to return 1, after printing errors.
}
if (any_errors()) {
+ print_all_errors();
return 1;
}
- MAIN_TIME_SECTION("type check");
-
checker->parser = parser;
init_checker(checker);
- defer (destroy_checker(checker));
+ defer (destroy_checker(checker)); // this is here because of a `goto`
+ if (build_context.cached && parser->total_seen_load_directive_count.load() == 0) {
+ MAIN_TIME_SECTION("check cached build (pre-semantic check)");
+ if (try_cached_build(checker, args)) {
+ goto end_of_code_gen;
+ }
+ }
+
+ MAIN_TIME_SECTION("type check");
check_parsed_files(checker);
+ check_defines(&build_context, checker);
if (any_errors()) {
+ print_all_errors();
return 1;
}
+ if (any_warnings()) {
+ print_all_errors();
+ }
+
+ if (build_context.show_defineables || build_context.export_defineables_file != "") {
+ TEMPORARY_ALLOCATOR_GUARD();
+ temp_alloc_defineable_strings(checker);
+ sort_defineables_and_remove_duplicates(checker);
+
+ if (build_context.show_defineables) {
+ show_defineables(checker);
+ }
+
+ if (build_context.export_defineables_file != "") {
+ export_defineables(checker, build_context.export_defineables_file);
+ }
+ }
if (build_context.command_kind == Command_strip_semicolon) {
return strip_semicolons(parser);
@@ -2597,6 +3592,14 @@ int main(int arg_count, char const **arg_ptr) {
return 0;
}
+ if (build_context.cached) {
+ MAIN_TIME_SECTION("check cached build");
+ if (try_cached_build(checker, args)) {
+ goto end_of_code_gen;
+ }
+ failed_to_cache_parsing = true;
+ }
+
#if ALLOW_TILDE
if (build_context.tilde_backend) {
LinkerData linker_data = {};
@@ -2607,12 +3610,17 @@ int main(int arg_count, char const **arg_ptr) {
switch (build_context.build_mode) {
case BuildMode_Executable:
+ case BuildMode_StaticLibrary:
case BuildMode_DynamicLibrary:
i32 result = linker_stage(&linker_data);
if (result) {
if (build_context.show_timings) {
show_timings(checker, &global_timings);
}
+
+ if (build_context.export_dependencies_format != DependenciesExportUnspecified) {
+ export_dependencies(checker);
+ }
return result;
}
break;
@@ -2620,20 +3628,30 @@ int main(int arg_count, char const **arg_ptr) {
} else
#endif
{
- MAIN_TIME_SECTION("LLVM API Code Gen");
lbGenerator *gen = gb_alloc_item(permanent_allocator(), lbGenerator);
if (!lb_init_generator(gen, checker)) {
return 1;
}
+
+ gbString label_code_gen = gb_string_make(heap_allocator(), "LLVM API Code Gen");
+ if (gen->modules.count > 1) {
+ label_code_gen = gb_string_append_fmt(label_code_gen, " ( %4td modules )", gen->modules.count);
+ }
+ MAIN_TIME_SECTION_WITH_LEN(label_code_gen, gb_string_length(label_code_gen));
if (lb_generate_code(gen)) {
switch (build_context.build_mode) {
case BuildMode_Executable:
+ case BuildMode_StaticLibrary:
case BuildMode_DynamicLibrary:
i32 result = linker_stage(gen);
if (result) {
if (build_context.show_timings) {
show_timings(checker, &global_timings);
}
+
+ if (build_context.export_dependencies_format != DependenciesExportUnspecified) {
+ export_dependencies(checker);
+ }
return result;
}
break;
@@ -2643,6 +3661,23 @@ int main(int arg_count, char const **arg_ptr) {
remove_temp_files(gen);
}
+end_of_code_gen:;
+
+ if (build_context.export_dependencies_format != DependenciesExportUnspecified) {
+ export_dependencies(checker);
+ }
+
+ if (build_context.cached) {
+ MAIN_TIME_SECTION("write cached build");
+ if (!build_context.build_cache_data.copy_already_done) {
+ try_copy_executable_to_cache();
+ }
+
+ if (failed_to_cache_parsing) {
+ write_cached_build(checker, args);
+ }
+ }
+
if (build_context.show_timings) {
show_timings(checker, &global_timings);
}
@@ -2651,7 +3686,7 @@ int main(int arg_count, char const **arg_ptr) {
String exe_name = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_Output]);
defer (gb_free(heap_allocator(), exe_name.text));
- return system_exec_command_line_app("odin run", "\"%.*s\" %.*s", LIT(exe_name), LIT(run_args_string));
+ system_must_exec_command_line_app("odin run", "\"%.*s\" %.*s", LIT(exe_name), LIT(run_args_string));
}
return 0;
}
diff --git a/src/microsoft_craziness.h b/src/microsoft_craziness.h
index 899583143..b0fd22a23 100644
--- a/src/microsoft_craziness.h
+++ b/src/microsoft_craziness.h
@@ -684,8 +684,14 @@ gb_internal void find_visual_studio_paths_from_env_vars(Find_Result *result) {
? str_lit("lib\\x64\\")
: str_lit("lib\\x86\\");
- result->vs_exe_path = mc_concat(vctid, exe);
- result->vs_library_path = mc_concat(vctid, lib);
+ if (string_ends_with(vctid, str_lit("\\"))) {
+ result->vs_exe_path = mc_concat(vctid, exe);
+ result->vs_library_path = mc_concat(vctid, lib);
+ } else {
+ result->vs_exe_path = mc_concat(vctid, str_lit("\\"), exe);
+ result->vs_library_path = mc_concat(vctid, str_lit("\\"), lib);
+ }
+
vs_found = true;
}
diff --git a/src/parser.cpp b/src/parser.cpp
index c0498b425..03c5a5962 100644
--- a/src/parser.cpp
+++ b/src/parser.cpp
@@ -1,16 +1,37 @@
#include "parser_pos.cpp"
+gb_internal bool in_vet_packages(AstFile *file) {
+ if (file == nullptr) {
+ return true;
+ }
+ if (file->pkg == nullptr) {
+ return true;
+ }
+ if (build_context.vet_packages.entries.count == 0) {
+ return true;
+ }
+ return string_set_exists(&build_context.vet_packages, file->pkg->name);
+}
+
gb_internal u64 ast_file_vet_flags(AstFile *f) {
- if (f->vet_flags_set) {
+ if (f != nullptr && f->vet_flags_set) {
return f->vet_flags;
}
- return build_context.vet_flags;
+
+ bool found = in_vet_packages(f);
+ if (found) {
+ return build_context.vet_flags;
+ }
+ return 0;
}
gb_internal bool ast_file_vet_style(AstFile *f) {
return (ast_file_vet_flags(f) & VetFlag_Style) != 0;
}
+gb_internal bool ast_file_vet_deprecated(AstFile *f) {
+ return (ast_file_vet_flags(f) & VetFlag_Deprecated) != 0;
+}
gb_internal bool file_allow_newline(AstFile *f) {
bool is_strict = build_context.strict_style || ast_file_vet_style(f);
@@ -32,22 +53,48 @@ gb_internal gbString get_file_line_as_string(TokenPos const &pos, i32 *offset_)
if (file == nullptr) {
return nullptr;
}
- isize offset = pos.offset;
-
u8 *start = file->tokenizer.start;
u8 *end = file->tokenizer.end;
if (start == end) {
return nullptr;
}
+
+ isize offset = pos.offset;
+ if (pos.line != 0 && offset == 0) {
+ for (i32 i = 1; i < pos.line; i++) {
+ while (start+offset < end) {
+ u8 c = start[offset++];
+ if (c == '\n') {
+ break;
+ }
+ }
+ }
+ for (i32 i = 1; i < pos.column; i++) {
+ u8 *ptr = start+offset;
+ u8 c = *ptr;
+ if (c & 0x80) {
+ offset += utf8_decode(ptr, end-ptr, nullptr);
+ } else {
+ offset++;
+ }
+ }
+ }
+
+
isize len = end-start;
if (len < offset) {
return nullptr;
}
-
u8 *pos_offset = start+offset;
u8 *line_start = pos_offset;
u8 *line_end = pos_offset;
+
+ if (offset > 0 && *line_start == '\n') {
+ // Prevent an error token that starts at the boundary of a line that
+ // leads to an empty line from advancing off its line.
+ line_start -= 1;
+ }
while (line_start >= start) {
if (*line_start == '\n') {
line_start += 1;
@@ -55,6 +102,11 @@ gb_internal gbString get_file_line_as_string(TokenPos const &pos, i32 *offset_)
}
line_start -= 1;
}
+ if (line_start == start - 1) {
+ // Prevent an error on the first line from stepping behind the boundary
+ // of the text.
+ line_start += 1;
+ }
while (line_end < end) {
if (*line_end == '\n') {
@@ -67,6 +119,7 @@ gb_internal gbString get_file_line_as_string(TokenPos const &pos, i32 *offset_)
if (offset_) *offset_ = cast(i32)(pos_offset - the_line.text);
+
return gb_string_make_length(heap_allocator(), the_line.text, the_line.len);
}
@@ -77,17 +130,17 @@ gb_internal isize ast_node_size(AstKind kind) {
}
-gb_global std::atomic<isize> global_total_node_memory_allocated;
+// gb_global std::atomic<isize> global_total_node_memory_allocated;
// NOTE(bill): And this below is why is I/we need a new language! Discriminated unions are a pain in C/C++
gb_internal Ast *alloc_ast_node(AstFile *f, AstKind kind) {
isize size = ast_node_size(kind);
- Ast *node = cast(Ast *)arena_alloc(&global_thread_local_ast_arena, size, 16);
+ Ast *node = cast(Ast *)arena_alloc(get_arena(ThreadArena_Permanent), size, 16);
node->kind = kind;
node->file_id = f ? f->id : 0;
- global_total_node_memory_allocated.fetch_add(size);
+ // global_total_node_memory_allocated.fetch_add(size);
return node;
}
@@ -230,6 +283,10 @@ gb_internal Ast *clone_ast(Ast *node, AstFile *f) {
case Ast_OrReturnExpr:
n->OrReturnExpr.expr = clone_ast(n->OrReturnExpr.expr, f);
break;
+ case Ast_OrBranchExpr:
+ n->OrBranchExpr.label = clone_ast(n->OrBranchExpr.label, f);
+ n->OrBranchExpr.expr = clone_ast(n->OrBranchExpr.expr, f);
+ break;
case Ast_TypeAssertion:
n->TypeAssertion.expr = clone_ast(n->TypeAssertion.expr, f);
n->TypeAssertion.type = clone_ast(n->TypeAssertion.type, f);
@@ -346,6 +403,11 @@ gb_internal Ast *clone_ast(Ast *node, AstFile *f) {
n->Field.names = clone_ast_array(n->Field.names, f);
n->Field.type = clone_ast(n->Field.type, f);
break;
+ case Ast_BitFieldField:
+ n->BitFieldField.name = clone_ast(n->BitFieldField.name, f);
+ n->BitFieldField.type = clone_ast(n->BitFieldField.type, f);
+ n->BitFieldField.bit_size = clone_ast(n->BitFieldField.bit_size, f);
+ break;
case Ast_FieldList:
n->FieldList.list = clone_ast_array(n->FieldList.list, f);
break;
@@ -383,10 +445,12 @@ gb_internal Ast *clone_ast(Ast *node, AstFile *f) {
n->DynamicArrayType.elem = clone_ast(n->DynamicArrayType.elem, f);
break;
case Ast_StructType:
- n->StructType.fields = clone_ast_array(n->StructType.fields, f);
+ n->StructType.fields = clone_ast_array(n->StructType.fields, f);
n->StructType.polymorphic_params = clone_ast(n->StructType.polymorphic_params, f);
- n->StructType.align = clone_ast(n->StructType.align, f);
- n->StructType.where_clauses = clone_ast_array(n->StructType.where_clauses, f);
+ n->StructType.align = clone_ast(n->StructType.align, f);
+ n->StructType.min_field_align = clone_ast(n->StructType.min_field_align, f);
+ n->StructType.max_field_align = clone_ast(n->StructType.max_field_align, f);
+ n->StructType.where_clauses = clone_ast_array(n->StructType.where_clauses, f);
break;
case Ast_UnionType:
n->UnionType.variants = clone_ast_array(n->UnionType.variants, f);
@@ -401,6 +465,10 @@ gb_internal Ast *clone_ast(Ast *node, AstFile *f) {
n->BitSetType.elem = clone_ast(n->BitSetType.elem, f);
n->BitSetType.underlying = clone_ast(n->BitSetType.underlying, f);
break;
+ case Ast_BitFieldType:
+ n->BitFieldType.backing_type = clone_ast(n->BitFieldType.backing_type, f);
+ n->BitFieldType.fields = clone_ast_array(n->BitFieldType.fields, f);
+ break;
case Ast_MapType:
n->MapType.count = clone_ast(n->MapType.count, f);
n->MapType.key = clone_ast(n->MapType.key, f);
@@ -538,7 +606,7 @@ gb_internal Ast *ast_unary_expr(AstFile *f, Token op, Ast *expr) {
syntax_error_with_verbose(expr, "'or_return' within an unary expression not wrapped in parentheses (...)");
break;
case Ast_OrBranchExpr:
- syntax_error_with_verbose(expr, "'or_%.*s' within an unary expression not wrapped in parentheses (...)", LIT(expr->OrBranchExpr.token.string));
+ syntax_error_with_verbose(expr, "'%.*s' within an unary expression not wrapped in parentheses (...)", LIT(expr->OrBranchExpr.token.string));
break;
}
@@ -566,7 +634,7 @@ gb_internal Ast *ast_binary_expr(AstFile *f, Token op, Ast *left, Ast *right) {
syntax_error_with_verbose(left, "'or_return' within a binary expression not wrapped in parentheses (...)");
break;
case Ast_OrBranchExpr:
- syntax_error_with_verbose(left, "'or_%.*s' within a binary expression not wrapped in parentheses (...)", LIT(left->OrBranchExpr.token.string));
+ syntax_error_with_verbose(left, "'%.*s' within a binary expression not wrapped in parentheses (...)", LIT(left->OrBranchExpr.token.string));
break;
}
if (right) switch (right->kind) {
@@ -574,7 +642,7 @@ gb_internal Ast *ast_binary_expr(AstFile *f, Token op, Ast *left, Ast *right) {
syntax_error_with_verbose(right, "'or_return' within a binary expression not wrapped in parentheses (...)");
break;
case Ast_OrBranchExpr:
- syntax_error_with_verbose(right, "'or_%.*s' within a binary expression not wrapped in parentheses (...)", LIT(right->OrBranchExpr.token.string));
+ syntax_error_with_verbose(right, "'%.*s' within a binary expression not wrapped in parentheses (...)", LIT(right->OrBranchExpr.token.string));
break;
}
@@ -701,7 +769,17 @@ gb_internal ExactValue exact_value_from_token(AstFile *f, Token const &token) {
}
ExactValue value = exact_value_from_basic_literal(token.kind, s);
if (value.kind == ExactValue_Invalid) {
- syntax_error(token, "Invalid token literal");
+ switch (token.kind) {
+ case Token_Integer:
+ syntax_error(token, "Invalid integer literal");
+ break;
+ case Token_Float:
+ syntax_error(token, "Invalid float literal");
+ break;
+ default:
+ syntax_error(token, "Invalid token literal");
+ break;
+ }
}
return value;
}
@@ -728,6 +806,9 @@ gb_internal Ast *ast_basic_directive(AstFile *f, Token token, Token name) {
Ast *result = alloc_ast_node(f, Ast_BasicDirective);
result->BasicDirective.token = token;
result->BasicDirective.name = name;
+ if (string_starts_with(name.string, str_lit("load"))) {
+ f->seen_load_directive_count++;
+ }
return result;
}
@@ -1040,6 +1121,18 @@ gb_internal Ast *ast_field(AstFile *f, Array<Ast *> const &names, Ast *type, Ast
return result;
}
+gb_internal Ast *ast_bit_field_field(AstFile *f, Ast *name, Ast *type, Ast *bit_size, Token tag,
+ CommentGroup *docs, CommentGroup *comment) {
+ Ast *result = alloc_ast_node(f, Ast_BitFieldField);
+ result->BitFieldField.name = name;
+ result->BitFieldField.type = type;
+ result->BitFieldField.bit_size = bit_size;
+ result->BitFieldField.tag = tag;
+ result->BitFieldField.docs = docs;
+ result->BitFieldField.comment = comment;
+ return result;
+}
+
gb_internal Ast *ast_field_list(AstFile *f, Token token, Array<Ast *> const &list) {
Ast *result = alloc_ast_node(f, Ast_FieldList);
result->FieldList.token = token;
@@ -1125,7 +1218,7 @@ gb_internal Ast *ast_dynamic_array_type(AstFile *f, Token token, Ast *elem) {
gb_internal Ast *ast_struct_type(AstFile *f, Token token, Slice<Ast *> fields, isize field_count,
Ast *polymorphic_params, bool is_packed, bool is_raw_union, bool is_no_copy,
- Ast *align,
+ Ast *align, Ast *min_field_align, Ast *max_field_align,
Token where_token, Array<Ast *> const &where_clauses) {
Ast *result = alloc_ast_node(f, Ast_StructType);
result->StructType.token = token;
@@ -1136,6 +1229,8 @@ gb_internal Ast *ast_struct_type(AstFile *f, Token token, Slice<Ast *> fields, i
result->StructType.is_raw_union = is_raw_union;
result->StructType.is_no_copy = is_no_copy;
result->StructType.align = align;
+ result->StructType.min_field_align = min_field_align;
+ result->StructType.max_field_align = max_field_align;
result->StructType.where_token = where_token;
result->StructType.where_clauses = slice_from_array(where_clauses);
return result;
@@ -1172,6 +1267,17 @@ gb_internal Ast *ast_bit_set_type(AstFile *f, Token token, Ast *elem, Ast *under
return result;
}
+gb_internal Ast *ast_bit_field_type(AstFile *f, Token token, Ast *backing_type, Token open, Array<Ast *> const &fields, Token close) {
+ Ast *result = alloc_ast_node(f, Ast_BitFieldType);
+ result->BitFieldType.token = token;
+ result->BitFieldType.backing_type = backing_type;
+ result->BitFieldType.open = open;
+ result->BitFieldType.fields = slice_from_array(fields);
+ result->BitFieldType.close = close;
+ return result;
+}
+
+
gb_internal Ast *ast_map_type(AstFile *f, Token token, Ast *key, Ast *value) {
Ast *result = alloc_ast_node(f, Ast_MapType);
result->MapType.token = token;
@@ -1243,14 +1349,16 @@ gb_internal Ast *ast_import_decl(AstFile *f, Token token, Token relpath, Token i
return result;
}
-gb_internal Ast *ast_foreign_import_decl(AstFile *f, Token token, Array<Token> filepaths, Token library_name,
- CommentGroup *docs, CommentGroup *comment) {
+gb_internal Ast *ast_foreign_import_decl(AstFile *f, Token token, Array<Ast *> filepaths, Token library_name,
+ bool multiple_filepaths,
+ CommentGroup *docs, CommentGroup *comment) {
Ast *result = alloc_ast_node(f, Ast_ForeignImportDecl);
result->ForeignImportDecl.token = token;
result->ForeignImportDecl.filepaths = slice_from_array(filepaths);
result->ForeignImportDecl.library_name = library_name;
result->ForeignImportDecl.docs = docs;
result->ForeignImportDecl.comment = comment;
+ result->ForeignImportDecl.multiple_filepaths = multiple_filepaths;
result->ForeignImportDecl.attributes.allocator = ast_allocator(f);
return result;
@@ -1412,7 +1520,7 @@ gb_internal bool skip_possible_newline(AstFile *f) {
return false;
}
-gb_internal bool skip_possible_newline_for_literal(AstFile *f) {
+gb_internal bool skip_possible_newline_for_literal(AstFile *f, bool ignore_strict_style=false) {
Token curr = f->curr_token;
if (token_is_newline(curr)) {
Token next = peek_token(f);
@@ -1420,6 +1528,10 @@ gb_internal bool skip_possible_newline_for_literal(AstFile *f) {
switch (next.kind) {
case Token_OpenBrace:
case Token_else:
+ if (build_context.strict_style && !ignore_strict_style) {
+ syntax_error(next, "With '-strict-style' the attached brace style (1TBS) is enforced");
+ }
+ /*fallthrough*/
case Token_where:
advance_token(f);
return true;
@@ -1444,9 +1556,26 @@ gb_internal Token expect_token(AstFile *f, TokenKind kind) {
if (prev.kind != kind) {
String c = token_strings[kind];
String p = token_to_string(prev);
+ begin_error_block();
syntax_error(f->curr_token, "Expected '%.*s', got '%.*s'", LIT(c), LIT(p));
+ if (kind == Token_Ident) switch (prev.kind) {
+ case Token_context:
+ error_line("\tSuggestion: '%.*s' is a keyword, would 'ctx' suffice?\n", LIT(prev.string));
+ break;
+ case Token_package:
+ error_line("\tSuggestion: '%.*s' is a keyword, would 'pkg' suffice?\n", LIT(prev.string));
+ break;
+ default:
+ if (token_is_keyword(prev.kind)) {
+ error_line("\tNote: '%.*s' is a keyword\n", LIT(prev.string));
+ }
+ break;
+ }
+
+ end_error_block();
+
if (prev.kind == Token_EOF) {
- gb_exit(1);
+ exit_with_errors();
}
}
@@ -1514,7 +1643,7 @@ gb_internal Token expect_operator(AstFile *f) {
LIT(p));
}
if (prev.kind == Token_Ellipsis) {
- syntax_warning(prev, "'..' for ranges has now been deprecated, prefer '..='");
+ syntax_error(prev, "'..' for ranges are not allowed, did you mean '..<' or '..='?");
f->tokens[f->curr_token_index].flags |= TokenFlag_Replace;
}
@@ -1812,6 +1941,9 @@ gb_internal Array<Ast *> parse_enum_field_list(AstFile *f) {
f->curr_token.kind != Token_EOF) {
CommentGroup *docs = f->lead_comment;
CommentGroup *comment = nullptr;
+
+ parse_enforce_tabs(f);
+
Ast *name = parse_value(f);
Ast *value = nullptr;
if (f->curr_token.kind == Token_Eq) {
@@ -2031,6 +2163,9 @@ gb_internal bool ast_on_same_line(Token const &x, Ast *yp) {
gb_internal Ast *parse_force_inlining_operand(AstFile *f, Token token) {
Ast *expr = parse_unary_expr(f, false);
Ast *e = strip_or_return_expr(expr);
+ if (e == nullptr) {
+ return expr;
+ }
if (e->kind != Ast_ProcLit && e->kind != Ast_CallExpr) {
syntax_error(expr, "%.*s must be followed by a procedure literal or call, got %.*s", LIT(token.string), LIT(ast_strings[expr->kind]));
return ast_bad_expr(f, token, f->curr_token);
@@ -2147,6 +2282,7 @@ gb_internal Array<Ast *> parse_union_variant_list(AstFile *f) {
auto variants = array_make<Ast *>(ast_allocator(f));
while (f->curr_token.kind != Token_CloseBrace &&
f->curr_token.kind != Token_EOF) {
+ parse_enforce_tabs(f);
Ast *type = parse_type(f);
if (type->kind != Ast_BadExpr) {
array_add(&variants, type);
@@ -2158,6 +2294,49 @@ gb_internal Array<Ast *> parse_union_variant_list(AstFile *f) {
return variants;
}
+gb_internal void parser_check_polymorphic_record_parameters(AstFile *f, Ast *polymorphic_params) {
+ if (polymorphic_params == nullptr) {
+ return;
+ }
+ if (polymorphic_params->kind != Ast_FieldList) {
+ return;
+ }
+
+
+ enum {Unknown, Dollar, Bare} prefix = Unknown;
+ gb_unused(prefix);
+
+ for (Ast *field : polymorphic_params->FieldList.list) {
+ if (field == nullptr || field->kind != Ast_Field) {
+ continue;
+ }
+ for (Ast *name : field->Field.names) {
+ if (name == nullptr) {
+ continue;
+ }
+ bool error = false;
+
+ if (name->kind == Ast_Ident) {
+ switch (prefix) {
+ case Unknown: prefix = Bare; break;
+ case Dollar: error = true; break;
+ case Bare: break;
+ }
+ } else if (name->kind == Ast_PolyType) {
+ switch (prefix) {
+ case Unknown: prefix = Dollar; break;
+ case Dollar: break;
+ case Bare: error = true; break;
+ }
+ }
+ if (error) {
+ syntax_error(name, "Mixture of polymorphic $ names and normal identifiers are not allowed within record parameters");
+ }
+ }
+ }
+}
+
+
gb_internal Ast *parse_operand(AstFile *f, bool lhs) {
Ast *operand = nullptr; // Operand
switch (f->curr_token.kind) {
@@ -2248,6 +2427,19 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) {
break;
}
return original_type;
+ } else if (name.string == "row_major" ||
+ name.string == "column_major") {
+ Ast *original_type = parse_type(f);
+ Ast *type = unparen_expr(original_type);
+ switch (type->kind) {
+ case Ast_MatrixType:
+ type->MatrixType.is_row_major = (name.string == "row_major");
+ break;
+ default:
+ syntax_error(type, "Expected a matrix type after #%.*s, got %.*s", LIT(name.string), LIT(ast_strings[type->kind]));
+ break;
+ }
+ return original_type;
} else if (name.string == "partial") {
Ast *tag = ast_basic_directive(f, token, name);
Ast *original_expr = parse_expr(f, lhs);
@@ -2257,9 +2449,6 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) {
return ast_bad_expr(f, token, name);
}
switch (expr->kind) {
- case Ast_ArrayType:
- syntax_error(expr, "#partial has been replaced with #sparse for non-contiguous enumerated array types");
- break;
case Ast_CompoundLit:
expr->CompoundLit.tag = tag;
break;
@@ -2299,6 +2488,7 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) {
tag = parse_call_expr(f, tag);
}
Ast *type = parse_type(f);
+ syntax_error(tag, "#relative types have now been removed in favour of \"core:relative\"");
return ast_relative_type(f, tag, type);
} else if (name.string == "force_inline" ||
name.string == "force_no_inline") {
@@ -2370,7 +2560,7 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) {
return type;
}
- skip_possible_newline_for_literal(f);
+ skip_possible_newline_for_literal(f, where_token.kind == Token_where);
if (allow_token(f, Token_Uninit)) {
if (where_token.kind != Token_Invalid) {
@@ -2447,6 +2637,9 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) {
return ast_pointer_type(f, token, elem);
} break;
+ case Token_Mul:
+ return parse_unary_expr(f, true);
+
case Token_OpenBracket: {
Token token = expect_token(f, Token_OpenBracket);
Ast *count_expr = nullptr;
@@ -2500,6 +2693,66 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) {
return ast_matrix_type(f, token, row_count, column_count, type);
} break;
+ case Token_bit_field: {
+ Token token = expect_token(f, Token_bit_field);
+ isize prev_level;
+
+ prev_level = f->expr_level;
+ f->expr_level = -1;
+
+ Ast *backing_type = parse_type_or_ident(f);
+ if (backing_type == nullptr) {
+ Token token = advance_token(f);
+ syntax_error(token, "Expected a backing type for a 'bit_field'");
+ backing_type = ast_bad_expr(f, token, f->curr_token);
+ }
+
+ skip_possible_newline_for_literal(f);
+ Token open = expect_token_after(f, Token_OpenBrace, "bit_field");
+
+
+ auto fields = array_make<Ast *>(ast_allocator(f), 0, 0);
+
+ while (f->curr_token.kind != Token_CloseBrace &&
+ f->curr_token.kind != Token_EOF) {
+ CommentGroup *docs = nullptr;
+ CommentGroup *comment = nullptr;
+
+ Ast *name = parse_ident(f);
+ bool err_once = false;
+ while (allow_token(f, Token_Comma)) {
+ Ast *dummy_name = parse_ident(f);
+ if (!err_once) {
+ error(dummy_name, "'bit_field' fields do not support multiple names per field");
+ err_once = true;
+ }
+ }
+ expect_token(f, Token_Colon);
+ Ast *type = parse_type(f);
+ expect_token(f, Token_Or);
+ Ast *bit_size = parse_expr(f, true);
+
+ Token tag = {};
+ if (f->curr_token.kind == Token_String) {
+ tag = expect_token(f, Token_String);
+ }
+
+ Ast *bf_field = ast_bit_field_field(f, name, type, bit_size, tag, docs, comment);
+ array_add(&fields, bf_field);
+
+ if (!allow_field_separator(f)) {
+ break;
+ }
+ }
+
+ Token close = expect_closing_brace_of_field_list(f);
+
+ f->expr_level = prev_level;
+
+ return ast_bit_field_type(f, token, backing_type, open, fields, close);
+ }
+
+
case Token_struct: {
Token token = expect_token(f, Token_struct);
Ast *polymorphic_params = nullptr;
@@ -2507,6 +2760,8 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) {
bool is_raw_union = false;
bool no_copy = false;
Ast *align = nullptr;
+ Ast *min_field_align = nullptr;
+ Ast *max_field_align = nullptr;
if (allow_token(f, Token_OpenParen)) {
isize param_count = 0;
@@ -2543,7 +2798,44 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) {
error_line("\tSuggestion: #align(%s)", s);
gb_string_free(s);
}
- } else if (tag.string == "raw_union") {
+ } else if (tag.string == "field_align") {
+ if (min_field_align) {
+ syntax_error(tag, "Duplicate struct tag '#%.*s'", LIT(tag.string));
+ }
+ syntax_warning(tag, "#field_align has been deprecated in favour of #min_field_align");
+ min_field_align = parse_expr(f, true);
+ if (min_field_align && min_field_align->kind != Ast_ParenExpr) {
+ ERROR_BLOCK();
+ gbString s = expr_to_string(min_field_align);
+ syntax_warning(tag, "#field_align requires parentheses around the expression");
+ error_line("\tSuggestion: #min_field_align(%s)", s);
+ gb_string_free(s);
+ }
+ } else if (tag.string == "min_field_align") {
+ if (min_field_align) {
+ syntax_error(tag, "Duplicate struct tag '#%.*s'", LIT(tag.string));
+ }
+ min_field_align = parse_expr(f, true);
+ if (min_field_align && min_field_align->kind != Ast_ParenExpr) {
+ ERROR_BLOCK();
+ gbString s = expr_to_string(min_field_align);
+ syntax_warning(tag, "#min_field_align requires parentheses around the expression");
+ error_line("\tSuggestion: #min_field_align(%s)", s);
+ gb_string_free(s);
+ }
+ } else if (tag.string == "max_field_align") {
+ if (max_field_align) {
+ syntax_error(tag, "Duplicate struct tag '#%.*s'", LIT(tag.string));
+ }
+ max_field_align = parse_expr(f, true);
+ if (max_field_align && max_field_align->kind != Ast_ParenExpr) {
+ ERROR_BLOCK();
+ gbString s = expr_to_string(max_field_align);
+ syntax_warning(tag, "#max_field_align requires parentheses around the expression");
+ error_line("\tSuggestion: #max_field_align(%s)", s);
+ gb_string_free(s);
+ }
+ }else if (tag.string == "raw_union") {
if (is_raw_union) {
syntax_error(tag, "Duplicate struct tag '#%.*s'", LIT(tag.string));
}
@@ -2591,7 +2883,9 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) {
decls = fields->FieldList.list;
}
- return ast_struct_type(f, token, decls, name_count, polymorphic_params, is_packed, is_raw_union, no_copy, align, where_token, where_clauses);
+ parser_check_polymorphic_record_parameters(f, polymorphic_params);
+
+ return ast_struct_type(f, token, decls, name_count, polymorphic_params, is_packed, is_raw_union, no_copy, align, min_field_align, max_field_align, where_token, where_clauses);
} break;
case Token_union: {
@@ -2683,6 +2977,8 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) {
auto variants = parse_union_variant_list(f);
Token close = expect_closing_brace_of_field_list(f);
+ parser_check_polymorphic_record_parameters(f, polymorphic_params);
+
return ast_union_type(f, token, variants, polymorphic_params, align, union_kind, where_token, where_clauses);
} break;
@@ -2714,6 +3010,10 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) {
elem = parse_expr(f, true);
f->allow_range = prev_allow_range;
+ if (elem == nullptr) {
+ syntax_error(token, "Expected a type or range, got nothing");
+ }
+
if (allow_token(f, Token_Semicolon)) {
underlying = parse_type(f);
} else if (allow_token(f, Token_Comma)) {
@@ -2723,6 +3023,7 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) {
underlying = parse_type(f);
}
+
expect_token(f, Token_CloseBracket);
return ast_bit_set_type(f, token, elem, underlying);
}
@@ -2888,7 +3189,7 @@ gb_internal Ast *parse_call_expr(AstFile *f, Ast *operand) {
Ast *call = ast_call_expr(f, operand, args, open_paren, close_paren, ellipsis);
Ast *o = unparen_expr(operand);
- if (o->kind == Ast_SelectorExpr && o->SelectorExpr.token.kind == Token_ArrowRight) {
+ if (o && o->kind == Ast_SelectorExpr && o->SelectorExpr.token.kind == Token_ArrowRight) {
return ast_selector_call_expr(f, o->SelectorExpr.token, o, call);
}
@@ -2904,7 +3205,7 @@ gb_internal void parse_check_or_return(Ast *operand, char const *msg) {
syntax_error_with_verbose(operand, "'or_return' use within %s is not wrapped in parentheses (...)", msg);
break;
case Ast_OrBranchExpr:
- syntax_error_with_verbose(operand, "'or_%.*s' use within %s is not wrapped in parentheses (...)", msg, LIT(operand->OrBranchExpr.token.string));
+ syntax_error_with_verbose(operand, "'%.*s' use within %s is not wrapped in parentheses (...)", msg, LIT(operand->OrBranchExpr.token.string));
break;
}
}
@@ -3103,7 +3404,9 @@ gb_internal Ast *parse_unary_expr(AstFile *f, bool lhs) {
case Token_Sub:
case Token_Xor:
case Token_And:
- case Token_Not: {
+ case Token_Not:
+ case Token_Mul: // Used for error handling when people do C-like things
+ {
Token token = advance_token(f);
Ast *expr = parse_unary_expr(f, lhs);
return ast_unary_expr(f, token, expr);
@@ -3301,9 +3604,24 @@ gb_internal Array<Ast *> parse_ident_list(AstFile *f, bool allow_poly_names) {
gb_internal Ast *parse_type(AstFile *f) {
Ast *type = parse_type_or_ident(f);
if (type == nullptr) {
- Token token = advance_token(f);
- syntax_error(token, "Expected a type");
+ Token prev_token = f->curr_token;
+ Token token = {};
+ if (f->curr_token.kind == Token_OpenBrace) {
+ token = f->curr_token;
+ } else {
+ token = advance_token(f);
+ }
+ String prev_token_str = prev_token.string;
+ if (prev_token_str == str_lit("\n")) {
+ syntax_error(token, "Expected a type, got newline");
+ } else {
+ syntax_error(token, "Expected a type, got '%.*s'", LIT(prev_token_str));
+ }
return ast_bad_expr(f, token, f->curr_token);
+ } else if (type->kind == Ast_ParenExpr &&
+ unparen_expr(type) == nullptr) {
+ syntax_error(type, "Expected a type within the parentheses");
+ return ast_bad_expr(f, type->ParenExpr.open, type->ParenExpr.close);
}
return type;
}
@@ -3490,6 +3808,7 @@ gb_internal Ast *parse_simple_stmt(AstFile *f, u32 flags) {
expect_token_after(f, Token_Colon, "identifier list");
if ((flags&StmtAllowFlag_Label) && lhs.count == 1) {
bool is_partial = false;
+ bool is_reverse = false;
Token partial_token = {};
if (f->curr_token.kind == Token_Hash) {
// NOTE(bill): This is purely for error messages
@@ -3499,6 +3818,11 @@ gb_internal Ast *parse_simple_stmt(AstFile *f, u32 flags) {
partial_token = expect_token(f, Token_Hash);
expect_token(f, Token_Ident);
is_partial = true;
+ } else if (name.kind == Token_Ident && name.string == "reverse" &&
+ peek_token_n(f, 1).kind == Token_for) {
+ partial_token = expect_token(f, Token_Hash);
+ expect_token(f, Token_Ident);
+ is_reverse = true;
}
}
switch (f->curr_token.kind) {
@@ -3531,8 +3855,22 @@ gb_internal Ast *parse_simple_stmt(AstFile *f, u32 flags) {
case Ast_TypeSwitchStmt:
stmt->TypeSwitchStmt.partial = true;
break;
+ default:
+ syntax_error(partial_token, "Incorrect use of directive, use '%.*s: #partial switch'", LIT(ast_token(name).string));
+ break;
+ }
+ } else if (is_reverse) {
+ switch (stmt->kind) {
+ case Ast_RangeStmt:
+ if (stmt->RangeStmt.reverse) {
+ syntax_error(token, "#reverse already applied to a 'for in' statement");
+ }
+ stmt->RangeStmt.reverse = true;
+ break;
+ default:
+ syntax_error(token, "#reverse can only be applied to a 'for in' statement");
+ break;
}
- syntax_error(partial_token, "Incorrect use of directive, use '#partial %.*s: switch'", LIT(ast_token(name).string));
}
return stmt;
@@ -3666,10 +4004,12 @@ gb_internal Ast *parse_proc_type(AstFile *f, Token proc_token) {
expect_token(f, Token_OpenParen);
+ f->expr_level += 1;
params = parse_field_list(f, nullptr, FieldFlag_Signature, Token_CloseParen, true, true);
if (file_allow_newline(f)) {
skip_possible_newline(f);
}
+ f->expr_level -= 1;
expect_token_after(f, Token_CloseParen, "parameter list");
results = parse_results(f, &diverging);
@@ -3727,14 +4067,16 @@ struct ParseFieldPrefixMapping {
FieldFlag flag;
};
-gb_global ParseFieldPrefixMapping parse_field_prefix_mappings[] = {
- {str_lit("using"), Token_using, FieldFlag_using},
- {str_lit("no_alias"), Token_Hash, FieldFlag_no_alias},
- {str_lit("c_vararg"), Token_Hash, FieldFlag_c_vararg},
- {str_lit("const"), Token_Hash, FieldFlag_const},
- {str_lit("any_int"), Token_Hash, FieldFlag_any_int},
- {str_lit("subtype"), Token_Hash, FieldFlag_subtype},
- {str_lit("by_ptr"), Token_Hash, FieldFlag_by_ptr},
+gb_global ParseFieldPrefixMapping const parse_field_prefix_mappings[] = {
+ {str_lit("using"), Token_using, FieldFlag_using},
+ {str_lit("no_alias"), Token_Hash, FieldFlag_no_alias},
+ {str_lit("no_capture"), Token_Hash, FieldFlag_no_capture},
+ {str_lit("c_vararg"), Token_Hash, FieldFlag_c_vararg},
+ {str_lit("const"), Token_Hash, FieldFlag_const},
+ {str_lit("any_int"), Token_Hash, FieldFlag_any_int},
+ {str_lit("subtype"), Token_Hash, FieldFlag_subtype},
+ {str_lit("by_ptr"), Token_Hash, FieldFlag_by_ptr},
+ {str_lit("no_broadcast"), Token_Hash, FieldFlag_no_broadcast},
};
@@ -3857,6 +4199,15 @@ gb_internal Array<Ast *> convert_to_ident_list(AstFile *f, Array<AstAndFlags> li
case Ast_Ident:
case Ast_BadExpr:
break;
+ case Ast_Implicit:
+ begin_error_block();
+ syntax_error(ident, "Expected an identifier, '%.*s' which is a keyword", LIT(ident->Implicit.string));
+ if (ident->Implicit.kind == Token_context) {
+ error_line("\tSuggestion: Would 'ctx' suffice as an alternative name?\n");
+ }
+ end_error_block();
+ ident = ast_ident(f, blank_token);
+ break;
case Ast_PolyType:
if (allow_poly_names) {
@@ -3870,6 +4221,7 @@ gb_internal Array<Ast *> convert_to_ident_list(AstFile *f, Array<AstAndFlags> li
}
/*fallthrough*/
+
default:
syntax_error(ident, "Expected an identifier");
ident = ast_ident(f, blank_token);
@@ -3911,8 +4263,6 @@ gb_internal bool allow_field_separator(AstFile *f) {
gb_internal Ast *parse_struct_field_list(AstFile *f, isize *name_count_) {
Token start_token = f->curr_token;
- auto decls = array_make<Ast *>(ast_allocator(f));
-
isize total_name_count = 0;
Ast *params = parse_field_list(f, &total_name_count, FieldFlag_Struct, Token_CloseBrace, false, false);
@@ -3973,6 +4323,7 @@ gb_internal Ast *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_fl
while (f->curr_token.kind != follow &&
f->curr_token.kind != Token_Colon &&
f->curr_token.kind != Token_EOF) {
+ if (!is_signature) parse_enforce_tabs(f);
u32 flags = parse_field_prefixes(f);
Ast *param = parse_var_type(f, allow_ellipsis, allow_typeid_token);
if (param->kind == Ast_Ellipsis) {
@@ -4053,15 +4404,21 @@ gb_internal Ast *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_fl
}
}
- allow_field_separator(f);
+ bool more_fields = allow_field_separator(f);
Ast *param = ast_field(f, names, type, default_value, set_flags, tag, docs, f->line_comment);
array_add(&params, param);
+ if (!more_fields) {
+ if (name_count_) *name_count_ = total_name_count;
+ return ast_field_list(f, start_token, params);
+ }
while (f->curr_token.kind != follow &&
f->curr_token.kind != Token_EOF &&
f->curr_token.kind != Token_Semicolon) {
CommentGroup *docs = f->lead_comment;
+
+ if (!is_signature) parse_enforce_tabs(f);
u32 set_flags = parse_field_prefixes(f);
Token tag = {};
Array<Ast *> names = parse_ident_list(f, allow_poly_names);
@@ -4222,12 +4579,19 @@ gb_internal bool parse_control_statement_semicolon_separator(AstFile *f) {
}
+
+
+
gb_internal Ast *parse_if_stmt(AstFile *f) {
if (f->curr_proc == nullptr) {
syntax_error(f->curr_token, "You cannot use an if statement in the file scope");
return ast_bad_stmt(f, f->curr_token, f->curr_token);
}
+ Ast *top_if_stmt = nullptr;
+
+ Ast *prev_if_stmt = nullptr;
+if_else_chain:;
Token token = expect_token(f, Token_if);
Ast *init = nullptr;
Ast *cond = nullptr;
@@ -4264,13 +4628,29 @@ gb_internal Ast *parse_if_stmt(AstFile *f) {
body = parse_block_stmt(f, false);
}
- skip_possible_newline_for_literal(f);
+ bool ignore_strict_style = false;
+ if (token.pos.line == ast_end_token(body).pos.line) {
+ ignore_strict_style = true;
+ }
+ skip_possible_newline_for_literal(f, ignore_strict_style);
+
+ Ast *curr_if_stmt = ast_if_stmt(f, token, init, cond, body, nullptr);
+ if (top_if_stmt == nullptr) {
+ top_if_stmt = curr_if_stmt;
+ }
+ if (prev_if_stmt != nullptr) {
+ prev_if_stmt->IfStmt.else_stmt = curr_if_stmt;
+ }
+
if (f->curr_token.kind == Token_else) {
Token else_token = expect_token(f, Token_else);
switch (f->curr_token.kind) {
case Token_if:
- else_stmt = parse_if_stmt(f);
- break;
+ // NOTE(bill): Instead of relying on recursive descent for an if-else chain
+ // we can just inline the tail-recursion manually with a simple loop like
+ // construct using a `goto`
+ prev_if_stmt = curr_if_stmt;
+ goto if_else_chain;
case Token_OpenBrace:
else_stmt = parse_block_stmt(f, false);
break;
@@ -4285,7 +4665,9 @@ gb_internal Ast *parse_if_stmt(AstFile *f) {
}
}
- return ast_if_stmt(f, token, init, cond, body, else_stmt);
+ curr_if_stmt->IfStmt.else_stmt = else_stmt;
+
+ return top_if_stmt;
}
gb_internal Ast *parse_when_stmt(AstFile *f) {
@@ -4296,9 +4678,12 @@ gb_internal Ast *parse_when_stmt(AstFile *f) {
isize prev_level = f->expr_level;
f->expr_level = -1;
+ bool prev_allow_in_expr = f->allow_in_expr;
+ f->allow_in_expr = true;
cond = parse_expr(f, false);
+ f->allow_in_expr = prev_allow_in_expr;
f->expr_level = prev_level;
if (cond == nullptr) {
@@ -4313,7 +4698,11 @@ gb_internal Ast *parse_when_stmt(AstFile *f) {
body = parse_block_stmt(f, true);
}
- skip_possible_newline_for_literal(f);
+ bool ignore_strict_style = false;
+ if (token.pos.line == ast_end_token(body).pos.line) {
+ ignore_strict_style = true;
+ }
+ skip_possible_newline_for_literal(f, ignore_strict_style);
if (f->curr_token.kind == Token_else) {
Token else_token = expect_token(f, Token_else);
switch (f->curr_token.kind) {
@@ -4609,7 +4998,7 @@ gb_internal Ast *parse_import_decl(AstFile *f, ImportDeclKind kind) {
}
if (f->in_when_statement) {
- syntax_error(import_name, "Cannot use 'import' within a 'when' statement. Prefer using the file suffixes (e.g. foo_windows.odin) or '//+build' tags");
+ syntax_error(import_name, "Cannot use 'import' within a 'when' statement. Prefer using the file suffixes (e.g. foo_windows.odin) or '#+build' tags");
}
if (kind != ImportDecl_Standard) {
@@ -4643,14 +5032,17 @@ gb_internal Ast *parse_foreign_decl(AstFile *f) {
if (is_blank_ident(lib_name)) {
syntax_error(lib_name, "Illegal foreign import name: '_'");
}
- Array<Token> filepaths = {};
+ bool multiple_filepaths = false;
+
+ Array<Ast *> filepaths = {};
if (allow_token(f, Token_OpenBrace)) {
+ multiple_filepaths = true;
array_init(&filepaths, ast_allocator(f));
while (f->curr_token.kind != Token_CloseBrace &&
f->curr_token.kind != Token_EOF) {
- Token path = expect_token(f, Token_String);
+ Ast *path = parse_expr(f, false);
array_add(&filepaths, path);
if (!allow_field_separator(f)) {
@@ -4659,9 +5051,10 @@ gb_internal Ast *parse_foreign_decl(AstFile *f) {
}
expect_closing_brace_of_field_list(f);
} else {
- filepaths = array_make<Token>(ast_allocator(f), 0, 1);
+ filepaths = array_make<Ast *>(ast_allocator(f), 0, 1);
Token path = expect_token(f, Token_String);
- array_add(&filepaths, path);
+ Ast *lit = ast_basic_lit(f, path);
+ array_add(&filepaths, lit);
}
Ast *s = nullptr;
@@ -4670,9 +5063,9 @@ gb_internal Ast *parse_foreign_decl(AstFile *f) {
s = ast_bad_decl(f, lib_name, f->curr_token);
} else if (f->curr_proc != nullptr) {
syntax_error(lib_name, "You cannot use foreign import within a procedure. This must be done at the file scope");
- s = ast_bad_decl(f, lib_name, filepaths[0]);
+ s = ast_bad_decl(f, lib_name, ast_token(filepaths[0]));
} else {
- s = ast_foreign_import_decl(f, token, filepaths, lib_name, docs, f->line_comment);
+ s = ast_foreign_import_decl(f, token, filepaths, lib_name, multiple_filepaths, docs, f->line_comment);
}
expect_semicolon(f);
return s;
@@ -4812,6 +5205,7 @@ gb_internal Ast *parse_stmt(AstFile *f) {
case Token_Xor:
case Token_Not:
case Token_And:
+ case Token_Mul: // Used for error handling when people do C-like things
s = parse_simple_stmt(f, StmtAllowFlag_Label);
expect_semicolon(f);
return s;
@@ -4930,7 +5324,7 @@ gb_internal Ast *parse_stmt(AstFile *f) {
} else if (tag == "unroll") {
return parse_unrolled_for_loop(f, name);
} else if (tag == "reverse") {
- Ast *for_stmt = parse_for_stmt(f);
+ Ast *for_stmt = parse_stmt(f);
if (for_stmt->kind == Ast_RangeStmt) {
if (for_stmt->RangeStmt.reverse) {
syntax_error(token, "#reverse already applied to a 'for in' statement");
@@ -4943,6 +5337,38 @@ gb_internal Ast *parse_stmt(AstFile *f) {
} else if (tag == "include") {
syntax_error(token, "#include is not a valid import declaration kind. Did you mean 'import'?");
s = ast_bad_stmt(f, token, f->curr_token);
+ } else if (tag == "define") {
+ s = ast_bad_stmt(f, token, f->curr_token);
+
+ if (name.pos.line == f->curr_token.pos.line) {
+ bool call_like = false;
+ Ast *macro_expr = nullptr;
+ Token ident = f->curr_token;
+ if (allow_token(f, Token_Ident) &&
+ name.pos.line == f->curr_token.pos.line) {
+ if (f->curr_token.kind == Token_OpenParen && f->curr_token.pos.column == ident.pos.column+ident.string.len) {
+ call_like = true;
+ (void)parse_call_expr(f, nullptr);
+ }
+
+ if (name.pos.line == f->curr_token.pos.line && f->curr_token.kind != Token_Semicolon) {
+ macro_expr = parse_expr(f, false);
+ }
+ }
+
+ ERROR_BLOCK();
+ syntax_error(ident, "#define is not a valid declaration, Odin does not have a C-like preprocessor.");
+ if (macro_expr == nullptr || call_like) {
+ error_line("\tNote: Odin does not support macros\n");
+ } else {
+ gbString s = expr_to_string(macro_expr);
+ error_line("\tSuggestion: Did you mean '%.*s :: %s'?\n", LIT(ident.string), s);
+ gb_string_free(s);
+ }
+ } else {
+ syntax_error(token, "#define is not a valid declaration, Odin does not have a C-like preprocessor.");
+ }
+
} else {
syntax_error(token, "Unknown tag directive used: '%.*s'", LIT(tag));
s = ast_bad_stmt(f, token, f->curr_token);
@@ -4960,6 +5386,12 @@ gb_internal Ast *parse_stmt(AstFile *f) {
s = ast_empty_stmt(f, token);
expect_semicolon(f);
return s;
+
+ case Token_FileTag:
+ // This is always an error because all valid file tags will have been processed in `parse_file` already.
+ // Any remaining file tags must be past the package line and thus invalid.
+ syntax_error(token, "Lines starting with #+ (file tags) are only allowed before the package line.");
+ return ast_bad_stmt(f, token, f->curr_token);
}
// Error correction statements
@@ -4994,12 +5426,49 @@ gb_internal Ast *parse_stmt(AstFile *f) {
return ast_bad_stmt(f, token, f->curr_token);
}
+
+gb_internal void parse_enforce_tabs(AstFile *f) {
+ // Checks to see if tabs have been used for indentation
+ if ((ast_file_vet_flags(f) & VetFlag_Tabs) == 0) {
+ return;
+ }
+
+ Token prev = f->prev_token;
+ Token curr = f->curr_token;
+ if (prev.pos.line < curr.pos.line) {
+ u8 *start = f->tokenizer.start+prev.pos.offset;
+ u8 *end = f->tokenizer.start+curr.pos.offset;
+ u8 *it = end;
+ while (it > start) {
+ if (*it == '\n') {
+ it++;
+ break;
+ }
+ it--;
+ }
+
+ isize len = end-it;
+ for (isize i = 0; i < len; i++) {
+ if (it[i] == '/') {
+ // ignore comments
+ break;
+ }
+ if (it[i] == ' ') {
+ syntax_error(curr, "With '-vet-tabs', tabs must be used for indentation");
+ break;
+ }
+ }
+ }
+}
+
gb_internal Array<Ast *> parse_stmt_list(AstFile *f) {
auto list = array_make<Ast *>(ast_allocator(f));
while (f->curr_token.kind != Token_case &&
f->curr_token.kind != Token_CloseBrace &&
f->curr_token.kind != Token_EOF) {
+ parse_enforce_tabs(f);
+
Ast *stmt = parse_stmt(f);
if (stmt && stmt->kind != Ast_EmptyStmt) {
array_add(&list, stmt);
@@ -5025,7 +5494,7 @@ gb_internal ParseFileError init_ast_file(AstFile *f, String const &fullpath, Tok
if (!string_ends_with(f->fullpath, str_lit(".odin"))) {
return ParseFile_WrongExtension;
}
- zero_item(&f->tokenizer);
+ gb_zero_item(&f->tokenizer);
f->tokenizer.curr_file_id = f->id;
TokenizerInitError err = init_tokenizer_from_fullpath(&f->tokenizer, f->fullpath, build_context.copy_file_contents);
@@ -5156,6 +5625,7 @@ gb_internal WORKER_TASK_PROC(parser_worker_proc) {
gb_internal void parser_add_file_to_process(Parser *p, AstPackage *pkg, FileInfo fi, TokenPos pos) {
ImportedFile f = {pkg, fi, pos, p->file_to_process_count++};
+ f.pos.file_id = cast(i32)(f.index+1);
auto wd = gb_alloc_item(permanent_allocator(), ParserWorkerData);
wd->parser = p;
wd->imported_file = f;
@@ -5192,6 +5662,7 @@ gb_internal WORKER_TASK_PROC(foreign_file_worker_proc) {
gb_internal void parser_add_foreign_file_to_process(Parser *p, AstPackage *pkg, AstForeignFileKind kind, FileInfo fi, TokenPos pos) {
// TODO(bill): Use a better allocator
ImportedFile f = {pkg, fi, pos, p->file_to_process_count++};
+ f.pos.file_id = cast(i32)(f.index+1);
auto wd = gb_alloc_item(permanent_allocator(), ForeignFileWorkerData);
wd->parser = p;
wd->imported_file = f;
@@ -5219,7 +5690,7 @@ gb_internal AstPackage *try_add_import_path(Parser *p, String path, String const
pkg->foreign_files.allocator = permanent_allocator();
// NOTE(bill): Single file initial package
- if (kind == Package_Init && string_ends_with(path, FILE_EXT)) {
+ if (kind == Package_Init && !path_is_directory(path) && string_ends_with(path, FILE_EXT)) {
FileInfo fi = {};
fi.name = filename_from_path(path);
fi.fullpath = path;
@@ -5264,14 +5735,31 @@ gb_internal AstPackage *try_add_import_path(Parser *p, String path, String const
return nullptr;
}
+ isize files_with_ext = 0;
isize files_to_reserve = 1; // always reserve 1
for (FileInfo fi : list) {
String name = fi.name;
String ext = path_extension(name);
+ if (ext == FILE_EXT) {
+ files_with_ext += 1;
+ }
if (ext == FILE_EXT && !is_excluded_target_filename(name)) {
files_to_reserve += 1;
}
}
+ if (files_with_ext == 0 || files_to_reserve == 1) {
+ ERROR_BLOCK();
+ if (files_with_ext != 0) {
+ syntax_error(pos, "Directory contains no .odin files for the specified platform: %.*s", LIT(rel_path));
+ } else {
+ syntax_error(pos, "Empty directory that contains no .odin files: %.*s", LIT(rel_path));
+ }
+ if (build_context.command_kind == Command_test) {
+ error_line("\tSuggestion: Make an .odin file that imports packages to test and use the `-all-packages` flag.");
+ }
+ return nullptr;
+ }
+
array_reserve(&pkg->files, files_to_reserve);
for (FileInfo fi : list) {
@@ -5391,9 +5879,19 @@ gb_internal bool is_package_name_reserved(String const &name) {
}
-gb_internal bool determine_path_from_string(BlockingMutex *file_mutex, Ast *node, String base_dir, String const &original_string, String *path) {
+gb_internal bool determine_path_from_string(BlockingMutex *file_mutex, Ast *node, String base_dir, String const &original_string, String *path, bool use_check_errors=false) {
GB_ASSERT(path != nullptr);
+ void (*do_error)(Ast *, char const *, ...);
+ void (*do_warning)(Token const &, char const *, ...);
+
+ do_error = &syntax_error;
+ do_warning = &syntax_warning;
+ if (use_check_errors) {
+ do_error = &error;
+ do_error = &warning;
+ }
+
// NOTE(bill): if file_mutex == nullptr, this means that the code is used within the semantics stage
String collection_name = {};
@@ -5420,7 +5918,7 @@ gb_internal bool determine_path_from_string(BlockingMutex *file_mutex, Ast *node
String file_str = {};
if (colon_pos == 0) {
- syntax_error(node, "Expected a collection name");
+ do_error(node, "Expected a collection name");
return false;
}
@@ -5435,19 +5933,41 @@ gb_internal bool determine_path_from_string(BlockingMutex *file_mutex, Ast *node
if (has_windows_drive) {
String sub_file_path = substring(file_str, 3, file_str.len);
if (!is_import_path_valid(sub_file_path)) {
- syntax_error(node, "Invalid import path: '%.*s'", LIT(file_str));
+ do_error(node, "Invalid import path: '%.*s'", LIT(file_str));
return false;
}
} else if (!is_import_path_valid(file_str)) {
- syntax_error(node, "Invalid import path: '%.*s'", LIT(file_str));
+ do_error(node, "Invalid import path: '%.*s'", LIT(file_str));
return false;
}
-
if (collection_name.len > 0) {
+ // NOTE(bill): `base:runtime` == `core:runtime`
+ if (collection_name == "core") {
+ bool replace_with_base = false;
+ if (string_starts_with(file_str, str_lit("runtime"))) {
+ replace_with_base = true;
+ } else if (string_starts_with(file_str, str_lit("intrinsics"))) {
+ replace_with_base = true;
+ } if (string_starts_with(file_str, str_lit("builtin"))) {
+ replace_with_base = true;
+ }
+
+ if (replace_with_base) {
+ collection_name = str_lit("base");
+ }
+ if (replace_with_base) {
+ if (ast_file_vet_deprecated(node->file())) {
+ do_error(node, "import \"core:%.*s\" has been deprecated in favour of \"base:%.*s\"", LIT(file_str), LIT(file_str));
+ } else {
+ do_warning(ast_token(node), "import \"core:%.*s\" has been deprecated in favour of \"base:%.*s\"", LIT(file_str), LIT(file_str));
+ }
+ }
+ }
+
if (collection_name == "system") {
if (node->kind != Ast_ForeignImportDecl) {
- syntax_error(node, "The library collection 'system' is restrict for 'foreign_library'");
+ do_error(node, "The library collection 'system' is restrict for 'foreign import'");
return false;
} else {
*path = file_str;
@@ -5455,32 +5975,17 @@ gb_internal bool determine_path_from_string(BlockingMutex *file_mutex, Ast *node
}
} else if (!find_library_collection_path(collection_name, &base_dir)) {
// NOTE(bill): It's a naughty name
- syntax_error(node, "Unknown library collection: '%.*s'", LIT(collection_name));
+ do_error(node, "Unknown library collection: '%.*s'", LIT(collection_name));
return false;
}
- } else {
-#if !defined(GB_SYSTEM_WINDOWS)
- // @NOTE(vassvik): foreign imports of shared libraries that are not in the system collection on
- // linux/mac have to be local to the executable for consistency with shared libraries.
- // Unix does not have a concept of "import library" for shared/dynamic libraries,
- // so we need to pass the relative path to the linker, and add the current
- // working directory of the exe to the library search paths.
- // Static libraries can be linked directly with the full pathname
- //
- if (node->kind == Ast_ForeignImportDecl && string_ends_with(file_str, str_lit(".so"))) {
- *path = file_str;
- return true;
- }
-#endif
}
-
if (is_package_name_reserved(file_str)) {
*path = file_str;
- if (collection_name == "core") {
+ if (collection_name == "core" || collection_name == "base") {
return true;
} else {
- syntax_error(node, "The package '%.*s' must be imported with the core library collection: 'core:%.*s'", LIT(file_str), LIT(file_str));
+ do_error(node, "The package '%.*s' must be imported with the 'base' library collection: 'base:%.*s'", LIT(file_str), LIT(file_str));
return false;
}
}
@@ -5496,7 +6001,8 @@ gb_internal bool determine_path_from_string(BlockingMutex *file_mutex, Ast *node
if (has_windows_drive) {
*path = file_str;
} else {
- String fullpath = string_trim_whitespace(get_fullpath_relative(permanent_allocator(), base_dir, file_str));
+ bool ok = false;
+ String fullpath = string_trim_whitespace(get_fullpath_relative(permanent_allocator(), base_dir, file_str, &ok));
*path = fullpath;
}
return true;
@@ -5564,31 +6070,30 @@ gb_internal void parse_setup_file_decls(Parser *p, AstFile *f, String const &bas
} else if (node->kind == Ast_ForeignImportDecl) {
ast_node(fl, ForeignImportDecl, node);
- auto fullpaths = array_make<String>(permanent_allocator(), 0, fl->filepaths.count);
-
- for (Token const &fp : fl->filepaths) {
- String file_str = string_trim_whitespace(string_value_from_token(f, fp));
+ if (fl->filepaths.count == 0) {
+ syntax_error(decls[i], "No foreign paths found");
+ decls[i] = ast_bad_decl(f, ast_token(fl->filepaths[0]), ast_end_token(fl->filepaths[fl->filepaths.count-1]));
+ goto end;
+ } else if (!fl->multiple_filepaths &&
+ fl->filepaths.count == 1) {
+ Ast *fp = fl->filepaths[0];
+ GB_ASSERT(fp->kind == Ast_BasicLit);
+ Token fp_token = fp->BasicLit.token;
+ String file_str = string_trim_whitespace(string_value_from_token(f, fp_token));
String fullpath = file_str;
- if (allow_check_foreign_filepath()) {
+ if (!is_arch_wasm() || string_ends_with(fullpath, str_lit(".o"))) {
String foreign_path = {};
bool ok = determine_path_from_string(&p->file_decl_mutex, node, base_dir, file_str, &foreign_path);
if (!ok) {
- decls[i] = ast_bad_decl(f, fp, fl->filepaths[fl->filepaths.count-1]);
+ decls[i] = ast_bad_decl(f, fp_token, fp_token);
goto end;
}
fullpath = foreign_path;
}
- array_add(&fullpaths, fullpath);
- }
- if (fullpaths.count == 0) {
- syntax_error(decls[i], "No foreign paths found");
- decls[i] = ast_bad_decl(f, fl->filepaths[0], fl->filepaths[fl->filepaths.count-1]);
- goto end;
+ fl->fullpaths = slice_make<String>(permanent_allocator(), 1);
+ fl->fullpaths[0] = fullpath;
}
- fl->fullpaths = slice_from_array(fullpaths);
-
-
} else if (node->kind == Ast_WhenStmt) {
ast_node(ws, WhenStmt, node);
parse_setup_file_when_stmt(p, f, base_dir, ws);
@@ -5618,7 +6123,7 @@ gb_internal String build_tag_get_token(String s, String *out) {
}
gb_internal bool parse_build_tag(Token token_for_pos, String s) {
- String const prefix = str_lit("+build");
+ String const prefix = str_lit("build");
GB_ASSERT(string_starts_with(s, prefix));
s = string_trim_whitespace(substring(s, prefix.len, s.len));
@@ -5631,6 +6136,10 @@ gb_internal bool parse_build_tag(Token token_for_pos, String s) {
while (s.len > 0) {
bool this_kind_correct = true;
+ bool this_kind_os_seen = false;
+ bool this_kind_arch_seen = false;
+ int num_tokens = 0;
+
do {
String p = string_trim_whitespace(build_tag_get_token(s, &s));
if (p.len == 0) break;
@@ -5656,7 +6165,18 @@ gb_internal bool parse_build_tag(Token token_for_pos, String s) {
TargetOsKind os = get_target_os_from_string(p);
TargetArchKind arch = get_target_arch_from_string(p);
+ num_tokens += 1;
+
+ // Catches 'windows linux', which is an impossible combination.
+ // Also catches usage of more than two things within a comma separated group.
+ if (num_tokens > 2 || (this_kind_os_seen && os != TargetOs_Invalid) || (this_kind_arch_seen && arch != TargetArch_Invalid)) {
+ syntax_error(token_for_pos, "Invalid build tag: Missing ',' before '%.*s'. Format: '#+build linux, windows amd64, darwin'", LIT(p));
+ break;
+ }
+
if (os != TargetOs_Invalid) {
+ this_kind_os_seen = true;
+
GB_ASSERT(arch == TargetArch_Invalid);
if (is_notted) {
this_kind_correct = this_kind_correct && (os != build_context.metrics.os);
@@ -5664,6 +6184,8 @@ gb_internal bool parse_build_tag(Token token_for_pos, String s) {
this_kind_correct = this_kind_correct && (os == build_context.metrics.os);
}
} else if (arch != TargetArch_Invalid) {
+ this_kind_arch_seen = true;
+
if (is_notted) {
this_kind_correct = this_kind_correct && (arch != build_context.metrics.arch);
} else {
@@ -5703,7 +6225,7 @@ gb_internal String vet_tag_get_token(String s, String *out) {
gb_internal u64 parse_vet_tag(Token token_for_pos, String s) {
- String const prefix = str_lit("+vet");
+ String const prefix = str_lit("vet");
GB_ASSERT(string_starts_with(s, prefix));
s = string_trim_whitespace(substring(s, prefix.len, s.len));
@@ -5743,10 +6265,16 @@ gb_internal u64 parse_vet_tag(Token token_for_pos, String s) {
syntax_error(token_for_pos, "Invalid vet flag name: %.*s", LIT(p));
error_line("\tExpected one of the following\n");
error_line("\tunused\n");
+ error_line("\tunused-variables\n");
+ error_line("\tunused-imports\n");
+ error_line("\tunused-procedures\n");
error_line("\tshadowing\n");
error_line("\tusing-stmt\n");
error_line("\tusing-param\n");
+ error_line("\tstyle\n");
error_line("\textra\n");
+ error_line("\tcast\n");
+ error_line("\ttabs\n");
return build_context.vet_flags;
}
}
@@ -5764,6 +6292,63 @@ gb_internal u64 parse_vet_tag(Token token_for_pos, String s) {
return vet_flags &~ vet_not_flags;
}
+gb_internal u64 parse_feature_tag(Token token_for_pos, String s) {
+ String const prefix = str_lit("feature");
+ GB_ASSERT(string_starts_with(s, prefix));
+ s = string_trim_whitespace(substring(s, prefix.len, s.len));
+
+ if (s.len == 0) {
+ return OptInFeatureFlag_NONE;
+ }
+
+ u64 feature_flags = 0;
+ u64 feature_not_flags = 0;
+
+ while (s.len > 0) {
+ String p = string_trim_whitespace(vet_tag_get_token(s, &s));
+ if (p.len == 0) {
+ break;
+ }
+
+ bool is_notted = false;
+ if (p[0] == '!') {
+ is_notted = true;
+ p = substring(p, 1, p.len);
+ if (p.len == 0) {
+ syntax_error(token_for_pos, "Expected a feature flag name after '!'");
+ return OptInFeatureFlag_NONE;
+ }
+ }
+
+ u64 flag = get_feature_flag_from_name(p);
+ if (flag != OptInFeatureFlag_NONE) {
+ if (is_notted) {
+ feature_not_flags |= flag;
+ } else {
+ feature_flags |= flag;
+ }
+ } 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");
+ return 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;
+ }
+ if (feature_flags != 0 && feature_not_flags == 0) {
+ return feature_flags;
+ }
+ GB_ASSERT(feature_flags != 0 && feature_not_flags != 0);
+ return feature_flags &~ feature_not_flags;
+}
+
gb_internal String dir_from_path(String path) {
String base_dir = path;
for (isize i = path.len-1; i >= 0; i--) {
@@ -5808,7 +6393,7 @@ gb_internal isize calc_decl_count(Ast *decl) {
}
gb_internal bool parse_build_project_directory_tag(Token token_for_pos, String s) {
- String const prefix = str_lit("+build-project-name");
+ String const prefix = str_lit("build-project-name");
GB_ASSERT(string_starts_with(s, prefix));
s = string_trim_whitespace(substring(s, prefix.len, s.len));
if (s.len == 0) {
@@ -5852,6 +6437,51 @@ gb_internal bool parse_build_project_directory_tag(Token token_for_pos, String s
return any_correct;
}
+gb_internal bool parse_file_tag(const String &lc, const Token &tok, AstFile *f) {
+ if (string_starts_with(lc, str_lit("build-project-name"))) {
+ if (!parse_build_project_directory_tag(tok, lc)) {
+ return false;
+ }
+ } else if (string_starts_with(lc, str_lit("build"))) {
+ if (!parse_build_tag(tok, lc)) {
+ return false;
+ }
+ } 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("ignore"))) {
+ return false;
+ } else if (string_starts_with(lc, str_lit("private"))) {
+ f->flags |= AstFile_IsPrivatePkg;
+ String command = string_trim_starts_with(lc, str_lit("private "));
+ command = string_trim_whitespace(command);
+ if (lc == "private") {
+ f->flags |= AstFile_IsPrivatePkg;
+ } else if (command == "package") {
+ f->flags |= AstFile_IsPrivatePkg;
+ } else if (command == "file") {
+ f->flags |= AstFile_IsPrivateFile;
+ }
+ } else if (string_starts_with(lc, str_lit("feature"))) {
+ f->feature_flags |= parse_feature_tag(tok, lc);
+ f->feature_flags_set = true;
+ } else if (lc == "lazy") {
+ if (build_context.ignore_lazy) {
+ // Ignore
+ } else if (f->pkg->kind == Package_Init && build_context.command_kind == Command_doc) {
+ // Ignore
+ } else {
+ f->flags |= AstFile_IsLazy;
+ }
+ } else if (lc == "no-instrumentation") {
+ f->flags |= AstFile_NoInstrumentation;
+ } else {
+ error(tok, "Unknown tag '%.*s'", LIT(lc));
+ }
+
+ return true;
+}
+
gb_internal bool parse_file(Parser *p, AstFile *f) {
if (f->tokens.count == 0) {
return true;
@@ -5870,8 +6500,45 @@ gb_internal bool parse_file(Parser *p, AstFile *f) {
CommentGroup *docs = f->lead_comment;
+ Array<Token> tags = array_make<Token>(temporary_allocator());
+ bool first_invalid_token_set = false;
+ Token first_invalid_token = {};
+
+ while (f->curr_token.kind != Token_package && f->curr_token.kind != Token_EOF) {
+ if (f->curr_token.kind == Token_Comment) {
+ consume_comment_groups(f, f->prev_token);
+ } else if (f->curr_token.kind == Token_FileTag) {
+ array_add(&tags, f->curr_token);
+ advance_token(f);
+ } else {
+ if (!first_invalid_token_set) {
+ first_invalid_token_set = true;
+ first_invalid_token = f->curr_token;
+ }
+
+ advance_token(f);
+ }
+ }
+
if (f->curr_token.kind != Token_package) {
- syntax_error(f->curr_token, "Expected a package declaration at the beginning of the file");
+ ERROR_BLOCK();
+
+ // The while loop above scanned until it found the package token. If we never
+ // found one, then make this error appear on the first invalid token line.
+ Token t = first_invalid_token_set ? first_invalid_token : f->curr_token;
+ syntax_error(t, "Expected a package declaration at the beginning of the file");
+
+ // IMPORTANT NOTE(bill): this is technically a race condition with the suggestion, but it's ony a suggession
+ // so in practice is should be "fine"
+ if (f->pkg && f->pkg->name != "") {
+ error_line("\tSuggestion: Add 'package %.*s' to the top of the file\n", LIT(f->pkg->name));
+ }
+ return false;
+ }
+
+ // There was an OK package declaration. But there some invalid token was hit before the package declaration.
+ if (first_invalid_token_set) {
+ syntax_error(first_invalid_token, "Expected only comments or lines starting with '#+' before the package declaration");
return false;
}
@@ -5879,14 +6546,6 @@ gb_internal bool parse_file(Parser *p, AstFile *f) {
if (f->package_token.kind != Token_package) {
return false;
}
- if (docs != nullptr) {
- TokenPos end = token_pos_end(docs->list[docs->list.count-1]);
- if (end.line == f->package_token.pos.line || end.line+1 == f->package_token.pos.line) {
- // Okay
- } else {
- docs = nullptr;
- }
- }
Token package_name = expect_token_after(f, Token_Ident, "package");
if (package_name.kind == Token_Ident) {
@@ -5900,53 +6559,38 @@ gb_internal bool parse_file(Parser *p, AstFile *f) {
}
f->package_name = package_name.string;
- if (!f->pkg->is_single_file && docs != nullptr && docs->list.count > 0) {
- for (Token const &tok : docs->list) {
- GB_ASSERT(tok.kind == Token_Comment);
- String str = tok.string;
- if (string_starts_with(str, str_lit("//"))) {
+ {
+ if (docs != nullptr && docs->list.count > 0) {
+ for (Token const &tok : docs->list) {
+ GB_ASSERT(tok.kind == Token_Comment);
+ String str = tok.string;
+
+ if (!string_starts_with(str, str_lit("//"))) {
+ continue;
+ }
+
String lc = string_trim_whitespace(substring(str, 2, str.len));
- if (lc.len > 0 && lc[0] == '+') {
- if (string_starts_with(lc, str_lit("+build-project-name"))) {
- if (!parse_build_project_directory_tag(tok, lc)) {
- return false;
- }
- } else if (string_starts_with(lc, str_lit("+build"))) {
- if (!parse_build_tag(tok, lc)) {
- return false;
- }
- } 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("+ignore"))) {
- return false;
- } else if (string_starts_with(lc, str_lit("+private"))) {
- f->flags |= AstFile_IsPrivatePkg;
- String command = string_trim_starts_with(lc, str_lit("+private "));
- command = string_trim_whitespace(command);
- if (lc == "+private") {
- f->flags |= AstFile_IsPrivatePkg;
- } else if (command == "package") {
- f->flags |= AstFile_IsPrivatePkg;
- } else if (command == "file") {
- f->flags |= AstFile_IsPrivateFile;
- }
- } else if (lc == "+lazy") {
- if (build_context.ignore_lazy) {
- // Ignore
- } else if (f->flags & AstFile_IsTest) {
- // Ignore
- } else if (f->pkg->kind == Package_Init && build_context.command_kind == Command_doc) {
- // Ignore
- } else {
- f->flags |= AstFile_IsLazy;
- }
- } else {
- warning(tok, "Ignoring unknown tag '%.*s'", LIT(lc));
+ if (string_starts_with(lc, str_lit("+"))) {
+ syntax_warning(tok, "'//+' is deprecated: Use '#+' instead");
+ String lt = substring(lc, 1, lc.len);
+ if (parse_file_tag(lt, tok, f) == false) {
+ return false;
}
}
}
}
+
+ for (Token const &tok : tags) {
+ GB_ASSERT(tok.kind == Token_FileTag);
+ String str = tok.string;
+
+ if (string_starts_with(str, str_lit("#+"))) {
+ String lt = string_trim_whitespace(substring(str, 2, str.len));
+ if (parse_file_tag(lt, tok, f) == false) {
+ return false;
+ }
+ }
+ }
}
Ast *pd = ast_package_decl(f, f->package_token, package_name, docs, f->line_comment);
@@ -5967,7 +6611,7 @@ gb_internal bool parse_file(Parser *p, AstFile *f) {
}
f->total_file_decl_count += calc_decl_count(stmt);
- if (stmt->kind == Ast_WhenStmt || stmt->kind == Ast_ExprStmt || stmt->kind == Ast_ImportDecl) {
+ if (stmt->kind == Ast_WhenStmt || stmt->kind == Ast_ExprStmt || stmt->kind == Ast_ImportDecl || stmt->kind == Ast_ForeignBlockDecl) {
f->delayed_decl_count += 1;
}
}
@@ -6007,7 +6651,7 @@ gb_internal ParseFileError process_imported_file(Parser *p, ImportedFile importe
if (err == ParseFile_EmptyFile) {
if (fi.fullpath == p->init_fullpath) {
syntax_error(pos, "Initial file is empty - %.*s\n", LIT(p->init_fullpath));
- gb_exit(1);
+ exit_with_errors();
}
} else {
switch (err) {
@@ -6051,11 +6695,6 @@ gb_internal ParseFileError process_imported_file(Parser *p, ImportedFile importe
if (build_context.command_kind == Command_test) {
String name = file->fullpath;
name = remove_extension_from_path(name);
-
- String test_suffix = str_lit("_test");
- if (string_ends_with(name, test_suffix) && name != test_suffix) {
- file->flags |= AstFile_IsTest;
- }
}
@@ -6090,10 +6729,11 @@ gb_internal ParseFileError parse_packages(Parser *p, String init_filename) {
GB_ASSERT(init_filename.text[init_filename.len] == 0);
String init_fullpath = path_to_full_path(permanent_allocator(), init_filename);
+
if (!path_is_directory(init_fullpath)) {
String const ext = str_lit(".odin");
if (!string_ends_with(init_fullpath, ext)) {
- error_line("Expected either a directory or a .odin file, got '%.*s'\n", LIT(init_filename));
+ error({}, "Expected either a directory or a .odin file, got '%.*s'\n", LIT(init_filename));
return ParseFile_WrongExtension;
}
} else if (init_fullpath.len != 0) {
@@ -6103,10 +6743,9 @@ gb_internal ParseFileError parse_packages(Parser *p, String init_filename) {
}
if ((build_context.command_kind & Command__does_build) &&
build_context.build_mode == BuildMode_Executable) {
- String short_path = filename_from_path(path);
- char *cpath = alloc_cstring(temporary_allocator(), short_path);
- if (gb_file_exists(cpath)) {
- error_line("Please specify the executable name with -out:<string> as a directory exists with the same name in the current working directory");
+ String output_path = path_to_string(temporary_allocator(), build_context.build_paths[8]);
+ if (path_is_directory(output_path)) {
+ error({}, "Please specify the executable name with -out:<string> as a directory exists with the same name in the current working directory");
return ParseFile_DirectoryAlreadyExists;
}
}
@@ -6116,7 +6755,11 @@ gb_internal ParseFileError parse_packages(Parser *p, String init_filename) {
{ // Add these packages serially and then process them parallel
TokenPos init_pos = {};
{
- String s = get_fullpath_core(permanent_allocator(), str_lit("runtime"));
+ bool ok = false;
+ String s = get_fullpath_base_collection(permanent_allocator(), str_lit("runtime"), &ok);
+ if (!ok) {
+ compiler_error("Unable to find The 'base:runtime' package. Is the ODIN_ROOT set up correctly?");
+ }
try_add_import_path(p, s, s, init_pos, Package_Runtime);
}
@@ -6124,7 +6767,11 @@ gb_internal ParseFileError parse_packages(Parser *p, String init_filename) {
p->init_fullpath = init_fullpath;
if (build_context.command_kind == Command_test) {
- String s = get_fullpath_core(permanent_allocator(), str_lit("testing"));
+ bool ok = false;
+ String s = get_fullpath_core_collection(permanent_allocator(), str_lit("testing"), &ok);
+ if (!ok) {
+ compiler_error("Unable to find The 'core:testing' package. Is the ODIN_ROOT set up correctly?");
+ }
try_add_import_path(p, s, s, init_pos, Package_Normal);
}
@@ -6134,7 +6781,7 @@ gb_internal ParseFileError parse_packages(Parser *p, String init_filename) {
if (!path_is_directory(fullpath)) {
String const ext = str_lit(".odin");
if (!string_ends_with(fullpath, ext)) {
- error_line("Expected either a directory or a .odin file, got '%.*s'\n", LIT(fullpath));
+ error({}, "Expected either a directory or a .odin file, got '%.*s'\n", LIT(fullpath));
return ParseFile_WrongExtension;
}
}
@@ -6165,6 +6812,13 @@ gb_internal ParseFileError parse_packages(Parser *p, String init_filename) {
}
}
}
+
+ for (AstPackage *pkg : p->packages) {
+ for (AstFile *file : pkg->files) {
+ p->total_seen_load_directive_count += file->seen_load_directive_count;
+ }
+ }
+
return ParseFile_None;
}
diff --git a/src/parser.hpp b/src/parser.hpp
index bce818652..bbf70d03e 100644
--- a/src/parser.hpp
+++ b/src/parser.hpp
@@ -74,13 +74,15 @@ enum AstFileFlag : u32 {
AstFile_IsPrivatePkg = 1<<0,
AstFile_IsPrivateFile = 1<<1,
- AstFile_IsTest = 1<<3,
AstFile_IsLazy = 1<<4,
+
+ AstFile_NoInstrumentation = 1<<5,
};
enum AstDelayQueueKind {
AstDelayQueue_Import,
AstDelayQueue_Expr,
+ AstDelayQueue_ForeignBlock,
AstDelayQueue_COUNT,
};
@@ -106,7 +108,9 @@ struct AstFile {
String package_name;
u64 vet_flags;
+ u64 feature_flags;
bool vet_flags_set;
+ bool feature_flags_set;
// >= 0: In Expression
// < 0: In Control Clause
@@ -139,6 +143,8 @@ struct AstFile {
// This is effectively a queue but does not require any multi-threading capabilities
Array<Ast *> delayed_decls_queues[AstDelayQueue_COUNT];
+ std::atomic<isize> seen_load_directive_count;
+
#define PARSER_MAX_FIX_COUNT 6
isize fix_count;
TokenPos fix_prev_pos;
@@ -209,6 +215,8 @@ struct Parser {
std::atomic<isize> total_token_count;
std::atomic<isize> total_line_count;
+ std::atomic<isize> total_seen_load_directive_count;
+
// TODO(bill): What should this mutex be per?
// * Parser
// * Package
@@ -308,6 +316,8 @@ enum StateFlag : u8 {
enum ViralStateFlag : u8 {
ViralStateFlag_ContainsDeferredProcedure = 1<<0,
+ ViralStateFlag_ContainsOrBreak = 1<<1,
+ ViralStateFlag_ContainsOrReturn = 1<<2,
};
@@ -322,9 +332,12 @@ enum FieldFlag : u32 {
FieldFlag_any_int = 1<<6,
FieldFlag_subtype = 1<<7,
FieldFlag_by_ptr = 1<<8,
+ FieldFlag_no_broadcast = 1<<9, // disallow array programming
+
+ FieldFlag_no_capture = 1<<11,
// Internal use by the parser only
- FieldFlag_Tags = 1<<10,
+ FieldFlag_Tags = 1<<15,
FieldFlag_Results = 1<<16,
@@ -332,7 +345,10 @@ enum FieldFlag : u32 {
FieldFlag_Invalid = 1u<<31,
// Parameter List Restrictions
- FieldFlag_Signature = FieldFlag_ellipsis|FieldFlag_using|FieldFlag_no_alias|FieldFlag_c_vararg|FieldFlag_const|FieldFlag_any_int|FieldFlag_by_ptr,
+ FieldFlag_Signature = FieldFlag_ellipsis|FieldFlag_using|FieldFlag_no_alias|FieldFlag_c_vararg|
+ FieldFlag_const|FieldFlag_any_int|FieldFlag_by_ptr|FieldFlag_no_broadcast|
+ FieldFlag_no_capture,
+
FieldFlag_Struct = FieldFlag_using|FieldFlag_subtype|FieldFlag_Tags,
};
@@ -427,6 +443,7 @@ AST_KIND(_ExprBegin, "", bool) \
Ast *expr, *selector; \
u8 swizzle_count; /*maximum of 4 components, if set, count >= 2*/ \
u8 swizzle_indices; /*2 bits per component*/ \
+ bool is_bit_field; \
}) \
AST_KIND(ImplicitSelectorExpr, "implicit selector expression", struct { Token token; Ast *selector; }) \
AST_KIND(SelectorCallExpr, "selector call expression", struct { \
@@ -452,6 +469,7 @@ AST_KIND(_ExprBegin, "", bool) \
bool optional_ok_one; \
bool was_selector; \
AstSplitArgs *split_args; \
+ Entity *entity_procedure_of; \
}) \
AST_KIND(FieldValue, "field value", struct { Token eq; Ast *field, *value; }) \
AST_KIND(EnumFieldValue, "enum field value", struct { \
@@ -625,7 +643,8 @@ AST_KIND(_DeclBegin, "", bool) \
}) \
AST_KIND(ForeignImportDecl, "foreign import declaration", struct { \
Token token; \
- Slice<Token> filepaths; \
+ Slice<Ast *> filepaths; \
+ bool multiple_filepaths; \
Token library_name; \
String collection_name; \
Slice<String> fullpaths; \
@@ -648,6 +667,14 @@ AST_KIND(_DeclEnd, "", bool) \
CommentGroup * docs; \
CommentGroup * comment; \
}) \
+ AST_KIND(BitFieldField, "bit field field", struct { \
+ Ast * name; \
+ Ast * type; \
+ Ast * bit_size; \
+ Token tag; \
+ CommentGroup *docs; \
+ CommentGroup *comment; \
+ }) \
AST_KIND(FieldList, "field list", struct { \
Token token; \
Slice<Ast *> list; \
@@ -711,6 +738,8 @@ AST_KIND(_TypeBegin, "", bool) \
isize field_count; \
Ast *polymorphic_params; \
Ast *align; \
+ Ast *min_field_align; \
+ Ast *max_field_align; \
Token where_token; \
Slice<Ast *> where_clauses; \
bool is_packed; \
@@ -739,6 +768,14 @@ AST_KIND(_TypeBegin, "", bool) \
Ast * elem; \
Ast * underlying; \
}) \
+ AST_KIND(BitFieldType, "bit field type", struct { \
+ Scope *scope; \
+ Token token; \
+ Ast * backing_type; \
+ Token open; \
+ Slice<Ast *> fields; /* BitFieldField */ \
+ Token close; \
+ }) \
AST_KIND(MapType, "map type", struct { \
Token token; \
Ast *count; \
@@ -750,6 +787,7 @@ AST_KIND(_TypeBegin, "", bool) \
Ast *row_count; \
Ast *column_count; \
Ast *elem; \
+ bool is_row_major; \
}) \
AST_KIND(_TypeEnd, "", bool)
@@ -844,13 +882,14 @@ gb_internal gb_inline bool is_ast_when_stmt(Ast *node) {
return node->kind == Ast_WhenStmt;
}
-gb_global gb_thread_local Arena global_thread_local_ast_arena = {};
-
gb_internal gb_inline gbAllocator ast_allocator(AstFile *f) {
- return arena_allocator(&global_thread_local_ast_arena);
+ return permanent_allocator();
}
gb_internal Ast *alloc_ast_node(AstFile *f, AstKind kind);
gb_internal gbString expr_to_string(Ast *expression);
-gb_internal bool allow_field_separator(AstFile *f); \ No newline at end of file
+gb_internal bool allow_field_separator(AstFile *f);
+
+
+gb_internal void parse_enforce_tabs(AstFile *f); \ No newline at end of file
diff --git a/src/parser_pos.cpp b/src/parser_pos.cpp
index f49c40f16..1ffd3a82f 100644
--- a/src/parser_pos.cpp
+++ b/src/parser_pos.cpp
@@ -111,6 +111,7 @@ gb_internal Token ast_token(Ast *node) {
case Ast_UnionType: return node->UnionType.token;
case Ast_EnumType: return node->EnumType.token;
case Ast_BitSetType: return node->BitSetType.token;
+ case Ast_BitFieldType: return node->BitFieldType.token;
case Ast_MapType: return node->MapType.token;
case Ast_MatrixType: return node->MatrixType.token;
}
@@ -277,7 +278,7 @@ Token ast_end_token(Ast *node) {
case Ast_ImportDecl: return node->ImportDecl.relpath;
case Ast_ForeignImportDecl:
if (node->ForeignImportDecl.filepaths.count > 0) {
- return node->ForeignImportDecl.filepaths[node->ForeignImportDecl.filepaths.count-1];
+ return ast_end_token(node->ForeignImportDecl.filepaths[node->ForeignImportDecl.filepaths.count-1]);
}
if (node->ForeignImportDecl.library_name.kind != Token_Invalid) {
return node->ForeignImportDecl.library_name;
@@ -364,6 +365,8 @@ Token ast_end_token(Ast *node) {
return ast_end_token(node->BitSetType.underlying);
}
return ast_end_token(node->BitSetType.elem);
+ case Ast_BitFieldType:
+ return node->BitFieldType.close;
case Ast_MapType: return ast_end_token(node->MapType.value);
case Ast_MatrixType: return ast_end_token(node->MatrixType.elem);
}
diff --git a/src/path.cpp b/src/path.cpp
index de80c9def..2c08ddd98 100644
--- a/src/path.cpp
+++ b/src/path.cpp
@@ -1,461 +1,465 @@
-/*
- Path handling utilities.
-*/
-#if !defined(GB_SYSTEM_WINDOWS)
-#include <unistd.h>
-#endif
-
-gb_internal String remove_extension_from_path(String const &s) {
- if (s.len != 0 && s.text[s.len-1] == '.') {
- return s;
- }
- for (isize i = s.len-1; i >= 0; i--) {
- if (s[i] == '.') {
- return substring(s, 0, i);
- }
- }
- return s;
-}
-
-gb_internal String remove_directory_from_path(String const &s) {
- isize len = 0;
- for (isize i = s.len-1; i >= 0; i--) {
- if (s[i] == '/' ||
- s[i] == '\\') {
- break;
- }
- len += 1;
- }
- return substring(s, s.len-len, s.len);
-}
-
-
-// NOTE(Mark Naughton): getcwd as String
-#if !defined(GB_SYSTEM_WINDOWS)
-gb_internal String get_current_directory(void) {
- char cwd[256];
- getcwd(cwd, 256);
-
- return make_string_c(cwd);
-}
-
-#else
-gb_internal String get_current_directory(void) {
- gbAllocator a = heap_allocator();
-
- wchar_t cwd[256];
- GetCurrentDirectoryW(256, cwd);
-
- String16 wstr = make_string16_c(cwd);
-
- return string16_to_string(a, wstr);
-}
-#endif
-
-gb_internal bool path_is_directory(String path);
-
-gb_internal String directory_from_path(String const &s) {
- if (path_is_directory(s)) {
- return s;
- }
-
- isize i = s.len-1;
- for (; i >= 0; i--) {
- if (s[i] == '/' ||
- s[i] == '\\') {
- break;
- }
- }
- if (i >= 0) {
- return substring(s, 0, i);
- }
- return substring(s, 0, 0);
-}
-
-#if defined(GB_SYSTEM_WINDOWS)
- gb_internal bool path_is_directory(String path) {
- gbAllocator a = heap_allocator();
- String16 wstr = string_to_string16(a, path);
- defer (gb_free(a, wstr.text));
-
- i32 attribs = GetFileAttributesW(wstr.text);
- if (attribs < 0) return false;
-
- return (attribs & FILE_ATTRIBUTE_DIRECTORY) != 0;
- }
-
-#else
- gb_internal bool path_is_directory(String path) {
- gbAllocator a = heap_allocator();
- char *copy = cast(char *)copy_string(a, path).text;
- defer (gb_free(a, copy));
-
- struct stat s;
- if (stat(copy, &s) == 0) {
- return (s.st_mode & S_IFDIR) != 0;
- }
- return false;
- }
-#endif
-
-
-gb_internal String path_to_full_path(gbAllocator a, String path) {
- gbAllocator ha = heap_allocator();
- char *path_c = gb_alloc_str_len(ha, cast(char *)path.text, path.len);
- defer (gb_free(ha, path_c));
-
- char *fullpath = gb_path_get_full_name(a, path_c);
- String res = string_trim_whitespace(make_string_c(fullpath));
-#if defined(GB_SYSTEM_WINDOWS)
- for (isize i = 0; i < res.len; i++) {
- if (res.text[i] == '\\') {
- res.text[i] = '/';
- }
- }
-#endif
- return copy_string(a, res);
-}
-
-struct Path {
- String basename;
- String name;
- String ext;
-};
-
-// NOTE(Jeroen): Naively turns a Path into a string.
-gb_internal String path_to_string(gbAllocator a, Path path) {
- if (path.basename.len + path.name.len + path.ext.len == 0) {
- return make_string(nullptr, 0);
- }
-
- isize len = path.basename.len + 1 + path.name.len + 1;
- if (path.ext.len > 0) {
- len += path.ext.len + 1;
- }
-
- u8 *str = gb_alloc_array(a, u8, len);
-
- isize i = 0;
- gb_memmove(str+i, path.basename.text, path.basename.len); i += path.basename.len;
-
- gb_memmove(str+i, "/", 1); i += 1;
-
- gb_memmove(str+i, path.name.text, path.name.len); i += path.name.len;
- if (path.ext.len > 0) {
- gb_memmove(str+i, ".", 1); i += 1;
- gb_memmove(str+i, path.ext.text, path.ext.len); i += path.ext.len;
- }
- str[i] = 0;
-
- String res = make_string(str, i);
- res = string_trim_whitespace(res);
- return res;
-}
-
-// NOTE(Jeroen): Naively turns a Path into a string, then normalizes it using `path_to_full_path`.
-gb_internal String path_to_full_path(gbAllocator a, Path path) {
- String temp = path_to_string(heap_allocator(), path);
- defer (gb_free(heap_allocator(), temp.text));
-
- return path_to_full_path(a, temp);
-}
-
-// NOTE(Jeroen): Takes a path like "odin" or "W:\Odin", turns it into a full path,
-// and then breaks it into its components to make a Path.
-gb_internal Path path_from_string(gbAllocator a, String const &path) {
- Path res = {};
-
- if (path.len == 0) return res;
-
- String fullpath = path_to_full_path(a, path);
- defer (gb_free(heap_allocator(), fullpath.text));
-
- res.basename = directory_from_path(fullpath);
- res.basename = copy_string(a, res.basename);
-
- if (path_is_directory(fullpath)) {
- // It's a directory. We don't need to tinker with the name and extension.
- // It could have a superfluous trailing `/`. Remove it if so.
- if (res.basename.len > 0 && res.basename.text[res.basename.len - 1] == '/') {
- res.basename.len--;
- }
- return res;
- }
-
- // Note(Dragos): Is the copy_string required if it's a substring?
- isize name_start = (res.basename.len > 0) ? res.basename.len + 1 : res.basename.len;
- res.name = substring(fullpath, name_start, fullpath.len);
- res.name = remove_extension_from_path(res.name);
- res.name = copy_string(a, res.name);
-
- res.ext = path_extension(fullpath, false); // false says not to include the dot.
- res.ext = copy_string(a, res.ext);
- return res;
-}
-
-// NOTE(Jeroen): Takes a path String and returns the last path element.
-gb_internal String last_path_element(String const &path) {
- isize count = 0;
- u8 * start = (u8 *)(&path.text[path.len - 1]);
- for (isize length = path.len; length > 0 && path.text[length - 1] != '/'; length--) {
- count++;
- start--;
- }
- if (count > 0) {
- start++; // Advance past the `/` and return the substring.
- String res = make_string(start, count);
- return res;
- }
- // Must be a root path like `/` or `C:/`, return empty String.
- return STR_LIT("");
-}
-
-gb_internal bool path_is_directory(Path path) {
- String path_string = path_to_full_path(heap_allocator(), path);
- defer (gb_free(heap_allocator(), path_string.text));
-
- return path_is_directory(path_string);
-}
-
-struct FileInfo {
- String name;
- String fullpath;
- i64 size;
- bool is_dir;
-};
-
-enum ReadDirectoryError {
- ReadDirectory_None,
-
- ReadDirectory_InvalidPath,
- ReadDirectory_NotExists,
- ReadDirectory_Permission,
- ReadDirectory_NotDir,
- ReadDirectory_Empty,
- ReadDirectory_Unknown,
-
- ReadDirectory_COUNT,
-};
-
-gb_internal i64 get_file_size(String path) {
- char *c_str = alloc_cstring(heap_allocator(), path);
- defer (gb_free(heap_allocator(), c_str));
-
- gbFile f = {};
- gbFileError err = gb_file_open(&f, c_str);
- defer (gb_file_close(&f));
- if (err != gbFileError_None) {
- return -1;
- }
- return gb_file_size(&f);
-}
-
-
-#if defined(GB_SYSTEM_WINDOWS)
-gb_internal ReadDirectoryError read_directory(String path, Array<FileInfo> *fi) {
- GB_ASSERT(fi != nullptr);
-
-
- while (path.len > 0) {
- Rune end = path[path.len-1];
- if (end == '/') {
- path.len -= 1;
- } else if (end == '\\') {
- path.len -= 1;
- } else {
- break;
- }
- }
-
- if (path.len == 0) {
- return ReadDirectory_InvalidPath;
- }
- {
- char *c_str = alloc_cstring(temporary_allocator(), path);
- gbFile f = {};
- gbFileError file_err = gb_file_open(&f, c_str);
- defer (gb_file_close(&f));
-
- switch (file_err) {
- case gbFileError_Invalid: return ReadDirectory_InvalidPath;
- case gbFileError_NotExists: return ReadDirectory_NotExists;
- // case gbFileError_Permission: return ReadDirectory_Permission;
- }
- }
-
- if (!path_is_directory(path)) {
- return ReadDirectory_NotDir;
- }
-
-
- gbAllocator a = heap_allocator();
- char *new_path = gb_alloc_array(a, char, path.len+3);
- defer (gb_free(a, new_path));
-
- gb_memmove(new_path, path.text, path.len);
- gb_memmove(new_path+path.len, "/*", 2);
- new_path[path.len+2] = 0;
-
- String np = make_string(cast(u8 *)new_path, path.len+2);
- String16 wstr = string_to_string16(a, np);
- defer (gb_free(a, wstr.text));
-
- WIN32_FIND_DATAW file_data = {};
- HANDLE find_file = FindFirstFileW(wstr.text, &file_data);
- if (find_file == INVALID_HANDLE_VALUE) {
- return ReadDirectory_Unknown;
- }
- defer (FindClose(find_file));
-
- array_init(fi, a, 0, 100);
-
- do {
- 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));
- if (name == "." || name == "..") {
- gb_free(a, name.text);
- continue;
- }
-
- String filepath = {};
- filepath.len = path.len+1+name.len;
- filepath.text = gb_alloc_array(a, u8, filepath.len+1);
- defer (gb_free(a, filepath.text));
- gb_memmove(filepath.text, path.text, path.len);
- gb_memmove(filepath.text+path.len, "/", 1);
- gb_memmove(filepath.text+path.len+1, name.text, name.len);
-
- FileInfo info = {};
- info.name = name;
- info.fullpath = path_to_full_path(a, filepath);
- info.size = cast(i64)size;
- info.is_dir = (file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
- array_add(fi, info);
- } while (FindNextFileW(find_file, &file_data));
-
- if (fi->count == 0) {
- return ReadDirectory_Empty;
- }
-
- return ReadDirectory_None;
-}
-#elif defined(GB_SYSTEM_LINUX) || defined(GB_SYSTEM_OSX) || defined(GB_SYSTEM_FREEBSD) || defined(GB_SYSTEM_OPENBSD)
-
-#include <dirent.h>
-
-gb_internal ReadDirectoryError read_directory(String path, Array<FileInfo> *fi) {
- GB_ASSERT(fi != nullptr);
-
- gbAllocator a = heap_allocator();
-
- char *c_path = alloc_cstring(a, path);
- defer (gb_free(a, c_path));
-
- DIR *dir = opendir(c_path);
- if (!dir) {
- switch (errno) {
- case ENOENT:
- return ReadDirectory_NotExists;
- case EACCES:
- return ReadDirectory_Permission;
- case ENOTDIR:
- return ReadDirectory_NotDir;
- default:
- // ENOMEM: out of memory
- // EMFILE: per-process limit on open fds reached
- // ENFILE: system-wide limit on total open files reached
- return ReadDirectory_Unknown;
- }
- GB_PANIC("unreachable");
- }
-
- array_init(fi, a, 0, 100);
-
- for (;;) {
- struct dirent *entry = readdir(dir);
- if (entry == nullptr) {
- break;
- }
-
- String name = make_string_c(entry->d_name);
- if (name == "." || name == "..") {
- continue;
- }
-
- String filepath = {};
- filepath.len = path.len+1+name.len;
- filepath.text = gb_alloc_array(a, u8, filepath.len+1);
- defer (gb_free(a, filepath.text));
- gb_memmove(filepath.text, path.text, path.len);
- gb_memmove(filepath.text+path.len, "/", 1);
- gb_memmove(filepath.text+path.len+1, name.text, name.len);
- filepath.text[filepath.len] = 0;
-
-
- struct stat dir_stat = {};
-
- if (stat((char *)filepath.text, &dir_stat)) {
- continue;
- }
-
- if (S_ISDIR(dir_stat.st_mode)) {
- continue;
- }
-
- i64 size = dir_stat.st_size;
-
- FileInfo info = {};
- info.name = name;
- info.fullpath = path_to_full_path(a, filepath);
- info.size = size;
- array_add(fi, info);
- }
-
- if (fi->count == 0) {
- return ReadDirectory_Empty;
- }
-
- return ReadDirectory_None;
-}
-
-
-#else
-#error Implement read_directory
-#endif
-
-#if !defined(GB_SYSTEM_WINDOWS)
-gb_internal bool write_directory(String path) {
- char const *pathname = (char *) path.text;
-
- if (access(pathname, W_OK) < 0) {
- return false;
- }
-
- return true;
-}
-#else
-gb_internal bool write_directory(String path) {
- String16 wstr = string_to_string16(heap_allocator(), path);
- LPCWSTR wdirectory_name = wstr.text;
-
- HANDLE directory = CreateFileW(wdirectory_name,
- GENERIC_WRITE,
- 0,
- NULL,
- OPEN_EXISTING,
- FILE_FLAG_BACKUP_SEMANTICS,
- NULL);
-
- if (directory == INVALID_HANDLE_VALUE) {
- DWORD error_code = GetLastError();
- if (error_code == ERROR_ACCESS_DENIED) {
- return false;
- }
- }
-
- CloseHandle(directory);
- return true;
-}
-#endif
+/*
+ Path handling utilities.
+*/
+#if !defined(GB_SYSTEM_WINDOWS)
+#include <unistd.h>
+#endif
+
+gb_internal String remove_extension_from_path(String const &s) {
+ if (s.len != 0 && s.text[s.len-1] == '.') {
+ return s;
+ }
+ for (isize i = s.len-1; i >= 0; i--) {
+ if (s[i] == '.') {
+ return substring(s, 0, i);
+ }
+ }
+ return s;
+}
+
+gb_internal String remove_directory_from_path(String const &s) {
+ isize len = 0;
+ for (isize i = s.len-1; i >= 0; i--) {
+ if (s[i] == '/' ||
+ s[i] == '\\') {
+ break;
+ }
+ len += 1;
+ }
+ return substring(s, s.len-len, s.len);
+}
+
+
+// NOTE(Mark Naughton): getcwd as String
+#if !defined(GB_SYSTEM_WINDOWS)
+gb_internal String get_current_directory(void) {
+ char cwd[256];
+ getcwd(cwd, 256);
+
+ return make_string_c(cwd);
+}
+
+#else
+gb_internal String get_current_directory(void) {
+ gbAllocator a = heap_allocator();
+
+ wchar_t cwd[256];
+ GetCurrentDirectoryW(256, cwd);
+
+ String16 wstr = make_string16_c(cwd);
+
+ return string16_to_string(a, wstr);
+}
+#endif
+
+gb_internal bool path_is_directory(String path);
+
+gb_internal String directory_from_path(String const &s) {
+ if (path_is_directory(s)) {
+ return s;
+ }
+
+ isize i = s.len-1;
+ for (; i >= 0; i--) {
+ if (s[i] == '/' ||
+ s[i] == '\\') {
+ break;
+ }
+ }
+ if (i >= 0) {
+ return substring(s, 0, i);
+ }
+ return substring(s, 0, 0);
+}
+
+#if defined(GB_SYSTEM_WINDOWS)
+ gb_internal bool path_is_directory(String path) {
+ gbAllocator a = heap_allocator();
+ String16 wstr = string_to_string16(a, path);
+ defer (gb_free(a, wstr.text));
+
+ i32 attribs = GetFileAttributesW(wstr.text);
+ if (attribs < 0) return false;
+
+ return (attribs & FILE_ATTRIBUTE_DIRECTORY) != 0;
+ }
+
+#else
+ gb_internal bool path_is_directory(String path) {
+ gbAllocator a = heap_allocator();
+ char *copy = cast(char *)copy_string(a, path).text;
+ defer (gb_free(a, copy));
+
+ struct stat s;
+ if (stat(copy, &s) == 0) {
+ return (s.st_mode & S_IFDIR) != 0;
+ }
+ return false;
+ }
+#endif
+
+
+gb_internal String path_to_full_path(gbAllocator a, String path) {
+ gbAllocator ha = heap_allocator();
+ char *path_c = gb_alloc_str_len(ha, cast(char *)path.text, path.len);
+ defer (gb_free(ha, path_c));
+
+ char *fullpath = gb_path_get_full_name(a, path_c);
+ String res = string_trim_whitespace(make_string_c(fullpath));
+#if defined(GB_SYSTEM_WINDOWS)
+ for (isize i = 0; i < res.len; i++) {
+ if (res.text[i] == '\\') {
+ res.text[i] = '/';
+ }
+ }
+#endif
+ return copy_string(a, res);
+}
+
+struct Path {
+ String basename;
+ String name;
+ String ext;
+};
+
+// NOTE(Jeroen): Naively turns a Path into a string.
+gb_internal String path_to_string(gbAllocator a, Path path) {
+ if (path.basename.len + path.name.len + path.ext.len == 0) {
+ return make_string(nullptr, 0);
+ }
+
+ isize len = path.basename.len + 1 + path.name.len + 1;
+ if (path.ext.len > 0) {
+ len += path.ext.len + 1;
+ }
+
+ u8 *str = gb_alloc_array(a, u8, len);
+
+ isize i = 0;
+ gb_memmove(str+i, path.basename.text, path.basename.len); i += path.basename.len;
+
+ gb_memmove(str+i, "/", 1); i += 1;
+
+ gb_memmove(str+i, path.name.text, path.name.len); i += path.name.len;
+ if (path.ext.len > 0) {
+ gb_memmove(str+i, ".", 1); i += 1;
+ gb_memmove(str+i, path.ext.text, path.ext.len); i += path.ext.len;
+ }
+ str[i] = 0;
+
+ String res = make_string(str, i);
+ res = string_trim_whitespace(res);
+ return res;
+}
+
+gb_internal String quote_path(gbAllocator a, Path path) {
+ String temp = path_to_string(a, path);
+ String quoted = concatenate3_strings(a, str_lit("\""), temp, str_lit("\""));
+ gb_free(a, temp.text);
+ return quoted;
+}
+
+// NOTE(Jeroen): Naively turns a Path into a string, then normalizes it using `path_to_full_path`.
+gb_internal String path_to_full_path(gbAllocator a, Path path) {
+ String temp = path_to_string(heap_allocator(), path);
+ defer (gb_free(heap_allocator(), temp.text));
+
+ return path_to_full_path(a, temp);
+}
+
+// NOTE(Jeroen): Takes a path like "odin" or "W:\Odin", turns it into a full path,
+// and then breaks it into its components to make a Path.
+gb_internal Path path_from_string(gbAllocator a, String const &path) {
+ Path res = {};
+
+ if (path.len == 0) return res;
+
+ String fullpath = path_to_full_path(a, path);
+ defer (gb_free(heap_allocator(), fullpath.text));
+
+ res.basename = directory_from_path(fullpath);
+ res.basename = copy_string(a, res.basename);
+
+ if (path_is_directory(fullpath)) {
+ // It's a directory. We don't need to tinker with the name and extension.
+ // It could have a superfluous trailing `/`. Remove it if so.
+ if (res.basename.len > 0 && res.basename.text[res.basename.len - 1] == '/') {
+ res.basename.len--;
+ }
+ return res;
+ }
+
+ // Note(Dragos): Is the copy_string required if it's a substring?
+ isize name_start = (res.basename.len > 0) ? res.basename.len + 1 : res.basename.len;
+ res.name = substring(fullpath, name_start, fullpath.len);
+ res.name = remove_extension_from_path(res.name);
+ res.name = copy_string(a, res.name);
+
+ res.ext = path_extension(fullpath, false); // false says not to include the dot.
+ res.ext = copy_string(a, res.ext);
+ return res;
+}
+
+// NOTE(Jeroen): Takes a path String and returns the last path element.
+gb_internal String last_path_element(String const &path) {
+ isize count = 0;
+ u8 * start = (u8 *)(&path.text[path.len - 1]);
+ for (isize length = path.len; length > 0 && path.text[length - 1] != '/'; length--) {
+ count++;
+ start--;
+ }
+ if (count > 0) {
+ start++; // Advance past the `/` and return the substring.
+ String res = make_string(start, count);
+ return res;
+ }
+ // Must be a root path like `/` or `C:/`, return empty String.
+ return STR_LIT("");
+}
+
+gb_internal bool path_is_directory(Path path) {
+ String path_string = path_to_full_path(heap_allocator(), path);
+ defer (gb_free(heap_allocator(), path_string.text));
+
+ return path_is_directory(path_string);
+}
+
+struct FileInfo {
+ String name;
+ String fullpath;
+ i64 size;
+ bool is_dir;
+};
+
+enum ReadDirectoryError {
+ ReadDirectory_None,
+
+ ReadDirectory_InvalidPath,
+ ReadDirectory_NotExists,
+ ReadDirectory_Permission,
+ ReadDirectory_NotDir,
+ ReadDirectory_Empty,
+ ReadDirectory_Unknown,
+
+ ReadDirectory_COUNT,
+};
+
+gb_internal i64 get_file_size(String path) {
+ char *c_str = alloc_cstring(heap_allocator(), path);
+ defer (gb_free(heap_allocator(), c_str));
+
+ gbFile f = {};
+ gbFileError err = gb_file_open(&f, c_str);
+ defer (gb_file_close(&f));
+ if (err != gbFileError_None) {
+ return -1;
+ }
+ return gb_file_size(&f);
+}
+
+
+#if defined(GB_SYSTEM_WINDOWS)
+gb_internal ReadDirectoryError read_directory(String path, Array<FileInfo> *fi) {
+ GB_ASSERT(fi != nullptr);
+
+
+ while (path.len > 0) {
+ Rune end = path[path.len-1];
+ if (end == '/') {
+ path.len -= 1;
+ } else if (end == '\\') {
+ path.len -= 1;
+ } else {
+ break;
+ }
+ }
+
+ if (path.len == 0) {
+ return ReadDirectory_InvalidPath;
+ }
+ {
+ char *c_str = alloc_cstring(temporary_allocator(), path);
+ gbFile f = {};
+ gbFileError file_err = gb_file_open(&f, c_str);
+ defer (gb_file_close(&f));
+
+ switch (file_err) {
+ case gbFileError_Invalid: return ReadDirectory_InvalidPath;
+ case gbFileError_NotExists: return ReadDirectory_NotExists;
+ // case gbFileError_Permission: return ReadDirectory_Permission;
+ }
+ }
+
+ if (!path_is_directory(path)) {
+ return ReadDirectory_NotDir;
+ }
+
+
+ gbAllocator a = heap_allocator();
+ char *new_path = gb_alloc_array(a, char, path.len+3);
+ defer (gb_free(a, new_path));
+
+ gb_memmove(new_path, path.text, path.len);
+ gb_memmove(new_path+path.len, "/*", 2);
+ new_path[path.len+2] = 0;
+
+ String np = make_string(cast(u8 *)new_path, path.len+2);
+ String16 wstr = string_to_string16(a, np);
+ defer (gb_free(a, wstr.text));
+
+ WIN32_FIND_DATAW file_data = {};
+ HANDLE find_file = FindFirstFileW(wstr.text, &file_data);
+ if (find_file == INVALID_HANDLE_VALUE) {
+ return ReadDirectory_Unknown;
+ }
+ defer (FindClose(find_file));
+
+ array_init(fi, a, 0, 100);
+
+ do {
+ 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));
+ if (name == "." || name == "..") {
+ gb_free(a, name.text);
+ continue;
+ }
+
+ String filepath = {};
+ filepath.len = path.len+1+name.len;
+ filepath.text = gb_alloc_array(a, u8, filepath.len+1);
+ defer (gb_free(a, filepath.text));
+ gb_memmove(filepath.text, path.text, path.len);
+ gb_memmove(filepath.text+path.len, "/", 1);
+ gb_memmove(filepath.text+path.len+1, name.text, name.len);
+
+ FileInfo info = {};
+ info.name = name;
+ info.fullpath = path_to_full_path(a, filepath);
+ info.size = cast(i64)size;
+ info.is_dir = (file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
+ array_add(fi, info);
+ } while (FindNextFileW(find_file, &file_data));
+
+ if (fi->count == 0) {
+ return ReadDirectory_Empty;
+ }
+
+ return ReadDirectory_None;
+}
+#elif defined(GB_SYSTEM_LINUX) || defined(GB_SYSTEM_OSX) || defined(GB_SYSTEM_FREEBSD) || defined(GB_SYSTEM_OPENBSD) || defined(GB_SYSTEM_NETBSD) || defined(GB_SYSTEM_HAIKU)
+
+#include <dirent.h>
+
+gb_internal ReadDirectoryError read_directory(String path, Array<FileInfo> *fi) {
+ GB_ASSERT(fi != nullptr);
+
+ gbAllocator a = heap_allocator();
+
+ char *c_path = alloc_cstring(a, path);
+ defer (gb_free(a, c_path));
+
+ DIR *dir = opendir(c_path);
+ if (!dir) {
+ switch (errno) {
+ case ENOENT:
+ return ReadDirectory_NotExists;
+ case EACCES:
+ return ReadDirectory_Permission;
+ case ENOTDIR:
+ return ReadDirectory_NotDir;
+ default:
+ // ENOMEM: out of memory
+ // EMFILE: per-process limit on open fds reached
+ // ENFILE: system-wide limit on total open files reached
+ return ReadDirectory_Unknown;
+ }
+ GB_PANIC("unreachable");
+ }
+
+ array_init(fi, a, 0, 100);
+
+ for (;;) {
+ struct dirent *entry = readdir(dir);
+ if (entry == nullptr) {
+ break;
+ }
+
+ String name = make_string_c(entry->d_name);
+ if (name == "." || name == "..") {
+ continue;
+ }
+
+ String filepath = {};
+ filepath.len = path.len+1+name.len;
+ filepath.text = gb_alloc_array(a, u8, filepath.len+1);
+ defer (gb_free(a, filepath.text));
+ gb_memmove(filepath.text, path.text, path.len);
+ gb_memmove(filepath.text+path.len, "/", 1);
+ gb_memmove(filepath.text+path.len+1, name.text, name.len);
+ filepath.text[filepath.len] = 0;
+
+
+ struct stat dir_stat = {};
+
+ if (stat((char *)filepath.text, &dir_stat)) {
+ continue;
+ }
+
+ i64 size = dir_stat.st_size;
+
+ FileInfo info = {};
+ info.name = copy_string(a, name);
+ info.fullpath = path_to_full_path(a, filepath);
+ info.size = size;
+ info.is_dir = S_ISDIR(dir_stat.st_mode);
+ array_add(fi, info);
+ }
+
+ if (fi->count == 0) {
+ return ReadDirectory_Empty;
+ }
+
+ return ReadDirectory_None;
+}
+
+
+#else
+#error Implement read_directory
+#endif
+
+#if !defined(GB_SYSTEM_WINDOWS)
+gb_internal bool write_directory(String path) {
+ char const *pathname = (char *) path.text;
+
+ if (access(pathname, W_OK) < 0) {
+ return false;
+ }
+
+ return true;
+}
+#else
+gb_internal bool write_directory(String path) {
+ String16 wstr = string_to_string16(heap_allocator(), path);
+ LPCWSTR wdirectory_name = wstr.text;
+
+ HANDLE directory = CreateFileW(wdirectory_name,
+ GENERIC_WRITE,
+ 0,
+ NULL,
+ OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS,
+ NULL);
+
+ if (directory == INVALID_HANDLE_VALUE) {
+ DWORD error_code = GetLastError();
+ if (error_code == ERROR_ACCESS_DENIED) {
+ return false;
+ }
+ }
+
+ CloseHandle(directory);
+ return true;
+}
+#endif
diff --git a/src/ptr_map.cpp b/src/ptr_map.cpp
index 23278014f..1c157c386 100644
--- a/src/ptr_map.cpp
+++ b/src/ptr_map.cpp
@@ -9,30 +9,22 @@ enum {
};
-struct MapFindResult {
- MapIndex hash_index;
- MapIndex entry_prev;
- MapIndex entry_index;
-};
-
enum : MapIndex { MAP_SENTINEL = ~(MapIndex)0 };
+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;
- MapIndex next;
+ K key;
+ V value;
};
template <typename K, typename V>
struct PtrMap {
- MapIndex * hashes;
- usize hashes_count;
PtrMapEntry<K, V> *entries;
u32 count;
- u32 entries_capacity;
+ u32 capacity;
};
@@ -69,7 +61,6 @@ template <typename K, typename V> gb_internal void map_grow (PtrMap<
template <typename K, typename V> gb_internal void map_rehash (PtrMap<K, V> *h, isize new_count);
template <typename K, typename V> gb_internal void map_reserve (PtrMap<K, V> *h, isize cap);
-#if PTR_MAP_ENABLE_MULTI_MAP
// Mutlivalued map procedure
template <typename K, typename V> gb_internal PtrMapEntry<K, V> * multi_map_find_first(PtrMap<K, V> *h, K key);
template <typename K, typename V> gb_internal PtrMapEntry<K, V> * multi_map_find_next (PtrMap<K, V> *h, PtrMapEntry<K, V> *e);
@@ -79,7 +70,6 @@ template <typename K, typename V> gb_internal void multi_map_get_all (PtrMap<
template <typename K, typename V> gb_internal void multi_map_insert (PtrMap<K, V> *h, K key, V const &value);
template <typename K, typename V> gb_internal void multi_map_remove (PtrMap<K, V> *h, K key, PtrMapEntry<K, V> *e);
template <typename K, typename V> gb_internal void multi_map_remove_all(PtrMap<K, V> *h, K key);
-#endif
gb_internal gbAllocator map_allocator(void) {
return heap_allocator();
@@ -94,24 +84,433 @@ gb_internal gb_inline void map_init(PtrMap<K, V> *h, isize capacity) {
template <typename K, typename V>
gb_internal gb_inline void map_destroy(PtrMap<K, V> *h) {
gbAllocator a = map_allocator();
+ gb_free(a, h->entries);
+}
+
+
+template <typename K, typename V>
+gb_internal void map__insert(PtrMap<K, V> *h, K key, V const &value) {
+ if (h->count+1 >= h->capacity) {
+ map_grow(h);
+ }
+ u32 hash = ptr_map_hash_key(key);
+ u32 mask = h->capacity-1;
+ MapIndex index = hash & mask;
+ MapIndex original_index = index;
+ do {
+ auto *entry = h->entries+index;
+ if (!entry->key || entry->key == cast(K)MAP_TOMBSTONE) {
+ entry->key = key;
+ entry->value = value;
+ h->count += 1;
+ return;
+ }
+ index = (index+1)&mask;
+ } while (index != original_index);
+
+ GB_PANIC("FAILED TO INSERT");
+}
+
+template <typename K, typename V>
+gb_internal b32 map__full(PtrMap<K, V> *h) {
+ return 0.75f * h->capacity <= h->count;
+}
+
+template <typename K, typename V>
+gb_internal gb_inline void map_grow(PtrMap<K, V> *h) {
+ isize new_capacity = gb_max(h->capacity<<1, 16);
+ map_reserve(h, new_capacity);
+}
+
+template <typename K, typename V>
+gb_internal void try_map_grow(PtrMap<K, V> *h) {
+ if (h->capacity == 0 || map__full(h)) {
+ map_grow(h);
+ }
+}
+
+
+template <typename K, typename V>
+gb_internal void map_reserve(PtrMap<K, V> *h, isize cap) {
+ if (cap < h->capacity) {
+ return;
+ }
+ cap = next_pow2_isize(cap);
+ typedef PtrMapEntry<K, V> EntryType;
+
+ PtrMap<K, V> new_h = {};
+ new_h.count = 0;
+ new_h.capacity = cast(u32)cap;
+ new_h.entries = gb_alloc_array(map_allocator(), EntryType, new_h.capacity);
+
+ if (h->count) {
+ for (u32 i = 0; i < h->capacity; i++) {
+ auto *entry = h->entries+i;
+ if (entry->key &&
+ entry->key != cast(K)MAP_TOMBSTONE) {
+ map__insert(&new_h, entry->key, entry->value);
+ }
+ }
+ }
+ map_destroy(h);
+ *h = new_h;
+}
+
+template <typename K, typename V>
+gb_internal V *map_get(PtrMap<K, V> *h, K key) {
+ if (h->count == 0) {
+ return nullptr;
+ }
+ if (key == 0) {
+ GB_PANIC("0 key");
+ }
+
+ u32 hash = ptr_map_hash_key(key);
+ u32 mask = (h->capacity-1);
+ u32 index = hash & mask;
+ u32 original_index = index;
+ do {
+ auto *entry = h->entries+index;
+ if (!entry->key) {
+ // NOTE(bill): no found, but there isn't any key removal for this hash map
+ return nullptr;
+ } else if (entry->key == key) {
+ return &entry->value;
+ }
+ index = (index+1) & mask;
+ } while (original_index != index);
+ return nullptr;
+}
+template <typename K, typename V>
+gb_internal V *map_try_get(PtrMap<K, V> *h, K key, MapIndex *found_index_) {
+ if (found_index_) *found_index_ = ~(MapIndex)0;
+
+ if (h->count == 0) {
+ return nullptr;
+ }
+ if (key == 0) {
+ GB_PANIC("0 key");
+ }
+
+ u32 hash = ptr_map_hash_key(key);
+ u32 mask = (h->capacity-1);
+ u32 index = hash & mask;
+ u32 original_index = index;
+ do {
+ auto *entry = h->entries+index;
+ if (!entry->key) {
+ // NOTE(bill): no found, but there isn't any key removal for this hash map
+ return nullptr;
+ } else if (entry->key == key) {
+ if (found_index_) *found_index_ = index;
+ return &entry->value;
+ }
+ index = (index+1) & mask;
+ } while (original_index != index);
+ return nullptr;
+}
+
+
+template <typename K, typename V>
+gb_internal void map_set_internal_from_try_get(PtrMap<K, V> *h, K key, V const &value, MapIndex found_index) {
+ if (found_index != MAP_SENTINEL) {
+ GB_ASSERT(h->entries[found_index].key == key);
+ h->entries[found_index].value = value;
+ } else {
+ map_set(h, key, value);
+ }
+}
+
+template <typename K, typename V>
+gb_internal V &map_must_get(PtrMap<K, V> *h, K key) {
+ V *ptr = map_get(h, key);
+ GB_ASSERT(ptr != nullptr);
+ return *ptr;
+}
+
+template <typename K, typename V>
+gb_internal void map_set(PtrMap<K, V> *h, K key, V const &value) {
+ GB_ASSERT(key != 0);
+ try_map_grow(h);
+ auto *found = map_get(h, key);
+ if (found) {
+ *found = value;
+ return;
+ }
+ map__insert(h, key, value);
+}
+
+// returns true if it previously existed
+template <typename K, typename V>
+gb_internal bool map_set_if_not_previously_exists(PtrMap<K, V> *h, K key, V const &value) {
+ try_map_grow(h);
+ auto *found = map_get(h, key);
+ if (found) {
+ return true;
+ }
+ map__insert(h, key, value);
+ return false;
+}
+
+
+template <typename K, typename V>
+gb_internal void map_remove(PtrMap<K, V> *h, K key) {
+ MapIndex found_index = 0;
+ if (map_try_get(h, key, &found_index)) {
+ h->entries[found_index].key = cast(K)MAP_TOMBSTONE;
+ h->count -= 1;
+ }
+}
+
+template <typename K, typename V>
+gb_internal gb_inline void map_clear(PtrMap<K, V> *h) {
+ h->count = 0;
+ gb_zero_array(h->entries, h->capacity);
+}
+
+
+#if PTR_MAP_ENABLE_MULTI_MAP
+template <typename K, typename V>
+gb_internal PtrMapEntry<K, V> *multi_map_find_first(PtrMap<K, V> *h, K key) {
+ if (h->count == 0) {
+ return nullptr;
+ }
+ u32 hash = ptr_map_hash_key(key);
+ u32 mask = (h->capacity-1);
+ u32 index = hash & mask;
+ u32 original_index = index;
+ do {
+ auto *entry = h->entries+index;
+ if (!entry->key) {
+ // NOTE(bill): no found, but there isn't any key removal for this hash map
+ return nullptr;
+ } else if (entry->key == key) {
+ return entry;
+ }
+ index = (index+1) & mask;
+ } while (original_index != index);
+ return nullptr;
+}
+
+template <typename K, typename V>
+gb_internal PtrMapEntry<K, V> *multi_map_find_next(PtrMap<K, V> *h, PtrMapEntry<K, V> *e) {
+ u32 mask = h->capacity-1;
+ MapIndex index = cast(MapIndex)(e - h->entries);
+ MapIndex original_index = index;
+ do {
+ index = (index+1)&mask;
+ auto *entry = h->entries+index;
+ if (!entry->key) {
+ return nullptr;
+ }
+ if (entry->key == e->key) {
+ return entry;
+ }
+ } while (original_index != index);
+ return nullptr;
+}
+
+template <typename K, typename V>
+gb_internal isize multi_map_count(PtrMap<K, V> *h, K key) {
+ isize count = 0;
+ PtrMapEntry<K, V> *e = multi_map_find_first(h, key);
+ while (e != nullptr) {
+ count++;
+ e = multi_map_find_next(h, e);
+ }
+ return count;
+}
+
+template <typename K, typename V>
+gb_internal void multi_map_get_all(PtrMap<K, V> *h, K key, V *items) {
+ usize i = 0;
+ PtrMapEntry<K, V> *e = multi_map_find_first(h, key);
+ while (e != nullptr) {
+ items[i++] = e->value;
+ e = multi_map_find_next(h, e);
+ }
+}
+
+template <typename K, typename V>
+gb_internal void multi_map_insert(PtrMap<K, V> *h, K key, V const &value) {
+ try_map_grow(h);
+ map__insert(h, key, value);
+}
+
+// template <typename K, typename V>
+// gb_internal void multi_map_remove(PtrMap<K, V> *h, K key, PtrMapEntry<K, V> *e) {
+// if (fr.entry_index != MAP_SENTINEL) {
+// map__erase(h, fr);
+// }
+// }
+
+template <typename K, typename V>
+gb_internal void multi_map_remove_all(PtrMap<K, V> *h, K key) {
+ while (map_get(h, key) != nullptr) {
+ map_remove(h, key);
+ }
+}
+#endif
+
+
+
+
+template <typename K, typename V>
+struct PtrMapIterator {
+ PtrMap<K, V> *map;
+ MapIndex index;
+
+ PtrMapIterator<K, V> &operator++() noexcept {
+ for (;;) {
+ ++index;
+ if (map->capacity == index) {
+ return *this;
+ }
+ PtrMapEntry<K, V> *entry = map->entries+index;
+ if (entry->key && entry->key != cast(K)MAP_TOMBSTONE) {
+ return *this;
+ }
+ }
+ }
+
+ bool operator==(PtrMapIterator<K, V> const &other) const noexcept {
+ return this->map == other->map && this->index == other->index;
+ }
+
+ operator PtrMapEntry<K, V> *() const {
+ return map->entries+index;
+ }
+};
+
+
+template <typename K, typename V>
+gb_internal PtrMapIterator<K, V> end(PtrMap<K, V> &m) noexcept {
+ return PtrMapIterator<K, V>{&m, m.capacity};
+}
+
+template <typename K, typename V>
+gb_internal PtrMapIterator<K, V> const end(PtrMap<K, V> const &m) noexcept {
+ return PtrMapIterator<K, V>{&m, m.capacity};
+}
+
+
+
+template <typename K, typename V>
+gb_internal PtrMapIterator<K, V> begin(PtrMap<K, V> &m) noexcept {
+ if (m.count == 0) {
+ return end(m);
+ }
+
+ MapIndex index = 0;
+ while (index < m.capacity) {
+ auto key = m.entries[index].key;
+ if (key && key != cast(K)MAP_TOMBSTONE) {
+ break;
+ }
+ index++;
+ }
+ return PtrMapIterator<K, V>{&m, index};
+}
+template <typename K, typename V>
+gb_internal PtrMapIterator<K, V> const begin(PtrMap<K, V> const &m) noexcept {
+ if (m.count == 0) {
+ return end(m);
+ }
+
+ MapIndex index = 0;
+ while (index < m.capacity) {
+ auto key = m.entries[index].key;
+ if (key && key != cast(K)MAP_TOMBSTONE) {
+ break;
+ }
+ index++;
+ }
+ return PtrMapIterator<K, V>{&m, index};
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+struct MapFindResult {
+ MapIndex hash_index;
+ MapIndex entry_prev;
+ MapIndex entry_index;
+};
+
+template <typename K, typename V>
+struct OrderedInsertPtrMapEntry {
+ static_assert(sizeof(K) == sizeof(void *), "Key size must be pointer size");
+
+ K key;
+ V value;
+ MapIndex next;
+};
+
+template <typename K, typename V>
+struct OrderedInsertPtrMap {
+ MapIndex *hashes;
+ usize hashes_count;
+ OrderedInsertPtrMapEntry<K, V> *entries;
+ u32 count;
+ u32 entries_capacity;
+};
+
+
+template <typename K, typename V> gb_internal void map_destroy (OrderedInsertPtrMap<K, V> *h);
+template <typename K, typename V> gb_internal V * map_get (OrderedInsertPtrMap<K, V> *h, K key);
+template <typename K, typename V> gb_internal void map_set (OrderedInsertPtrMap<K, V> *h, K key, V const &value);
+template <typename K, typename V> gb_internal bool map_set_if_not_previously_exists(OrderedInsertPtrMap<K, V> *h, K key, V const &value); // returns true if it previously existed
+template <typename K, typename V> gb_internal void map_remove (OrderedInsertPtrMap<K, V> *h, K key);
+template <typename K, typename V> gb_internal void map_clear (OrderedInsertPtrMap<K, V> *h);
+template <typename K, typename V> gb_internal void map_grow (OrderedInsertPtrMap<K, V> *h);
+template <typename K, typename V> gb_internal void map_rehash (OrderedInsertPtrMap<K, V> *h, isize new_count);
+template <typename K, typename V> gb_internal void map_reserve (OrderedInsertPtrMap<K, V> *h, isize cap);
+
+// Mutlivalued map procedure
+template <typename K, typename V> gb_internal OrderedInsertPtrMapEntry<K, V> * multi_map_find_first(OrderedInsertPtrMap<K, V> *h, K key);
+template <typename K, typename V> gb_internal OrderedInsertPtrMapEntry<K, V> * multi_map_find_next (OrderedInsertPtrMap<K, V> *h, OrderedInsertPtrMapEntry<K, V> *e);
+
+template <typename K, typename V> gb_internal isize multi_map_count (OrderedInsertPtrMap<K, V> *h, K key);
+template <typename K, typename V> gb_internal void multi_map_get_all (OrderedInsertPtrMap<K, V> *h, K key, V *items);
+template <typename K, typename V> gb_internal void multi_map_insert (OrderedInsertPtrMap<K, V> *h, K key, V const &value);
+template <typename K, typename V> gb_internal void multi_map_remove (OrderedInsertPtrMap<K, V> *h, K key, OrderedInsertPtrMapEntry<K, V> *e);
+template <typename K, typename V> gb_internal void multi_map_remove_all(OrderedInsertPtrMap<K, V> *h, K key);
+
+template <typename K, typename V>
+gb_internal gb_inline void map_init(OrderedInsertPtrMap<K, V> *h, isize capacity) {
+ capacity = next_pow2_isize(capacity);
+ map_reserve(h, capacity);
+}
+
+template <typename K, typename V>
+gb_internal gb_inline void map_destroy(OrderedInsertPtrMap<K, V> *h) {
+ gbAllocator a = map_allocator();
gb_free(a, h->hashes);
gb_free(a, h->entries);
}
template <typename K, typename V>
-gb_internal void map__resize_hashes(PtrMap<K, V> *h, usize count) {
+gb_internal void map__resize_hashes(OrderedInsertPtrMap<K, V> *h, usize count) {
h->hashes_count = cast(u32)resize_array_raw(&h->hashes, map_allocator(), h->hashes_count, count, MAP_CACHE_LINE_SIZE);
}
template <typename K, typename V>
-gb_internal void map__reserve_entries(PtrMap<K, V> *h, usize capacity) {
+gb_internal void map__reserve_entries(OrderedInsertPtrMap<K, V> *h, usize capacity) {
h->entries_capacity = cast(u32)resize_array_raw(&h->entries, map_allocator(), h->entries_capacity, capacity, MAP_CACHE_LINE_SIZE);
}
template <typename K, typename V>
-gb_internal MapIndex map__add_entry(PtrMap<K, V> *h, K key) {
- PtrMapEntry<K, V> e = {};
+gb_internal MapIndex map__add_entry(OrderedInsertPtrMap<K, V> *h, K key) {
+ OrderedInsertPtrMapEntry<K, V> e = {};
e.key = key;
e.next = MAP_SENTINEL;
if (h->count+1 >= h->entries_capacity) {
@@ -122,7 +521,7 @@ gb_internal MapIndex map__add_entry(PtrMap<K, V> *h, K key) {
}
template <typename K, typename V>
-gb_internal MapFindResult map__find(PtrMap<K, V> *h, K key) {
+gb_internal MapFindResult map__find(OrderedInsertPtrMap<K, V> *h, K key) {
MapFindResult fr = {MAP_SENTINEL, MAP_SENTINEL, MAP_SENTINEL};
if (h->hashes_count == 0) {
return fr;
@@ -142,7 +541,7 @@ gb_internal MapFindResult map__find(PtrMap<K, V> *h, K key) {
}
template <typename K, typename V>
-gb_internal MapFindResult map__find_from_entry(PtrMap<K, V> *h, PtrMapEntry<K, V> *e) {
+gb_internal MapFindResult map__find_from_entry(OrderedInsertPtrMap<K, V> *h, OrderedInsertPtrMapEntry<K, V> *e) {
MapFindResult fr = {MAP_SENTINEL, MAP_SENTINEL, MAP_SENTINEL};
if (h->hashes_count == 0) {
return fr;
@@ -161,24 +560,24 @@ gb_internal MapFindResult map__find_from_entry(PtrMap<K, V> *h, PtrMapEntry<K, V
}
template <typename K, typename V>
-gb_internal b32 map__full(PtrMap<K, V> *h) {
+gb_internal b32 map__full(OrderedInsertPtrMap<K, V> *h) {
return 0.75f * h->hashes_count <= h->count;
}
template <typename K, typename V>
-gb_internal gb_inline void map_grow(PtrMap<K, V> *h) {
+gb_internal gb_inline void map_grow(OrderedInsertPtrMap<K, V> *h) {
isize new_count = gb_max(h->hashes_count<<1, 16);
map_rehash(h, new_count);
}
template <typename K, typename V>
-gb_internal void map_reset_entries(PtrMap<K, V> *h) {
+gb_internal void map_reset_entries(OrderedInsertPtrMap<K, V> *h) {
for (usize i = 0; i < h->hashes_count; i++) {
h->hashes[i] = MAP_SENTINEL;
}
for (usize i = 0; i < h->count; i++) {
MapFindResult fr;
- PtrMapEntry<K, V> *e = &h->entries[i];
+ OrderedInsertPtrMapEntry<K, V> *e = &h->entries[i];
e->next = MAP_SENTINEL;
fr = map__find_from_entry(h, e);
if (fr.entry_prev == MAP_SENTINEL) {
@@ -190,7 +589,7 @@ gb_internal void map_reset_entries(PtrMap<K, V> *h) {
}
template <typename K, typename V>
-gb_internal void map_reserve(PtrMap<K, V> *h, isize cap) {
+gb_internal void map_reserve(OrderedInsertPtrMap<K, V> *h, isize cap) {
if (h->count*2 < h->hashes_count) {
return;
}
@@ -201,12 +600,12 @@ gb_internal void map_reserve(PtrMap<K, V> *h, isize cap) {
template <typename K, typename V>
-gb_internal void map_rehash(PtrMap<K, V> *h, isize new_count) {
+gb_internal void map_rehash(OrderedInsertPtrMap<K, V> *h, isize new_count) {
map_reserve(h, new_count);
}
template <typename K, typename V>
-gb_internal V *map_get(PtrMap<K, V> *h, K key) {
+gb_internal V *map_get(OrderedInsertPtrMap<K, V> *h, K key) {
MapIndex hash_index = MAP_SENTINEL;
MapIndex entry_prev = MAP_SENTINEL;
MapIndex entry_index = MAP_SENTINEL;
@@ -226,7 +625,7 @@ gb_internal V *map_get(PtrMap<K, V> *h, K key) {
return nullptr;
}
template <typename K, typename V>
-gb_internal V *map_try_get(PtrMap<K, V> *h, K key, MapFindResult *fr_) {
+gb_internal V *map_try_get(OrderedInsertPtrMap<K, V> *h, K key, MapFindResult *fr_) {
MapFindResult fr = {MAP_SENTINEL, MAP_SENTINEL, MAP_SENTINEL};
if (h->hashes_count != 0) {
u32 hash = ptr_map_hash_key(key);
@@ -250,7 +649,7 @@ gb_internal V *map_try_get(PtrMap<K, V> *h, K key, MapFindResult *fr_) {
template <typename K, typename V>
-gb_internal void map_set_internal_from_try_get(PtrMap<K, V> *h, K key, V const &value, MapFindResult const &fr) {
+gb_internal void map_set_internal_from_try_get(OrderedInsertPtrMap<K, V> *h, K key, V const &value, MapFindResult const &fr) {
MapIndex index = map__add_entry(h, key);
if (fr.entry_prev != MAP_SENTINEL) {
h->entries[fr.entry_prev].next = index;
@@ -261,14 +660,14 @@ gb_internal void map_set_internal_from_try_get(PtrMap<K, V> *h, K key, V const &
}
template <typename K, typename V>
-gb_internal V &map_must_get(PtrMap<K, V> *h, K key) {
+gb_internal V &map_must_get(OrderedInsertPtrMap<K, V> *h, K key) {
V *ptr = map_get(h, key);
GB_ASSERT(ptr != nullptr);
return *ptr;
}
template <typename K, typename V>
-gb_internal void map_set(PtrMap<K, V> *h, K key, V const &value) {
+gb_internal void map_set(OrderedInsertPtrMap<K, V> *h, K key, V const &value) {
MapIndex index;
MapFindResult fr;
if (h->hashes_count == 0) {
@@ -294,7 +693,7 @@ gb_internal void map_set(PtrMap<K, V> *h, K key, V const &value) {
// returns true if it previously existed
template <typename K, typename V>
-gb_internal bool map_set_if_not_previously_exists(PtrMap<K, V> *h, K key, V const &value) {
+gb_internal bool map_set_if_not_previously_exists(OrderedInsertPtrMap<K, V> *h, K key, V const &value) {
MapIndex index;
MapFindResult fr;
if (h->hashes_count == 0) {
@@ -321,7 +720,7 @@ gb_internal bool map_set_if_not_previously_exists(PtrMap<K, V> *h, K key, V cons
template <typename K, typename V>
-gb_internal void map__erase(PtrMap<K, V> *h, MapFindResult const &fr) {
+gb_internal void map__erase(OrderedInsertPtrMap<K, V> *h, MapFindResult const &fr) {
MapFindResult last;
if (fr.entry_prev == MAP_SENTINEL) {
h->hashes[fr.hash_index] = h->entries[fr.entry_index].next;
@@ -334,7 +733,7 @@ gb_internal void map__erase(PtrMap<K, V> *h, MapFindResult const &fr) {
}
h->entries[fr.entry_index] = h->entries[h->count-1];
h->count--;
-
+
last = map__find(h, h->entries[fr.entry_index].key);
if (last.entry_prev != MAP_SENTINEL) {
h->entries[last.entry_prev].next = fr.entry_index;
@@ -344,7 +743,7 @@ gb_internal void map__erase(PtrMap<K, V> *h, MapFindResult const &fr) {
}
template <typename K, typename V>
-gb_internal void map_remove(PtrMap<K, V> *h, K key) {
+gb_internal void map_remove(OrderedInsertPtrMap<K, V> *h, K key) {
MapFindResult fr = map__find(h, key);
if (fr.entry_index != MAP_SENTINEL) {
map__erase(h, fr);
@@ -352,7 +751,7 @@ gb_internal void map_remove(PtrMap<K, V> *h, K key) {
}
template <typename K, typename V>
-gb_internal gb_inline void map_clear(PtrMap<K, V> *h) {
+gb_internal gb_inline void map_clear(OrderedInsertPtrMap<K, V> *h) {
h->count = 0;
for (usize i = 0; i < h->hashes_count; i++) {
h->hashes[i] = MAP_SENTINEL;
@@ -360,9 +759,8 @@ gb_internal gb_inline void map_clear(PtrMap<K, V> *h) {
}
-#if PTR_MAP_ENABLE_MULTI_MAP
template <typename K, typename V>
-gb_internal PtrMapEntry<K, V> *multi_map_find_first(PtrMap<K, V> *h, K key) {
+gb_internal OrderedInsertPtrMapEntry<K, V> *multi_map_find_first(OrderedInsertPtrMap<K, V> *h, K key) {
MapIndex i = map__find(h, key).entry_index;
if (i == MAP_SENTINEL) {
return nullptr;
@@ -371,7 +769,7 @@ gb_internal PtrMapEntry<K, V> *multi_map_find_first(PtrMap<K, V> *h, K key) {
}
template <typename K, typename V>
-gb_internal PtrMapEntry<K, V> *multi_map_find_next(PtrMap<K, V> *h, PtrMapEntry<K, V> *e) {
+gb_internal OrderedInsertPtrMapEntry<K, V> *multi_map_find_next(OrderedInsertPtrMap<K, V> *h, OrderedInsertPtrMapEntry<K, V> *e) {
MapIndex i = e->next;
while (i != MAP_SENTINEL) {
if (h->entries[i].key == e->key) {
@@ -383,9 +781,9 @@ gb_internal PtrMapEntry<K, V> *multi_map_find_next(PtrMap<K, V> *h, PtrMapEntry<
}
template <typename K, typename V>
-gb_internal isize multi_map_count(PtrMap<K, V> *h, K key) {
+gb_internal isize multi_map_count(OrderedInsertPtrMap<K, V> *h, K key) {
isize count = 0;
- PtrMapEntry<K, V> *e = multi_map_find_first(h, key);
+ OrderedInsertPtrMapEntry<K, V> *e = multi_map_find_first(h, key);
while (e != nullptr) {
count++;
e = multi_map_find_next(h, e);
@@ -394,9 +792,9 @@ gb_internal isize multi_map_count(PtrMap<K, V> *h, K key) {
}
template <typename K, typename V>
-gb_internal void multi_map_get_all(PtrMap<K, V> *h, K key, V *items) {
+gb_internal void multi_map_get_all(OrderedInsertPtrMap<K, V> *h, K key, V *items) {
usize i = 0;
- PtrMapEntry<K, V> *e = multi_map_find_first(h, key);
+ OrderedInsertPtrMapEntry<K, V> *e = multi_map_find_first(h, key);
while (e != nullptr) {
items[i++] = e->value;
e = multi_map_find_next(h, e);
@@ -404,7 +802,7 @@ gb_internal void multi_map_get_all(PtrMap<K, V> *h, K key, V *items) {
}
template <typename K, typename V>
-gb_internal void multi_map_insert(PtrMap<K, V> *h, K key, V const &value) {
+gb_internal void multi_map_insert(OrderedInsertPtrMap<K, V> *h, K key, V const &value) {
MapFindResult fr;
MapIndex i;
if (h->hashes_count == 0) {
@@ -427,7 +825,7 @@ gb_internal void multi_map_insert(PtrMap<K, V> *h, K key, V const &value) {
}
template <typename K, typename V>
-gb_internal void multi_map_remove(PtrMap<K, V> *h, K key, PtrMapEntry<K, V> *e) {
+gb_internal void multi_map_remove(OrderedInsertPtrMap<K, V> *h, K key, OrderedInsertPtrMapEntry<K, V> *e) {
MapFindResult fr = map__find_from_entry(h, e);
if (fr.entry_index != MAP_SENTINEL) {
map__erase(h, fr);
@@ -435,30 +833,29 @@ gb_internal void multi_map_remove(PtrMap<K, V> *h, K key, PtrMapEntry<K, V> *e)
}
template <typename K, typename V>
-gb_internal void multi_map_remove_all(PtrMap<K, V> *h, K key) {
+gb_internal void multi_map_remove_all(OrderedInsertPtrMap<K, V> *h, K key) {
while (map_get(h, key) != nullptr) {
map_remove(h, key);
}
}
-#endif
template <typename K, typename V>
-gb_internal PtrMapEntry<K, V> *begin(PtrMap<K, V> &m) {
+gb_internal OrderedInsertPtrMapEntry<K, V> *begin(OrderedInsertPtrMap<K, V> &m) {
return m.entries;
}
template <typename K, typename V>
-gb_internal PtrMapEntry<K, V> const *begin(PtrMap<K, V> const &m) {
+gb_internal OrderedInsertPtrMapEntry<K, V> const *begin(OrderedInsertPtrMap<K, V> const &m) {
return m.entries;
}
template <typename K, typename V>
-gb_internal PtrMapEntry<K, V> *end(PtrMap<K, V> &m) {
+gb_internal OrderedInsertPtrMapEntry<K, V> *end(OrderedInsertPtrMap<K, V> &m) {
return m.entries + m.count;
}
template <typename K, typename V>
-gb_internal PtrMapEntry<K, V> const *end(PtrMap<K, V> const &m) {
+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/queue.cpp b/src/queue.cpp
index 2ad9cb29f..dee9ad1f8 100644
--- a/src/queue.cpp
+++ b/src/queue.cpp
@@ -16,7 +16,7 @@ struct MPSCQueue {
std::atomic<isize> count;
};
-template <typename T> gb_internal void mpsc_init (MPSCQueue<T> *q);
+template <typename T> gb_internal void mpsc_init (MPSCQueue<T> *q, gbAllocator const &allocator);
template <typename T> gb_internal void mpsc_destroy(MPSCQueue<T> *q);
template <typename T> gb_internal isize mpsc_enqueue(MPSCQueue<T> *q, T const &value);
template <typename T> gb_internal bool mpsc_dequeue(MPSCQueue<T> *q, T *value_);
diff --git a/src/string.cpp b/src/string.cpp
index 6eac4f53b..b001adf0e 100644
--- a/src/string.cpp
+++ b/src/string.cpp
@@ -10,6 +10,10 @@ struct String {
return text[i];
}
};
+struct String_Iterator {
+ String const &str;
+ isize pos;
+};
// NOTE(bill): used for printf style arguments
#define LIT(x) ((int)(x).len), (x).text
#if defined(GB_COMPILER_MSVC) && _MSC_VER < 1700
@@ -84,6 +88,12 @@ gb_internal char *alloc_cstring(gbAllocator a, String s) {
return c_str;
}
+gb_internal wchar_t *alloc_wstring(gbAllocator a, String16 s) {
+ wchar_t *c_str = gb_alloc_array(a, wchar_t, s.len+1);
+ gb_memmove(c_str, s.text, s.len*2);
+ c_str[s.len] = '\0';
+ return c_str;
+}
gb_internal gb_inline bool str_eq_ignore_case(String const &a, String const &b) {
@@ -100,6 +110,16 @@ gb_internal gb_inline bool str_eq_ignore_case(String const &a, String const &b)
return false;
}
+template <isize N>
+gb_internal gb_inline bool str_eq_ignore_case(String const &a, char const (&b_)[N]) {
+ if (a.len != N-1) {
+ return false;
+ }
+ String b = {cast(u8 *)b_, N-1};
+ return str_eq_ignore_case(a, b);
+}
+
+
gb_internal void string_to_lower(String *s) {
for (isize i = 0; i < s->len; i++) {
s->text[i] = gb_char_to_lower(s->text[i]);
@@ -136,6 +156,7 @@ gb_internal isize string_index_byte(String const &s, u8 x) {
gb_internal gb_inline bool str_eq(String const &a, String 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(String const &a, String const &b) { return !str_eq(a, b); }
@@ -158,6 +179,9 @@ template <isize N> gb_internal bool operator > (String const &a, char const (&b
template <isize N> gb_internal bool operator <= (String const &a, char const (&b)[N]) { return str_le(a, make_string(cast(u8 *)b, N-1)); }
template <isize N> gb_internal bool operator >= (String const &a, char const (&b)[N]) { return str_ge(a, make_string(cast(u8 *)b, N-1)); }
+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 string_starts_with(String const &s, String const &prefix) {
if (prefix.len > s.len) {
return false;
@@ -201,11 +225,36 @@ gb_internal gb_inline String string_trim_starts_with(String const &s, String con
}
+gb_internal String string_split_iterator(String_Iterator *it, const char sep) {
+ isize start = it->pos;
+ isize end = it->str.len;
+
+ if (start == end) {
+ return str_lit("");
+ }
+
+ isize i = start;
+ for (; i < it->str.len; i++) {
+ if (it->str[i] == sep) {
+ String res = substring(it->str, start, i);
+ it->pos += res.len + 1;
+ return res;
+ }
+ }
+ it->pos = end;
+ return substring(it->str, start, end);
+}
+
+gb_internal gb_inline bool is_separator(u8 const &ch) {
+ return (ch == '/' || ch == '\\');
+}
+
+
gb_internal gb_inline isize string_extension_position(String const &str) {
isize dot_pos = -1;
isize i = str.len;
while (i --> 0) {
- if (str[i] == GB_PATH_SEPARATOR)
+ if (is_separator(str[i]))
break;
if (str[i] == '.') {
dot_pos = i;
@@ -240,6 +289,43 @@ gb_internal String string_trim_whitespace(String str) {
return str;
}
+gb_internal String string_trim_trailing_whitespace(String str) {
+ while (str.len > 0) {
+ u8 c = str[str.len-1];
+ if (rune_is_whitespace(c) || c == 0) {
+ str.len -= 1;
+ } else {
+ break;
+ }
+ }
+ return str;
+}
+
+gb_internal String split_lines_first_line_from_array(Array<u8> const &array, gbAllocator allocator) {
+ String_Iterator it = {{array.data, array.count}, 0};
+
+ String line = string_split_iterator(&it, '\n');
+ line = string_trim_trailing_whitespace(line);
+ return line;
+}
+
+gb_internal Array<String> split_lines_from_array(Array<u8> const &array, gbAllocator allocator) {
+ Array<String> lines = {};
+ lines.allocator = allocator;
+
+ String_Iterator it = {{array.data, array.count}, 0};
+
+ for (;;) {
+ String line = string_split_iterator(&it, '\n');
+ if (line.len == 0) {
+ break;
+ }
+ line = string_trim_trailing_whitespace(line);
+ array_add(&lines, line);
+ }
+
+ return lines;
+}
gb_internal bool string_contains_char(String const &s, u8 c) {
isize i;
@@ -250,6 +336,25 @@ gb_internal bool string_contains_char(String const &s, u8 c) {
return false;
}
+gb_internal bool string_contains_string(String const &haystack, String const &needle) {
+ if (needle.len == 0) return true;
+ if (needle.len > haystack.len) return false;
+
+ for (isize i = 0; i <= haystack.len - needle.len; i++) {
+ bool found = true;
+ for (isize j = 0; j < needle.len; j++) {
+ if (haystack[i + j] != needle[j]) {
+ found = false;
+ break;
+ }
+ }
+ if (found) {
+ return true;
+ }
+ }
+ return false;
+}
+
gb_internal String filename_from_path(String s) {
isize i = string_extension_position(s);
if (i >= 0) {
@@ -259,8 +364,7 @@ gb_internal String filename_from_path(String s) {
if (i > 0) {
isize j = 0;
for (j = s.len-1; j >= 0; j--) {
- if (s[j] == '/' ||
- s[j] == '\\') {
+ if (is_separator(s[j])) {
break;
}
}
@@ -269,6 +373,17 @@ gb_internal String filename_from_path(String s) {
return make_string(nullptr, 0);
}
+
+gb_internal String filename_without_directory(String s) {
+ isize j = 0;
+ for (j = s.len-1; j >= 0; j--) {
+ if (is_separator(s[j])) {
+ break;
+ }
+ }
+ return substring(s, gb_max(j+1, 0), s.len);
+}
+
gb_internal String concatenate_strings(gbAllocator a, String const &x, String const &y) {
isize len = x.len+y.len;
u8 *data = gb_alloc_array(a, u8, len+1);
@@ -297,6 +412,32 @@ gb_internal String concatenate4_strings(gbAllocator a, String const &x, String c
return make_string(data, len);
}
+#if defined(GB_SYSTEM_WINDOWS)
+gb_internal String escape_char(gbAllocator a, String s, char cte) {
+ isize buf_len = s.len;
+ isize cte_count = 0;
+ for (isize j = 0; j < s.len; j++) {
+ if (s.text[j] == cte) {
+ cte_count++;
+ }
+ }
+
+ u8 *buf = gb_alloc_array(a, u8, buf_len+cte_count);
+ isize i = 0;
+ for (isize j = 0; j < s.len; j++) {
+ u8 c = s.text[j];
+
+ if (c == cte) {
+ buf[i++] = '\\';
+ buf[i++] = c;
+ } else {
+ buf[i++] = c;
+ }
+ }
+ return make_string(buf, i);
+}
+#endif
+
gb_internal String string_join_and_quote(gbAllocator a, Array<String> strings) {
if (!strings.count) {
return make_string(nullptr, 0);
@@ -312,7 +453,11 @@ gb_internal String string_join_and_quote(gbAllocator a, Array<String> strings) {
if (i > 0) {
s = gb_string_append_fmt(s, " ");
}
+#if defined(GB_SYSTEM_WINDOWS)
+ s = gb_string_append_fmt(s, "\"%.*s\" ", LIT(escape_char(a, strings[i], '\\')));
+#else
s = gb_string_append_fmt(s, "\"%.*s\" ", LIT(strings[i]));
+#endif
}
return make_string(cast(u8 *) s, gb_string_length(s));
@@ -325,7 +470,26 @@ gb_internal String copy_string(gbAllocator a, String const &s) {
return make_string(data, s.len);
}
-
+gb_internal String normalize_path(gbAllocator a, String const &path, String const &sep) {
+ String s;
+ if (sep.len < 1) {
+ return path;
+ }
+ if (path.len < 1) {
+ s = STR_LIT("");
+ } else if (is_separator(path[path.len-1])) {
+ s = copy_string(a, path);
+ } else {
+ s = concatenate_strings(a, path, sep);
+ }
+ isize i;
+ for (i = 0; i < s.len; i++) {
+ if (is_separator(s.text[i])) {
+ s.text[i] = sep.text[0];
+ }
+ }
+ return s;
+}
#if defined(GB_SYSTEM_WINDOWS)
@@ -417,6 +581,40 @@ gb_internal String string16_to_string(gbAllocator a, String16 s) {
+gb_internal String temporary_directory(gbAllocator allocator) {
+#if defined(GB_SYSTEM_WINDOWS)
+ DWORD n = GetTempPathW(0, nullptr);
+ if (n == 0) {
+ return String{0};
+ }
+ DWORD len = gb_max(MAX_PATH, n);
+ wchar_t *b = gb_alloc_array(heap_allocator(), wchar_t, len+1);
+ defer (gb_free(heap_allocator(), b));
+ n = GetTempPathW(len, b);
+ if (n == 3 && b[1] == ':' && b[2] == '\\') {
+
+ } else if (n > 0 && b[n-1] == '\\') {
+ n -= 1;
+ }
+ b[n] = 0;
+ String16 s = make_string16(b, n);
+ return string16_to_string(allocator, s);
+#else
+ char const *tmp_env = gb_get_env("TMPDIR", allocator);
+ if (tmp_env) {
+ return make_string_c(tmp_env);
+ }
+
+#if defined(P_tmpdir)
+ String tmp_macro = make_string_c(P_tmpdir);
+ if (tmp_macro.len != 0) {
+ return copy_string(allocator, tmp_macro);
+ }
+#endif
+
+ return copy_string(allocator, str_lit("/tmp"));
+#endif
+}
@@ -520,12 +718,12 @@ gb_internal bool unquote_char(String s, u8 quote, Rune *rune, bool *multiple_byt
Rune r = -1;
isize size = utf8_decode(s.text, s.len, &r);
*rune = r;
- *multiple_bytes = true;
- *tail_string = make_string(s.text+size, s.len-size);
+ if (multiple_bytes) *multiple_bytes = true;
+ if (tail_string) *tail_string = make_string(s.text+size, s.len-size);
return true;
} else if (s[0] != '\\') {
*rune = s[0];
- *tail_string = make_string(s.text+1, s.len-1);
+ if (tail_string) *tail_string = make_string(s.text+1, s.len-1);
return true;
}
@@ -611,10 +809,10 @@ gb_internal bool unquote_char(String s, u8 quote, Rune *rune, bool *multiple_byt
return false;
}
*rune = r;
- *multiple_bytes = true;
+ if (multiple_bytes) *multiple_bytes = true;
} break;
}
- *tail_string = s;
+ if (tail_string) *tail_string = s;
return true;
}
diff --git a/src/string_map.cpp b/src/string_map.cpp
index f8b86a950..802bf5853 100644
--- a/src/string_map.cpp
+++ b/src/string_map.cpp
@@ -2,8 +2,8 @@ GB_STATIC_ASSERT(sizeof(MapIndex) == sizeof(u32));
struct StringHashKey {
- u32 hash;
String string;
+ u32 hash;
operator String() const noexcept {
return this->string;
@@ -13,7 +13,8 @@ struct StringHashKey {
}
};
gb_internal gb_inline u32 string_hash(String const &s) {
- return fnv32a(s.text, s.len) & 0x7fffffff;
+ u32 res = fnv32a(s.text, s.len) & 0x7fffffff;
+ return res | (res == 0);
}
gb_internal gb_inline StringHashKey string_hash_string(String const &s) {
@@ -23,6 +24,9 @@ gb_internal gb_inline StringHashKey string_hash_string(String const &s) {
return hash_key;
}
+
+#if 1 /* old string map */
+
template <typename T>
struct StringMapEntry {
String key;
@@ -278,36 +282,6 @@ gb_internal gb_inline void string_map_set(StringMap<T> *h, StringHashKey const &
}
-
-// template <typename T>
-// gb_internal void string_map__erase(StringMap<T> *h, MapFindResult const &fr) {
-// MapFindResult last;
-// if (fr.entry_prev == MAP_SENTINEL) {
-// h->hashes[fr.hash_index] = h->entries[fr.entry_index].next;
-// } else {
-// h->entries[fr.entry_prev].next = h->entries[fr.entry_index].next;
-// }
-// if (fr.entry_index == h->count-1) {
-// array_pop(&h->entries);
-// return;
-// }
-// h->entries[fr.entry_index] = h->entries[h->count-1];
-// last = string_map__find(h, h->entries[fr.entry_index].key);
-// if (last.entry_prev != MAP_SENTINEL) {
-// h->entries[last.entry_prev].next = fr.entry_index;
-// } else {
-// h->hashes[last.hash_index] = fr.entry_index;
-// }
-// }
-
-// template <typename T>
-// gb_internal void string_map_remove(StringMap<T> *h, StringHashKey const &key) {
-// MapFindResult fr = string_map__find(h, key);
-// if (fr.entry_index != MAP_SENTINEL) {
-// string_map__erase(h, fr);
-// }
-// }
-
template <typename T>
gb_internal gb_inline void string_map_clear(StringMap<T> *h) {
h->count = 0;
@@ -329,11 +303,297 @@ gb_internal StringMapEntry<T> const *begin(StringMap<T> const &m) noexcept {
template <typename T>
-gb_internal StringMapEntry<T> *end(StringMap<T> &m) {
+gb_internal StringMapEntry<T> *end(StringMap<T> &m) noexcept {
return m.entries + m.count;
}
template <typename T>
gb_internal StringMapEntry<T> const *end(StringMap<T> const &m) noexcept {
return m.entries + m.count;
-} \ No newline at end of file
+}
+
+#else /* new string map */
+
+template <typename T>
+struct StringMapEntry {
+ String key;
+ u32 hash;
+ T value;
+};
+
+template <typename T>
+struct StringMap {
+ StringMapEntry<T> *entries;
+ u32 count;
+ u32 capacity;
+};
+
+
+template <typename T> gb_internal void string_map_init (StringMap<T> *h, usize capacity = 16);
+template <typename T> gb_internal void string_map_destroy (StringMap<T> *h);
+
+template <typename T> gb_internal T * string_map_get (StringMap<T> *h, char const *key);
+template <typename T> gb_internal T * string_map_get (StringMap<T> *h, String const &key);
+template <typename T> gb_internal T * string_map_get (StringMap<T> *h, StringHashKey const &key);
+
+template <typename T> gb_internal T & string_map_must_get(StringMap<T> *h, char const *key);
+template <typename T> gb_internal T & string_map_must_get(StringMap<T> *h, String const &key);
+template <typename T> gb_internal T & string_map_must_get(StringMap<T> *h, StringHashKey const &key);
+
+template <typename T> gb_internal void string_map_set (StringMap<T> *h, char const *key, T const &value);
+template <typename T> gb_internal void string_map_set (StringMap<T> *h, String const &key, T const &value);
+template <typename T> gb_internal void string_map_set (StringMap<T> *h, StringHashKey const &key, T const &value);
+
+// template <typename T> gb_internal void string_map_remove (StringMap<T> *h, StringHashKey const &key);
+template <typename T> gb_internal void string_map_clear (StringMap<T> *h);
+template <typename T> gb_internal void string_map_grow (StringMap<T> *h);
+template <typename T> gb_internal void string_map_reserve (StringMap<T> *h, usize new_count);
+
+gb_internal gbAllocator string_map_allocator(void) {
+ return heap_allocator();
+}
+
+template <typename T>
+gb_internal gb_inline void string_map_init(StringMap<T> *h, usize capacity) {
+ capacity = next_pow2_isize(capacity);
+ string_map_reserve(h, capacity);
+}
+
+template <typename T>
+gb_internal gb_inline void string_map_destroy(StringMap<T> *h) {
+ gb_free(string_map_allocator(), h->entries);
+}
+
+
+template <typename T>
+gb_internal void string_map__insert(StringMap<T> *h, u32 hash, String const &key, T const &value) {
+ if (h->count+1 >= h->capacity) {
+ string_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 string_map__full(StringMap<T> *h) {
+ return 0.75f * h->count <= h->capacity;
+}
+
+template <typename T>
+gb_inline void string_map_grow(StringMap<T> *h) {
+ isize new_capacity = gb_max(h->capacity<<1, 16);
+ string_map_reserve(h, new_capacity);
+}
+
+
+template <typename T>
+gb_internal void string_map_reserve(StringMap<T> *h, usize cap) {
+ if (cap < h->capacity) {
+ return;
+ }
+ cap = next_pow2_isize(cap);
+
+ StringMap<T> new_h = {};
+ new_h.count = 0;
+ new_h.capacity = cast(u32)cap;
+ new_h.entries = gb_alloc_array(string_map_allocator(), StringMapEntry<T>, new_h.capacity);
+
+ if (h->count) {
+ for (u32 i = 0; i < h->capacity; i++) {
+ auto *entry = h->entries+i;
+ if (entry->hash) {
+ string_map__insert(&new_h, entry->hash, entry->key, entry->value);
+ }
+ }
+ }
+ string_map_destroy(h);
+ *h = new_h;
+}
+
+template <typename T>
+gb_internal T *string_map_get(StringMap<T> *h, u32 hash, String 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 *string_map_get(StringMap<T> *h, StringHashKey const &key) {
+ return string_map_get(h, key.hash, key.string);
+}
+
+template <typename T>
+gb_internal gb_inline T *string_map_get(StringMap<T> *h, String const &key) {
+ return string_map_get(h, string_hash(key), key);
+}
+
+template <typename T>
+gb_internal gb_inline T *string_map_get(StringMap<T> *h, char const *key) {
+ String k = make_string_c(key);
+ return string_map_get(h, string_hash(k), k);
+}
+
+template <typename T>
+gb_internal T &string_map_must_get(StringMap<T> *h, u32 hash, String const &key) {
+ T *found = string_map_get(h, hash, key);
+ GB_ASSERT(found != nullptr);
+ return *found;
+}
+
+template <typename T>
+gb_internal T &string_map_must_get(StringMap<T> *h, StringHashKey const &key) {
+ return string_map_must_get(h, key.hash, key.string);
+}
+
+template <typename T>
+gb_internal gb_inline T &string_map_must_get(StringMap<T> *h, String const &key) {
+ return string_map_must_get(h, string_hash(key), key);
+}
+
+template <typename T>
+gb_internal gb_inline T &string_map_must_get(StringMap<T> *h, char const *key) {
+ String k = make_string_c(key);
+ return string_map_must_get(h, string_hash(k), k);
+}
+
+template <typename T>
+gb_internal void string_map_set(StringMap<T> *h, u32 hash, String const &key, T const &value) {
+ if (h->count == 0) {
+ string_map_grow(h);
+ }
+ auto *found = string_map_get(h, hash, key);
+ if (found) {
+ *found = value;
+ return;
+ }
+ string_map__insert(h, hash, key, value);
+}
+
+template <typename T>
+gb_internal gb_inline void string_map_set(StringMap<T> *h, String const &key, T const &value) {
+ string_map_set(h, string_hash_string(key), value);
+}
+
+template <typename T>
+gb_internal gb_inline void string_map_set(StringMap<T> *h, char const *key, T const &value) {
+ string_map_set(h, string_hash_string(make_string_c(key)), value);
+}
+
+template <typename T>
+gb_internal gb_inline void string_map_set(StringMap<T> *h, StringHashKey const &key, T const &value) {
+ string_map_set(h, key.hash, key.string, value);
+}
+
+
+template <typename T>
+gb_internal gb_inline void string_map_clear(StringMap<T> *h) {
+ h->count = 0;
+ gb_zero_array(h->entries, h->capacity);
+}
+
+
+template <typename T>
+struct StringMapIterator {
+ StringMap<T> *map;
+ MapIndex index;
+
+ StringMapIterator<T> &operator++() noexcept {
+ for (;;) {
+ ++index;
+ if (map->capacity == index) {
+ return *this;
+ }
+ StringMapEntry<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 StringMapEntry<T> *() const {
+ return map->entries+index;
+ }
+};
+
+
+template <typename T>
+gb_internal StringMapIterator<T> end(StringMap<T> &m) noexcept {
+ return StringMapIterator<T>{&m, m.capacity};
+}
+
+template <typename T>
+gb_internal StringMapIterator<T> const end(StringMap<T> const &m) noexcept {
+ return StringMapIterator<T>{&m, m.capacity};
+}
+
+
+
+template <typename T>
+gb_internal StringMapIterator<T> begin(StringMap<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(StringMap<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/string_set.cpp b/src/string_set.cpp
index fb4640c20..a37d8ba80 100644
--- a/src/string_set.cpp
+++ b/src/string_set.cpp
@@ -208,7 +208,9 @@ gb_internal void string_set__erase(StringSet *s, MapFindResult fr) {
}
auto *entry = &s->entries[fr.entry_index];
*entry = s->entries[s->entries.count-1];
- StringHashKey key = {entry->hash, entry->value};
+ StringHashKey key;
+ key.hash = entry->hash;
+ key.string = entry->value;
last = string_set__find(s, key);
if (last.entry_prev != MAP_SENTINEL) {
s->entries[last.entry_prev].next = fr.entry_index;
diff --git a/src/thread_pool.cpp b/src/thread_pool.cpp
index 5dbbe37c4..8363a4553 100644
--- a/src/thread_pool.cpp
+++ b/src/thread_pool.cpp
@@ -3,20 +3,28 @@
struct WorkerTask;
struct ThreadPool;
-gb_thread_local Thread *current_thread;
+gb_global gb_thread_local Thread *current_thread;
+gb_internal Thread *get_current_thread(void) {
+ return current_thread;
+}
gb_internal void thread_pool_init(ThreadPool *pool, isize worker_count, char const *worker_name);
gb_internal void thread_pool_destroy(ThreadPool *pool);
gb_internal bool thread_pool_add_task(ThreadPool *pool, WorkerTaskProc *proc, void *data);
gb_internal void thread_pool_wait(ThreadPool *pool);
+enum GrabState {
+ Grab_Success = 0,
+ Grab_Empty = 1,
+ Grab_Failed = 2,
+};
+
struct ThreadPool {
- gbAllocator threads_allocator;
- Slice<Thread> threads;
+ gbAllocator threads_allocator;
+ Slice<Thread> threads;
std::atomic<bool> running;
Futex tasks_available;
-
Futex tasks_left;
};
@@ -46,7 +54,7 @@ gb_internal void thread_pool_destroy(ThreadPool *pool) {
for_array_off(i, 1, pool->threads) {
Thread *t = &pool->threads[i];
- pool->tasks_available.fetch_add(1, std::memory_order_relaxed);
+ pool->tasks_available.fetch_add(1, std::memory_order_acquire);
futex_broadcast(&pool->tasks_available);
thread_join_and_destroy(t);
}
@@ -54,51 +62,86 @@ gb_internal void thread_pool_destroy(ThreadPool *pool) {
gb_free(pool->threads_allocator, pool->threads.data);
}
-void thread_pool_queue_push(Thread *thread, WorkerTask task) {
- u64 capture;
- u64 new_capture;
- do {
- capture = thread->head_and_tail.load();
-
- u64 mask = thread->capacity - 1;
- u64 head = (capture >> 32) & mask;
- u64 tail = ((u32)capture) & mask;
+TaskRingBuffer *task_ring_grow(TaskRingBuffer *ring, isize bottom, isize top) {
+ TaskRingBuffer *new_ring = task_ring_init(ring->size * 2);
+ for (isize i = top; i < bottom; i++) {
+ new_ring->buffer[i % new_ring->size] = ring->buffer[i % ring->size];
+ }
+ return new_ring;
+}
- u64 new_head = (head + 1) & mask;
- GB_ASSERT_MSG(new_head != tail, "Thread Queue Full!");
+void thread_pool_queue_push(Thread *thread, WorkerTask task) {
+ isize bot = thread->queue.bottom.load(std::memory_order_relaxed);
+ isize top = thread->queue.top.load(std::memory_order_acquire);
+ TaskRingBuffer *cur_ring = thread->queue.ring.load(std::memory_order_relaxed);
+
+ isize size = bot - top;
+ if (size > (cur_ring->size - 1)) {
+ // Queue is full
+ thread->queue.ring = task_ring_grow(thread->queue.ring, bot, top);
+ cur_ring = thread->queue.ring.load(std::memory_order_relaxed);
+ }
- // This *must* be done in here, to avoid a potential race condition where we no longer own the slot by the time we're assigning
- thread->queue[head] = task;
- new_capture = (new_head << 32) | tail;
- } while (!thread->head_and_tail.compare_exchange_weak(capture, new_capture));
+ cur_ring->buffer[bot % cur_ring->size] = task;
+ std::atomic_thread_fence(std::memory_order_release);
+ thread->queue.bottom.store(bot + 1, std::memory_order_relaxed);
thread->pool->tasks_left.fetch_add(1, std::memory_order_release);
thread->pool->tasks_available.fetch_add(1, std::memory_order_relaxed);
futex_broadcast(&thread->pool->tasks_available);
}
-bool thread_pool_queue_pop(Thread *thread, WorkerTask *task) {
- u64 capture;
- u64 new_capture;
- do {
- capture = thread->head_and_tail.load(std::memory_order_acquire);
-
- u64 mask = thread->capacity - 1;
- u64 head = (capture >> 32) & mask;
- u64 tail = ((u32)capture) & mask;
+GrabState thread_pool_queue_take(Thread *thread, WorkerTask *task) {
+ isize bot = thread->queue.bottom.load(std::memory_order_relaxed) - 1;
+ TaskRingBuffer *cur_ring = thread->queue.ring.load(std::memory_order_relaxed);
+ thread->queue.bottom.store(bot, std::memory_order_relaxed);
+ std::atomic_thread_fence(std::memory_order_seq_cst);
+
+ isize top = thread->queue.top.load(std::memory_order_relaxed);
+ if (top <= bot) {
+
+ // Queue is not empty
+ *task = cur_ring->buffer[bot % cur_ring->size];
+ if (top == bot) {
+ // Only one entry left in queue
+ if (!thread->queue.top.compare_exchange_strong(top, top + 1, std::memory_order_seq_cst, std::memory_order_relaxed)) {
+ // Race failed
+ thread->queue.bottom.store(bot + 1, std::memory_order_relaxed);
+ return Grab_Empty;
+ }
- u64 new_tail = (tail + 1) & mask;
- if (tail == head) {
- return false;
+ thread->queue.bottom.store(bot + 1, std::memory_order_relaxed);
+ return Grab_Success;
}
- // Making a copy of the task before we increment the tail, avoiding the same potential race condition as above
- *task = thread->queue[tail];
-
- new_capture = (head << 32) | new_tail;
- } while (!thread->head_and_tail.compare_exchange_weak(capture, new_capture, std::memory_order_release));
+ // We got a task without hitting a race
+ return Grab_Success;
+ } else {
+ // Queue is empty
+ thread->queue.bottom.store(bot + 1, std::memory_order_relaxed);
+ return Grab_Empty;
+ }
+}
- return true;
+GrabState thread_pool_queue_steal(Thread *thread, WorkerTask *task) {
+ isize top = thread->queue.top.load(std::memory_order_acquire);
+ std::atomic_thread_fence(std::memory_order_seq_cst);
+ isize bot = thread->queue.bottom.load(std::memory_order_acquire);
+
+ GrabState ret = Grab_Empty;
+ if (top < bot) {
+ // Queue is not empty
+ TaskRingBuffer *cur_ring = thread->queue.ring.load(std::memory_order_consume);
+ *task = cur_ring->buffer[top % cur_ring->size];
+
+ if (!thread->queue.top.compare_exchange_strong(top, top + 1, std::memory_order_seq_cst, std::memory_order_relaxed)) {
+ // Race failed
+ ret = Grab_Failed;
+ } else {
+ ret = Grab_Success;
+ }
+ }
+ return ret;
}
gb_internal bool thread_pool_add_task(ThreadPool *pool, WorkerTaskProc *proc, void *data) {
@@ -115,12 +158,11 @@ gb_internal void thread_pool_wait(ThreadPool *pool) {
while (pool->tasks_left.load(std::memory_order_acquire)) {
// if we've got tasks on our queue, run them
- while (thread_pool_queue_pop(current_thread, &task)) {
+ while (!thread_pool_queue_take(current_thread, &task)) {
task.do_work(task.data);
pool->tasks_left.fetch_sub(1, std::memory_order_release);
}
-
// is this mem-barriered enough?
// This *must* be executed in this order, so the futex wakes immediately
// if rem_tasks has changed since we checked last, otherwise the program
@@ -145,7 +187,7 @@ gb_internal THREAD_PROC(thread_pool_thread_proc) {
usize finished_tasks = 0;
i32 state;
- while (thread_pool_queue_pop(current_thread, &task)) {
+ while (!thread_pool_queue_take(current_thread, &task)) {
task.do_work(task.data);
pool->tasks_left.fetch_sub(1, std::memory_order_release);
@@ -167,7 +209,12 @@ gb_internal THREAD_PROC(thread_pool_thread_proc) {
Thread *thread = &pool->threads.data[idx];
WorkerTask task;
- if (thread_pool_queue_pop(thread, &task)) {
+
+ GrabState ret = thread_pool_queue_steal(thread, &task);
+ switch (ret) {
+ case Grab_Empty:
+ continue;
+ case Grab_Success:
task.do_work(task.data);
pool->tasks_left.fetch_sub(1, std::memory_order_release);
@@ -175,6 +222,8 @@ gb_internal THREAD_PROC(thread_pool_thread_proc) {
futex_signal(&pool->tasks_left);
}
+ /*fallthrough*/
+ case Grab_Failed:
goto main_loop_continue;
}
}
@@ -182,6 +231,7 @@ gb_internal THREAD_PROC(thread_pool_thread_proc) {
// if we've done all our work, and there's nothing to steal, go to sleep
state = pool->tasks_available.load(std::memory_order_acquire);
+ if (!pool->running) { break; }
futex_wait(&pool->tasks_available, state);
main_loop_continue:;
diff --git a/src/threading.cpp b/src/threading.cpp
index 3ddc05b0a..af8fd803c 100644
--- a/src/threading.cpp
+++ b/src/threading.cpp
@@ -46,6 +46,18 @@ typedef struct WorkerTask {
void *data;
} WorkerTask;
+typedef struct TaskRingBuffer {
+ std::atomic<isize> size;
+ std::atomic<WorkerTask *> buffer;
+} TaskRingBuffer;
+
+typedef struct TaskQueue {
+ std::atomic<isize> top;
+ std::atomic<isize> bottom;
+
+ std::atomic<TaskRingBuffer *> ring;
+} TaskQueue;
+
struct Thread {
#if defined(GB_SYSTEM_WINDOWS)
void *win32_handle;
@@ -54,13 +66,13 @@ struct Thread {
#endif
isize idx;
+ isize stack_size;
- WorkerTask *queue;
- size_t capacity;
- std::atomic<uint64_t> head_and_tail;
-
- isize stack_size;
+ struct TaskQueue queue;
struct ThreadPool *pool;
+
+ struct Arena *permanent_arena;
+ struct Arena *temporary_arena;
};
typedef std::atomic<i32> Futex;
@@ -107,6 +119,20 @@ gb_internal void thread_set_name (Thread *t, char const *name);
gb_internal void yield_thread(void);
gb_internal void yield_process(void);
+struct Wait_Signal {
+ Futex futex;
+};
+
+gb_internal void wait_signal_until_available(Wait_Signal *ws) {
+ if (ws->futex.load() == 0) {
+ futex_wait(&ws->futex, 0);
+ }
+}
+
+gb_internal void wait_signal_set(Wait_Signal *ws) {
+ ws->futex.store(1);
+ futex_broadcast(&ws->futex);
+}
struct MutexGuard {
MutexGuard() = delete;
@@ -119,17 +145,25 @@ struct MutexGuard {
explicit MutexGuard(RecursiveMutex *rm) noexcept : rm{rm} {
mutex_lock(this->rm);
}
+ explicit MutexGuard(RwMutex *rwm) noexcept : rwm{rwm} {
+ rw_mutex_lock(this->rwm);
+ }
explicit MutexGuard(BlockingMutex &bm) noexcept : bm{&bm} {
mutex_lock(this->bm);
}
explicit MutexGuard(RecursiveMutex &rm) noexcept : rm{&rm} {
mutex_lock(this->rm);
}
+ explicit MutexGuard(RwMutex &rwm) noexcept : rwm{&rwm} {
+ rw_mutex_lock(this->rwm);
+ }
~MutexGuard() noexcept {
if (this->bm) {
mutex_unlock(this->bm);
} else if (this->rm) {
mutex_unlock(this->rm);
+ } else if (this->rwm) {
+ rw_mutex_unlock(this->rwm);
}
}
@@ -137,10 +171,12 @@ struct MutexGuard {
BlockingMutex *bm;
RecursiveMutex *rm;
+ RwMutex *rwm;
};
#define MUTEX_GUARD_BLOCK(m) if (MutexGuard GB_DEFER_3(_mutex_guard_){m})
#define MUTEX_GUARD(m) mutex_lock(m); defer (mutex_unlock(m))
+#define RW_MUTEX_GUARD(m) rw_mutex_lock(m); defer (rw_mutex_unlock(m))
struct RecursiveMutex {
@@ -210,7 +246,7 @@ gb_internal void semaphore_wait(Semaphore *s) {
original_count = s->count().load(std::memory_order_relaxed);
}
- if (!s->count().compare_exchange_strong(original_count, original_count-1, std::memory_order_acquire, std::memory_order_acquire)) {
+ if (s->count().compare_exchange_strong(original_count, original_count-1, std::memory_order_acquire, std::memory_order_acquire)) {
return;
}
}
@@ -466,6 +502,12 @@ gb_internal u32 thread_current_id(void) {
__asm__("mov %%fs:0x10,%0" : "=r"(thread_id));
#elif defined(GB_SYSTEM_LINUX)
thread_id = gettid();
+#elif defined(GB_SYSTEM_HAIKU)
+ thread_id = find_thread(NULL);
+#elif defined(GB_SYSTEM_FREEBSD)
+ thread_id = pthread_getthreadid_np();
+#elif defined(GB_SYSTEM_NETBSD)
+ thread_id = (u32)_lwp_self();
#else
#error Unsupported architecture for thread_current_id()
#endif
@@ -487,6 +529,9 @@ gb_internal gb_inline void yield_thread(void) {
_mm_pause();
#elif defined(GB_CPU_ARM)
__asm__ volatile ("yield" : : : "memory");
+#elif defined(GB_CPU_RISCV)
+ // I guess?
+ __asm__ volatile ("nop" : : : "memory");
#else
#error Unknown architecture
#endif
@@ -521,6 +566,20 @@ gb_internal void *internal_thread_proc(void *arg) {
}
#endif
+gb_internal TaskRingBuffer *task_ring_init(isize size) {
+ TaskRingBuffer *ring = gb_alloc_item(heap_allocator(), TaskRingBuffer);
+ ring->size = size;
+ ring->buffer = gb_alloc_array(heap_allocator(), WorkerTask, ring->size);
+ return ring;
+}
+
+gb_internal void thread_queue_destroy(TaskQueue *q) {
+ gb_free(heap_allocator(), (*q->ring).buffer);
+ gb_free(heap_allocator(), q->ring);
+}
+
+gb_internal void thread_init_arenas(Thread *t);
+
gb_internal void thread_init(ThreadPool *pool, Thread *t, isize idx) {
gb_zero_item(t);
#if defined(GB_SYSTEM_WINDOWS)
@@ -529,13 +588,13 @@ gb_internal void thread_init(ThreadPool *pool, Thread *t, isize idx) {
t->posix_handle = 0;
#endif
- t->capacity = 1 << 14; // must be a power of 2
- t->queue = gb_alloc_array(heap_allocator(), WorkerTask, t->capacity);
- t->head_and_tail = 0;
+ // Size must be a power of 2
+ t->queue.ring = task_ring_init(1 << 14);
t->pool = pool;
t->idx = idx;
-}
+ thread_init_arenas(t);
+}
gb_internal void thread_init_and_start(ThreadPool *pool, Thread *t, isize idx) {
thread_init(pool, t, idx);
@@ -568,7 +627,7 @@ gb_internal void thread_join_and_destroy(Thread *t) {
t->posix_handle = 0;
#endif
- gb_free(heap_allocator(), t->queue);
+ thread_queue_destroy(&t->queue);
}
gb_internal void thread_set_name(Thread *t, char const *name) {
@@ -600,15 +659,23 @@ gb_internal void thread_set_name(Thread *t, char const *name) {
pthread_setname_np(name);
#elif defined(GB_SYSTEM_FREEBSD) || defined(GB_SYSTEM_OPENBSD)
pthread_set_name_np(t->posix_handle, name);
+#elif defined(GB_SYSTEM_NETBSD)
+ pthread_setname_np(t->posix_handle, "%s", (void*)name);
#else
// TODO(bill): Test if this works
pthread_setname_np(t->posix_handle, name);
#endif
}
-#if defined(GB_SYSTEM_LINUX)
-#include <linux/futex.h>
+#if defined(GB_SYSTEM_LINUX) || defined(GB_SYSTEM_NETBSD)
+
#include <sys/syscall.h>
+#ifdef GB_SYSTEM_LINUX
+ #include <linux/futex.h>
+#else
+ #include <sys/futex.h>
+ #define SYS_futex SYS___futex
+#endif
gb_internal void futex_signal(Futex *addr) {
int ret = syscall(SYS_futex, addr, FUTEX_WAKE | FUTEX_PRIVATE_FLAG, 1, NULL, NULL, 0);
@@ -660,7 +727,7 @@ gb_internal void futex_broadcast(Futex *addr) {
gb_internal void futex_wait(Futex *addr, Footex val) {
for (;;) {
int ret = _umtx_op(addr, UMTX_OP_WAIT_UINT, val, 0, NULL);
- if (ret == 0) {
+ if (ret == -1) {
if (errno == ETIMEDOUT || errno == EINTR) {
continue;
}
@@ -732,15 +799,59 @@ gb_internal void futex_wait(Futex *f, Footex val) {
#elif defined(GB_SYSTEM_OSX)
-#define UL_COMPARE_AND_WAIT 0x00000001
-#define ULF_NO_ERRNO 0x01000000
+// IMPORTANT NOTE(laytan): We use `OS_SYNC_*_SHARED` and `UL_COMPARE_AND_WAIT_SHARED` flags here.
+// these flags tell the kernel that we are using these futexes across different processes which
+// causes it to opt-out of some optimisations.
+//
+// BUT this is not actually the case! We should be using the normal non-shared version and letting
+// the kernel optimize (I've measured it to be about 10% faster at the parsing/type checking stages).
+//
+// However we have reports of people on MacOS running into kernel panics, and this seems to fix it for them.
+// Which means there is probably a bug in the kernel in one of these non-shared optimisations causing the panic.
+//
+// The panic also doesn't seem to happen on normal M1 CPUs, and happen more on later CPUs or pro/max series.
+// Probably because they have more going on in terms of threads etc.
+
+#if __has_include(<os/os_sync_wait_on_address.h>)
+ #define DARWIN_WAIT_ON_ADDRESS_AVAILABLE
+ #include <os/os_sync_wait_on_address.h>
+#endif
+
+#define UL_COMPARE_AND_WAIT 0x00000001
+#define UL_COMPARE_AND_WAIT_SHARED 0x00000003
+#define ULF_NO_ERRNO 0x01000000
extern "C" int __ulock_wait(uint32_t operation, void *addr, uint64_t value, uint32_t timeout); /* timeout is specified in microseconds */
extern "C" int __ulock_wake(uint32_t operation, void *addr, uint64_t wake_value);
gb_internal void futex_signal(Futex *f) {
+ #ifdef DARWIN_WAIT_ON_ADDRESS_AVAILABLE
+ if (__builtin_available(macOS 14.4, *)) {
+ for (;;) {
+ int ret = os_sync_wake_by_address_any(f, sizeof(Futex), OS_SYNC_WAKE_BY_ADDRESS_SHARED);
+ if (ret >= 0) {
+ return;
+ }
+ if (errno == EINTR || errno == EFAULT) {
+ continue;
+ }
+ if (errno == ENOENT) {
+ return;
+ }
+ GB_PANIC("Failed in futex wake %d %d!\n", ret, errno);
+ }
+ } else {
+ #endif
+ // UL_COMPARE_AND_WAIT_SHARED is only available on macOS 10.15+
+ int WAIT_FLAG;
+ if (__builtin_available(macOS 10.15, *)) {
+ WAIT_FLAG = UL_COMPARE_AND_WAIT_SHARED;
+ } else {
+ WAIT_FLAG = UL_COMPARE_AND_WAIT;
+ }
+
for (;;) {
- int ret = __ulock_wake(UL_COMPARE_AND_WAIT | ULF_NO_ERRNO, f, 0);
+ int ret = __ulock_wake(WAIT_FLAG | ULF_NO_ERRNO, f, 0);
if (ret >= 0) {
return;
}
@@ -752,12 +863,40 @@ gb_internal void futex_signal(Futex *f) {
}
GB_PANIC("Failed in futex wake!\n");
}
+ #ifdef DARWIN_WAIT_ON_ADDRESS_AVAILABLE
+ }
+ #endif
}
gb_internal void futex_broadcast(Futex *f) {
+ #ifdef DARWIN_WAIT_ON_ADDRESS_AVAILABLE
+ if (__builtin_available(macOS 14.4, *)) {
+ for (;;) {
+ int ret = os_sync_wake_by_address_all(f, sizeof(Footex), OS_SYNC_WAKE_BY_ADDRESS_SHARED);
+ if (ret >= 0) {
+ return;
+ }
+ if (errno == EINTR || errno == EFAULT) {
+ continue;
+ }
+ if (errno == ENOENT) {
+ return;
+ }
+ GB_PANIC("Failed in futext wake %d %d!\n", ret, errno);
+ }
+ } else {
+ #endif
+ // UL_COMPARE_AND_WAIT_SHARED is only available on macOS 10.15+
+ int WAIT_FLAG;
+ if (__builtin_available(macOS 10.15, *)) {
+ WAIT_FLAG = UL_COMPARE_AND_WAIT_SHARED;
+ } else {
+ WAIT_FLAG = UL_COMPARE_AND_WAIT;
+ }
+
for (;;) {
enum { ULF_WAKE_ALL = 0x00000100 };
- int ret = __ulock_wake(UL_COMPARE_AND_WAIT | ULF_NO_ERRNO | ULF_WAKE_ALL, f, 0);
+ int ret = __ulock_wake(WAIT_FLAG | ULF_NO_ERRNO | ULF_WAKE_ALL, f, 0);
if (ret == 0) {
return;
}
@@ -769,11 +908,42 @@ gb_internal void futex_broadcast(Futex *f) {
}
GB_PANIC("Failed in futex wake!\n");
}
+ #ifdef DARWIN_WAIT_ON_ADDRESS_AVAILABLE
+ }
+ #endif
}
gb_internal void futex_wait(Futex *f, Footex val) {
+ #ifdef DARWIN_WAIT_ON_ADDRESS_AVAILABLE
+ if (__builtin_available(macOS 14.4, *)) {
+ for (;;) {
+ int ret = os_sync_wait_on_address(f, cast(uint64_t)(val), sizeof(Footex), OS_SYNC_WAIT_ON_ADDRESS_SHARED);
+ if (ret >= 0) {
+ if (*f != val) {
+ return;
+ }
+ continue;
+ }
+ if (errno == EINTR || errno == EFAULT) {
+ continue;
+ }
+ if (errno == ENOENT) {
+ return;
+ }
+ GB_PANIC("Failed in futex wait %d %d!\n", ret, errno);
+ }
+ } else {
+ #endif
+ // UL_COMPARE_AND_WAIT_SHARED is only available on macOS 10.15+
+ int WAIT_FLAG;
+ if (__builtin_available(macOS 10.15, *)) {
+ WAIT_FLAG = UL_COMPARE_AND_WAIT_SHARED;
+ } else {
+ WAIT_FLAG = UL_COMPARE_AND_WAIT;
+ }
+
for (;;) {
- int ret = __ulock_wait(UL_COMPARE_AND_WAIT | ULF_NO_ERRNO, f, val, 0);
+ int ret = __ulock_wait(WAIT_FLAG | ULF_NO_ERRNO, f, val, 0);
if (ret >= 0) {
if (*f != val) {
return;
@@ -789,7 +959,11 @@ gb_internal void futex_wait(Futex *f, Footex val) {
GB_PANIC("Failed in futex wait!\n");
}
+ #ifdef DARWIN_WAIT_ON_ADDRESS_AVAILABLE
+ }
+ #endif
}
+
#elif defined(GB_SYSTEM_WINDOWS)
gb_internal void futex_signal(Futex *f) {
@@ -805,8 +979,178 @@ gb_internal void futex_wait(Futex *f, Footex val) {
WaitOnAddress(f, (void *)&val, sizeof(val), INFINITE);
} while (f->load() == val);
}
+
+#elif defined(GB_SYSTEM_HAIKU)
+
+// Futex implementation taken from https://tavianator.com/2023/futex.html
+
+#include <pthread.h>
+#include <atomic>
+
+struct _Spinlock {
+ std::atomic_flag state;
+
+ void init() {
+ state.clear();
+ }
+
+ void lock() {
+ while (state.test_and_set(std::memory_order_acquire)) {
+ #if defined(GB_CPU_X86)
+ _mm_pause();
+ #else
+ (void)0; // spin...
+ #endif
+ }
+ }
+
+ void unlock() {
+ state.clear(std::memory_order_release);
+ }
+};
+
+struct Futex_Waitq;
+
+struct Futex_Waiter {
+ _Spinlock lock;
+ pthread_t thread;
+ Futex *futex;
+ Futex_Waitq *waitq;
+ Futex_Waiter *prev, *next;
+};
+
+struct Futex_Waitq {
+ _Spinlock lock;
+ Futex_Waiter list;
+
+ void init() {
+ auto head = &list;
+ head->prev = head->next = head;
+ }
+};
+
+// FIXME: This approach may scale badly in the future,
+// possible solution - hash map (leads to deadlocks now).
+
+Futex_Waitq g_waitq = {
+ .lock = ATOMIC_FLAG_INIT,
+ .list = {
+ .prev = &g_waitq.list,
+ .next = &g_waitq.list,
+ },
+};
+
+Futex_Waitq *get_waitq(Futex *f) {
+ // Future hash map method...
+ return &g_waitq;
+}
+
+void futex_signal(Futex *f) {
+ auto waitq = get_waitq(f);
+
+ waitq->lock.lock();
+
+ auto head = &waitq->list;
+ for (auto waiter = head->next; waiter != head; waiter = waiter->next) {
+ if (waiter->futex != f) {
+ continue;
+ }
+ waitq->lock.unlock();
+ pthread_kill(waiter->thread, SIGCONT);
+ return;
+ }
+
+ waitq->lock.unlock();
+}
+
+void futex_broadcast(Futex *f) {
+ auto waitq = get_waitq(f);
+
+ waitq->lock.lock();
+
+ auto head = &waitq->list;
+ for (auto waiter = head->next; waiter != head; waiter = waiter->next) {
+ if (waiter->futex != f) {
+ continue;
+ }
+ if (waiter->next == head) {
+ waitq->lock.unlock();
+ pthread_kill(waiter->thread, SIGCONT);
+ return;
+ } else {
+ pthread_kill(waiter->thread, SIGCONT);
+ }
+ }
+
+ waitq->lock.unlock();
+}
+
+void futex_wait(Futex *f, Footex val) {
+ Futex_Waiter waiter;
+ waiter.thread = pthread_self();
+ waiter.futex = f;
+
+ auto waitq = get_waitq(f);
+ while (waitq->lock.state.test_and_set(std::memory_order_acquire)) {
+ if (f->load(std::memory_order_relaxed) != val) {
+ return;
+ }
+ #if defined(GB_CPU_X86)
+ _mm_pause();
+ #else
+ (void)0; // spin...
+ #endif
+ }
+
+ waiter.waitq = waitq;
+ waiter.lock.init();
+ waiter.lock.lock();
+
+ auto head = &waitq->list;
+ waiter.prev = head->prev;
+ waiter.next = head;
+ waiter.prev->next = &waiter;
+ waiter.next->prev = &waiter;
+
+ waiter.prev->next = &waiter;
+ waiter.next->prev = &waiter;
+
+ sigset_t old_mask, mask;
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGCONT);
+ pthread_sigmask(SIG_BLOCK, &mask, &old_mask);
+
+ if (f->load(std::memory_order_relaxed) == val) {
+ waiter.lock.unlock();
+ waitq->lock.unlock();
+
+ int sig;
+ sigwait(&mask, &sig);
+
+ waitq->lock.lock();
+ waiter.lock.lock();
+
+ while (waitq != waiter.waitq) {
+ auto req = waiter.waitq;
+ waiter.lock.unlock();
+ waitq->lock.unlock();
+ waitq = req;
+ waitq->lock.lock();
+ waiter.lock.lock();
+ }
+ }
+
+ waiter.prev->next = waiter.next;
+ waiter.next->prev = waiter.prev;
+
+ pthread_sigmask(SIG_SETMASK, &old_mask, NULL);
+
+ waiter.lock.unlock();
+ waitq->lock.unlock();
+}
+
#endif
#if defined(GB_SYSTEM_WINDOWS)
#pragma warning(pop)
-#endif \ No newline at end of file
+#endif
diff --git a/src/tilde.cpp b/src/tilde.cpp
index b27c42a12..f6fed0f9a 100644
--- a/src/tilde.cpp
+++ b/src/tilde.cpp
@@ -215,7 +215,7 @@ gb_internal void cg_set_debug_pos_from_node(cgProcedure *p, Ast *node) {
TokenPos pos = ast_token(node).pos;
TB_SourceFile **file = map_get(&p->module->file_id_map, cast(uintptr)pos.file_id);
if (file) {
- tb_inst_set_location(p->func, *file, pos.line, pos.column);
+ tb_inst_location(p->func, *file, pos.line, pos.column);
}
}
}
@@ -363,7 +363,6 @@ gb_internal bool cg_global_variables_create(cgModule *m, Array<cgGlobalVariable>
if (is_foreign) {
linkage = TB_LINKAGE_PUBLIC;
// lb_add_foreign_library_path(m, e->Variable.foreign_library);
- // lb_set_wasm_import_attributes(g.value, e, name);
} else if (is_export) {
linkage = TB_LINKAGE_PUBLIC;
}
@@ -373,7 +372,7 @@ gb_internal bool cg_global_variables_create(cgModule *m, Array<cgGlobalVariable>
TB_Global *global = tb_global_create(m->mod, name.len, cast(char const *)name.text, debug_type, linkage);
cgValue g = cg_value(global, alloc_type_pointer(e->type));
- TB_ModuleSection *section = tb_module_get_data(m->mod);
+ TB_ModuleSectionHandle section = tb_module_get_data(m->mod);
if (e->Variable.thread_local_model != "") {
section = tb_module_get_tls(m->mod);
@@ -726,6 +725,10 @@ gb_internal bool cg_generate_code(Checker *c, LinkerData *linker_data) {
linker_data_init(linker_data, info, c->parser->init_fullpath);
+ #if defined(GB_SYSTEM_OSX)
+ linker_enable_system_library_linking(linker_data);
+ #endif
+
cg_global_arena_init();
cgModule *m = cg_module_create(c);
@@ -821,6 +824,7 @@ gb_internal bool cg_generate_code(Checker *c, LinkerData *linker_data) {
case TargetOs_essence:
case TargetOs_freebsd:
case TargetOs_openbsd:
+ case TargetOs_haiku:
debug_format = TB_DEBUGFMT_DWARF;
break;
}
diff --git a/src/tilde.hpp b/src/tilde.hpp
index 7088912fd..d141b2330 100644
--- a/src/tilde.hpp
+++ b/src/tilde.hpp
@@ -8,8 +8,8 @@
#include "tilde/tb.h"
#include "tilde/tb_arena.h"
-#define TB_TYPE_F16 TB_DataType{ { TB_INT, 0, 16 } }
-#define TB_TYPE_I128 TB_DataType{ { TB_INT, 0, 128 } }
+#define TB_TYPE_F16 TB_DataType{ { TB_INT, 16 } }
+#define TB_TYPE_I128 TB_DataType{ { TB_INT, 128 } }
#define TB_TYPE_INT TB_TYPE_INTN(cast(u16)(8*build_context.int_size))
#define TB_TYPE_INTPTR TB_TYPE_INTN(cast(u16)(8*build_context.ptr_size))
diff --git a/src/tilde/tb.h b/src/tilde/tb.h
index 5bb98fe70..4fe21c902 100644
--- a/src/tilde/tb.h
+++ b/src/tilde/tb.h
@@ -4,9 +4,15 @@
// SSA - single static assignment
// GVN - global value numbering
// CSE - common subexpression elimination
+// CFG - control flow graph
// DSE - dead store elimination
// GCM - global code motion
// SROA - scalar replacement of aggregates
+// SCCP - sparse conditional constant propagation
+// RPO - reverse postorder
+// BB - basic block
+// ZTC - zero trip count
+// SCC - strongly connected components
#ifndef TB_CORE_H
#define TB_CORE_H
@@ -21,7 +27,7 @@
// https://semver.org/
#define TB_VERSION_MAJOR 0
-#define TB_VERSION_MINOR 2
+#define TB_VERSION_MINOR 3
#define TB_VERSION_PATCH 0
#ifndef TB_API
@@ -125,12 +131,6 @@ typedef struct TB_FeatureSet {
TB_FeatureSet_X64 x64;
} TB_FeatureSet;
-typedef enum TB_BranchHint {
- TB_BRANCH_HINT_NONE,
- TB_BRANCH_HINT_LIKELY,
- TB_BRANCH_HINT_UNLIKELY
-} TB_BranchHint;
-
typedef enum TB_Linkage {
TB_LINKAGE_PUBLIC,
TB_LINKAGE_PRIVATE
@@ -151,12 +151,6 @@ typedef enum TB_MemoryOrder {
TB_MEM_ORDER_SEQ_CST,
} TB_MemoryOrder;
-typedef enum TB_ISelMode {
- // FastISel
- TB_ISEL_FAST,
- TB_ISEL_COMPLEX
-} TB_ISelMode;
-
typedef enum TB_DataTypeEnum {
// Integers, note void is an i0 and bool is an i1
// i(0-64)
@@ -166,12 +160,14 @@ typedef enum TB_DataTypeEnum {
TB_FLOAT,
// Pointers
TB_PTR,
- // Tuples, these cannot be used in memory ops, just accessed via projections
- TB_TUPLE,
// represents control flow for REGION, BRANCH
TB_CONTROL,
// represents memory (and I/O)
TB_MEMORY,
+ // continuation (usually just return addresses :p)
+ TB_CONT,
+ // Tuples, these cannot be used in memory ops, just accessed via projections
+ TB_TUPLE,
} TB_DataTypeEnum;
typedef enum TB_FloatFormat {
@@ -181,15 +177,13 @@ typedef enum TB_FloatFormat {
typedef union TB_DataType {
struct {
- uint8_t type;
- // Only integers and floats can be wide.
- uint8_t width;
+ uint16_t type : 4;
// for integers it's the bitwidth
- uint16_t data;
+ uint16_t data : 12;
};
- uint32_t raw;
+ uint16_t raw;
} TB_DataType;
-static_assert(sizeof(TB_DataType) == 4, "im expecting this to be a uint32_t");
+static_assert(sizeof(TB_DataType) == 2, "im expecting this to be a uint16_t");
// classify data types
#define TB_IS_VOID_TYPE(x) ((x).type == TB_INT && (x).data == 0)
@@ -240,6 +234,11 @@ typedef enum TB_NodeTypeEnum {
TB_MACHINE_OP, // (Control, Memory) & Buffer -> (Control, Memory)
// reads the TSC on x64
TB_CYCLE_COUNTER, // (Control) -> Int64
+ // prefetches data for reading. The number next to the
+ //
+ // 0 is temporal
+ // 1-3 are just cache levels
+ TB_PREFETCH, // (Memory, Ptr) & Int -> Memory
////////////////////////////////
// CONTROL
@@ -269,7 +268,11 @@ typedef enum TB_NodeTypeEnum {
// trap will not be continuable but will stop execution.
TB_TRAP, // (Control) -> (Control)
// unreachable means it won't trap or be continuable.
- TB_UNREACHABLE, // (Control) -> (Control)
+ TB_UNREACHABLE, // (Control) -> ()
+ // this is generated when a path becomes disconnected
+ // from the main IR, it'll be reduced by the monotonic
+ // rewrites.
+ TB_DEAD, // () -> (Control)
////////////////////////////////
// CONTROL + MEMORY
@@ -278,38 +281,49 @@ typedef enum TB_NodeTypeEnum {
// target pointer (or syscall number) and the rest are just data args.
TB_CALL, // (Control, Memory, Data, Data...) -> (Control, Memory, Data)
TB_SYSCALL, // (Control, Memory, Data, Data...) -> (Control, Memory, Data)
+ // performs call while recycling the stack frame somewhat
+ TB_TAILCALL, // (Control, Memory, RPC, Data, Data...) -> ()
// safepoint polls are the same except they only trigger if the poll site
// says to (platform specific but almost always just the page being made
// unmapped/guard), 3rd argument is the poll site.
- TB_SAFEPOINT_POLL, // (Control, Memory, Ptr, Data...) -> (Control)
+ TB_SAFEPOINT_POLL, // (Control, Memory, Ptr?, Data...) -> (Control)
+ // this safepoint which doesn't emit any poll site, it's just
+ // an address, this is used by AOT compiles to encode line info.
+ TB_SAFEPOINT_NOP, // (Control, Memory, Ptr?, Data...) -> (Control)
////////////////////////////////
// MEMORY
////////////////////////////////
+ // MERGEMEM will join multiple non-aliasing memory effects, because
+ // they don't alias there's no ordering guarentee.
+ TB_MERGEMEM, // (Memory...) -> Memory
// LOAD and STORE are standard memory accesses, they can be folded away.
- TB_LOAD, // (Memory, Ptr) -> Data
- TB_STORE, // (Memory, Ptr, Data) -> Memory
+ TB_LOAD, // (Control?, Memory, Ptr) -> Data
+ TB_STORE, // (Control, Memory, Ptr, Data) -> Memory
// bulk memory ops.
- TB_MEMCPY, // (Memory, Ptr, Ptr, Size) -> Memory
- TB_MEMSET, // (Memory, Ptr, Int8, Size) -> Memory
+ TB_MEMCPY, // (Control, Memory, Ptr, Ptr, Size) -> Memory
+ TB_MEMSET, // (Control, Memory, Ptr, Int8, Size) -> Memory
// these memory accesses represent "volatile" which means
// they may produce side effects and thus cannot be eliminated.
- TB_READ, // (Memory, Ptr) -> (Memory, Data)
- TB_WRITE, // (Memory, Ptr, Data) -> (Memory, Data)
+ TB_READ, // (Control, Memory, Ptr) -> (Memory, Data)
+ TB_WRITE, // (Control, Memory, Ptr, Data) -> (Memory, Data)
// atomics have multiple observers (if not they wouldn't need to
// be atomic) and thus produce side effects everywhere just like
// volatiles except they have synchronization guarentees. the atomic
// data ops will return the value before the operation is performed.
// Atomic CAS return the old value and a boolean for success (true if
// the value was changed)
- TB_ATOMIC_LOAD, // (Memory, Ptr) -> (Memory, Data)
- TB_ATOMIC_XCHG, // (Memory, Ptr, Data) -> (Memory, Data)
- TB_ATOMIC_ADD, // (Memory, Ptr, Data) -> (Memory, Data)
- TB_ATOMIC_SUB, // (Memory, Ptr, Data) -> (Memory, Data)
- TB_ATOMIC_AND, // (Memory, Ptr, Data) -> (Memory, Data)
- TB_ATOMIC_XOR, // (Memory, Ptr, Data) -> (Memory, Data)
- TB_ATOMIC_OR, // (Memory, Ptr, Data) -> (Memory, Data)
- TB_ATOMIC_CAS, // (Memory, Data, Data) -> (Memory, Data, Bool)
+ TB_ATOMIC_LOAD, // (Control, Memory, Ptr) -> (Memory, Data)
+ TB_ATOMIC_XCHG, // (Control, Memory, Ptr, Data) -> (Memory, Data)
+ TB_ATOMIC_ADD, // (Control, Memory, Ptr, Data) -> (Memory, Data)
+ TB_ATOMIC_SUB, // (Control, Memory, Ptr, Data) -> (Memory, Data)
+ TB_ATOMIC_AND, // (Control, Memory, Ptr, Data) -> (Memory, Data)
+ TB_ATOMIC_XOR, // (Control, Memory, Ptr, Data) -> (Memory, Data)
+ TB_ATOMIC_OR, // (Control, Memory, Ptr, Data) -> (Memory, Data)
+ TB_ATOMIC_CAS, // (Control, Memory, Data, Data) -> (Memory, Data, Bool)
+
+ // like a multi-way branch but without the control flow aspect, but for data.
+ TB_LOOKUP,
////////////////////////////////
// POINTERS
@@ -375,6 +389,8 @@ typedef enum TB_NodeTypeEnum {
TB_FSUB,
TB_FMUL,
TB_FDIV,
+ TB_FMAX,
+ TB_FMIN,
// Comparisons
TB_CMP_EQ,
@@ -418,10 +434,17 @@ typedef uint32_t TB_CharUnits;
typedef struct {
// used by the debug info export
int id;
+
size_t len;
uint8_t path[];
} TB_SourceFile;
+typedef struct TB_Location {
+ TB_SourceFile* file;
+ int line, column;
+ uint32_t pos;
+} TB_Location;
+
// SO refers to shared objects which mean either shared libraries (.so or .dll)
// or executables (.exe or ELF executables)
typedef enum {
@@ -441,31 +464,38 @@ typedef struct TB_DebugType TB_DebugType;
typedef struct TB_ModuleSection TB_ModuleSection;
typedef struct TB_FunctionPrototype TB_FunctionPrototype;
-typedef struct TB_Attrib TB_Attrib;
+enum { TB_MODULE_SECTION_NONE = -1 };
+typedef int32_t TB_ModuleSectionHandle;
+typedef struct TB_Attrib TB_Attrib;
// target-specific, just a unique ID for the registers
typedef int TB_PhysicalReg;
+// Thread local module state
+typedef struct TB_ThreadInfo TB_ThreadInfo;
+
+typedef enum {
+ TB_SYMBOL_NONE,
+ TB_SYMBOL_EXTERNAL,
+ TB_SYMBOL_GLOBAL,
+ TB_SYMBOL_FUNCTION,
+ TB_SYMBOL_MAX,
+} TB_SymbolTag;
+
// Refers generically to objects within a module
//
// TB_Function, TB_Global, and TB_External are all subtypes of TB_Symbol
// and thus are safely allowed to cast into a symbol for operations.
typedef struct TB_Symbol {
- enum TB_SymbolTag {
- TB_SYMBOL_NONE,
-
- // symbol is dead now
- TB_SYMBOL_TOMBSTONE,
-
- TB_SYMBOL_EXTERNAL,
- TB_SYMBOL_GLOBAL,
- TB_SYMBOL_FUNCTION,
-
- TB_SYMBOL_MAX,
- } tag;
-
- // refers to the prev or next symbol with the same tag
- struct TB_Symbol* next;
+ #ifdef __cplusplus
+ TB_SymbolTag tag;
+ #else
+ _Atomic TB_SymbolTag tag;
+ #endif
+
+ // which thread info it's tied to (we may need to remove it, this
+ // is used for that)
+ TB_ThreadInfo* info;
char* name;
// It's kinda a weird circular reference but yea
@@ -484,15 +514,28 @@ typedef struct TB_Symbol {
} TB_Symbol;
typedef struct TB_Node TB_Node;
+typedef struct User User;
+struct User {
+ User* next;
+ TB_Node* n;
+ int slot;
+};
+
struct TB_Node {
TB_NodeType type;
- uint16_t input_count; // number of node inputs.
+ uint16_t input_count;
TB_DataType dt;
// makes it easier to track in graph walks
- size_t gvn;
+ uint32_t gvn;
- TB_Attrib* attribs;
+ // only value while inside of a TB_Passes,
+ // these are unordered and usually just
+ // help perform certain transformations or
+ // analysis (not necessarily semantics)
+ User* users;
+
+ // ordered def-use edges, jolly ol' semantics
TB_Node** inputs;
char extra[];
@@ -506,8 +549,6 @@ struct TB_Node {
// this represents switch (many targets), if (one target) and goto (only default) logic.
typedef struct { // TB_BRANCH
size_t succ_count;
- TB_Node** succ;
-
int64_t keys[];
} TB_NodeBranch;
@@ -527,16 +568,17 @@ typedef struct { // any integer binary operator
TB_ArithmeticBehavior ab;
} TB_NodeBinopInt;
-typedef struct { // TB_MULPAIR
- TB_Node *lo, *hi;
-} TB_NodeArithPair;
-
typedef struct {
TB_CharUnits align;
} TB_NodeMemAccess;
typedef struct {
+ int level;
+} TB_NodePrefetch;
+
+typedef struct {
TB_CharUnits size, align;
+ int alias_index; // 0 if local is used beyond direct memops, 1...n as a unique alias name
} TB_NodeLocal;
typedef struct {
@@ -577,32 +619,41 @@ typedef struct {
} TB_NodeAtomic;
typedef struct {
+ // line info on safepoints
+ TB_SourceFile* file;
+ int line, column;
+} TB_NodeSafepoint;
+
+typedef struct {
TB_FunctionPrototype* proto;
+ int proj_count;
TB_Node* projs[];
} TB_NodeCall;
typedef struct {
- uint32_t id;
-} TB_NodeSafepoint;
+ TB_FunctionPrototype* proto;
+} TB_NodeTailcall;
typedef struct {
- TB_Node* end;
const char* tag;
- // position in a postorder walk
- int postorder_id;
- // immediate dominator (can be approximate)
- int dom_depth;
- TB_Node* dom;
+ // magic factor for hot-code, higher means run more often
+ float freq;
// used for IR building only, stale after that.
- //
- // this represents the first and last memory values within a region,
- // if a region ever has multiple predecessors we apply a join on these
- // memory.
TB_Node *mem_in, *mem_out;
} TB_NodeRegion;
+typedef struct {
+ int64_t key;
+ uint64_t val;
+} TB_LookupEntry;
+
+typedef struct {
+ size_t entry_count;
+ TB_LookupEntry entries[];
+} TB_NodeLookup;
+
typedef struct TB_MultiOutput {
size_t count;
union {
@@ -634,6 +685,12 @@ typedef struct {
int32_t values[];
} TB_Safepoint;
+typedef enum {
+ TB_MODULE_SECTION_WRITE = 1,
+ TB_MODULE_SECTION_EXEC = 2,
+ TB_MODULE_SECTION_TLS = 4,
+} TB_ModuleSectionFlags;
+
// *******************************
// Public macros
// *******************************
@@ -641,35 +698,37 @@ typedef struct {
#define TB_TYPE_TUPLE TB_DataType{ { TB_TUPLE } }
#define TB_TYPE_CONTROL TB_DataType{ { TB_CONTROL } }
-#define TB_TYPE_VOID TB_DataType{ { TB_INT, 0, 0 } }
-#define TB_TYPE_I8 TB_DataType{ { TB_INT, 0, 8 } }
-#define TB_TYPE_I16 TB_DataType{ { TB_INT, 0, 16 } }
-#define TB_TYPE_I32 TB_DataType{ { TB_INT, 0, 32 } }
-#define TB_TYPE_I64 TB_DataType{ { TB_INT, 0, 64 } }
-#define TB_TYPE_F32 TB_DataType{ { TB_FLOAT, 0, TB_FLT_32 } }
-#define TB_TYPE_F64 TB_DataType{ { TB_FLOAT, 0, TB_FLT_64 } }
-#define TB_TYPE_BOOL TB_DataType{ { TB_INT, 0, 1 } }
-#define TB_TYPE_PTR TB_DataType{ { TB_PTR, 0, 0 } }
-#define TB_TYPE_MEMORY TB_DataType{ { TB_MEMORY,0, 0 } }
-#define TB_TYPE_INTN(N) TB_DataType{ { TB_INT, 0, (N) } }
-#define TB_TYPE_PTRN(N) TB_DataType{ { TB_PTR, 0, (N) } }
+#define TB_TYPE_VOID TB_DataType{ { TB_INT, 0 } }
+#define TB_TYPE_I8 TB_DataType{ { TB_INT, 8 } }
+#define TB_TYPE_I16 TB_DataType{ { TB_INT, 16 } }
+#define TB_TYPE_I32 TB_DataType{ { TB_INT, 32 } }
+#define TB_TYPE_I64 TB_DataType{ { TB_INT, 64 } }
+#define TB_TYPE_F32 TB_DataType{ { TB_FLOAT, TB_FLT_32 } }
+#define TB_TYPE_F64 TB_DataType{ { TB_FLOAT, TB_FLT_64 } }
+#define TB_TYPE_BOOL TB_DataType{ { TB_INT, 1 } }
+#define TB_TYPE_PTR TB_DataType{ { TB_PTR, 0 } }
+#define TB_TYPE_MEMORY TB_DataType{ { TB_MEMORY,0 } }
+#define TB_TYPE_CONT TB_DataType{ { TB_CONT, 0 } }
+#define TB_TYPE_INTN(N) TB_DataType{ { TB_INT, (N) } }
+#define TB_TYPE_PTRN(N) TB_DataType{ { TB_PTR, (N) } }
#else
#define TB_TYPE_TUPLE (TB_DataType){ { TB_TUPLE } }
#define TB_TYPE_CONTROL (TB_DataType){ { TB_CONTROL } }
-#define TB_TYPE_VOID (TB_DataType){ { TB_INT, 0, 0 } }
-#define TB_TYPE_I8 (TB_DataType){ { TB_INT, 0, 8 } }
-#define TB_TYPE_I16 (TB_DataType){ { TB_INT, 0, 16 } }
-#define TB_TYPE_I32 (TB_DataType){ { TB_INT, 0, 32 } }
-#define TB_TYPE_I64 (TB_DataType){ { TB_INT, 0, 64 } }
-#define TB_TYPE_F32 (TB_DataType){ { TB_FLOAT, 0, TB_FLT_32 } }
-#define TB_TYPE_F64 (TB_DataType){ { TB_FLOAT, 0, TB_FLT_64 } }
-#define TB_TYPE_BOOL (TB_DataType){ { TB_INT, 0, 1 } }
-#define TB_TYPE_PTR (TB_DataType){ { TB_PTR, 0, 0 } }
-#define TB_TYPE_MEMORY (TB_DataType){ { TB_MEMORY,0, 0 } }
-#define TB_TYPE_INTN(N) (TB_DataType){ { TB_INT, 0, (N) } }
-#define TB_TYPE_PTRN(N) (TB_DataType){ { TB_PTR, 0, (N) } }
+#define TB_TYPE_VOID (TB_DataType){ { TB_INT, 0 } }
+#define TB_TYPE_I8 (TB_DataType){ { TB_INT, 8 } }
+#define TB_TYPE_I16 (TB_DataType){ { TB_INT, 16 } }
+#define TB_TYPE_I32 (TB_DataType){ { TB_INT, 32 } }
+#define TB_TYPE_I64 (TB_DataType){ { TB_INT, 64 } }
+#define TB_TYPE_F32 (TB_DataType){ { TB_FLOAT, TB_FLT_32 } }
+#define TB_TYPE_F64 (TB_DataType){ { TB_FLOAT, TB_FLT_64 } }
+#define TB_TYPE_BOOL (TB_DataType){ { TB_INT, 1 } }
+#define TB_TYPE_PTR (TB_DataType){ { TB_PTR, 0 } }
+#define TB_TYPE_CONT (TB_DataType){ { TB_CONT, 0 } }
+#define TB_TYPE_MEMORY (TB_DataType){ { TB_MEMORY,0 } }
+#define TB_TYPE_INTN(N) (TB_DataType){ { TB_INT, (N) } }
+#define TB_TYPE_PTRN(N) (TB_DataType){ { TB_PTR, (N) } }
#endif
@@ -681,7 +740,8 @@ typedef struct TB_Arena TB_Arena;
// 0 for default
TB_API void tb_arena_create(TB_Arena* restrict arena, size_t chunk_size);
TB_API void tb_arena_destroy(TB_Arena* restrict arena);
-TB_API bool tb_arena_is_empty(TB_Arena* arena);
+TB_API bool tb_arena_is_empty(TB_Arena* restrict arena);
+TB_API void tb_arena_clear(TB_Arena* restrict arena);
////////////////////////////////
// Module management
@@ -692,8 +752,6 @@ TB_API TB_Module* tb_module_create(TB_Arch arch, TB_System sys, const TB_Feature
// Creates a module but defaults on the architecture and system based on the host machine
TB_API TB_Module* tb_module_create_for_host(const TB_FeatureSet* features, bool is_jit);
-TB_API size_t tb_module_get_function_count(TB_Module* m);
-
// Frees all resources for the TB_Module and it's functions, globals and
// compiled code.
TB_API void tb_module_destroy(TB_Module* m);
@@ -703,9 +761,16 @@ TB_API void tb_module_destroy(TB_Module* m);
// dont and the tls_index is used, it'll crash
TB_API void tb_module_set_tls_index(TB_Module* m, ptrdiff_t len, const char* name);
-// You don't need to manually call this unless you want to resolve locations before
-// exporting.
-TB_API void tb_module_layout_sections(TB_Module* m);
+TB_API TB_ModuleSectionHandle tb_module_create_section(TB_Module* m, ptrdiff_t len, const char* name, TB_ModuleSectionFlags flags, TB_ComdatType comdat);
+
+typedef struct {
+ TB_ThreadInfo* info;
+ size_t i;
+} TB_SymbolIter;
+
+// Lovely iterator for all the symbols... it's probably not "fast"
+TB_SymbolIter tb_symbol_iter(TB_Module* mod);
+TB_Symbol* tb_symbol_iter_next(TB_SymbolIter* iter);
////////////////////////////////
// Compiled code introspection
@@ -728,6 +793,9 @@ TB_API void tb_output_print_asm(TB_FunctionOutput* out, FILE* fp);
TB_API uint8_t* tb_output_get_code(TB_FunctionOutput* out, size_t* out_length);
+// returns NULL if there's no line info
+TB_API TB_Location* tb_output_get_locations(TB_FunctionOutput* out, size_t* out_count);
+
// returns NULL if no assembly was generated
TB_API TB_Assembly* tb_output_get_asm(TB_FunctionOutput* out);
@@ -738,17 +806,55 @@ TB_API TB_Safepoint* tb_safepoint_get(TB_Function* f, uint32_t relative_ip);
// JIT compilation
////////////////////////////////
typedef struct TB_JIT TB_JIT;
+typedef struct TB_CPUContext TB_CPUContext;
// passing 0 to jit_heap_capacity will default to 4MiB
TB_API TB_JIT* tb_jit_begin(TB_Module* m, size_t jit_heap_capacity);
TB_API void* tb_jit_place_function(TB_JIT* jit, TB_Function* f);
TB_API void* tb_jit_place_global(TB_JIT* jit, TB_Global* g);
+TB_API void tb_jit_dump_heap(TB_JIT* jit);
TB_API void tb_jit_end(TB_JIT* jit);
+typedef struct {
+ TB_Symbol* base;
+ uint32_t offset;
+} TB_ResolvedAddr;
+
+typedef struct {
+ TB_Function* f;
+ TB_Location* loc;
+ uint32_t start, end;
+} TB_ResolvedLine;
+
+TB_API TB_ResolvedAddr tb_jit_addr2sym(TB_JIT* jit, void* ptr);
+TB_API TB_ResolvedLine tb_jit_addr2line(TB_JIT* jit, void* ptr);
TB_API void* tb_jit_get_code_ptr(TB_Function* f);
-// Generates a 2MiB stack
-TB_API void* tb_jit_stack_create(size_t* out_size);
+typedef enum {
+ // just keeps running
+ TB_DBG_NONE,
+ // stops after one instruction
+ TB_DBG_INST,
+ // stops once the line changes
+ TB_DBG_LINE,
+} TB_DbgStep;
+
+// Debugger stuff
+// creates a new context we can run JIT code in, you don't
+// technically need this but it's a nice helper for writing
+// JITs especially when it comes to breakpoints (and eventually
+// safepoints)
+TB_API TB_CPUContext* tb_jit_thread_create(void* entry, void* arg);
+// runs until TB_DbgStep condition is met
+TB_API bool tb_jit_thread_resume(TB_JIT* jit, TB_CPUContext* cpu, TB_DbgStep step);
+TB_API void* tb_jit_thread_pc(TB_CPUContext* cpu);
+TB_API void tb_jit_breakpoint(TB_JIT* jit, void* addr);
+TB_API void tb_jit_thread_dump_stack(TB_JIT* jit, TB_CPUContext* cpu);
+
+////////////////////////////////
+// Disassembler
+////////////////////////////////
+TB_API ptrdiff_t tb_print_disassembly_inst(TB_Arch arch, size_t length, const void* ptr);
////////////////////////////////
// Exporter
@@ -820,18 +926,7 @@ TB_API void tb_linker_append_library(TB_Linker* l, TB_Slice ar_name, TB_Slice co
////////////////////////////////
// Symbols
////////////////////////////////
-#define TB_FOR_FUNCTIONS(it, module) for (TB_Function* it = tb_first_function(module); it != NULL; it = tb_next_function(it))
-TB_API TB_Function* tb_first_function(TB_Module* m);
-TB_API TB_Function* tb_next_function(TB_Function* f);
-
-#define TB_FOR_EXTERNALS(it, module) for (TB_External* it = tb_first_external(module); it != NULL; it = tb_next_external(it))
-TB_API TB_External* tb_first_external(TB_Module* m);
-TB_API TB_External* tb_next_external(TB_External* e);
-
-// this is used JIT scenarios to tell the compiler what externals map to
-TB_API TB_ExternalType tb_extern_get_type(TB_External* e);
TB_API TB_Global* tb_extern_transmute(TB_External* e, TB_DebugType* dbg_type, TB_Linkage linkage);
-
TB_API TB_External* tb_extern_create(TB_Module* m, ptrdiff_t len, const char* name, TB_ExternalType type);
TB_API TB_SourceFile* tb_get_source_file(TB_Module* m, const char* path);
@@ -876,7 +971,7 @@ TB_API TB_FunctionPrototype* tb_prototype_create(TB_Module* m, TB_CallingConv cc
// into the correct ABI and exposing sane looking nodes to the parameters.
//
// returns the parameters
-TB_API TB_Node** tb_function_set_prototype_from_dbg(TB_Function* f, TB_DebugType* dbg, TB_Arena* arena, size_t* out_param_count);
+TB_API TB_Node** tb_function_set_prototype_from_dbg(TB_Function* f, TB_ModuleSectionHandle section, TB_DebugType* dbg, TB_Arena* arena, size_t* out_param_count);
TB_API TB_FunctionPrototype* tb_prototype_from_dbg(TB_Module* m, TB_DebugType* dbg);
// used for ABI parameter passing
@@ -899,7 +994,7 @@ TB_API TB_PassingRule tb_get_passing_rule_from_dbg(TB_Module* mod, TB_DebugType*
TB_API TB_Global* tb_global_create(TB_Module* m, ptrdiff_t len, const char* name, TB_DebugType* dbg_type, TB_Linkage linkage);
// allocate space for the global
-TB_API void tb_global_set_storage(TB_Module* m, TB_ModuleSection* section, TB_Global* global, size_t size, size_t align, size_t max_objects);
+TB_API void tb_global_set_storage(TB_Module* m, TB_ModuleSectionHandle section, TB_Global* global, size_t size, size_t align, size_t max_objects);
// returns a buffer which the user can fill to then have represented in the initializer
TB_API void* tb_global_add_region(TB_Module* m, TB_Global* global, size_t offset, size_t size);
@@ -908,10 +1003,10 @@ TB_API void* tb_global_add_region(TB_Module* m, TB_Global* global, size_t offset
// depends on the pointer size
TB_API void tb_global_add_symbol_reloc(TB_Module* m, TB_Global* global, size_t offset, const TB_Symbol* symbol);
-TB_API TB_ModuleSection* tb_module_get_text(TB_Module* m);
-TB_API TB_ModuleSection* tb_module_get_rdata(TB_Module* m);
-TB_API TB_ModuleSection* tb_module_get_data(TB_Module* m);
-TB_API TB_ModuleSection* tb_module_get_tls(TB_Module* m);
+TB_API TB_ModuleSectionHandle tb_module_get_text(TB_Module* m);
+TB_API TB_ModuleSectionHandle tb_module_get_rdata(TB_Module* m);
+TB_API TB_ModuleSectionHandle tb_module_get_data(TB_Module* m);
+TB_API TB_ModuleSectionHandle tb_module_get_tls(TB_Module* m);
////////////////////////////////
// Function Attributes
@@ -919,7 +1014,6 @@ TB_API TB_ModuleSection* tb_module_get_tls(TB_Module* m);
// These are parts of a function that describe metadata for instructions
TB_API void tb_function_attrib_variable(TB_Function* f, TB_Node* n, TB_Node* parent, ptrdiff_t len, const char* name, TB_DebugType* type);
TB_API void tb_function_attrib_scope(TB_Function* f, TB_Node* n, TB_Node* parent);
-TB_API void tb_function_attrib_location(TB_Function* f, TB_Node* n, TB_SourceFile* file, int line, int column);
////////////////////////////////
// Debug info Generation
@@ -951,16 +1045,8 @@ TB_API TB_DebugType** tb_debug_func_params(TB_DebugType* type);
TB_API TB_DebugType** tb_debug_func_returns(TB_DebugType* type);
////////////////////////////////
-// IR access
-////////////////////////////////
-// it is an index to the input
-#define TB_FOR_INPUT_IN_NODE(it, parent) for (TB_Node **it = parent->inputs, **__end = it + (parent)->input_count; it != __end; it++)
-
-////////////////////////////////
// Symbols
////////////////////////////////
-TB_API bool tb_symbol_is_comdat(const TB_Symbol* s);
-
// returns NULL if the tag doesn't match
TB_API TB_Function* tb_symbol_as_function(TB_Symbol* s);
TB_API TB_External* tb_symbol_as_external(TB_Symbol* s);
@@ -974,16 +1060,13 @@ TB_API void tb_get_data_type_size(TB_Module* mod, TB_DataType dt, size_t* size,
// the user_data is expected to be a valid FILE*
TB_API void tb_default_print_callback(void* user_data, const char* fmt, ...);
-TB_API void tb_inst_set_location(TB_Function* f, TB_SourceFile* file, int line, int column);
-TB_API void tb_inst_reset_location(TB_Function* f);
+TB_API void tb_inst_location(TB_Function* f, TB_SourceFile* file, int line, int column);
// this is where the STOP will be
TB_API void tb_inst_set_exit_location(TB_Function* f, TB_SourceFile* file, int line, int column);
-TB_API bool tb_has_effects(TB_Node* n);
-
// if section is NULL, default to .text
-TB_API TB_Function* tb_function_create(TB_Module* m, ptrdiff_t len, const char* name, TB_Linkage linkage, TB_ComdatType comdat);
+TB_API TB_Function* tb_function_create(TB_Module* m, ptrdiff_t len, const char* name, TB_Linkage linkage);
TB_API TB_Arena* tb_function_get_arena(TB_Function* f);
@@ -994,11 +1077,9 @@ TB_API void tb_symbol_bind_ptr(TB_Symbol* s, void* ptr);
TB_API const char* tb_symbol_get_name(TB_Symbol* s);
// if arena is NULL, defaults to module arena which is freed on tb_free_thread_resources
-TB_API void tb_function_set_prototype(TB_Function* f, TB_FunctionPrototype* p, TB_Arena* arena);
+TB_API void tb_function_set_prototype(TB_Function* f, TB_ModuleSectionHandle section, TB_FunctionPrototype* p, TB_Arena* arena);
TB_API TB_FunctionPrototype* tb_function_get_prototype(TB_Function* f);
-TB_API void tb_function_print(TB_Function* f, TB_PrintCallback callback, void* user_data);
-
TB_API void tb_inst_set_control(TB_Function* f, TB_Node* control);
TB_API TB_Node* tb_inst_get_control(TB_Function* f);
@@ -1010,7 +1091,7 @@ TB_API void tb_inst_set_region_name(TB_Function* f, TB_Node* n, ptrdiff_t len, c
TB_API void tb_inst_unreachable(TB_Function* f);
TB_API void tb_inst_debugbreak(TB_Function* f);
TB_API void tb_inst_trap(TB_Function* f);
-TB_API TB_Node* tb_inst_poison(TB_Function* f);
+TB_API TB_Node* tb_inst_poison(TB_Function* f, TB_DataType dt);
TB_API TB_Node* tb_inst_param(TB_Function* f, int param_id);
@@ -1135,6 +1216,7 @@ TB_API TB_Node* tb_inst_cmp_fge(TB_Function* f, TB_Node* a, TB_Node* b);
// General intrinsics
TB_API TB_Node* tb_inst_va_start(TB_Function* f, TB_Node* a);
TB_API TB_Node* tb_inst_cycle_counter(TB_Function* f);
+TB_API TB_Node* tb_inst_prefetch(TB_Function* f, TB_Node* addr, int level);
// x86 Intrinsics
TB_API TB_Node* tb_inst_x86_ldmxcsr(TB_Function* f, TB_Node* a);
@@ -1145,8 +1227,8 @@ TB_API TB_Node* tb_inst_x86_rsqrt(TB_Function* f, TB_Node* a);
// Control flow
TB_API TB_Node* tb_inst_syscall(TB_Function* f, TB_DataType dt, TB_Node* syscall_num, size_t param_count, TB_Node** params);
TB_API TB_MultiOutput tb_inst_call(TB_Function* f, TB_FunctionPrototype* proto, TB_Node* target, size_t param_count, TB_Node** params);
+TB_API void tb_inst_tailcall(TB_Function* f, TB_FunctionPrototype* proto, TB_Node* target, size_t param_count, TB_Node** params);
-// Managed
TB_API TB_Node* tb_inst_safepoint(TB_Function* f, TB_Node* poke_site, size_t param_count, TB_Node** params);
TB_API TB_Node* tb_inst_incomplete_phi(TB_Function* f, TB_DataType dt, TB_Node* region, size_t preds);
@@ -1197,12 +1279,16 @@ TB_API void tb_pass_exit(TB_Passes* opt);
TB_API void tb_pass_peephole(TB_Passes* opt, TB_PeepholeFlags flags);
TB_API void tb_pass_sroa(TB_Passes* opt);
TB_API bool tb_pass_mem2reg(TB_Passes* opt);
+TB_API bool tb_pass_loop(TB_Passes* opt);
-TB_API void tb_pass_schedule(TB_Passes* opt);
+// this just runs the optimizer in the default configuration
+TB_API void tb_pass_optimize(TB_Passes* opt);
// analysis
// print: prints IR in a flattened text form.
TB_API bool tb_pass_print(TB_Passes* opt);
+// print-dot: prints IR as DOT
+TB_API void tb_pass_print_dot(TB_Passes* opt, TB_PrintCallback callback, void* user_data);
// codegen
TB_API TB_FunctionOutput* tb_pass_codegen(TB_Passes* opt, bool emit_asm);
diff --git a/src/tilde/tb.lib b/src/tilde/tb.lib
index 4b3cdb9b0..510569b0e 100644
--- a/src/tilde/tb.lib
+++ b/src/tilde/tb.lib
Binary files differ
diff --git a/src/tilde/tb_coff.h b/src/tilde/tb_coff.h
index ae8f63863..ddedd6ffe 100644
--- a/src/tilde/tb_coff.h
+++ b/src/tilde/tb_coff.h
@@ -313,7 +313,11 @@ size_t tb_coff_parse_symbol(TB_COFF_Parser* restrict parser, size_t i, TB_Object
out_sym->name = (TB_Slice){ strlen((const char*) data), data };
} else {
// normal inplace string
- size_t len = strlen((const char*) sym->short_name);
+ size_t len = 1;
+ const char* name = (const char*) sym->short_name;
+ while (len < 8 && name[len] != 0) {
+ len++;
+ }
out_sym->name = (TB_Slice){ len, sym->short_name };
}
diff --git a/src/tilde/tb_x64.h b/src/tilde/tb_x64.h
index 5f93f6bdb..58b3d656c 100644
--- a/src/tilde/tb_x64.h
+++ b/src/tilde/tb_x64.h
@@ -27,10 +27,21 @@ typedef enum {
TB_X86_INSTR_DIRECTION = (1u << 6u),
// uses the second data type because the instruction is weird like MOVSX or MOVZX
- TB_X86_INSTR_TWO_DATA_TYPES = (1u << 7u)
+ TB_X86_INSTR_TWO_DATA_TYPES = (1u << 7u),
+
+ // REP prefix is present
+ TB_X86_INSTR_REP = (1u << 8u),
+
+ // REPNE prefix is present
+ TB_X86_INSTR_REPNE = (1u << 9u),
} TB_X86_InstFlags;
typedef enum {
+ TB_X86_RAX, TB_X86_RCX, TB_X86_RDX, TB_X86_RBX, TB_X86_RSP, TB_X86_RBP, TB_X86_RSI, TB_X86_RDI,
+ TB_X86_R8, TB_X86_R9, TB_X86_R10, TB_X86_R11, TB_X86_R12, TB_X86_R13, TB_X86_R14, TB_X86_R15,
+} TB_X86_GPR;
+
+typedef enum {
TB_X86_SEGMENT_DEFAULT = 0,
TB_X86_SEGMENT_ES, TB_X86_SEGMENT_CS,
@@ -60,20 +71,21 @@ typedef enum {
} TB_X86_DataType;
typedef struct {
- int16_t type;
+ int32_t opcode;
- // registers (there's 4 max taking up 4bit slots each)
- uint16_t regs;
- uint8_t flags;
+ // registers (there's 4 max taking up 8bit slots each)
+ int8_t regs[4];
+ uint16_t flags;
// bitpacking amirite
- TB_X86_DataType data_type : 4;
- TB_X86_DataType data_type2 : 4;
+ TB_X86_DataType data_type : 8;
+ TB_X86_DataType data_type2 : 8;
TB_X86_Segment segment : 4;
uint8_t length : 4;
// memory operand
// X86_INSTR_USE_MEMOP
+ uint8_t base, index, scale;
int32_t disp;
// immediate operand
@@ -85,6 +97,9 @@ typedef struct {
};
} TB_X86_Inst;
-TB_X86_Inst tb_x86_disasm(size_t length, const uint8_t data[length]);
+bool tb_x86_disasm(TB_X86_Inst* restrict inst, size_t length, const uint8_t* data);
+const char* tb_x86_reg_name(int8_t reg, TB_X86_DataType dt);
+const char* tb_x86_type_name(TB_X86_DataType dt);
+const char* tb_x86_mnemonic(TB_X86_Inst* inst);
#endif /* TB_X64_H */
diff --git a/src/tilde_const.cpp b/src/tilde_const.cpp
index 691409fe9..456b2cdc7 100644
--- a/src/tilde_const.cpp
+++ b/src/tilde_const.cpp
@@ -705,7 +705,7 @@ gb_internal TB_Global *cg_global_const_comp_literal(cgModule *m, Type *original_
i64 align = type_align_of(original_type);
// READ ONLY?
- TB_ModuleSection *section = nullptr;
+ TB_ModuleSectionHandle section = 0;
if (is_type_string(original_type) || is_type_cstring(original_type)) {
section = tb_module_get_rdata(m->mod);
} else {
diff --git a/src/tilde_expr.cpp b/src/tilde_expr.cpp
index 867b761d4..236d0cf7d 100644
--- a/src/tilde_expr.cpp
+++ b/src/tilde_expr.cpp
@@ -3262,7 +3262,7 @@ gb_internal cgValue cg_build_expr_internal(cgProcedure *p, Ast *expr) {
if (is_type_untyped(type)) {
return cg_value(cast(TB_Node *)nullptr, t_untyped_uninit);
}
- return cg_value(tb_inst_poison(p->func), type);
+ return cg_value(tb_inst_poison(p->func, cg_data_type(type)), type);
case_end;
case_ast_node(de, DerefExpr, expr);
diff --git a/src/tilde_proc.cpp b/src/tilde_proc.cpp
index f5f37d73e..8e9b80144 100644
--- a/src/tilde_proc.cpp
+++ b/src/tilde_proc.cpp
@@ -95,7 +95,7 @@ gb_internal cgProcedure *cg_procedure_create(cgModule *m, Entity *entity, bool i
}
if (p->symbol == nullptr) {
- p->func = tb_function_create(m->mod, link_name.len, cast(char const *)link_name.text, linkage, TB_COMDAT_NONE);
+ p->func = tb_function_create(m->mod, link_name.len, cast(char const *)link_name.text, linkage);
p->debug_type = cg_debug_type_for_proc(m, p->type);
p->proto = tb_prototype_from_dbg(m->mod, p->debug_type);
@@ -148,7 +148,7 @@ gb_internal cgProcedure *cg_procedure_create_dummy(cgModule *m, String const &li
TB_Linkage linkage = TB_LINKAGE_PRIVATE;
- p->func = tb_function_create(m->mod, link_name.len, cast(char const *)link_name.text, linkage, TB_COMDAT_NONE);
+ p->func = tb_function_create(m->mod, link_name.len, cast(char const *)link_name.text, linkage);
p->debug_type = cg_debug_type_for_proc(m, p->type);
p->proto = tb_prototype_from_dbg(m->mod, p->debug_type);
@@ -224,7 +224,8 @@ gb_internal void cg_procedure_begin(cgProcedure *p) {
return;
}
- tb_function_set_prototype(p->func, p->proto, cg_arena());
+ TB_ModuleSectionHandle section = tb_module_get_text(p->module->mod);
+ tb_function_set_prototype(p->func, section, p->proto, cg_arena());
if (p->body == nullptr) {
return;
@@ -400,7 +401,7 @@ gb_internal WORKER_TASK_PROC(cg_procedure_compile_worker_proc) {
fflush(stdout);
}
if (false) { // GraphViz printing
- tb_function_print(p->func, tb_default_print_callback, stdout);
+ tb_pass_print_dot(opt, tb_default_print_callback, stdout);
}
// compile
@@ -860,7 +861,6 @@ gb_internal cgValue cg_build_call_expr_internal(cgProcedure *p, Ast *expr) {
}
GB_ASSERT(e->kind == Entity_Variable);
-
if (pt->variadic && pt->variadic_index == i) {
cgValue variadic_args = cg_const_nil(p, e->type);
auto variadic = slice(ce->split_args->positional, pt->variadic_index, ce->split_args->positional.count);
@@ -963,8 +963,8 @@ gb_internal cgValue cg_build_call_expr_internal(cgProcedure *p, Ast *expr) {
if (pt->variadic && param_index == pt->variadic_index) {
if (!is_c_vararg && args[arg_index].node == nullptr) {
args[arg_index++] = cg_const_nil(p, e->type);
+ continue;
}
- continue;
}
cgValue arg = args[arg_index];
diff --git a/src/tilde_stmt.cpp b/src/tilde_stmt.cpp
index 1a7dcce4d..597a4262c 100644
--- a/src/tilde_stmt.cpp
+++ b/src/tilde_stmt.cpp
@@ -24,7 +24,7 @@ gb_internal TB_Node *cg_control_region(cgProcedure *p, char const *name) {
}
gb_internal cgValue cg_emit_load(cgProcedure *p, cgValue const &ptr, bool is_volatile) {
- GB_ASSERT(is_type_pointer(ptr.type));
+ GB_ASSERT_MSG(is_type_pointer(ptr.type), "%s", type_to_string(ptr.type));
Type *type = type_deref(ptr.type);
TB_DataType dt = cg_data_type(type);
@@ -326,7 +326,7 @@ gb_internal void cg_addr_store(cgProcedure *p, cgAddr addr, cgValue value) {
GB_ASSERT(value.type != nullptr);
if (is_type_untyped_uninit(value.type)) {
Type *t = cg_addr_type(addr);
- value = cg_value(tb_inst_poison(p->func), t);
+ value = cg_value(tb_inst_poison(p->func, cg_data_type(t)), t);
// TODO(bill): IS THIS EVEN A GOOD IDEA?
} else if (is_type_untyped_nil(value.type)) {
Type *t = cg_addr_type(addr);
@@ -1982,16 +1982,25 @@ gb_internal bool cg_switch_stmt_can_be_trivial_jump_table(AstSwitchStmt *ss) {
if (ss->tag == nullptr) {
return false;
}
+ enum { DISALLOW_64_SWITCH = true };
+
bool is_typeid = false;
TypeAndValue tv = type_and_value_of_expr(ss->tag);
if (is_type_integer(core_type(tv.type))) {
- if (type_size_of(tv.type) > 8) {
+ i64 sz = type_size_of(tv.type);
+ if (sz > 8) {
+ return false;
+ }
+ if (DISALLOW_64_SWITCH && sz == 8) {
return false;
}
// okay
} else if (is_type_typeid(tv.type)) {
// okay
is_typeid = true;
+ if (DISALLOW_64_SWITCH && build_context.ptr_size == 8) {
+ return false;
+ }
} else {
return false;
}
@@ -2481,7 +2490,7 @@ gb_internal void cg_build_mutable_value_decl(cgProcedure *p, Ast *node) {
TB_DebugType *debug_type = cg_debug_type(m, e->type);
TB_Global *global = tb_global_create(m->mod, mangled_name.len, cast(char const *)mangled_name.text, debug_type, TB_LINKAGE_PRIVATE);
- TB_ModuleSection *section = tb_module_get_data(m->mod);
+ TB_ModuleSectionHandle section = tb_module_get_data(m->mod);
if (e->Variable.thread_local_model != "") {
section = tb_module_get_tls(m->mod);
String model = e->Variable.thread_local_model;
diff --git a/src/tilde_type_info.cpp b/src/tilde_type_info.cpp
index ad219071f..58e8d3087 100644
--- a/src/tilde_type_info.cpp
+++ b/src/tilde_type_info.cpp
@@ -119,10 +119,6 @@ gb_internal u64 cg_typeid_as_u64(cgModule *m, Type *type) {
data |= (reserved &~ (1ull<<1)) << 63ull; // reserved
}
- if (type == t_string) {
- gb_printf_err("%llu\n", data);
- }
-
return data;
}
@@ -479,6 +475,13 @@ gb_internal void cg_setup_type_info_data(cgModule *m) {
// }
tag_type = t_type_info_named;
+ i64 name_offset = type_offset_of(tag_type, 0);
+ String name = t->Named.type_name->token.string;
+ cg_global_const_string(m, name, t_string, global, offset+name_offset);
+
+ i64 base_offset = type_offset_of(tag_type, 1);
+ cg_global_const_type_info_ptr(m, t->Named.base, global, offset+base_offset);
+
if (t->Named.type_name->pkg) {
i64 pkg_offset = type_offset_of(tag_type, 2);
String pkg_name = t->Named.type_name->pkg->name;
@@ -499,8 +502,6 @@ gb_internal void cg_setup_type_info_data(cgModule *m) {
TokenPos pos = t->Named.type_name->token.pos;
cg_global_source_code_location_const(m, proc_name, pos, global, offset+loc_offset);
- i64 base_offset = type_offset_of(tag_type, 1);
- cg_global_const_type_info_ptr(m, t->Named.base, global, offset+base_offset);
break;
}
diff --git a/src/timings.cpp b/src/timings.cpp
index baa8b80da..3f8402b36 100644
--- a/src/timings.cpp
+++ b/src/timings.cpp
@@ -33,22 +33,23 @@ gb_internal u64 win32_time_stamp__freq(void) {
#include <mach/mach_time.h>
+gb_internal mach_timebase_info_data_t osx_init_timebase_info(void) {
+ mach_timebase_info_data_t data;
+ data.numer = 0;
+ data.denom = 0;
+ kern_return_t r = mach_timebase_info(&data);
+ GB_ASSERT(r == KERN_SUCCESS);
+
+ return data;
+}
+
gb_internal u64 osx_time_stamp_time_now(void) {
return mach_absolute_time();
}
gb_internal u64 osx_time_stamp__freq(void) {
- mach_timebase_info_data_t data;
- data.numer = 0;
- data.denom = 0;
- mach_timebase_info(&data);
-#if defined(GB_CPU_ARM)
- // NOTE(bill, 2021-02-25): M1 Chip seems to have a different freq count
- // TODO(bill): Is this truly correct?
- return (1000000llu * cast(u64)data.numer) / cast(u64)data.denom;
-#else
- return (1000000000llu * cast(u64)data.numer) / cast(u64)data.denom;
-#endif
+ gb_local_persist mach_timebase_info_data_t data = osx_init_timebase_info();
+ return 1000000000ull * cast(u64)data.denom / cast(u64)data.numer;
}
#elif defined(GB_SYSTEM_UNIX)
@@ -145,9 +146,10 @@ gb_internal f64 time_stamp_as_us(TimeStamp const &ts, u64 freq) {
return 1000000.0*time_stamp_as_s(ts, freq);
}
-#define MAIN_TIME_SECTION(str) do { debugf("[Section] %s\n", str); timings_start_section(&global_timings, str_lit(str)); } while (0)
-#define TIME_SECTION(str) do { debugf("[Section] %s\n", str); if (build_context.show_more_timings) timings_start_section(&global_timings, str_lit(str)); } while (0)
-#define TIME_SECTION_WITH_LEN(str, len) do { debugf("[Section] %s\n", str); if (build_context.show_more_timings) timings_start_section(&global_timings, make_string((u8 *)str, len)); } while (0)
+#define MAIN_TIME_SECTION(str) do { debugf("[Section] %s\n", str); timings_start_section(&global_timings, str_lit(str)); } while (0)
+#define MAIN_TIME_SECTION_WITH_LEN(str, len) do { debugf("[Section] %s\n", str); timings_start_section(&global_timings, make_string((u8 *)str, len)); } while (0)
+#define TIME_SECTION(str) do { debugf("[Section] %s\n", str); if (build_context.show_more_timings) timings_start_section(&global_timings, str_lit(str)); } while (0)
+#define TIME_SECTION_WITH_LEN(str, len) do { debugf("[Section] %s\n", str); if (build_context.show_more_timings) timings_start_section(&global_timings, make_string((u8 *)str, len)); } while (0)
enum TimingUnit {
diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp
index dd9908be5..53f6135d0 100644
--- a/src/tokenizer.cpp
+++ b/src/tokenizer.cpp
@@ -2,6 +2,7 @@
TOKEN_KIND(Token_Invalid, "Invalid"), \
TOKEN_KIND(Token_EOF, "EOF"), \
TOKEN_KIND(Token_Comment, "Comment"), \
+ TOKEN_KIND(Token_FileTag, "FileTag"), \
\
TOKEN_KIND(Token__LiteralBegin, ""), \
TOKEN_KIND(Token_Ident, "identifier"), \
@@ -106,6 +107,7 @@ TOKEN_KIND(Token__KeywordBegin, ""), \
TOKEN_KIND(Token_union, "union"), \
TOKEN_KIND(Token_enum, "enum"), \
TOKEN_KIND(Token_bit_set, "bit_set"), \
+ TOKEN_KIND(Token_bit_field, "bit_field"), \
TOKEN_KIND(Token_map, "map"), \
TOKEN_KIND(Token_dynamic, "dynamic"), \
TOKEN_KIND(Token_auto_cast, "auto_cast"), \
@@ -192,6 +194,7 @@ gb_internal void init_keyword_hash_table(void) {
gb_global Array<String> global_file_path_strings; // index is file id
gb_global Array<struct AstFile *> global_files; // index is file id
+gb_global BlockingMutex global_files_mutex;
gb_internal String get_file_path_string(i32 index);
gb_internal struct AstFile *thread_safe_get_ast_file_from_id(i32 index);
@@ -430,7 +433,10 @@ gb_internal gb_inline i32 digit_value(Rune r) {
return 16; // NOTE(bill): Larger than highest possible
}
-gb_internal gb_inline void scan_mantissa(Tokenizer *t, i32 base) {
+gb_internal gb_inline void scan_mantissa(Tokenizer *t, i32 base, bool force_base) {
+ if (!force_base) {
+ base = 16; // always check for any possible letter
+ }
while (digit_value(t->curr_rune) < base || t->curr_rune == '_') {
advance_to_next_rune(t);
}
@@ -455,7 +461,7 @@ gb_internal void scan_number_to_token(Tokenizer *t, Token *token, bool seen_deci
token->string.len += 1;
token->pos.column -= 1;
token->kind = Token_Float;
- scan_mantissa(t, 10);
+ scan_mantissa(t, 10, true);
goto exponent;
}
@@ -465,44 +471,50 @@ gb_internal void scan_number_to_token(Tokenizer *t, Token *token, bool seen_deci
switch (t->curr_rune) {
case 'b': // Binary
advance_to_next_rune(t);
- scan_mantissa(t, 2);
+ scan_mantissa(t, 2, false);
if (t->curr - prev <= 2) {
+ tokenizer_err(t, "Invalid binary integer");
token->kind = Token_Invalid;
}
goto end;
case 'o': // Octal
advance_to_next_rune(t);
- scan_mantissa(t, 8);
+ scan_mantissa(t, 8, false);
if (t->curr - prev <= 2) {
+ tokenizer_err(t, "Invalid octal integer");
token->kind = Token_Invalid;
}
goto end;
case 'd': // Decimal
advance_to_next_rune(t);
- scan_mantissa(t, 10);
+ scan_mantissa(t, 10, false);
if (t->curr - prev <= 2) {
+ tokenizer_err(t, "Invalid explicitly decimal integer");
token->kind = Token_Invalid;
}
goto end;
case 'z': // Dozenal
advance_to_next_rune(t);
- scan_mantissa(t, 12);
+ scan_mantissa(t, 12, false);
if (t->curr - prev <= 2) {
+ tokenizer_err(t, "Invalid dozenal integer");
token->kind = Token_Invalid;
}
goto end;
case 'x': // Hexadecimal
advance_to_next_rune(t);
- scan_mantissa(t, 16);
+ scan_mantissa(t, 16, false);
if (t->curr - prev <= 2) {
+ tokenizer_err(t, "Invalid hexadecimal integer");
token->kind = Token_Invalid;
}
goto end;
case 'h': // Hexadecimal Float
token->kind = Token_Float;
advance_to_next_rune(t);
- scan_mantissa(t, 16);
+ scan_mantissa(t, 16, false);
if (t->curr - prev <= 2) {
+ tokenizer_err(t, "Invalid hexadecimal float");
token->kind = Token_Invalid;
} else {
u8 *start = prev+2;
@@ -525,12 +537,12 @@ gb_internal void scan_number_to_token(Tokenizer *t, Token *token, bool seen_deci
}
goto end;
default:
- scan_mantissa(t, 10);
+ scan_mantissa(t, 10, true);
goto fraction;
}
}
- scan_mantissa(t, 10);
+ scan_mantissa(t, 10, true);
fraction:
@@ -542,7 +554,7 @@ fraction:
advance_to_next_rune(t);
token->kind = Token_Float;
- scan_mantissa(t, 10);
+ scan_mantissa(t, 10, true);
}
exponent:
@@ -552,7 +564,7 @@ exponent:
if (t->curr_rune == '-' || t->curr_rune == '+') {
advance_to_next_rune(t);
}
- scan_mantissa(t, 10);
+ scan_mantissa(t, 10, false);
}
switch (t->curr_rune) {
@@ -765,9 +777,8 @@ gb_internal void tokenizer_get_token(Tokenizer *t, Token *token, int repeat=0) {
}
}
- // TODO(bill): Better Error Handling
if (valid && n != 1) {
- tokenizer_err(t, "Invalid rune literal");
+ tokenizer_err(t, token->pos, "Invalid rune literal");
}
token->string.len = t->curr - token->string.text;
goto semicolon_check;
@@ -776,7 +787,6 @@ gb_internal void tokenizer_get_token(Tokenizer *t, Token *token, int repeat=0) {
case '`': // Raw String Literal
case '"': // String Literal
{
- bool has_carriage_return = false;
i32 success;
Rune quote = curr_rune;
token->kind = Token_String;
@@ -806,9 +816,6 @@ gb_internal void tokenizer_get_token(Tokenizer *t, Token *token, int repeat=0) {
if (r == quote) {
break;
}
- if (r == '\r') {
- has_carriage_return = true;
- }
}
}
token->string.len = t->curr - token->string.text;
@@ -933,6 +940,20 @@ gb_internal void tokenizer_get_token(Tokenizer *t, Token *token, int repeat=0) {
if (t->curr_rune == '!') {
token->kind = Token_Comment;
tokenizer_skip_line(t);
+ } else if (t->curr_rune == '+') {
+ token->kind = Token_FileTag;
+
+ // Skip until end of line or until we hit what is probably a comment.
+ // The parsing of tags happens in `parse_file`.
+ while (t->curr_rune != GB_RUNE_EOF) {
+ if (t->curr_rune == '\n') {
+ break;
+ }
+ if (t->curr_rune == '/') {
+ break;
+ }
+ advance_to_next_rune(t);
+ }
}
break;
case '/':
diff --git a/src/types.cpp b/src/types.cpp
index 574e628c5..233f903a3 100644
--- a/src/types.cpp
+++ b/src/types.cpp
@@ -137,13 +137,17 @@ struct TypeStruct {
Scope * scope;
i64 custom_align;
+ i64 custom_min_field_align;
+ i64 custom_max_field_align;
Type * polymorphic_params; // Type_Tuple
Type * polymorphic_parent;
+ Wait_Signal polymorphic_wait_signal;
Type * soa_elem;
i32 soa_count;
StructSoaKind soa_kind;
- RwMutex fields_mutex;
+ Wait_Signal fields_wait_signal;
+ BlockingMutex soa_mutex;
BlockingMutex offset_mutex; // for settings offsets
bool is_polymorphic;
@@ -165,6 +169,7 @@ struct TypeUnion {
i64 custom_align;
Type * polymorphic_params; // Type_Tuple
Type * polymorphic_parent;
+ Wait_Signal polymorphic_wait_signal;
i16 tag_size;
bool is_polymorphic;
@@ -182,6 +187,8 @@ struct TypeProc {
isize specialization_count;
ProcCallingConvention calling_convention;
i32 variadic_index;
+ String require_target_feature;
+ String enable_target_feature;
// TODO(bill): Make this a flag set rather than bools
bool variadic;
bool require_results;
@@ -230,6 +237,7 @@ struct TypeProc {
Type *key; \
Type *value; \
Type *lookup_result_type; \
+ Type *debug_metadata_type; \
}) \
TYPE_KIND(Struct, TypeStruct) \
TYPE_KIND(Union, TypeUnion) \
@@ -264,14 +272,6 @@ struct TypeProc {
Type *elem; \
Type *generic_count; \
}) \
- TYPE_KIND(RelativePointer, struct { \
- Type *pointer_type; \
- Type *base_integer; \
- }) \
- TYPE_KIND(RelativeMultiPointer, struct { \
- Type *pointer_type; \
- Type *base_integer; \
- }) \
TYPE_KIND(Matrix, struct { \
Type *elem; \
i64 row_count; \
@@ -279,6 +279,16 @@ struct TypeProc {
Type *generic_row_count; \
Type *generic_column_count; \
i64 stride_in_bytes; \
+ bool is_row_major; \
+ }) \
+ TYPE_KIND(BitField, struct { \
+ Scope * scope; \
+ Type * backing_type; \
+ Slice<Entity *> fields; \
+ String * tags; /*count == fields.count*/ \
+ Slice<u8> bit_sizes; \
+ Slice<i64> bit_offsets; \
+ Ast * node; \
}) \
TYPE_KIND(SoaPointer, struct { Type *elem; })
@@ -349,10 +359,12 @@ enum Typeid_Kind : u8 {
Typeid_Map,
Typeid_Bit_Set,
Typeid_Simd_Vector,
- Typeid_Relative_Pointer,
- Typeid_Relative_Multi_Pointer,
Typeid_Matrix,
Typeid_SoaPointer,
+ Typeid_Bit_Field,
+
+ Typeid__COUNT
+
};
// IMPORTANT NOTE(bill): This must match the same as the in core.odin
@@ -374,6 +386,9 @@ enum : int {
gb_internal bool is_type_comparable(Type *t);
gb_internal bool is_type_simple_compare(Type *t);
+gb_internal Type *type_deref(Type *t, bool allow_multi_pointer=false);
+gb_internal Type *base_type(Type *t);
+gb_internal Type *alloc_type_multi_pointer(Type *elem);
gb_internal u32 type_info_flags_of_type(Type *type) {
if (type == nullptr) {
@@ -398,6 +413,7 @@ struct Selection {
bool indirect; // Set if there was a pointer deref anywhere down the line
u8 swizzle_count; // maximum components = 4
u8 swizzle_indices; // 2 bits per component, representing which swizzle index
+ bool is_bit_field;
bool pseudo_field;
};
gb_global Selection const empty_selection = {0};
@@ -434,6 +450,15 @@ gb_internal Selection sub_selection(Selection const &sel, isize offset) {
return res;
}
+gb_internal Selection trim_selection(Selection const &sel) {
+ Selection res = {};
+ res.index.data = sel.index.data;
+ res.index.count = gb_max(sel.index.count - 1, 0);
+ res.index.capacity = res.index.count;
+ return res;
+}
+
+
gb_global Type basic_types[] = {
{Type_Basic, {Basic_Invalid, 0, 0, STR_LIT("invalid type")}},
@@ -546,6 +571,14 @@ gb_global Type *t_f16 = &basic_types[Basic_f16];
gb_global Type *t_f32 = &basic_types[Basic_f32];
gb_global Type *t_f64 = &basic_types[Basic_f64];
+gb_global Type *t_f16be = &basic_types[Basic_f16be];
+gb_global Type *t_f32be = &basic_types[Basic_f32be];
+gb_global Type *t_f64be = &basic_types[Basic_f64be];
+
+gb_global Type *t_f16le = &basic_types[Basic_f16le];
+gb_global Type *t_f32le = &basic_types[Basic_f32le];
+gb_global Type *t_f64le = &basic_types[Basic_f64le];
+
gb_global Type *t_complex32 = &basic_types[Basic_complex32];
gb_global Type *t_complex64 = &basic_types[Basic_complex64];
gb_global Type *t_complex128 = &basic_types[Basic_complex128];
@@ -635,10 +668,9 @@ gb_global Type *t_type_info_enum = nullptr;
gb_global Type *t_type_info_map = nullptr;
gb_global Type *t_type_info_bit_set = nullptr;
gb_global Type *t_type_info_simd_vector = nullptr;
-gb_global Type *t_type_info_relative_pointer = nullptr;
-gb_global Type *t_type_info_relative_multi_pointer = nullptr;
gb_global Type *t_type_info_matrix = nullptr;
gb_global Type *t_type_info_soa_pointer = nullptr;
+gb_global Type *t_type_info_bit_field = nullptr;
gb_global Type *t_type_info_named_ptr = nullptr;
gb_global Type *t_type_info_integer_ptr = nullptr;
@@ -664,10 +696,9 @@ gb_global Type *t_type_info_enum_ptr = nullptr;
gb_global Type *t_type_info_map_ptr = nullptr;
gb_global Type *t_type_info_bit_set_ptr = nullptr;
gb_global Type *t_type_info_simd_vector_ptr = nullptr;
-gb_global Type *t_type_info_relative_pointer_ptr = nullptr;
-gb_global Type *t_type_info_relative_multi_pointer_ptr = nullptr;
gb_global Type *t_type_info_matrix_ptr = nullptr;
gb_global Type *t_type_info_soa_pointer_ptr = nullptr;
+gb_global Type *t_type_info_bit_field_ptr = nullptr;
gb_global Type *t_allocator = nullptr;
gb_global Type *t_allocator_ptr = nullptr;
@@ -678,6 +709,10 @@ gb_global Type *t_allocator_error = nullptr;
gb_global Type *t_source_code_location = nullptr;
gb_global Type *t_source_code_location_ptr = nullptr;
+gb_global Type *t_load_directory_file = nullptr;
+gb_global Type *t_load_directory_file_ptr = nullptr;
+gb_global Type *t_load_directory_file_slice = nullptr;
+
gb_global Type *t_map_info = nullptr;
gb_global Type *t_map_cell_info = nullptr;
gb_global Type *t_raw_map = nullptr;
@@ -733,7 +768,7 @@ gb_internal i64 type_offset_of (Type *t, i64 index, Type **field_type_=null
gb_internal gbString type_to_string (Type *type, bool shorthand=true);
gb_internal gbString type_to_string (Type *type, gbAllocator allocator, bool shorthand=true);
gb_internal i64 type_size_of_internal(Type *t, TypePath *path);
-gb_internal void init_map_internal_types(Type *type);
+gb_internal i64 type_align_of_internal(Type *t, TypePath *path);
gb_internal Type * bit_set_to_int(Type *t);
gb_internal bool are_types_identical(Type *x, Type *y);
@@ -744,10 +779,6 @@ gb_internal bool is_type_proc(Type *t);
gb_internal bool is_type_slice(Type *t);
gb_internal bool is_type_integer(Type *t);
gb_internal bool type_set_offsets(Type *t);
-gb_internal Type *base_type(Type *t);
-
-gb_internal i64 type_size_of_internal(Type *t, TypePath *path);
-gb_internal i64 type_align_of_internal(Type *t, TypePath *path);
// IMPORTANT TODO(bill): SHould this TypePath code be removed since type cycle checking is handled much earlier on?
@@ -825,11 +856,13 @@ gb_internal void type_path_pop(TypePath *tp) {
#define FAILURE_SIZE 0
#define FAILURE_ALIGNMENT 0
+gb_internal bool type_ptr_set_exists(PtrSet<Type *> *s, Type *t);
+
gb_internal bool type_ptr_set_update(PtrSet<Type *> *s, Type *t) {
if (t == nullptr) {
return true;
}
- if (ptr_set_exists(s, t)) {
+ if (type_ptr_set_exists(s, t)) {
return true;
}
ptr_set_add(s, t);
@@ -898,6 +931,9 @@ gb_internal Type *core_type(Type *t) {
case Type_Enum:
t = t->Enum.base_type;
continue;
+ case Type_BitField:
+ t = t->BitField.backing_type;
+ continue;
}
break;
}
@@ -915,7 +951,7 @@ gb_internal Type *alloc_type(TypeKind kind) {
// gbAllocator a = heap_allocator();
gbAllocator a = permanent_allocator();
Type *t = gb_alloc_item(a, Type);
- zero_item(t);
+ gb_zero_item(t);
t->kind = kind;
t->cached_size = -1;
t->cached_align = -1;
@@ -950,6 +986,27 @@ gb_internal Type *alloc_type_soa_pointer(Type *elem) {
return t;
}
+gb_internal Type *alloc_type_pointer_to_multi_pointer(Type *ptr) {
+ Type *original_type = ptr;
+ ptr = base_type(ptr);
+ if (ptr->kind == Type_Pointer) {
+ return alloc_type_multi_pointer(ptr->Pointer.elem);
+ } else if (ptr->kind != Type_MultiPointer) {
+ GB_PANIC("Invalid type: %s", type_to_string(original_type));
+ }
+ return original_type;
+}
+
+gb_internal Type *alloc_type_multi_pointer_to_pointer(Type *ptr) {
+ Type *original_type = ptr;
+ ptr = base_type(ptr);
+ if (ptr->kind == Type_MultiPointer) {
+ return alloc_type_pointer(ptr->MultiPointer.elem);
+ } else if (ptr->kind != Type_Pointer) {
+ GB_PANIC("Invalid type: %s", type_to_string(original_type));
+ }
+ return original_type;
+}
gb_internal Type *alloc_type_array(Type *elem, i64 count, Type *generic_count = nullptr) {
if (generic_count != nullptr) {
@@ -965,7 +1022,7 @@ gb_internal Type *alloc_type_array(Type *elem, i64 count, Type *generic_count =
return t;
}
-gb_internal Type *alloc_type_matrix(Type *elem, i64 row_count, i64 column_count, Type *generic_row_count = nullptr, Type *generic_column_count = nullptr) {
+gb_internal Type *alloc_type_matrix(Type *elem, i64 row_count, i64 column_count, Type *generic_row_count, Type *generic_column_count, bool is_row_major) {
if (generic_row_count != nullptr || generic_column_count != nullptr) {
Type *t = alloc_type(Type_Matrix);
t->Matrix.elem = elem;
@@ -973,12 +1030,14 @@ gb_internal Type *alloc_type_matrix(Type *elem, i64 row_count, i64 column_count,
t->Matrix.column_count = column_count;
t->Matrix.generic_row_count = generic_row_count;
t->Matrix.generic_column_count = generic_column_count;
+ t->Matrix.is_row_major = is_row_major;
return t;
}
Type *t = alloc_type(Type_Matrix);
t->Matrix.elem = elem;
t->Matrix.row_count = row_count;
t->Matrix.column_count = column_count;
+ t->Matrix.is_row_major = is_row_major;
return t;
}
@@ -1020,6 +1079,14 @@ gb_internal Type *alloc_type_struct() {
return t;
}
+gb_internal Type *alloc_type_struct_complete() {
+ Type *t = alloc_type(Type_Struct);
+ wait_signal_set(&t->Struct.fields_wait_signal);
+ wait_signal_set(&t->Struct.polymorphic_wait_signal);
+ return t;
+}
+
+
gb_internal Type *alloc_type_union() {
Type *t = alloc_type(Type_Union);
return t;
@@ -1032,21 +1099,8 @@ gb_internal Type *alloc_type_enum() {
return t;
}
-gb_internal Type *alloc_type_relative_pointer(Type *pointer_type, Type *base_integer) {
- GB_ASSERT(is_type_pointer(pointer_type));
- GB_ASSERT(is_type_integer(base_integer));
- Type *t = alloc_type(Type_RelativePointer);
- t->RelativePointer.pointer_type = pointer_type;
- t->RelativePointer.base_integer = base_integer;
- return t;
-}
-
-gb_internal Type *alloc_type_relative_multi_pointer(Type *pointer_type, Type *base_integer) {
- GB_ASSERT(is_type_multi_pointer(pointer_type));
- GB_ASSERT(is_type_integer(base_integer));
- Type *t = alloc_type(Type_RelativeMultiPointer);
- t->RelativeMultiPointer.pointer_type = pointer_type;
- t->RelativeMultiPointer.base_integer = base_integer;
+gb_internal Type *alloc_type_bit_field() {
+ Type *t = alloc_type(Type_BitField);
return t;
}
@@ -1132,7 +1186,7 @@ gb_internal Type *alloc_type_simd_vector(i64 count, Type *elem, Type *generic_co
////////////////////////////////////////////////////////////////
-gb_internal Type *type_deref(Type *t, bool allow_multi_pointer=false) {
+gb_internal Type *type_deref(Type *t, bool allow_multi_pointer) {
if (t != nullptr) {
Type *bt = base_type(t);
if (bt == nullptr) {
@@ -1141,8 +1195,6 @@ gb_internal Type *type_deref(Type *t, bool allow_multi_pointer=false) {
switch (bt->kind) {
case Type_Pointer:
return bt->Pointer.elem;
- case Type_RelativePointer:
- return type_deref(bt->RelativePointer.pointer_type);
case Type_SoaPointer:
{
Type *elem = base_type(bt->SoaPointer.elem);
@@ -1389,6 +1441,7 @@ gb_internal i64 matrix_align_of(Type *t, struct TypePath *tp) {
Type *elem = t->Matrix.elem;
i64 row_count = gb_max(t->Matrix.row_count, 1);
+ i64 column_count = gb_max(t->Matrix.column_count, 1);
bool pop = type_path_push(tp, elem);
if (tp->failure) {
@@ -1406,13 +1459,13 @@ gb_internal i64 matrix_align_of(Type *t, struct TypePath *tp) {
// could be maximally aligned but as a compromise, having no padding will be
// beneficial to third libraries that assume no padding
- i64 total_expected_size = row_count*t->Matrix.column_count*elem_size;
+ i64 total_expected_size = row_count*column_count*elem_size;
// i64 min_alignment = prev_pow2(elem_align * row_count);
i64 min_alignment = prev_pow2(total_expected_size);
- while ((total_expected_size % min_alignment) != 0) {
+ while (total_expected_size != 0 && (total_expected_size % min_alignment) != 0) {
min_alignment >>= 1;
}
- GB_ASSERT(min_alignment >= elem_align);
+ min_alignment = gb_max(min_alignment, elem_align);
i64 align = gb_min(min_alignment, build_context.max_simd_align);
return align;
@@ -1438,12 +1491,15 @@ gb_internal i64 matrix_type_stride_in_bytes(Type *t, struct TypePath *tp) {
i64 stride_in_bytes = 0;
// NOTE(bill, 2021-10-25): The alignment strategy here is to have zero padding
- // It would be better for performance to pad each column so that each column
+ // It would be better for performance to pad each column/row so that each column/row
// could be maximally aligned but as a compromise, having no padding will be
// beneficial to third libraries that assume no padding
- i64 row_count = t->Matrix.row_count;
- stride_in_bytes = elem_size*row_count;
-
+
+ if (t->Matrix.is_row_major) {
+ stride_in_bytes = elem_size*t->Matrix.column_count;
+ } else {
+ stride_in_bytes = elem_size*t->Matrix.row_count;
+ }
t->Matrix.stride_in_bytes = stride_in_bytes;
return stride_in_bytes;
}
@@ -1470,14 +1526,18 @@ gb_internal i64 matrix_indices_to_offset(Type *t, i64 row_index, i64 column_inde
GB_ASSERT(0 <= row_index && row_index < t->Matrix.row_count);
GB_ASSERT(0 <= column_index && column_index < t->Matrix.column_count);
i64 stride_elems = matrix_type_stride_in_elems(t);
- // NOTE(bill): Column-major layout internally
- return row_index + stride_elems*column_index;
+ if (t->Matrix.is_row_major) {
+ return column_index + stride_elems*row_index;
+ } else {
+ // NOTE(bill): Column-major layout internally
+ return row_index + stride_elems*column_index;
+ }
}
gb_internal i64 matrix_row_major_index_to_offset(Type *t, i64 index) {
t = base_type(t);
GB_ASSERT(t->kind == Type_Matrix);
-
+
i64 row_index = index/t->Matrix.column_count;
i64 column_index = index%t->Matrix.column_count;
return matrix_indices_to_offset(t, row_index, column_index);
@@ -1548,18 +1608,29 @@ gb_internal Type *base_array_type(Type *t) {
return t;
}
-gb_internal bool is_type_generic(Type *t) {
- t = base_type(t);
- return t->kind == Type_Generic;
-}
-gb_internal bool is_type_relative_pointer(Type *t) {
- t = base_type(t);
- return t->kind == Type_RelativePointer;
+gb_internal Type *base_any_array_type(Type *t) {
+ Type *bt = base_type(t);
+ if (is_type_array(bt)) {
+ return bt->Array.elem;
+ } else if (is_type_slice(bt)) {
+ return bt->Slice.elem;
+ } else if (is_type_dynamic_array(bt)) {
+ return bt->DynamicArray.elem;
+ } else if (is_type_enumerated_array(bt)) {
+ return bt->EnumeratedArray.elem;
+ } else if (is_type_simd_vector(bt)) {
+ return bt->SimdVector.elem;
+ } else if (is_type_matrix(bt)) {
+ return bt->Matrix.elem;
+ }
+ return t;
}
-gb_internal bool is_type_relative_multi_pointer(Type *t) {
+
+
+gb_internal bool is_type_generic(Type *t) {
t = base_type(t);
- return t->kind == Type_RelativeMultiPointer;
+ return t->kind == Type_Generic;
}
gb_internal bool is_type_u8_slice(Type *t) {
@@ -1699,6 +1770,10 @@ gb_internal bool is_type_bit_set(Type *t) {
t = base_type(t);
return (t->kind == Type_BitSet);
}
+gb_internal bool is_type_bit_field(Type *t) {
+ t = base_type(t);
+ return (t->kind == Type_BitField);
+}
gb_internal bool is_type_map(Type *t) {
t = base_type(t);
return t->kind == Type_Map;
@@ -1918,6 +1993,24 @@ gb_internal bool is_type_valid_bit_set_elem(Type *t) {
return false;
}
+
+gb_internal bool is_valid_bit_field_backing_type(Type *type) {
+ if (type == nullptr) {
+ return false;
+ }
+ type = base_type(type);
+ if (is_type_untyped(type)) {
+ return false;
+ }
+ if (is_type_integer(type)) {
+ return true;
+ }
+ if (type->kind == Type_Array) {
+ return is_type_integer(type->Array.elem);
+ }
+ return false;
+}
+
gb_internal Type *bit_set_to_int(Type *t) {
GB_ASSERT(is_type_bit_set(t));
Type *bt = base_type(t);
@@ -1925,6 +2018,9 @@ gb_internal Type *bit_set_to_int(Type *t) {
if (underlying != nullptr && is_type_integer(underlying)) {
return underlying;
}
+ if (underlying != nullptr && is_valid_bit_field_backing_type(underlying)) {
+ return underlying;
+ }
i64 sz = type_size_of(t);
switch (sz) {
@@ -1957,6 +2053,9 @@ gb_internal bool is_type_valid_vector_elem(Type *t) {
if (is_type_boolean(t)) {
return true;
}
+ if (t->Basic.kind == Basic_rawptr) {
+ return true;
+ }
}
return false;
}
@@ -1976,8 +2075,6 @@ gb_internal bool is_type_indexable(Type *t) {
return true;
case Type_EnumeratedArray:
return true;
- case Type_RelativeMultiPointer:
- return true;
case Type_Matrix:
return true;
}
@@ -1995,8 +2092,6 @@ gb_internal bool is_type_sliceable(Type *t) {
return true;
case Type_EnumeratedArray:
return false;
- case Type_RelativeMultiPointer:
- return true;
case Type_Matrix:
return false;
}
@@ -2040,21 +2135,24 @@ gb_internal bool is_type_polymorphic_record_unspecialized(Type *t) {
t = base_type(t);
if (t->kind == Type_Struct) {
return t->Struct.is_polymorphic && !t->Struct.is_poly_specialized;
- } else if (t->kind == Type_Struct) {
- return t->Struct.is_polymorphic && !t->Struct.is_poly_specialized;
+ } else if (t->kind == Type_Union) {
+ return t->Union.is_polymorphic && !t->Union.is_poly_specialized;
}
return false;
}
+
gb_internal TypeTuple *get_record_polymorphic_params(Type *t) {
t = base_type(t);
switch (t->kind) {
case Type_Struct:
+ wait_signal_until_available(&t->Struct.polymorphic_wait_signal);
if (t->Struct.polymorphic_params) {
return &t->Struct.polymorphic_params->Tuple;
}
break;
case Type_Union:
+ wait_signal_until_available(&t->Union.polymorphic_wait_signal);
if (t->Union.polymorphic_params) {
return &t->Union.polymorphic_params->Tuple;
}
@@ -2200,27 +2298,7 @@ gb_internal bool is_type_polymorphic(Type *t, bool or_specialized=false) {
return true;
}
break;
-
- case Type_RelativeMultiPointer:
- if (is_type_polymorphic(t->RelativeMultiPointer.pointer_type, or_specialized)) {
- return true;
- }
- if (t->RelativeMultiPointer.base_integer != nullptr &&
- is_type_polymorphic(t->RelativeMultiPointer.base_integer, or_specialized)) {
- return true;
- }
- break;
- case Type_RelativePointer:
- if (is_type_polymorphic(t->RelativePointer.pointer_type, or_specialized)) {
- return true;
- }
- if (t->RelativePointer.base_integer != nullptr &&
- is_type_polymorphic(t->RelativePointer.base_integer, or_specialized)) {
- return true;
- }
- break;
}
-
return false;
}
@@ -2262,14 +2340,11 @@ gb_internal bool type_has_nil(Type *t) {
}
}
return false;
-
- case Type_RelativePointer:
- case Type_RelativeMultiPointer:
- return true;
}
return false;
}
+
gb_internal bool elem_type_can_be_constant(Type *t) {
t = base_type(t);
if (t == t_invalid) {
@@ -2357,6 +2432,9 @@ gb_internal bool is_type_comparable(Type *t) {
case Type_SimdVector:
return true;
+
+ case Type_BitField:
+ return is_type_comparable(t->BitField.backing_type);
}
return false;
}
@@ -2430,10 +2508,6 @@ gb_internal bool is_type_load_safe(Type *type) {
}
return true;
- case Type_RelativePointer:
- case Type_RelativeMultiPointer:
- return true;
-
case Type_Pointer:
case Type_MultiPointer:
case Type_Slice:
@@ -2641,6 +2715,7 @@ gb_internal bool are_types_identical_internal(Type *x, Type *y, bool check_tuple
case Type_Matrix:
return x->Matrix.row_count == y->Matrix.row_count &&
x->Matrix.column_count == y->Matrix.column_count &&
+ x->Matrix.is_row_major == y->Matrix.is_row_major &&
are_types_identical(x->Matrix.elem, y->Matrix.elem);
case Type_DynamicArray:
@@ -2665,8 +2740,14 @@ gb_internal bool are_types_identical_internal(Type *x, Type *y, bool check_tuple
case Type_Union:
if (x->Union.variants.count == y->Union.variants.count &&
- x->Union.custom_align == y->Union.custom_align &&
x->Union.kind == y->Union.kind) {
+
+ if (x->Union.custom_align != y->Union.custom_align) {
+ if (type_align_of(x) != type_align_of(y)) {
+ return false;
+ }
+ }
+
// NOTE(bill): zeroth variant is nullptr
for_array(i, x->Union.variants) {
if (!are_types_identical(x->Union.variants[i], y->Union.variants[i])) {
@@ -2682,10 +2763,16 @@ gb_internal bool are_types_identical_internal(Type *x, Type *y, bool check_tuple
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.custom_align == y->Struct.custom_align &&
x->Struct.soa_kind == y->Struct.soa_kind &&
x->Struct.soa_count == y->Struct.soa_count &&
are_types_identical(x->Struct.soa_elem, y->Struct.soa_elem)) {
+
+ if (x->Struct.custom_align != y->Struct.custom_align) {
+ if (type_align_of(x) != type_align_of(y)) {
+ return false;
+ }
+ }
+
for_array(i, x->Struct.fields) {
Entity *xf = x->Struct.fields[i];
Entity *yf = y->Struct.fields[i];
@@ -2764,6 +2851,29 @@ gb_internal bool are_types_identical_internal(Type *x, Type *y, bool check_tuple
return are_types_identical(x->SimdVector.elem, y->SimdVector.elem);
}
break;
+
+ case Type_BitField:
+ if (are_types_identical(x->BitField.backing_type, y->BitField.backing_type) &&
+ x->BitField.fields.count == y->BitField.fields.count) {
+ for_array(i, x->BitField.fields) {
+ Entity *a = x->BitField.fields[i];
+ Entity *b = y->BitField.fields[i];
+ if (!are_types_identical(a->type, b->type)) {
+ return false;
+ }
+ if (a->token.string != b->token.string) {
+ return false;
+ }
+ if (x->BitField.bit_sizes[i] != y->BitField.bit_sizes[i]) {
+ return false;
+ }
+ if (x->BitField.bit_offsets[i] != y->BitField.bit_offsets[i]) {
+ return false;
+ }
+ }
+ return true;
+ }
+ break;
}
return false;
@@ -2787,6 +2897,49 @@ gb_internal Type *default_type(Type *type) {
return type;
}
+// See https://en.cppreference.com/w/c/language/conversion#Default_argument_promotions
+gb_internal Type *c_vararg_promote_type(Type *type) {
+ GB_ASSERT(type != nullptr);
+
+ Type *core = core_type(type);
+ GB_ASSERT(core->kind != Type_BitSet);
+
+ if (core->kind == Type_Basic) {
+ switch (core->Basic.kind) {
+ case Basic_f16:
+ case Basic_f32:
+ case Basic_UntypedFloat:
+ return t_f64;
+ case Basic_f16le:
+ case Basic_f32le:
+ return t_f64le;
+ case Basic_f16be:
+ case Basic_f32be:
+ return t_f64be;
+
+ case Basic_UntypedBool:
+ case Basic_bool:
+ case Basic_b8:
+ case Basic_b16:
+ case Basic_i8:
+ case Basic_i16:
+ case Basic_u8:
+ case Basic_u16:
+ return t_i32;
+
+ case Basic_i16le:
+ case Basic_u16le:
+ return t_i32le;
+
+ case Basic_i16be:
+ case Basic_u16be:
+ return t_i32be;
+ }
+ }
+
+ return type;
+}
+
gb_internal bool union_variant_index_types_equal(Type *v, Type *vt) {
if (are_types_identical(v, vt)) {
return true;
@@ -2863,7 +3016,22 @@ gb_internal Type *union_tag_type(Type *u) {
return t_uint;
}
+gb_internal int matched_target_features(TypeProc *t) {
+ if (t->require_target_feature.len == 0) {
+ return 0;
+ }
+ int matches = 0;
+ String_Iterator it = {t->require_target_feature, 0};
+ for (;;) {
+ String str = string_split_iterator(&it, ',');
+ if (str == "") break;
+ if (check_target_feature_is_valid_for_target_arch(str, nullptr)) {
+ matches += 1;
+ }
+ }
+ return matches;
+}
enum ProcTypeOverloadKind {
ProcOverload_Identical, // The types are identical
@@ -2875,6 +3043,7 @@ enum ProcTypeOverloadKind {
ProcOverload_ResultCount,
ProcOverload_ResultTypes,
ProcOverload_Polymorphic,
+ ProcOverload_TargetFeatures,
ProcOverload_NotProcedure,
@@ -2932,6 +3101,10 @@ gb_internal ProcTypeOverloadKind are_proc_types_overload_safe(Type *x, Type *y)
}
}
+ if (matched_target_features(&px) != matched_target_features(&py)) {
+ return ProcOverload_TargetFeatures;
+ }
+
if (px.params != nullptr && py.params != nullptr) {
Entity *ex = px.params->Tuple.variables[0];
Entity *ey = py.params->Tuple.variables[0];
@@ -2961,9 +3134,8 @@ gb_internal Selection lookup_field_from_index(Type *type, i64 index) {
isize max_count = 0;
switch (type->kind) {
case Type_Struct:
- rw_mutex_shared_lock(&type->Struct.fields_mutex);
+ wait_signal_until_available(&type->Struct.fields_wait_signal);
max_count = type->Struct.fields.count;
- rw_mutex_shared_unlock(&type->Struct.fields_mutex);
break;
case Type_Tuple: max_count = type->Tuple.variables.count; break;
}
@@ -2974,8 +3146,7 @@ gb_internal Selection lookup_field_from_index(Type *type, i64 index) {
switch (type->kind) {
case Type_Struct: {
- rw_mutex_shared_lock(&type->Struct.fields_mutex);
- defer (rw_mutex_shared_unlock(&type->Struct.fields_mutex));
+ wait_signal_until_available(&type->Struct.fields_wait_signal);
for (isize i = 0; i < max_count; i++) {
Entity *f = type->Struct.fields[i];
if (f->kind == Entity_Variable) {
@@ -3031,7 +3202,7 @@ gb_internal Selection lookup_field_with_selection(Type *type_, String field_name
mutex_lock(md->mutex);
defer (mutex_unlock(md->mutex));
for (TypeNameObjCMetadataEntry const &entry : md->type_entries) {
- GB_ASSERT(entry.entity->kind == Entity_Procedure);
+ GB_ASSERT(entry.entity->kind == Entity_Procedure || entry.entity->kind == Entity_ProcGroup);
if (entry.name == field_name) {
sel.entity = entry.entity;
sel.pseudo_field = true;
@@ -3040,9 +3211,8 @@ gb_internal Selection lookup_field_with_selection(Type *type_, String field_name
}
}
if (type->kind == Type_Struct) {
- rw_mutex_shared_lock(&type->Struct.fields_mutex);
+ // wait_signal_until_available(&type->Struct.fields_wait_signal);
isize field_count = type->Struct.fields.count;
- rw_mutex_shared_unlock(&type->Struct.fields_mutex);
if (field_count != 0) for_array(i, type->Struct.fields) {
Entity *f = type->Struct.fields[i];
if (f->flags&EntityFlag_Using) {
@@ -3071,9 +3241,8 @@ gb_internal Selection lookup_field_with_selection(Type *type_, String field_name
}
if (type->kind == Type_Struct) {
- rw_mutex_shared_lock(&type->Struct.fields_mutex);
+ // wait_signal_until_available(&type->Struct.fields_wait_signal);
Scope *s = type->Struct.scope;
- rw_mutex_shared_unlock(&type->Struct.fields_mutex);
if (s != nullptr) {
Entity *found = scope_lookup_current(s, field_name);
if (found != nullptr && found->kind != Entity_Variable) {
@@ -3121,9 +3290,12 @@ gb_internal Selection lookup_field_with_selection(Type *type_, String field_name
}
}
- rw_mutex_shared_lock(&type->Struct.fields_mutex);
+ if (is_type_polymorphic(type)) {
+ // NOTE(bill): A polymorphic struct has no fields, this only hits in the case of an error
+ return sel;
+ }
+ wait_signal_until_available(&type->Struct.fields_wait_signal);
isize field_count = type->Struct.fields.count;
- rw_mutex_shared_unlock(&type->Struct.fields_mutex);
if (field_count != 0) for_array(i, type->Struct.fields) {
Entity *f = type->Struct.fields[i];
if (f->kind != Entity_Variable || (f->flags & EntityFlag_Field) == 0) {
@@ -3165,6 +3337,21 @@ gb_internal Selection lookup_field_with_selection(Type *type_, String field_name
else if (field_name == "a") mapped_field_name = str_lit("w");
return lookup_field_with_selection(type, mapped_field_name, is_type, sel, allow_blank_ident);
}
+ } else if (type->kind == Type_BitField) {
+ for_array(i, type->BitField.fields) {
+ Entity *f = type->BitField.fields[i];
+ if (f->kind != Entity_Variable || (f->flags & EntityFlag_Field) == 0) {
+ continue;
+ }
+ String str = f->token.string;
+ if (field_name == str) {
+ selection_add_index(&sel, i); // HACK(bill): Leaky memory
+ sel.entity = f;
+ sel.is_bit_field = true;
+ return sel;
+ }
+ }
+
} else if (type->kind == Type_Basic) {
switch (type->Basic.kind) {
case Basic_any: {
@@ -3305,31 +3492,6 @@ gb_internal Selection lookup_field_with_selection(Type *type_, String field_name
}
return sel;
- } else if (type->kind == Type_Array) {
- if (type->Array.count <= 4) {
- // HACK(bill): Memory leak
- switch (type->Array.count) {
- #define _ARRAY_FIELD_CASE_IF(_length, _name) \
- if (field_name == (_name)) { \
- selection_add_index(&sel, (_length)-1); \
- sel.entity = alloc_entity_array_elem(nullptr, make_token_ident(str_lit(_name)), type->Array.elem, (_length)-1); \
- return sel; \
- }
- #define _ARRAY_FIELD_CASE(_length, _name0, _name1) \
- case (_length): \
- _ARRAY_FIELD_CASE_IF(_length, _name0); \
- _ARRAY_FIELD_CASE_IF(_length, _name1); \
- /*fallthrough*/
-
- _ARRAY_FIELD_CASE(4, "w", "a");
- _ARRAY_FIELD_CASE(3, "z", "b");
- _ARRAY_FIELD_CASE(2, "y", "g");
- _ARRAY_FIELD_CASE(1, "x", "r");
- default: break;
-
- #undef _ARRAY_FIELD_CASE
- }
- }
} else if (type->kind == Type_DynamicArray) {
GB_ASSERT(t_allocator != nullptr);
String allocator_str = str_lit("allocator");
@@ -3350,7 +3512,53 @@ gb_internal Selection lookup_field_with_selection(Type *type_, String field_name
sel.entity = entity__allocator;
return sel;
}
+
+
+#define _ARRAY_FIELD_CASE_IF(_length, _name) \
+ if (field_name == (_name)) { \
+ selection_add_index(&sel, (_length)-1); \
+ sel.entity = alloc_entity_array_elem(nullptr, make_token_ident(str_lit(_name)), elem, (_length)-1); \
+ return sel; \
}
+#define _ARRAY_FIELD_CASE(_length, _name0, _name1) \
+case (_length): \
+ _ARRAY_FIELD_CASE_IF(_length, _name0); \
+ _ARRAY_FIELD_CASE_IF(_length, _name1); \
+ /*fallthrough*/
+
+
+ } else if (type->kind == Type_Array) {
+
+ Type *elem = type->Array.elem;
+
+ if (type->Array.count <= 4) {
+ // HACK(bill): Memory leak
+ switch (type->Array.count) {
+
+ _ARRAY_FIELD_CASE(4, "w", "a");
+ _ARRAY_FIELD_CASE(3, "z", "b");
+ _ARRAY_FIELD_CASE(2, "y", "g");
+ _ARRAY_FIELD_CASE(1, "x", "r");
+ default: break;
+ }
+ }
+ } else if (type->kind == Type_SimdVector) {
+
+ Type *elem = type->SimdVector.elem;
+ if (type->SimdVector.count <= 4) {
+ // HACK(bill): Memory leak
+ switch (type->SimdVector.count) {
+ _ARRAY_FIELD_CASE(4, "w", "a");
+ _ARRAY_FIELD_CASE(3, "z", "b");
+ _ARRAY_FIELD_CASE(2, "y", "g");
+ _ARRAY_FIELD_CASE(1, "x", "r");
+ default: break;
+ }
+ }
+ }
+
+#undef _ARRAY_FIELD_CASE
+#undef _ARRAY_FIELD_CASE
return sel;
}
@@ -3414,8 +3622,6 @@ gb_internal Slice<i32> struct_fields_index_by_increasing_offset(gbAllocator allo
-gb_internal i64 type_size_of_internal (Type *t, TypePath *path);
-gb_internal i64 type_align_of_internal(Type *t, TypePath *path);
gb_internal i64 type_size_of(Type *t);
gb_internal i64 type_align_of(Type *t);
@@ -3565,6 +3771,8 @@ gb_internal i64 type_align_of_internal(Type *t, TypePath *path) {
case Type_Slice:
return build_context.int_size;
+ case Type_BitField:
+ return type_align_of_internal(t->BitField.backing_type, path);
case Type_Tuple: {
i64 max = 1;
@@ -3615,6 +3823,8 @@ gb_internal i64 type_align_of_internal(Type *t, TypePath *path) {
return 1;
}
+ type_set_offsets(t);
+
i64 max = 1;
for_array(i, t->Struct.fields) {
Type *field_type = t->Struct.fields[i]->type;
@@ -3628,6 +3838,14 @@ gb_internal i64 type_align_of_internal(Type *t, TypePath *path) {
max = align;
}
}
+
+ if (t->Struct.custom_min_field_align > 0) {
+ max = gb_max(max, t->Struct.custom_min_field_align);
+ }
+ if (t->Struct.custom_max_field_align != 0 &&
+ t->Struct.custom_max_field_align > t->Struct.custom_min_field_align) {
+ max = gb_min(max, t->Struct.custom_max_field_align);
+ }
return max;
} break;
@@ -3652,11 +3870,6 @@ gb_internal i64 type_align_of_internal(Type *t, TypePath *path) {
case Type_Matrix:
return matrix_align_of(t, path);
- case Type_RelativePointer:
- return type_align_of_internal(t->RelativePointer.base_integer, path);
- case Type_RelativeMultiPointer:
- return type_align_of_internal(t->RelativeMultiPointer.base_integer, path);
-
case Type_SoaPointer:
return build_context.int_size;
}
@@ -3666,10 +3879,19 @@ gb_internal i64 type_align_of_internal(Type *t, TypePath *path) {
return gb_clamp(next_pow2(type_size_of_internal(t, path)), 1, build_context.max_align);
}
-gb_internal i64 *type_set_offsets_of(Slice<Entity *> const &fields, bool is_packed, bool is_raw_union) {
+gb_internal i64 *type_set_offsets_of(Slice<Entity *> const &fields, bool is_packed, bool is_raw_union, i64 min_field_align, i64 max_field_align) {
gbAllocator a = permanent_allocator();
auto offsets = gb_alloc_array(a, i64, fields.count);
i64 curr_offset = 0;
+
+ if (min_field_align == 0) {
+ min_field_align = 1;
+ }
+
+ TypePath path{};
+ type_path_init(&path);
+ defer (type_path_free(&path));
+
if (is_raw_union) {
for_array(i, fields) {
offsets[i] = 0;
@@ -3679,7 +3901,7 @@ gb_internal i64 *type_set_offsets_of(Slice<Entity *> const &fields, bool is_pack
if (fields[i]->kind != Entity_Variable) {
offsets[i] = -1;
} else {
- i64 size = type_size_of(fields[i]->type);
+ i64 size = type_size_of_internal(fields[i]->type, &path);
offsets[i] = curr_offset;
curr_offset += size;
}
@@ -3690,8 +3912,11 @@ gb_internal i64 *type_set_offsets_of(Slice<Entity *> const &fields, bool is_pack
offsets[i] = -1;
} else {
Type *t = fields[i]->type;
- i64 align = gb_max(type_align_of(t), 1);
- i64 size = gb_max(type_size_of( t), 0);
+ i64 align = gb_max(type_align_of_internal(t, &path), min_field_align);
+ if (max_field_align > min_field_align) {
+ align = gb_min(align, max_field_align);
+ }
+ i64 size = gb_max(type_size_of_internal(t, &path), 0);
curr_offset = align_formula(curr_offset, align);
offsets[i] = curr_offset;
curr_offset += size;
@@ -3707,7 +3932,7 @@ gb_internal bool type_set_offsets(Type *t) {
MUTEX_GUARD(&t->Struct.offset_mutex);
if (!t->Struct.are_offsets_set) {
t->Struct.are_offsets_being_processed = true;
- t->Struct.offsets = type_set_offsets_of(t->Struct.fields, t->Struct.is_packed, t->Struct.is_raw_union);
+ 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_set = true;
return true;
@@ -3716,7 +3941,7 @@ gb_internal bool type_set_offsets(Type *t) {
MUTEX_GUARD(&t->Tuple.mutex);
if (!t->Tuple.are_offsets_set) {
t->Tuple.are_offsets_being_processed = true;
- t->Tuple.offsets = type_set_offsets_of(t->Tuple.variables, t->Tuple.is_packed, false);
+ 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_set = true;
return true;
@@ -3932,13 +4157,15 @@ gb_internal i64 type_size_of_internal(Type *t, TypePath *path) {
case Type_Matrix: {
i64 stride_in_bytes = matrix_type_stride_in_bytes(t, path);
- return stride_in_bytes * t->Matrix.column_count;
+ if (t->Matrix.is_row_major) {
+ return stride_in_bytes * t->Matrix.row_count;
+ } else {
+ return stride_in_bytes * t->Matrix.column_count;
+ }
}
- case Type_RelativePointer:
- return type_size_of_internal(t->RelativePointer.base_integer, path);
- case Type_RelativeMultiPointer:
- return type_size_of_internal(t->RelativeMultiPointer.base_integer, path);
+ case Type_BitField:
+ return type_size_of_internal(t->BitField.backing_type, path);
}
// Catch all
@@ -4085,7 +4312,7 @@ gb_internal i64 type_offset_of_from_selection(Type *type, Selection sel) {
return offset;
}
-gb_internal isize check_is_assignable_to_using_subtype(Type *src, Type *dst, isize level = 0, bool src_is_ptr = false) {
+gb_internal isize check_is_assignable_to_using_subtype(Type *src, Type *dst, isize level = 0, bool src_is_ptr = false, bool allow_polymorphic=false) {
Type *prev_src = src;
src = type_deref(src);
if (!src_is_ptr) {
@@ -4097,11 +4324,21 @@ gb_internal isize check_is_assignable_to_using_subtype(Type *src, Type *dst, isi
return 0;
}
+ bool dst_is_polymorphic = is_type_polymorphic(dst);
+
for_array(i, src->Struct.fields) {
Entity *f = src->Struct.fields[i];
if (f->kind != Entity_Variable || (f->flags&EntityFlags_IsSubtype) == 0) {
continue;
}
+ if (allow_polymorphic && dst_is_polymorphic) {
+ Type *fb = base_type(type_deref(f->type));
+ if (fb->kind == Type_Struct) {
+ if (fb->Struct.polymorphic_parent == dst) {
+ return true;
+ }
+ }
+ }
if (are_types_identical(f->type, dst)) {
return level+1;
@@ -4111,7 +4348,7 @@ gb_internal isize check_is_assignable_to_using_subtype(Type *src, Type *dst, isi
return level+1;
}
}
- isize nested_level = check_is_assignable_to_using_subtype(f->type, dst, level+1, src_is_ptr);
+ isize nested_level = check_is_assignable_to_using_subtype(f->type, dst, level+1, src_is_ptr, allow_polymorphic);
if (nested_level > 0) {
return nested_level;
}
@@ -4127,6 +4364,13 @@ gb_internal bool is_type_subtype_of(Type *src, Type *dst) {
return 0 < check_is_assignable_to_using_subtype(src, dst, 0, is_type_pointer(src));
}
+gb_internal bool is_type_subtype_of_and_allow_polymorphic(Type *src, Type *dst) {
+ if (are_types_identical(src, dst)) {
+ return true;
+ }
+
+ return 0 < check_is_assignable_to_using_subtype(src, dst, 0, is_type_pointer(src), true);
+}
gb_internal bool has_type_got_objc_class_attribute(Type *t) {
@@ -4194,7 +4438,70 @@ gb_internal Type *alloc_type_proc_from_types(Type **param_types, unsigned param_
return t;
}
-
+// gb_internal Type *type_from_selection(Type *type, Selection const &sel) {
+// for (i32 index : sel.index) {
+// Type *bt = base_type(type_deref(type));
+// switch (bt->kind) {
+// case Type_Struct:
+// type = bt->Struct.fields[index]->type;
+// break;
+// case Type_Tuple:
+// type = bt->Tuple.variables[index]->type;
+// break;
+// case Type_BitField:
+// type = bt->BitField.fields[index]->type;
+// break;
+// case Type_Array:
+// type = bt->Array.elem;
+// break;
+// case Type_EnumeratedArray:
+// type = bt->Array.elem;
+// break;
+// case Type_Slice:
+// switch (index) {
+// case 0: type = alloc_type_multi_pointer(bt->Slice.elem); break;
+// case 1: type = t_int; break;
+// }
+// break;
+// case Type_DynamicArray:
+// switch (index) {
+// case 0: type = alloc_type_multi_pointer(bt->DynamicArray.elem); break;
+// case 1: type = t_int; break;
+// case 2: type = t_int; break;
+// case 3: type = t_allocator; break;
+// }
+// break;
+// case Type_Map:
+// switch (index) {
+// case 0: type = t_uintptr; break;
+// case 1: type = t_int; break;
+// case 2: type = t_allocator; break;
+// }
+// break;
+// case Type_Basic:
+// if (is_type_complex_or_quaternion(bt)) {
+// type = base_complex_elem_type(bt);
+// } else {
+// switch (type->Basic.kind) {
+// case Basic_any:
+// switch (index) {
+// case 0: type = t_rawptr; break;
+// case 1: type = t_typeid; break;
+// }
+// break;
+// case Basic_string:
+// switch (index) {
+// case 0: type = t_u8_multi_ptr; break;
+// case 1: type = t_int; break;
+// }
+// break;
+// }
+// }
+// break;
+// }
+// }
+// return type;
+// }
gb_internal gbString write_type_to_string(gbString str, Type *type, bool shorthand=false) {
if (type == nullptr) {
@@ -4484,24 +4791,31 @@ gb_internal gbString write_type_to_string(gbString str, Type *type, bool shortha
str = gb_string_append_fmt(str, "#simd[%d]", cast(int)type->SimdVector.count);
str = write_type_to_string(str, type->SimdVector.elem);
break;
-
- case Type_RelativePointer:
- str = gb_string_append_fmt(str, "#relative(");
- str = write_type_to_string(str, type->RelativePointer.base_integer);
- str = gb_string_append_fmt(str, ") ");
- str = write_type_to_string(str, type->RelativePointer.pointer_type);
- break;
- case Type_RelativeMultiPointer:
- str = gb_string_append_fmt(str, "#relative(");
- str = write_type_to_string(str, type->RelativePointer.base_integer);
- str = gb_string_append_fmt(str, ") ");
- str = write_type_to_string(str, type->RelativePointer.pointer_type);
- break;
case Type_Matrix:
+ if (type->Matrix.is_row_major) {
+ 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);
break;
+
+ case Type_BitField:
+ str = gb_string_appendc(str, "bit_field ");
+ str = write_type_to_string(str, type->BitField.backing_type);
+ str = gb_string_appendc(str, " {");
+ for (isize i = 0; i < type->BitField.fields.count; i++) {
+ Entity *f = type->BitField.fields[i];
+ if (i > 0) {
+ str = gb_string_appendc(str, ", ");
+ }
+ 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 = gb_string_append_fmt(str, " | %u", type->BitField.bit_sizes[i]);
+ }
+ str = gb_string_appendc(str, " }");
+ break;
}
return str;
diff --git a/src/ucg/ucg.c b/src/ucg/ucg.c
new file mode 100644
index 000000000..c3e270e1a
--- /dev/null
+++ b/src/ucg/ucg.c
@@ -0,0 +1,686 @@
+/*
+ * SPDX-FileCopyrightText: (c) 2024 Feoramund
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+
+//
+// NOTE(Feoramund): This is my UCG library, adapted for use within the Odin compiler.
+// Most of the comments have been let alone and may not strictly apply anymore.
+//
+// 1. The UCG allocator interface was replaced by gbAllocator.
+// 2. The UCG UTF-8 decoder was replaced with the one already in the compiler.
+// 3. Non-essential code was stripped.
+// 4. Some types were changed for compatibility.
+//
+
+
+/* This is the data that is allocated when an allocator is passed to
+ * ucg_decode_grapheme_clusters. */
+typedef struct {
+ i32 byte_index;
+ i32 rune_index;
+ i32 width;
+} ucg_grapheme;
+
+
+/* #include "ucg.h" */
+#include "ucg_tables.h"
+
+#define UCG_TABLE_LEN(t) (sizeof(t) / sizeof(int32_t))
+
+#define ZERO_WIDTH_SPACE 0x200B
+#define ZERO_WIDTH_NON_JOINER 0x200C
+#define ZERO_WIDTH_JOINER 0x200D
+#define WORD_JOINER 0x2060
+
+int ucg_binary_search(int32_t value, const int32_t* table, int length, int stride) {
+ GB_ASSERT(table != NULL);
+ GB_ASSERT(length > 0);
+ GB_ASSERT(stride > 0);
+
+ int n = length;
+ int t = 0;
+ for (/**/; n > 1; /**/) {
+ int m = n / 2;
+ int p = t + m * stride;
+ if (value >= table[p]) {
+ t = p;
+ n = n - m;
+ } else {
+ n = m;
+ }
+ }
+ if (n != 0 && value >= table[t]) {
+ return t;
+ }
+ return -1;
+}
+
+//
+// The procedures below are accurate as of Unicode 15.1.0.
+//
+
+bool ucg_is_control(int32_t r) {
+ if (r <= 0x1F || (0x7F <= r && r <= 0x9F)) {
+ return true;
+ }
+ return false;
+}
+
+// Emoji_Modifier
+bool ucg_is_emoji_modifier(int32_t r) {
+ return 0x1F3FB <= r && r <= 0x1F3FF;
+}
+
+// Regional_Indicator
+bool ucg_is_regional_indicator(int32_t r) {
+ return 0x1F1E6 <= r && r <= 0x1F1FF;
+}
+
+// General_Category=Enclosing_Mark
+bool ucg_is_enclosing_mark(int32_t r) {
+ switch (r) {
+ case 0x0488:
+ case 0x0489:
+ case 0x1ABE:
+ return true;
+ }
+
+ if (0x20DD <= r && r <= 0x20E0) { return true; }
+ if (0x20E2 <= r && r <= 0x20E4) { return true; }
+ if (0xA670 <= r && r <= 0xA672) { return true; }
+
+ return false;
+}
+
+// Prepended_Concatenation_Mark
+bool ucg_is_prepended_concatenation_mark(int32_t r) {
+ switch (r) {
+ case 0x006DD:
+ case 0x0070F:
+ case 0x008E2:
+ case 0x110BD:
+ case 0x110CD:
+ return true;
+ }
+
+ if (0x00600 <= r && r <= 0x00605) { return true; }
+ if (0x00890 <= r && r <= 0x00891) { return true; }
+
+ return false;
+}
+
+// General_Category=Spacing_Mark
+bool ucg_is_spacing_mark(int32_t r) {
+ intptr_t p = ucg_binary_search(r, ucg_spacing_mark_ranges, UCG_TABLE_LEN(ucg_spacing_mark_ranges)/2, 2);
+ if (p >= 0 && ucg_spacing_mark_ranges[p] <= r && r <= ucg_spacing_mark_ranges[p+1]) {
+ return true;
+ }
+ return false;
+}
+
+// General_Category=Nonspacing_Mark
+bool ucg_is_nonspacing_mark(int32_t r) {
+ intptr_t p = ucg_binary_search(r, ucg_nonspacing_mark_ranges, UCG_TABLE_LEN(ucg_nonspacing_mark_ranges)/2, 2);
+ if (p >= 0 && ucg_nonspacing_mark_ranges[p] <= r && r <= ucg_nonspacing_mark_ranges[p+1]) {
+ return true;
+ }
+ return false;
+}
+
+// Extended_Pictographic
+bool ucg_is_emoji_extended_pictographic(int32_t r) {
+ intptr_t p = ucg_binary_search(r, ucg_emoji_extended_pictographic_ranges, UCG_TABLE_LEN(ucg_emoji_extended_pictographic_ranges)/2, 2);
+ if (p >= 0 && ucg_emoji_extended_pictographic_ranges[p] <= r && r <= ucg_emoji_extended_pictographic_ranges[p+1]) {
+ return true;
+ }
+ return false;
+}
+
+// Grapheme_Extend
+bool ucg_is_grapheme_extend(int32_t r) {
+ intptr_t p = ucg_binary_search(r, ucg_grapheme_extend_ranges, UCG_TABLE_LEN(ucg_grapheme_extend_ranges)/2, 2);
+ if (p >= 0 && ucg_grapheme_extend_ranges[p] <= r && r <= ucg_grapheme_extend_ranges[p+1]) {
+ return true;
+ }
+ return false;
+}
+
+
+// Hangul_Syllable_Type=Leading_Jamo
+bool ucg_is_hangul_syllable_leading(int32_t r) {
+ return (0x1100 <= r && r <= 0x115F) || (0xA960 <= r && r <= 0xA97C);
+}
+
+// Hangul_Syllable_Type=Vowel_Jamo
+bool ucg_is_hangul_syllable_vowel(int32_t r) {
+ return (0x1160 <= r && r <= 0x11A7) || (0xD7B0 <= r && r <= 0xD7C6);
+}
+
+// Hangul_Syllable_Type=Trailing_Jamo
+bool ucg_is_hangul_syllable_trailing(int32_t r) {
+ return (0x11A8 <= r && r <= 0x11FF) || (0xD7CB <= r && r <= 0xD7FB);
+}
+
+// Hangul_Syllable_Type=LV_Syllable
+bool ucg_is_hangul_syllable_lv(int32_t r) {
+ intptr_t p = ucg_binary_search(r, ucg_hangul_syllable_lv_singlets, UCG_TABLE_LEN(ucg_hangul_syllable_lv_singlets), 1);
+ if (p >= 0 && r == ucg_hangul_syllable_lv_singlets[p]) {
+ return true;
+ }
+ return false;
+}
+
+// Hangul_Syllable_Type=LVT_Syllable
+bool ucg_is_hangul_syllable_lvt(int32_t r) {
+ intptr_t p = ucg_binary_search(r, ucg_hangul_syllable_lvt_ranges, UCG_TABLE_LEN(ucg_hangul_syllable_lvt_ranges)/2, 2);
+ if (p >= 0 && ucg_hangul_syllable_lvt_ranges[p] <= r && r <= ucg_hangul_syllable_lvt_ranges[p+1]) {
+ return true;
+ }
+ return false;
+}
+
+
+// Indic_Syllabic_Category=Consonant_Preceding_Repha
+bool ucg_is_indic_consonant_preceding_repha(int32_t r) {
+ switch (r) {
+ case 0x00D4E:
+ case 0x11941:
+ case 0x11D46:
+ case 0x11F02:
+ return true;
+ }
+ return false;
+}
+
+// Indic_Syllabic_Category=Consonant_Prefixed
+bool ucg_is_indic_consonant_prefixed(int32_t r) {
+ switch (r) {
+ case 0x1193F:
+ case 0x11A3A:
+ return true;
+ }
+
+ if (0x111C2 <= r && r <= 0x111C3) { return true; }
+ if (0x11A84 <= r && r <= 0x11A89) { return true; }
+
+ return false;
+}
+
+// Indic_Conjunct_Break=Linker
+bool ucg_is_indic_conjunct_break_linker(int32_t r) {
+ switch (r) {
+ case 0x094D:
+ case 0x09CD:
+ case 0x0ACD:
+ case 0x0B4D:
+ case 0x0C4D:
+ case 0x0D4D:
+ return true;
+ }
+ return false;
+}
+
+// Indic_Conjunct_Break=Consonant
+bool ucg_is_indic_conjunct_break_consonant(int32_t r) {
+ intptr_t p = ucg_binary_search(r, ucg_indic_conjunct_break_consonant_ranges, UCG_TABLE_LEN(ucg_indic_conjunct_break_consonant_ranges)/2, 2);
+ if (p >= 0 && ucg_indic_conjunct_break_consonant_ranges[p] <= r && r <= ucg_indic_conjunct_break_consonant_ranges[p+1]) {
+ return true;
+ }
+ return false;
+}
+
+// Indic_Conjunct_Break=Extend
+bool ucg_is_indic_conjunct_break_extend(int32_t r) {
+ intptr_t p = ucg_binary_search(r, ucg_indic_conjunct_break_extend_ranges, UCG_TABLE_LEN(ucg_indic_conjunct_break_extend_ranges)/2, 2);
+ if (p >= 0 && ucg_indic_conjunct_break_extend_ranges[p] <= r && r <= ucg_indic_conjunct_break_extend_ranges[p+1]) {
+ return true;
+ }
+ return false;
+}
+
+
+/*
+```
+Indic_Syllabic_Category = Consonant_Preceding_Repha, or
+Indic_Syllabic_Category = Consonant_Prefixed, or
+Prepended_Concatenation_Mark = Yes
+```
+*/
+bool ucg_is_gcb_prepend_class(int32_t r) {
+ return ucg_is_indic_consonant_preceding_repha(r) || ucg_is_indic_consonant_prefixed(r) || ucg_is_prepended_concatenation_mark(r);
+}
+
+/*
+```
+Grapheme_Extend = Yes, or
+Emoji_Modifier = Yes
+
+This includes:
+General_Category = Nonspacing_Mark
+General_Category = Enclosing_Mark
+U+200C ZERO WIDTH NON-JOINER
+
+plus a few General_Category = Spacing_Mark needed for canonical equivalence.
+```
+*/
+bool ucg_is_gcb_extend_class(int32_t r) {
+ return ucg_is_grapheme_extend(r) || ucg_is_emoji_modifier(r);
+}
+
+// Return values:
+//
+// - 2 if East_Asian_Width=F or W, or
+// - 0 if non-printable / zero-width, or
+// - 1 in all other cases.
+//
+int ucg_normalized_east_asian_width(int32_t r) {
+ if (ucg_is_control(r)) {
+ return 0;
+ } else if (r <= 0x10FF) {
+ // Easy early out for low runes.
+ return 1;
+ }
+
+ switch (r) {
+ // This is a different interpretation of the BOM which occurs in the middle of text.
+ case 0xFEFF: /* ZERO_WIDTH_NO_BREAK_SPACE */
+ case ZERO_WIDTH_SPACE:
+ case ZERO_WIDTH_NON_JOINER:
+ case ZERO_WIDTH_JOINER:
+ case WORD_JOINER:
+ return 0;
+ }
+
+ intptr_t p = ucg_binary_search(r, ucg_normalized_east_asian_width_ranges, UCG_TABLE_LEN(ucg_normalized_east_asian_width_ranges)/3, 3);
+ if (p >= 0 && ucg_normalized_east_asian_width_ranges[p] <= r && r <= ucg_normalized_east_asian_width_ranges[p+1]) {
+ return (int)ucg_normalized_east_asian_width_ranges[p+2];
+ }
+ return 1;
+}
+
+//
+// End of Unicode 15.1.0 block.
+//
+
+enum grapheme_cluster_sequence {
+ None,
+ Indic,
+ Emoji,
+ Regional,
+};
+
+typedef struct {
+ ucg_grapheme* graphemes;
+ i32 rune_count;
+ i32 grapheme_count;
+ i32 width;
+
+ int32_t last_rune;
+ bool last_rune_breaks_forward;
+
+ i32 last_width;
+ i32 last_grapheme_count;
+
+ bool bypass_next_rune;
+
+ int regional_indicator_counter;
+
+ enum grapheme_cluster_sequence current_sequence;
+ bool continue_sequence;
+} ucg_decoder_state;
+
+
+void _ucg_decode_grapheme_clusters_deferred_step(
+ gbAllocator allocator,
+ ucg_decoder_state* state,
+ i32 byte_index,
+ int32_t this_rune
+) {
+ // "Break at the start and end of text, unless the text is empty."
+ //
+ // GB1: sot ÷ Any
+ // GB2: Any ÷ eot
+ if (state->rune_count == 0 && state->grapheme_count == 0) {
+ state->grapheme_count += 1;
+ }
+
+ if (state->grapheme_count > state->last_grapheme_count) {
+ state->width += ucg_normalized_east_asian_width(this_rune);
+
+ /* if (allocator != NULL) { */
+ state->graphemes = (ucg_grapheme*)gb_resize(allocator,
+ state->graphemes,
+ sizeof(ucg_grapheme) * (state->grapheme_count),
+ sizeof(ucg_grapheme) * (1 + state->grapheme_count));
+
+ ucg_grapheme append = {
+ byte_index,
+ state->rune_count,
+ state->width - state->last_width,
+ };
+
+ state->graphemes[state->grapheme_count - 1] = append;
+ /* } */
+
+ state->last_grapheme_count = state->grapheme_count;
+ state->last_width = state->width;
+ }
+
+ state->last_rune = this_rune;
+ state->rune_count += 1;
+
+ if (!state->continue_sequence) {
+ state->current_sequence = None;
+ state->regional_indicator_counter = 0;
+ }
+ state->continue_sequence = false;
+}
+
+int ucg_decode_grapheme_clusters(
+ gbAllocator allocator,
+ const uint8_t* str,
+ int str_len,
+
+ ucg_grapheme** out_graphemes,
+ i32* out_rune_count,
+ i32* out_grapheme_count,
+ i32* out_width
+) {
+ // The following procedure implements text segmentation by breaking on
+ // Grapheme Cluster Boundaries[1], using the values[2] and rules[3] from
+ // the Unicode® Standard Annex #29, entitled:
+ //
+ // UNICODE TEXT SEGMENTATION
+ //
+ // Version: Unicode 15.1.0
+ // Date: 2023-08-16
+ // Revision: 43
+ //
+ // This procedure is conformant[4] to UAX29-C1-1, otherwise known as the
+ // extended, non-legacy ruleset.
+ //
+ // Please see the references for more information.
+ //
+ //
+ // [1]: https://www.unicode.org/reports/tr29/#Grapheme_Cluster_Boundaries
+ // [2]: https://www.unicode.org/reports/tr29/#Default_Grapheme_Cluster_Table
+ // [3]: https://www.unicode.org/reports/tr29/#Grapheme_Cluster_Boundary_Rules
+ // [4]: https://www.unicode.org/reports/tr29/#Conformance
+
+ // Additionally, this procedure takes into account Standard Annex #11,
+ // in order to estimate how visually wide the string will appear on a
+ // monospaced display. This can only ever be a rough guess, as this tends
+ // to be an implementation detail relating to which fonts are being used,
+ // how codepoints are interpreted and drawn, if codepoint sequences are
+ // interpreted correctly, and et cetera.
+ //
+ // For example, a program may not properly interpret an emoji modifier
+ // sequence and print the component glyphs instead of one whole glyph.
+ //
+ // See here for more information: https://www.unicode.org/reports/tr11/
+ //
+ // NOTE: There is no explicit mention of what to do with zero-width spaces
+ // as far as grapheme cluster segmentation goes, therefore this
+ // implementation may count and return graphemes with a `width` of zero.
+ //
+ // Treat them as any other space.
+
+ ucg_decoder_state state = {0};
+
+#define UCG_DEFERRED_DECODE_STEP() (_ucg_decode_grapheme_clusters_deferred_step(allocator, &state, byte_index, this_rune))
+
+ for (i32 byte_index = 0, bytes_advanced = 0; byte_index < str_len; byte_index += bytes_advanced) {
+ int32_t this_rune = GB_RUNE_INVALID;
+ bytes_advanced = (i32)(utf8_decode(str+byte_index, str_len-byte_index, &this_rune));
+ if (this_rune == GB_RUNE_INVALID || bytes_advanced == 0) {
+ // There was a Unicode parsing error; bail out.
+ if (out_graphemes != NULL) { *out_graphemes = state.graphemes; }
+ if (out_rune_count != NULL) { *out_rune_count = state.rune_count; }
+ if (out_grapheme_count != NULL) { *out_grapheme_count = state.grapheme_count; }
+ if (out_width != NULL) { *out_width = state.width; }
+
+ // Return an error code.
+ return -1;
+ }
+
+ // "Do not break between a CR and LF. Otherwise, break before and after controls."
+ //
+ // GB3: CR × LF
+ // GB4: (Control | CR | LF) ÷
+ // GB5: ÷ (Control | CR | LF)
+ if (this_rune == '\n' && state.last_rune == '\r') {
+ state.last_rune_breaks_forward = false;
+ state.bypass_next_rune = false;
+ UCG_DEFERRED_DECODE_STEP(); continue;
+ }
+
+ if (ucg_is_control(this_rune)) {
+ state.grapheme_count += 1;
+ state.last_rune_breaks_forward = true;
+ state.bypass_next_rune = true;
+ UCG_DEFERRED_DECODE_STEP(); continue;
+ }
+
+ // (This check is for rules that work forwards, instead of backwards.)
+ if (state.bypass_next_rune) {
+ if (state.last_rune_breaks_forward) {
+ state.grapheme_count += 1;
+ state.last_rune_breaks_forward = false;
+ }
+
+ state.bypass_next_rune = false;
+ UCG_DEFERRED_DECODE_STEP(); continue;
+ }
+
+ // (Optimization 1: Prevent low runes from proceeding further.)
+ //
+ // * 0xA9 and 0xAE are in the Extended_Pictographic range,
+ // which is checked later in GB11.
+ if (this_rune != 0xA9 && this_rune != 0xAE && this_rune <= 0x2FF) {
+ state.grapheme_count += 1;
+ UCG_DEFERRED_DECODE_STEP(); continue;
+ }
+
+ // (Optimization 2: Check if the rune is in the Hangul space before getting specific.)
+ if (0x1100 <= this_rune && this_rune <= 0xD7FB) {
+ // "Do not break Hangul syllable sequences."
+ //
+ // GB6: L × (L | V | LV | LVT)
+ // GB7: (LV | V) × (V | T)
+ // GB8: (LVT | T) × T
+ if (ucg_is_hangul_syllable_leading(this_rune) ||
+ ucg_is_hangul_syllable_lv(this_rune) ||
+ ucg_is_hangul_syllable_lvt(this_rune))
+ {
+ if (!ucg_is_hangul_syllable_leading(state.last_rune)) {
+ state.grapheme_count += 1;
+ }
+ UCG_DEFERRED_DECODE_STEP(); continue;
+ }
+
+ if (ucg_is_hangul_syllable_vowel(this_rune)) {
+ if (ucg_is_hangul_syllable_leading(state.last_rune) ||
+ ucg_is_hangul_syllable_vowel(state.last_rune) ||
+ ucg_is_hangul_syllable_lv(state.last_rune))
+ {
+ UCG_DEFERRED_DECODE_STEP(); continue;
+ }
+ state.grapheme_count += 1;
+ UCG_DEFERRED_DECODE_STEP(); continue;
+ }
+
+ if (ucg_is_hangul_syllable_trailing(this_rune)) {
+ if (ucg_is_hangul_syllable_trailing(state.last_rune) ||
+ ucg_is_hangul_syllable_lvt(state.last_rune) ||
+ ucg_is_hangul_syllable_lv(state.last_rune) ||
+ ucg_is_hangul_syllable_vowel(state.last_rune))
+ {
+ UCG_DEFERRED_DECODE_STEP(); continue;
+ }
+ state.grapheme_count += 1;
+ UCG_DEFERRED_DECODE_STEP(); continue;
+ }
+ }
+
+ // "Do not break before extending characters or ZWJ."
+ //
+ // GB9: × (Extend | ZWJ)
+ if (this_rune == ZERO_WIDTH_JOINER) {
+ state.continue_sequence = true;
+ UCG_DEFERRED_DECODE_STEP(); continue;
+ }
+
+ if (ucg_is_gcb_extend_class(this_rune)) {
+ // (Support for GB9c.)
+ if (state.current_sequence == Indic) {
+ if (ucg_is_indic_conjunct_break_extend(this_rune) && (
+ ucg_is_indic_conjunct_break_linker(state.last_rune) ||
+ ucg_is_indic_conjunct_break_consonant(state.last_rune) ))
+ {
+ state.continue_sequence = true;
+ UCG_DEFERRED_DECODE_STEP(); continue;
+ }
+
+ if (ucg_is_indic_conjunct_break_linker(this_rune) && (
+ ucg_is_indic_conjunct_break_linker(state.last_rune) ||
+ ucg_is_indic_conjunct_break_extend(state.last_rune) ||
+ ucg_is_indic_conjunct_break_consonant(state.last_rune) ))
+ {
+ state.continue_sequence = true;
+ UCG_DEFERRED_DECODE_STEP(); continue;
+ }
+
+ UCG_DEFERRED_DECODE_STEP(); continue;
+ }
+
+ // (Support for GB11.)
+ if (state.current_sequence == Emoji && (
+ ucg_is_gcb_extend_class(state.last_rune) ||
+ ucg_is_emoji_extended_pictographic(state.last_rune) ))
+ {
+ state.continue_sequence = true;
+ }
+
+ UCG_DEFERRED_DECODE_STEP(); continue;
+ }
+
+ // _The GB9a and GB9b rules only apply to extended grapheme clusters:_
+ // "Do not break before SpacingMarks, or after Prepend characters."
+ //
+ // GB9a: × SpacingMark
+ // GB9b: Prepend ×
+ if (ucg_is_spacing_mark(this_rune)) {
+ UCG_DEFERRED_DECODE_STEP(); continue;
+ }
+
+ if (ucg_is_gcb_prepend_class(this_rune)) {
+ state.grapheme_count += 1;
+ state.bypass_next_rune = true;
+ UCG_DEFERRED_DECODE_STEP(); continue;
+ }
+
+ // _The GB9c rule only applies to extended grapheme clusters:_
+ // "Do not break within certain combinations with Indic_Conjunct_Break (InCB)=Linker."
+ //
+ // GB9c: \p{InCB=Consonant} [ \p{InCB=Extend} \p{InCB=Linker} ]* \p{InCB=Linker} [ \p{InCB=Extend} \p{InCB=Linker} ]* × \p{InCB=Consonant}
+ if (ucg_is_indic_conjunct_break_consonant(this_rune)) {
+ if (state.current_sequence == Indic) {
+ if (state.last_rune == ZERO_WIDTH_JOINER ||
+ ucg_is_indic_conjunct_break_linker(state.last_rune))
+ {
+ state.continue_sequence = true;
+ } else {
+ state.grapheme_count += 1;
+ }
+ } else {
+ state.grapheme_count += 1;
+ state.current_sequence = Indic;
+ state.continue_sequence = true;
+ }
+ UCG_DEFERRED_DECODE_STEP(); continue;
+ }
+
+ if (ucg_is_indic_conjunct_break_extend(this_rune)) {
+ if (state.current_sequence == Indic) {
+ if (ucg_is_indic_conjunct_break_consonant(state.last_rune) ||
+ ucg_is_indic_conjunct_break_linker(state.last_rune))
+ {
+ state.continue_sequence = true;
+ } else {
+ state.grapheme_count += 1;
+ }
+ }
+ UCG_DEFERRED_DECODE_STEP(); continue;
+ }
+
+ if (ucg_is_indic_conjunct_break_linker(this_rune)) {
+ if (state.current_sequence == Indic) {
+ if (ucg_is_indic_conjunct_break_extend(state.last_rune) ||
+ ucg_is_indic_conjunct_break_linker(state.last_rune))
+ {
+ state.continue_sequence = true;
+ } else {
+ state.grapheme_count += 1;
+ }
+ }
+ UCG_DEFERRED_DECODE_STEP(); continue;
+ }
+
+ //
+ // (Curiously, there is no GB10.)
+ //
+
+ // "Do not break within emoji modifier sequences or emoji zwj sequences."
+ //
+ // GB11: \p{Extended_Pictographic} Extend* ZWJ × \p{Extended_Pictographic}
+ if (ucg_is_emoji_extended_pictographic(this_rune)) {
+ if (state.current_sequence != Emoji || state.last_rune != ZERO_WIDTH_JOINER) {
+ state.grapheme_count += 1;
+ }
+ state.current_sequence = Emoji;
+ state.continue_sequence = true;
+ UCG_DEFERRED_DECODE_STEP(); continue;
+ }
+
+ // "Do not break within emoji flag sequences.
+ // That is, do not break between regional indicator (RI) symbols
+ // if there is an odd number of RI characters before the break point."
+ //
+ // GB12: sot (RI RI)* RI × RI
+ // GB13: [^RI] (RI RI)* RI × RI
+ if (ucg_is_regional_indicator(this_rune)) {
+ if ((state.regional_indicator_counter & 1) == 0) {
+ state.grapheme_count += 1;
+ }
+
+ state.current_sequence = Regional;
+ state.continue_sequence = true;
+ state.regional_indicator_counter += 1;
+
+ UCG_DEFERRED_DECODE_STEP(); continue;
+ }
+
+ // "Otherwise, break everywhere."
+ //
+ // GB999: Any ÷ Any
+ state.grapheme_count += 1;
+ UCG_DEFERRED_DECODE_STEP();
+ }
+
+#undef UCG_DEFERRED_DECODE_STEP
+
+ if (out_graphemes != NULL) { *out_graphemes = state.graphemes; }
+ if (out_rune_count != NULL) { *out_rune_count = state.rune_count; }
+ if (out_grapheme_count != NULL) { *out_grapheme_count = state.grapheme_count; }
+ if (out_width != NULL) { *out_width = state.width; }
+
+ return 0;
+}
+
+#undef UCG_TABLE_LEN
+#undef ZERO_WIDTH_SPACE
+#undef ZERO_WIDTH_NON_JOINER
+#undef ZERO_WIDTH_JOINER
+#undef WORD_JOINER
diff --git a/src/ucg/ucg_tables.h b/src/ucg/ucg_tables.h
new file mode 100644
index 000000000..a33f9f898
--- /dev/null
+++ b/src/ucg/ucg_tables.h
@@ -0,0 +1,2629 @@
+/*
+ * SPDX-FileCopyrightText: (c) 2024 Feoramund
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+#ifndef _UCG_TABLES_INCLUDED
+#define _UCG_TABLES_INCLUDED
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+
+//
+// The tables below are accurate as of Unicode 15.1.0.
+//
+
+static const int32_t ucg_spacing_mark_ranges[] = {
+ 0x0903, 0x0903,
+ 0x093B, 0x093B,
+ 0x093E, 0x0940,
+ 0x0949, 0x094C,
+ 0x094E, 0x094F,
+ 0x0982, 0x0983,
+ 0x09BE, 0x09C0,
+ 0x09C7, 0x09C8,
+ 0x09CB, 0x09CC,
+ 0x09D7, 0x09D7,
+ 0x0A03, 0x0A03,
+ 0x0A3E, 0x0A40,
+ 0x0A83, 0x0A83,
+ 0x0ABE, 0x0AC0,
+ 0x0AC9, 0x0AC9,
+ 0x0ACB, 0x0ACC,
+ 0x0B02, 0x0B03,
+ 0x0B3E, 0x0B3E,
+ 0x0B40, 0x0B40,
+ 0x0B47, 0x0B48,
+ 0x0B4B, 0x0B4C,
+ 0x0B57, 0x0B57,
+ 0x0BBE, 0x0BBF,
+ 0x0BC1, 0x0BC2,
+ 0x0BC6, 0x0BC8,
+ 0x0BCA, 0x0BCC,
+ 0x0BD7, 0x0BD7,
+ 0x0C01, 0x0C03,
+ 0x0C41, 0x0C44,
+ 0x0C82, 0x0C83,
+ 0x0CBE, 0x0CBE,
+ 0x0CC0, 0x0CC4,
+ 0x0CC7, 0x0CC8,
+ 0x0CCA, 0x0CCB,
+ 0x0CD5, 0x0CD6,
+ 0x0CF3, 0x0CF3,
+ 0x0D02, 0x0D03,
+ 0x0D3E, 0x0D40,
+ 0x0D46, 0x0D48,
+ 0x0D4A, 0x0D4C,
+ 0x0D57, 0x0D57,
+ 0x0D82, 0x0D83,
+ 0x0DCF, 0x0DD1,
+ 0x0DD8, 0x0DDF,
+ 0x0DF2, 0x0DF3,
+ 0x0F3E, 0x0F3F,
+ 0x0F7F, 0x0F7F,
+ 0x102B, 0x102C,
+ 0x1031, 0x1031,
+ 0x1038, 0x1038,
+ 0x103B, 0x103C,
+ 0x1056, 0x1057,
+ 0x1062, 0x1064,
+ 0x1067, 0x106D,
+ 0x1083, 0x1084,
+ 0x1087, 0x108C,
+ 0x108F, 0x108F,
+ 0x109A, 0x109C,
+ 0x1715, 0x1715,
+ 0x1734, 0x1734,
+ 0x17B6, 0x17B6,
+ 0x17BE, 0x17C5,
+ 0x17C7, 0x17C8,
+ 0x1923, 0x1926,
+ 0x1929, 0x192B,
+ 0x1930, 0x1931,
+ 0x1933, 0x1938,
+ 0x1A19, 0x1A1A,
+ 0x1A55, 0x1A55,
+ 0x1A57, 0x1A57,
+ 0x1A61, 0x1A61,
+ 0x1A63, 0x1A64,
+ 0x1A6D, 0x1A72,
+ 0x1B04, 0x1B04,
+ 0x1B35, 0x1B35,
+ 0x1B3B, 0x1B3B,
+ 0x1B3D, 0x1B41,
+ 0x1B43, 0x1B44,
+ 0x1B82, 0x1B82,
+ 0x1BA1, 0x1BA1,
+ 0x1BA6, 0x1BA7,
+ 0x1BAA, 0x1BAA,
+ 0x1BE7, 0x1BE7,
+ 0x1BEA, 0x1BEC,
+ 0x1BEE, 0x1BEE,
+ 0x1BF2, 0x1BF3,
+ 0x1C24, 0x1C2B,
+ 0x1C34, 0x1C35,
+ 0x1CE1, 0x1CE1,
+ 0x1CF7, 0x1CF7,
+ 0x302E, 0x302F,
+ 0xA823, 0xA824,
+ 0xA827, 0xA827,
+ 0xA880, 0xA881,
+ 0xA8B4, 0xA8C3,
+ 0xA952, 0xA953,
+ 0xA983, 0xA983,
+ 0xA9B4, 0xA9B5,
+ 0xA9BA, 0xA9BB,
+ 0xA9BE, 0xA9C0,
+ 0xAA2F, 0xAA30,
+ 0xAA33, 0xAA34,
+ 0xAA4D, 0xAA4D,
+ 0xAA7B, 0xAA7B,
+ 0xAA7D, 0xAA7D,
+ 0xAAEB, 0xAAEB,
+ 0xAAEE, 0xAAEF,
+ 0xAAF5, 0xAAF5,
+ 0xABE3, 0xABE4,
+ 0xABE6, 0xABE7,
+ 0xABE9, 0xABEA,
+ 0xABEC, 0xABEC,
+ 0x11000, 0x11000,
+ 0x11002, 0x11002,
+ 0x11082, 0x11082,
+ 0x110B0, 0x110B2,
+ 0x110B7, 0x110B8,
+ 0x1112C, 0x1112C,
+ 0x11145, 0x11146,
+ 0x11182, 0x11182,
+ 0x111B3, 0x111B5,
+ 0x111BF, 0x111C0,
+ 0x111CE, 0x111CE,
+ 0x1122C, 0x1122E,
+ 0x11232, 0x11233,
+ 0x11235, 0x11235,
+ 0x112E0, 0x112E2,
+ 0x11302, 0x11303,
+ 0x1133E, 0x1133F,
+ 0x11341, 0x11344,
+ 0x11347, 0x11348,
+ 0x1134B, 0x1134D,
+ 0x11357, 0x11357,
+ 0x11362, 0x11363,
+ 0x11435, 0x11437,
+ 0x11440, 0x11441,
+ 0x11445, 0x11445,
+ 0x114B0, 0x114B2,
+ 0x114B9, 0x114B9,
+ 0x114BB, 0x114BE,
+ 0x114C1, 0x114C1,
+ 0x115AF, 0x115B1,
+ 0x115B8, 0x115BB,
+ 0x115BE, 0x115BE,
+ 0x11630, 0x11632,
+ 0x1163B, 0x1163C,
+ 0x1163E, 0x1163E,
+ 0x116AC, 0x116AC,
+ 0x116AE, 0x116AF,
+ 0x116B6, 0x116B6,
+ 0x11720, 0x11721,
+ 0x11726, 0x11726,
+ 0x1182C, 0x1182E,
+ 0x11838, 0x11838,
+ 0x11930, 0x11935,
+ 0x11937, 0x11938,
+ 0x1193D, 0x1193D,
+ 0x11940, 0x11940,
+ 0x11942, 0x11942,
+ 0x119D1, 0x119D3,
+ 0x119DC, 0x119DF,
+ 0x119E4, 0x119E4,
+ 0x11A39, 0x11A39,
+ 0x11A57, 0x11A58,
+ 0x11A97, 0x11A97,
+ 0x11C2F, 0x11C2F,
+ 0x11C3E, 0x11C3E,
+ 0x11CA9, 0x11CA9,
+ 0x11CB1, 0x11CB1,
+ 0x11CB4, 0x11CB4,
+ 0x11D8A, 0x11D8E,
+ 0x11D93, 0x11D94,
+ 0x11D96, 0x11D96,
+ 0x11EF5, 0x11EF6,
+ 0x11F03, 0x11F03,
+ 0x11F34, 0x11F35,
+ 0x11F3E, 0x11F3F,
+ 0x11F41, 0x11F41,
+ 0x16F51, 0x16F87,
+ 0x16FF0, 0x16FF1,
+ 0x1D165, 0x1D166,
+ 0x1D16D, 0x1D172,
+};
+
+static const int32_t ucg_nonspacing_mark_ranges[] = {
+ 0x0300, 0x036F,
+ 0x0483, 0x0487,
+ 0x0591, 0x05BD,
+ 0x05BF, 0x05BF,
+ 0x05C1, 0x05C2,
+ 0x05C4, 0x05C5,
+ 0x05C7, 0x05C7,
+ 0x0610, 0x061A,
+ 0x064B, 0x065F,
+ 0x0670, 0x0670,
+ 0x06D6, 0x06DC,
+ 0x06DF, 0x06E4,
+ 0x06E7, 0x06E8,
+ 0x06EA, 0x06ED,
+ 0x0711, 0x0711,
+ 0x0730, 0x074A,
+ 0x07A6, 0x07B0,
+ 0x07EB, 0x07F3,
+ 0x07FD, 0x07FD,
+ 0x0816, 0x0819,
+ 0x081B, 0x0823,
+ 0x0825, 0x0827,
+ 0x0829, 0x082D,
+ 0x0859, 0x085B,
+ 0x0898, 0x089F,
+ 0x08CA, 0x08E1,
+ 0x08E3, 0x0902,
+ 0x093A, 0x093A,
+ 0x093C, 0x093C,
+ 0x0941, 0x0948,
+ 0x094D, 0x094D,
+ 0x0951, 0x0957,
+ 0x0962, 0x0963,
+ 0x0981, 0x0981,
+ 0x09BC, 0x09BC,
+ 0x09C1, 0x09C4,
+ 0x09CD, 0x09CD,
+ 0x09E2, 0x09E3,
+ 0x09FE, 0x09FE,
+ 0x0A01, 0x0A02,
+ 0x0A3C, 0x0A3C,
+ 0x0A41, 0x0A42,
+ 0x0A47, 0x0A48,
+ 0x0A4B, 0x0A4D,
+ 0x0A51, 0x0A51,
+ 0x0A70, 0x0A71,
+ 0x0A75, 0x0A75,
+ 0x0A81, 0x0A82,
+ 0x0ABC, 0x0ABC,
+ 0x0AC1, 0x0AC5,
+ 0x0AC7, 0x0AC8,
+ 0x0ACD, 0x0ACD,
+ 0x0AE2, 0x0AE3,
+ 0x0AFA, 0x0AFF,
+ 0x0B01, 0x0B01,
+ 0x0B3C, 0x0B3C,
+ 0x0B3F, 0x0B3F,
+ 0x0B41, 0x0B44,
+ 0x0B4D, 0x0B4D,
+ 0x0B55, 0x0B56,
+ 0x0B62, 0x0B63,
+ 0x0B82, 0x0B82,
+ 0x0BC0, 0x0BC0,
+ 0x0BCD, 0x0BCD,
+ 0x0C00, 0x0C00,
+ 0x0C04, 0x0C04,
+ 0x0C3C, 0x0C3C,
+ 0x0C3E, 0x0C40,
+ 0x0C46, 0x0C48,
+ 0x0C4A, 0x0C4D,
+ 0x0C55, 0x0C56,
+ 0x0C62, 0x0C63,
+ 0x0C81, 0x0C81,
+ 0x0CBC, 0x0CBC,
+ 0x0CBF, 0x0CBF,
+ 0x0CC6, 0x0CC6,
+ 0x0CCC, 0x0CCD,
+ 0x0CE2, 0x0CE3,
+ 0x0D00, 0x0D01,
+ 0x0D3B, 0x0D3C,
+ 0x0D41, 0x0D44,
+ 0x0D4D, 0x0D4D,
+ 0x0D62, 0x0D63,
+ 0x0D81, 0x0D81,
+ 0x0DCA, 0x0DCA,
+ 0x0DD2, 0x0DD4,
+ 0x0DD6, 0x0DD6,
+ 0x0E31, 0x0E31,
+ 0x0E34, 0x0E3A,
+ 0x0E47, 0x0E4E,
+ 0x0EB1, 0x0EB1,
+ 0x0EB4, 0x0EBC,
+ 0x0EC8, 0x0ECE,
+ 0x0F18, 0x0F19,
+ 0x0F35, 0x0F35,
+ 0x0F37, 0x0F37,
+ 0x0F39, 0x0F39,
+ 0x0F71, 0x0F7E,
+ 0x0F80, 0x0F84,
+ 0x0F86, 0x0F87,
+ 0x0F8D, 0x0F97,
+ 0x0F99, 0x0FBC,
+ 0x0FC6, 0x0FC6,
+ 0x102D, 0x1030,
+ 0x1032, 0x1037,
+ 0x1039, 0x103A,
+ 0x103D, 0x103E,
+ 0x1058, 0x1059,
+ 0x105E, 0x1060,
+ 0x1071, 0x1074,
+ 0x1082, 0x1082,
+ 0x1085, 0x1086,
+ 0x108D, 0x108D,
+ 0x109D, 0x109D,
+ 0x135D, 0x135F,
+ 0x1712, 0x1714,
+ 0x1732, 0x1733,
+ 0x1752, 0x1753,
+ 0x1772, 0x1773,
+ 0x17B4, 0x17B5,
+ 0x17B7, 0x17BD,
+ 0x17C6, 0x17C6,
+ 0x17C9, 0x17D3,
+ 0x17DD, 0x17DD,
+ 0x180B, 0x180D,
+ 0x180F, 0x180F,
+ 0x1885, 0x1886,
+ 0x18A9, 0x18A9,
+ 0x1920, 0x1922,
+ 0x1927, 0x1928,
+ 0x1932, 0x1932,
+ 0x1939, 0x193B,
+ 0x1A17, 0x1A18,
+ 0x1A1B, 0x1A1B,
+ 0x1A56, 0x1A56,
+ 0x1A58, 0x1A5E,
+ 0x1A60, 0x1A60,
+ 0x1A62, 0x1A62,
+ 0x1A65, 0x1A6C,
+ 0x1A73, 0x1A7C,
+ 0x1A7F, 0x1A7F,
+ 0x1AB0, 0x1ABD,
+ 0x1ABF, 0x1ACE,
+ 0x1B00, 0x1B03,
+ 0x1B34, 0x1B34,
+ 0x1B36, 0x1B3A,
+ 0x1B3C, 0x1B3C,
+ 0x1B42, 0x1B42,
+ 0x1B6B, 0x1B73,
+ 0x1B80, 0x1B81,
+ 0x1BA2, 0x1BA5,
+ 0x1BA8, 0x1BA9,
+ 0x1BAB, 0x1BAD,
+ 0x1BE6, 0x1BE6,
+ 0x1BE8, 0x1BE9,
+ 0x1BED, 0x1BED,
+ 0x1BEF, 0x1BF1,
+ 0x1C2C, 0x1C33,
+ 0x1C36, 0x1C37,
+ 0x1CD0, 0x1CD2,
+ 0x1CD4, 0x1CE0,
+ 0x1CE2, 0x1CE8,
+ 0x1CED, 0x1CED,
+ 0x1CF4, 0x1CF4,
+ 0x1CF8, 0x1CF9,
+ 0x1DC0, 0x1DFF,
+ 0x20D0, 0x20DC,
+ 0x20E1, 0x20E1,
+ 0x20E5, 0x20F0,
+ 0x2CEF, 0x2CF1,
+ 0x2D7F, 0x2D7F,
+ 0x2DE0, 0x2DFF,
+ 0x302A, 0x302D,
+ 0x3099, 0x309A,
+ 0xA66F, 0xA66F,
+ 0xA674, 0xA67D,
+ 0xA69E, 0xA69F,
+ 0xA6F0, 0xA6F1,
+ 0xA802, 0xA802,
+ 0xA806, 0xA806,
+ 0xA80B, 0xA80B,
+ 0xA825, 0xA826,
+ 0xA82C, 0xA82C,
+ 0xA8C4, 0xA8C5,
+ 0xA8E0, 0xA8F1,
+ 0xA8FF, 0xA8FF,
+ 0xA926, 0xA92D,
+ 0xA947, 0xA951,
+ 0xA980, 0xA982,
+ 0xA9B3, 0xA9B3,
+ 0xA9B6, 0xA9B9,
+ 0xA9BC, 0xA9BD,
+ 0xA9E5, 0xA9E5,
+ 0xAA29, 0xAA2E,
+ 0xAA31, 0xAA32,
+ 0xAA35, 0xAA36,
+ 0xAA43, 0xAA43,
+ 0xAA4C, 0xAA4C,
+ 0xAA7C, 0xAA7C,
+ 0xAAB0, 0xAAB0,
+ 0xAAB2, 0xAAB4,
+ 0xAAB7, 0xAAB8,
+ 0xAABE, 0xAABF,
+ 0xAAC1, 0xAAC1,
+ 0xAAEC, 0xAAED,
+ 0xAAF6, 0xAAF6,
+ 0xABE5, 0xABE5,
+ 0xABE8, 0xABE8,
+ 0xABED, 0xABED,
+ 0xFB1E, 0xFB1E,
+ 0xFE00, 0xFE0F,
+ 0xFE20, 0xFE2F,
+ 0x101FD, 0x101FD,
+ 0x102E0, 0x102E0,
+ 0x10376, 0x1037A,
+ 0x10A01, 0x10A03,
+ 0x10A05, 0x10A06,
+ 0x10A0C, 0x10A0F,
+ 0x10A38, 0x10A3A,
+ 0x10A3F, 0x10A3F,
+ 0x10AE5, 0x10AE6,
+ 0x10D24, 0x10D27,
+ 0x10EAB, 0x10EAC,
+ 0x10EFD, 0x10EFF,
+ 0x10F46, 0x10F50,
+ 0x10F82, 0x10F85,
+ 0x11001, 0x11001,
+ 0x11038, 0x11046,
+ 0x11070, 0x11070,
+ 0x11073, 0x11074,
+ 0x1107F, 0x11081,
+ 0x110B3, 0x110B6,
+ 0x110B9, 0x110BA,
+ 0x110C2, 0x110C2,
+ 0x11100, 0x11102,
+ 0x11127, 0x1112B,
+ 0x1112D, 0x11134,
+ 0x11173, 0x11173,
+ 0x11180, 0x11181,
+ 0x111B6, 0x111BE,
+ 0x111C9, 0x111CC,
+ 0x111CF, 0x111CF,
+ 0x1122F, 0x11231,
+ 0x11234, 0x11234,
+ 0x11236, 0x11237,
+ 0x1123E, 0x1123E,
+ 0x11241, 0x11241,
+ 0x112DF, 0x112DF,
+ 0x112E3, 0x112EA,
+ 0x11300, 0x11301,
+ 0x1133B, 0x1133C,
+ 0x11340, 0x11340,
+ 0x11366, 0x1136C,
+ 0x11370, 0x11374,
+ 0x11438, 0x1143F,
+ 0x11442, 0x11444,
+ 0x11446, 0x11446,
+ 0x1145E, 0x1145E,
+ 0x114B3, 0x114B8,
+ 0x114BA, 0x114BA,
+ 0x114BF, 0x114C0,
+ 0x114C2, 0x114C3,
+ 0x115B2, 0x115B5,
+ 0x115BC, 0x115BD,
+ 0x115BF, 0x115C0,
+ 0x115DC, 0x115DD,
+ 0x11633, 0x1163A,
+ 0x1163D, 0x1163D,
+ 0x1163F, 0x11640,
+ 0x116AB, 0x116AB,
+ 0x116AD, 0x116AD,
+ 0x116B0, 0x116B5,
+ 0x116B7, 0x116B7,
+ 0x1171D, 0x1171F,
+ 0x11722, 0x11725,
+ 0x11727, 0x1172B,
+ 0x1182F, 0x11837,
+ 0x11839, 0x1183A,
+ 0x1193B, 0x1193C,
+ 0x1193E, 0x1193E,
+ 0x11943, 0x11943,
+ 0x119D4, 0x119D7,
+ 0x119DA, 0x119DB,
+ 0x119E0, 0x119E0,
+ 0x11A01, 0x11A0A,
+ 0x11A33, 0x11A38,
+ 0x11A3B, 0x11A3E,
+ 0x11A47, 0x11A47,
+ 0x11A51, 0x11A56,
+ 0x11A59, 0x11A5B,
+ 0x11A8A, 0x11A96,
+ 0x11A98, 0x11A99,
+ 0x11C30, 0x11C36,
+ 0x11C38, 0x11C3D,
+ 0x11C3F, 0x11C3F,
+ 0x11C92, 0x11CA7,
+ 0x11CAA, 0x11CB0,
+ 0x11CB2, 0x11CB3,
+ 0x11CB5, 0x11CB6,
+ 0x11D31, 0x11D36,
+ 0x11D3A, 0x11D3A,
+ 0x11D3C, 0x11D3D,
+ 0x11D3F, 0x11D45,
+ 0x11D47, 0x11D47,
+ 0x11D90, 0x11D91,
+ 0x11D95, 0x11D95,
+ 0x11D97, 0x11D97,
+ 0x11EF3, 0x11EF4,
+ 0x11F00, 0x11F01,
+ 0x11F36, 0x11F3A,
+ 0x11F40, 0x11F40,
+ 0x11F42, 0x11F42,
+ 0x13440, 0x13440,
+ 0x13447, 0x13455,
+ 0x16AF0, 0x16AF4,
+ 0x16B30, 0x16B36,
+ 0x16F4F, 0x16F4F,
+ 0x16F8F, 0x16F92,
+ 0x16FE4, 0x16FE4,
+ 0x1BC9D, 0x1BC9E,
+ 0x1CF00, 0x1CF2D,
+ 0x1CF30, 0x1CF46,
+ 0x1D167, 0x1D169,
+ 0x1D17B, 0x1D182,
+ 0x1D185, 0x1D18B,
+ 0x1D1AA, 0x1D1AD,
+ 0x1D242, 0x1D244,
+ 0x1DA00, 0x1DA36,
+ 0x1DA3B, 0x1DA6C,
+ 0x1DA75, 0x1DA75,
+ 0x1DA84, 0x1DA84,
+ 0x1DA9B, 0x1DA9F,
+ 0x1DAA1, 0x1DAAF,
+ 0x1E000, 0x1E006,
+ 0x1E008, 0x1E018,
+ 0x1E01B, 0x1E021,
+ 0x1E023, 0x1E024,
+ 0x1E026, 0x1E02A,
+ 0x1E08F, 0x1E08F,
+ 0x1E130, 0x1E136,
+ 0x1E2AE, 0x1E2AE,
+ 0x1E2EC, 0x1E2EF,
+ 0x1E4EC, 0x1E4EF,
+ 0x1E8D0, 0x1E8D6,
+ 0x1E944, 0x1E94A,
+ 0xE0100, 0xE01EF,
+};
+
+static const int32_t ucg_emoji_extended_pictographic_ranges[] = {
+ 0x00A9, 0x00A9,
+ 0x00AE, 0x00AE,
+ 0x203C, 0x203C,
+ 0x2049, 0x2049,
+ 0x2122, 0x2122,
+ 0x2139, 0x2139,
+ 0x2194, 0x2199,
+ 0x21A9, 0x21AA,
+ 0x231A, 0x231B,
+ 0x2328, 0x2328,
+ 0x2388, 0x2388,
+ 0x23CF, 0x23CF,
+ 0x23E9, 0x23EC,
+ 0x23ED, 0x23EE,
+ 0x23EF, 0x23EF,
+ 0x23F0, 0x23F0,
+ 0x23F1, 0x23F2,
+ 0x23F3, 0x23F3,
+ 0x23F8, 0x23FA,
+ 0x24C2, 0x24C2,
+ 0x25AA, 0x25AB,
+ 0x25B6, 0x25B6,
+ 0x25C0, 0x25C0,
+ 0x25FB, 0x25FE,
+ 0x2600, 0x2601,
+ 0x2602, 0x2603,
+ 0x2604, 0x2604,
+ 0x2605, 0x2605,
+ 0x2607, 0x260D,
+ 0x260E, 0x260E,
+ 0x260F, 0x2610,
+ 0x2611, 0x2611,
+ 0x2612, 0x2612,
+ 0x2614, 0x2615,
+ 0x2616, 0x2617,
+ 0x2618, 0x2618,
+ 0x2619, 0x261C,
+ 0x261D, 0x261D,
+ 0x261E, 0x261F,
+ 0x2620, 0x2620,
+ 0x2621, 0x2621,
+ 0x2622, 0x2623,
+ 0x2624, 0x2625,
+ 0x2626, 0x2626,
+ 0x2627, 0x2629,
+ 0x262A, 0x262A,
+ 0x262B, 0x262D,
+ 0x262E, 0x262E,
+ 0x262F, 0x262F,
+ 0x2630, 0x2637,
+ 0x2638, 0x2639,
+ 0x263A, 0x263A,
+ 0x263B, 0x263F,
+ 0x2640, 0x2640,
+ 0x2641, 0x2641,
+ 0x2642, 0x2642,
+ 0x2643, 0x2647,
+ 0x2648, 0x2653,
+ 0x2654, 0x265E,
+ 0x265F, 0x265F,
+ 0x2660, 0x2660,
+ 0x2661, 0x2662,
+ 0x2663, 0x2663,
+ 0x2664, 0x2664,
+ 0x2665, 0x2666,
+ 0x2667, 0x2667,
+ 0x2668, 0x2668,
+ 0x2669, 0x267A,
+ 0x267B, 0x267B,
+ 0x267C, 0x267D,
+ 0x267E, 0x267E,
+ 0x267F, 0x267F,
+ 0x2680, 0x2685,
+ 0x2690, 0x2691,
+ 0x2692, 0x2692,
+ 0x2693, 0x2693,
+ 0x2694, 0x2694,
+ 0x2695, 0x2695,
+ 0x2696, 0x2697,
+ 0x2698, 0x2698,
+ 0x2699, 0x2699,
+ 0x269A, 0x269A,
+ 0x269B, 0x269C,
+ 0x269D, 0x269F,
+ 0x26A0, 0x26A1,
+ 0x26A2, 0x26A6,
+ 0x26A7, 0x26A7,
+ 0x26A8, 0x26A9,
+ 0x26AA, 0x26AB,
+ 0x26AC, 0x26AF,
+ 0x26B0, 0x26B1,
+ 0x26B2, 0x26BC,
+ 0x26BD, 0x26BE,
+ 0x26BF, 0x26C3,
+ 0x26C4, 0x26C5,
+ 0x26C6, 0x26C7,
+ 0x26C8, 0x26C8,
+ 0x26C9, 0x26CD,
+ 0x26CE, 0x26CE,
+ 0x26CF, 0x26CF,
+ 0x26D0, 0x26D0,
+ 0x26D1, 0x26D1,
+ 0x26D2, 0x26D2,
+ 0x26D3, 0x26D3,
+ 0x26D4, 0x26D4,
+ 0x26D5, 0x26E8,
+ 0x26E9, 0x26E9,
+ 0x26EA, 0x26EA,
+ 0x26EB, 0x26EF,
+ 0x26F0, 0x26F1,
+ 0x26F2, 0x26F3,
+ 0x26F4, 0x26F4,
+ 0x26F5, 0x26F5,
+ 0x26F6, 0x26F6,
+ 0x26F7, 0x26F9,
+ 0x26FA, 0x26FA,
+ 0x26FB, 0x26FC,
+ 0x26FD, 0x26FD,
+ 0x26FE, 0x2701,
+ 0x2702, 0x2702,
+ 0x2703, 0x2704,
+ 0x2705, 0x2705,
+ 0x2708, 0x270C,
+ 0x270D, 0x270D,
+ 0x270E, 0x270E,
+ 0x270F, 0x270F,
+ 0x2710, 0x2711,
+ 0x2712, 0x2712,
+ 0x2714, 0x2714,
+ 0x2716, 0x2716,
+ 0x271D, 0x271D,
+ 0x2721, 0x2721,
+ 0x2728, 0x2728,
+ 0x2733, 0x2734,
+ 0x2744, 0x2744,
+ 0x2747, 0x2747,
+ 0x274C, 0x274C,
+ 0x274E, 0x274E,
+ 0x2753, 0x2755,
+ 0x2757, 0x2757,
+ 0x2763, 0x2763,
+ 0x2764, 0x2764,
+ 0x2765, 0x2767,
+ 0x2795, 0x2797,
+ 0x27A1, 0x27A1,
+ 0x27B0, 0x27B0,
+ 0x27BF, 0x27BF,
+ 0x2934, 0x2935,
+ 0x2B05, 0x2B07,
+ 0x2B1B, 0x2B1C,
+ 0x2B50, 0x2B50,
+ 0x2B55, 0x2B55,
+ 0x3030, 0x3030,
+ 0x303D, 0x303D,
+ 0x3297, 0x3297,
+ 0x3299, 0x3299,
+ 0x1F000, 0x1F003,
+ 0x1F004, 0x1F004,
+ 0x1F005, 0x1F0CE,
+ 0x1F0CF, 0x1F0CF,
+ 0x1F0D0, 0x1F0FF,
+ 0x1F10D, 0x1F10F,
+ 0x1F12F, 0x1F12F,
+ 0x1F16C, 0x1F16F,
+ 0x1F170, 0x1F171,
+ 0x1F17E, 0x1F17F,
+ 0x1F18E, 0x1F18E,
+ 0x1F191, 0x1F19A,
+ 0x1F1AD, 0x1F1E5,
+ 0x1F201, 0x1F202,
+ 0x1F203, 0x1F20F,
+ 0x1F21A, 0x1F21A,
+ 0x1F22F, 0x1F22F,
+ 0x1F232, 0x1F23A,
+ 0x1F23C, 0x1F23F,
+ 0x1F249, 0x1F24F,
+ 0x1F250, 0x1F251,
+ 0x1F252, 0x1F2FF,
+ 0x1F300, 0x1F30C,
+ 0x1F30D, 0x1F30E,
+ 0x1F30F, 0x1F30F,
+ 0x1F310, 0x1F310,
+ 0x1F311, 0x1F311,
+ 0x1F312, 0x1F312,
+ 0x1F313, 0x1F315,
+ 0x1F316, 0x1F318,
+ 0x1F319, 0x1F319,
+ 0x1F31A, 0x1F31A,
+ 0x1F31B, 0x1F31B,
+ 0x1F31C, 0x1F31C,
+ 0x1F31D, 0x1F31E,
+ 0x1F31F, 0x1F320,
+ 0x1F321, 0x1F321,
+ 0x1F322, 0x1F323,
+ 0x1F324, 0x1F32C,
+ 0x1F32D, 0x1F32F,
+ 0x1F330, 0x1F331,
+ 0x1F332, 0x1F333,
+ 0x1F334, 0x1F335,
+ 0x1F336, 0x1F336,
+ 0x1F337, 0x1F34A,
+ 0x1F34B, 0x1F34B,
+ 0x1F34C, 0x1F34F,
+ 0x1F350, 0x1F350,
+ 0x1F351, 0x1F37B,
+ 0x1F37C, 0x1F37C,
+ 0x1F37D, 0x1F37D,
+ 0x1F37E, 0x1F37F,
+ 0x1F380, 0x1F393,
+ 0x1F394, 0x1F395,
+ 0x1F396, 0x1F397,
+ 0x1F398, 0x1F398,
+ 0x1F399, 0x1F39B,
+ 0x1F39C, 0x1F39D,
+ 0x1F39E, 0x1F39F,
+ 0x1F3A0, 0x1F3C4,
+ 0x1F3C5, 0x1F3C5,
+ 0x1F3C6, 0x1F3C6,
+ 0x1F3C7, 0x1F3C7,
+ 0x1F3C8, 0x1F3C8,
+ 0x1F3C9, 0x1F3C9,
+ 0x1F3CA, 0x1F3CA,
+ 0x1F3CB, 0x1F3CE,
+ 0x1F3CF, 0x1F3D3,
+ 0x1F3D4, 0x1F3DF,
+ 0x1F3E0, 0x1F3E3,
+ 0x1F3E4, 0x1F3E4,
+ 0x1F3E5, 0x1F3F0,
+ 0x1F3F1, 0x1F3F2,
+ 0x1F3F3, 0x1F3F3,
+ 0x1F3F4, 0x1F3F4,
+ 0x1F3F5, 0x1F3F5,
+ 0x1F3F6, 0x1F3F6,
+ 0x1F3F7, 0x1F3F7,
+ 0x1F3F8, 0x1F3FA,
+ 0x1F400, 0x1F407,
+ 0x1F408, 0x1F408,
+ 0x1F409, 0x1F40B,
+ 0x1F40C, 0x1F40E,
+ 0x1F40F, 0x1F410,
+ 0x1F411, 0x1F412,
+ 0x1F413, 0x1F413,
+ 0x1F414, 0x1F414,
+ 0x1F415, 0x1F415,
+ 0x1F416, 0x1F416,
+ 0x1F417, 0x1F429,
+ 0x1F42A, 0x1F42A,
+ 0x1F42B, 0x1F43E,
+ 0x1F43F, 0x1F43F,
+ 0x1F440, 0x1F440,
+ 0x1F441, 0x1F441,
+ 0x1F442, 0x1F464,
+ 0x1F465, 0x1F465,
+ 0x1F466, 0x1F46B,
+ 0x1F46C, 0x1F46D,
+ 0x1F46E, 0x1F4AC,
+ 0x1F4AD, 0x1F4AD,
+ 0x1F4AE, 0x1F4B5,
+ 0x1F4B6, 0x1F4B7,
+ 0x1F4B8, 0x1F4EB,
+ 0x1F4EC, 0x1F4ED,
+ 0x1F4EE, 0x1F4EE,
+ 0x1F4EF, 0x1F4EF,
+ 0x1F4F0, 0x1F4F4,
+ 0x1F4F5, 0x1F4F5,
+ 0x1F4F6, 0x1F4F7,
+ 0x1F4F8, 0x1F4F8,
+ 0x1F4F9, 0x1F4FC,
+ 0x1F4FD, 0x1F4FD,
+ 0x1F4FE, 0x1F4FE,
+ 0x1F4FF, 0x1F502,
+ 0x1F503, 0x1F503,
+ 0x1F504, 0x1F507,
+ 0x1F508, 0x1F508,
+ 0x1F509, 0x1F509,
+ 0x1F50A, 0x1F514,
+ 0x1F515, 0x1F515,
+ 0x1F516, 0x1F52B,
+ 0x1F52C, 0x1F52D,
+ 0x1F52E, 0x1F53D,
+ 0x1F546, 0x1F548,
+ 0x1F549, 0x1F54A,
+ 0x1F54B, 0x1F54E,
+ 0x1F54F, 0x1F54F,
+ 0x1F550, 0x1F55B,
+ 0x1F55C, 0x1F567,
+ 0x1F568, 0x1F56E,
+ 0x1F56F, 0x1F570,
+ 0x1F571, 0x1F572,
+ 0x1F573, 0x1F579,
+ 0x1F57A, 0x1F57A,
+ 0x1F57B, 0x1F586,
+ 0x1F587, 0x1F587,
+ 0x1F588, 0x1F589,
+ 0x1F58A, 0x1F58D,
+ 0x1F58E, 0x1F58F,
+ 0x1F590, 0x1F590,
+ 0x1F591, 0x1F594,
+ 0x1F595, 0x1F596,
+ 0x1F597, 0x1F5A3,
+ 0x1F5A4, 0x1F5A4,
+ 0x1F5A5, 0x1F5A5,
+ 0x1F5A6, 0x1F5A7,
+ 0x1F5A8, 0x1F5A8,
+ 0x1F5A9, 0x1F5B0,
+ 0x1F5B1, 0x1F5B2,
+ 0x1F5B3, 0x1F5BB,
+ 0x1F5BC, 0x1F5BC,
+ 0x1F5BD, 0x1F5C1,
+ 0x1F5C2, 0x1F5C4,
+ 0x1F5C5, 0x1F5D0,
+ 0x1F5D1, 0x1F5D3,
+ 0x1F5D4, 0x1F5DB,
+ 0x1F5DC, 0x1F5DE,
+ 0x1F5DF, 0x1F5E0,
+ 0x1F5E1, 0x1F5E1,
+ 0x1F5E2, 0x1F5E2,
+ 0x1F5E3, 0x1F5E3,
+ 0x1F5E4, 0x1F5E7,
+ 0x1F5E8, 0x1F5E8,
+ 0x1F5E9, 0x1F5EE,
+ 0x1F5EF, 0x1F5EF,
+ 0x1F5F0, 0x1F5F2,
+ 0x1F5F3, 0x1F5F3,
+ 0x1F5F4, 0x1F5F9,
+ 0x1F5FA, 0x1F5FA,
+ 0x1F5FB, 0x1F5FF,
+ 0x1F600, 0x1F600,
+ 0x1F601, 0x1F606,
+ 0x1F607, 0x1F608,
+ 0x1F609, 0x1F60D,
+ 0x1F60E, 0x1F60E,
+ 0x1F60F, 0x1F60F,
+ 0x1F610, 0x1F610,
+ 0x1F611, 0x1F611,
+ 0x1F612, 0x1F614,
+ 0x1F615, 0x1F615,
+ 0x1F616, 0x1F616,
+ 0x1F617, 0x1F617,
+ 0x1F618, 0x1F618,
+ 0x1F619, 0x1F619,
+ 0x1F61A, 0x1F61A,
+ 0x1F61B, 0x1F61B,
+ 0x1F61C, 0x1F61E,
+ 0x1F61F, 0x1F61F,
+ 0x1F620, 0x1F625,
+ 0x1F626, 0x1F627,
+ 0x1F628, 0x1F62B,
+ 0x1F62C, 0x1F62C,
+ 0x1F62D, 0x1F62D,
+ 0x1F62E, 0x1F62F,
+ 0x1F630, 0x1F633,
+ 0x1F634, 0x1F634,
+ 0x1F635, 0x1F635,
+ 0x1F636, 0x1F636,
+ 0x1F637, 0x1F640,
+ 0x1F641, 0x1F644,
+ 0x1F645, 0x1F64F,
+ 0x1F680, 0x1F680,
+ 0x1F681, 0x1F682,
+ 0x1F683, 0x1F685,
+ 0x1F686, 0x1F686,
+ 0x1F687, 0x1F687,
+ 0x1F688, 0x1F688,
+ 0x1F689, 0x1F689,
+ 0x1F68A, 0x1F68B,
+ 0x1F68C, 0x1F68C,
+ 0x1F68D, 0x1F68D,
+ 0x1F68E, 0x1F68E,
+ 0x1F68F, 0x1F68F,
+ 0x1F690, 0x1F690,
+ 0x1F691, 0x1F693,
+ 0x1F694, 0x1F694,
+ 0x1F695, 0x1F695,
+ 0x1F696, 0x1F696,
+ 0x1F697, 0x1F697,
+ 0x1F698, 0x1F698,
+ 0x1F699, 0x1F69A,
+ 0x1F69B, 0x1F6A1,
+ 0x1F6A2, 0x1F6A2,
+ 0x1F6A3, 0x1F6A3,
+ 0x1F6A4, 0x1F6A5,
+ 0x1F6A6, 0x1F6A6,
+ 0x1F6A7, 0x1F6AD,
+ 0x1F6AE, 0x1F6B1,
+ 0x1F6B2, 0x1F6B2,
+ 0x1F6B3, 0x1F6B5,
+ 0x1F6B6, 0x1F6B6,
+ 0x1F6B7, 0x1F6B8,
+ 0x1F6B9, 0x1F6BE,
+ 0x1F6BF, 0x1F6BF,
+ 0x1F6C0, 0x1F6C0,
+ 0x1F6C1, 0x1F6C5,
+ 0x1F6C6, 0x1F6CA,
+ 0x1F6CB, 0x1F6CB,
+ 0x1F6CC, 0x1F6CC,
+ 0x1F6CD, 0x1F6CF,
+ 0x1F6D0, 0x1F6D0,
+ 0x1F6D1, 0x1F6D2,
+ 0x1F6D3, 0x1F6D4,
+ 0x1F6D5, 0x1F6D5,
+ 0x1F6D6, 0x1F6D7,
+ 0x1F6D8, 0x1F6DB,
+ 0x1F6DC, 0x1F6DC,
+ 0x1F6DD, 0x1F6DF,
+ 0x1F6E0, 0x1F6E5,
+ 0x1F6E6, 0x1F6E8,
+ 0x1F6E9, 0x1F6E9,
+ 0x1F6EA, 0x1F6EA,
+ 0x1F6EB, 0x1F6EC,
+ 0x1F6ED, 0x1F6EF,
+ 0x1F6F0, 0x1F6F0,
+ 0x1F6F1, 0x1F6F2,
+ 0x1F6F3, 0x1F6F3,
+ 0x1F6F4, 0x1F6F6,
+ 0x1F6F7, 0x1F6F8,
+ 0x1F6F9, 0x1F6F9,
+ 0x1F6FA, 0x1F6FA,
+ 0x1F6FB, 0x1F6FC,
+ 0x1F6FD, 0x1F6FF,
+ 0x1F774, 0x1F77F,
+ 0x1F7D5, 0x1F7DF,
+ 0x1F7E0, 0x1F7EB,
+ 0x1F7EC, 0x1F7EF,
+ 0x1F7F0, 0x1F7F0,
+ 0x1F7F1, 0x1F7FF,
+ 0x1F80C, 0x1F80F,
+ 0x1F848, 0x1F84F,
+ 0x1F85A, 0x1F85F,
+ 0x1F888, 0x1F88F,
+ 0x1F8AE, 0x1F8FF,
+ 0x1F90C, 0x1F90C,
+ 0x1F90D, 0x1F90F,
+ 0x1F910, 0x1F918,
+ 0x1F919, 0x1F91E,
+ 0x1F91F, 0x1F91F,
+ 0x1F920, 0x1F927,
+ 0x1F928, 0x1F92F,
+ 0x1F930, 0x1F930,
+ 0x1F931, 0x1F932,
+ 0x1F933, 0x1F93A,
+ 0x1F93C, 0x1F93E,
+ 0x1F93F, 0x1F93F,
+ 0x1F940, 0x1F945,
+ 0x1F947, 0x1F94B,
+ 0x1F94C, 0x1F94C,
+ 0x1F94D, 0x1F94F,
+ 0x1F950, 0x1F95E,
+ 0x1F95F, 0x1F96B,
+ 0x1F96C, 0x1F970,
+ 0x1F971, 0x1F971,
+ 0x1F972, 0x1F972,
+ 0x1F973, 0x1F976,
+ 0x1F977, 0x1F978,
+ 0x1F979, 0x1F979,
+ 0x1F97A, 0x1F97A,
+ 0x1F97B, 0x1F97B,
+ 0x1F97C, 0x1F97F,
+ 0x1F980, 0x1F984,
+ 0x1F985, 0x1F991,
+ 0x1F992, 0x1F997,
+ 0x1F998, 0x1F9A2,
+ 0x1F9A3, 0x1F9A4,
+ 0x1F9A5, 0x1F9AA,
+ 0x1F9AB, 0x1F9AD,
+ 0x1F9AE, 0x1F9AF,
+ 0x1F9B0, 0x1F9B9,
+ 0x1F9BA, 0x1F9BF,
+ 0x1F9C0, 0x1F9C0,
+ 0x1F9C1, 0x1F9C2,
+ 0x1F9C3, 0x1F9CA,
+ 0x1F9CB, 0x1F9CB,
+ 0x1F9CC, 0x1F9CC,
+ 0x1F9CD, 0x1F9CF,
+ 0x1F9D0, 0x1F9E6,
+ 0x1F9E7, 0x1F9FF,
+ 0x1FA00, 0x1FA6F,
+ 0x1FA70, 0x1FA73,
+ 0x1FA74, 0x1FA74,
+ 0x1FA75, 0x1FA77,
+ 0x1FA78, 0x1FA7A,
+ 0x1FA7B, 0x1FA7C,
+ 0x1FA7D, 0x1FA7F,
+ 0x1FA80, 0x1FA82,
+ 0x1FA83, 0x1FA86,
+ 0x1FA87, 0x1FA88,
+ 0x1FA89, 0x1FA8F,
+ 0x1FA90, 0x1FA95,
+ 0x1FA96, 0x1FAA8,
+ 0x1FAA9, 0x1FAAC,
+ 0x1FAAD, 0x1FAAF,
+ 0x1FAB0, 0x1FAB6,
+ 0x1FAB7, 0x1FABA,
+ 0x1FABB, 0x1FABD,
+ 0x1FABE, 0x1FABE,
+ 0x1FABF, 0x1FABF,
+ 0x1FAC0, 0x1FAC2,
+ 0x1FAC3, 0x1FAC5,
+ 0x1FAC6, 0x1FACD,
+ 0x1FACE, 0x1FACF,
+ 0x1FAD0, 0x1FAD6,
+ 0x1FAD7, 0x1FAD9,
+ 0x1FADA, 0x1FADB,
+ 0x1FADC, 0x1FADF,
+ 0x1FAE0, 0x1FAE7,
+ 0x1FAE8, 0x1FAE8,
+ 0x1FAE9, 0x1FAEF,
+ 0x1FAF0, 0x1FAF6,
+ 0x1FAF7, 0x1FAF8,
+ 0x1FAF9, 0x1FAFF,
+ 0x1FC00, 0x1FFFD,
+};
+
+static const int32_t ucg_grapheme_extend_ranges[] = {
+ 0x0300, 0x036F,
+ 0x0483, 0x0487,
+ 0x0488, 0x0489,
+ 0x0591, 0x05BD,
+ 0x05BF, 0x05BF,
+ 0x05C1, 0x05C2,
+ 0x05C4, 0x05C5,
+ 0x05C7, 0x05C7,
+ 0x0610, 0x061A,
+ 0x064B, 0x065F,
+ 0x0670, 0x0670,
+ 0x06D6, 0x06DC,
+ 0x06DF, 0x06E4,
+ 0x06E7, 0x06E8,
+ 0x06EA, 0x06ED,
+ 0x0711, 0x0711,
+ 0x0730, 0x074A,
+ 0x07A6, 0x07B0,
+ 0x07EB, 0x07F3,
+ 0x07FD, 0x07FD,
+ 0x0816, 0x0819,
+ 0x081B, 0x0823,
+ 0x0825, 0x0827,
+ 0x0829, 0x082D,
+ 0x0859, 0x085B,
+ 0x0898, 0x089F,
+ 0x08CA, 0x08E1,
+ 0x08E3, 0x0902,
+ 0x093A, 0x093A,
+ 0x093C, 0x093C,
+ 0x0941, 0x0948,
+ 0x094D, 0x094D,
+ 0x0951, 0x0957,
+ 0x0962, 0x0963,
+ 0x0981, 0x0981,
+ 0x09BC, 0x09BC,
+ 0x09BE, 0x09BE,
+ 0x09C1, 0x09C4,
+ 0x09CD, 0x09CD,
+ 0x09D7, 0x09D7,
+ 0x09E2, 0x09E3,
+ 0x09FE, 0x09FE,
+ 0x0A01, 0x0A02,
+ 0x0A3C, 0x0A3C,
+ 0x0A41, 0x0A42,
+ 0x0A47, 0x0A48,
+ 0x0A4B, 0x0A4D,
+ 0x0A51, 0x0A51,
+ 0x0A70, 0x0A71,
+ 0x0A75, 0x0A75,
+ 0x0A81, 0x0A82,
+ 0x0ABC, 0x0ABC,
+ 0x0AC1, 0x0AC5,
+ 0x0AC7, 0x0AC8,
+ 0x0ACD, 0x0ACD,
+ 0x0AE2, 0x0AE3,
+ 0x0AFA, 0x0AFF,
+ 0x0B01, 0x0B01,
+ 0x0B3C, 0x0B3C,
+ 0x0B3E, 0x0B3E,
+ 0x0B3F, 0x0B3F,
+ 0x0B41, 0x0B44,
+ 0x0B4D, 0x0B4D,
+ 0x0B55, 0x0B56,
+ 0x0B57, 0x0B57,
+ 0x0B62, 0x0B63,
+ 0x0B82, 0x0B82,
+ 0x0BBE, 0x0BBE,
+ 0x0BC0, 0x0BC0,
+ 0x0BCD, 0x0BCD,
+ 0x0BD7, 0x0BD7,
+ 0x0C00, 0x0C00,
+ 0x0C04, 0x0C04,
+ 0x0C3C, 0x0C3C,
+ 0x0C3E, 0x0C40,
+ 0x0C46, 0x0C48,
+ 0x0C4A, 0x0C4D,
+ 0x0C55, 0x0C56,
+ 0x0C62, 0x0C63,
+ 0x0C81, 0x0C81,
+ 0x0CBC, 0x0CBC,
+ 0x0CBF, 0x0CBF,
+ 0x0CC2, 0x0CC2,
+ 0x0CC6, 0x0CC6,
+ 0x0CCC, 0x0CCD,
+ 0x0CD5, 0x0CD6,
+ 0x0CE2, 0x0CE3,
+ 0x0D00, 0x0D01,
+ 0x0D3B, 0x0D3C,
+ 0x0D3E, 0x0D3E,
+ 0x0D41, 0x0D44,
+ 0x0D4D, 0x0D4D,
+ 0x0D57, 0x0D57,
+ 0x0D62, 0x0D63,
+ 0x0D81, 0x0D81,
+ 0x0DCA, 0x0DCA,
+ 0x0DCF, 0x0DCF,
+ 0x0DD2, 0x0DD4,
+ 0x0DD6, 0x0DD6,
+ 0x0DDF, 0x0DDF,
+ 0x0E31, 0x0E31,
+ 0x0E34, 0x0E3A,
+ 0x0E47, 0x0E4E,
+ 0x0EB1, 0x0EB1,
+ 0x0EB4, 0x0EBC,
+ 0x0EC8, 0x0ECE,
+ 0x0F18, 0x0F19,
+ 0x0F35, 0x0F35,
+ 0x0F37, 0x0F37,
+ 0x0F39, 0x0F39,
+ 0x0F71, 0x0F7E,
+ 0x0F80, 0x0F84,
+ 0x0F86, 0x0F87,
+ 0x0F8D, 0x0F97,
+ 0x0F99, 0x0FBC,
+ 0x0FC6, 0x0FC6,
+ 0x102D, 0x1030,
+ 0x1032, 0x1037,
+ 0x1039, 0x103A,
+ 0x103D, 0x103E,
+ 0x1058, 0x1059,
+ 0x105E, 0x1060,
+ 0x1071, 0x1074,
+ 0x1082, 0x1082,
+ 0x1085, 0x1086,
+ 0x108D, 0x108D,
+ 0x109D, 0x109D,
+ 0x135D, 0x135F,
+ 0x1712, 0x1714,
+ 0x1732, 0x1733,
+ 0x1752, 0x1753,
+ 0x1772, 0x1773,
+ 0x17B4, 0x17B5,
+ 0x17B7, 0x17BD,
+ 0x17C6, 0x17C6,
+ 0x17C9, 0x17D3,
+ 0x17DD, 0x17DD,
+ 0x180B, 0x180D,
+ 0x180F, 0x180F,
+ 0x1885, 0x1886,
+ 0x18A9, 0x18A9,
+ 0x1920, 0x1922,
+ 0x1927, 0x1928,
+ 0x1932, 0x1932,
+ 0x1939, 0x193B,
+ 0x1A17, 0x1A18,
+ 0x1A1B, 0x1A1B,
+ 0x1A56, 0x1A56,
+ 0x1A58, 0x1A5E,
+ 0x1A60, 0x1A60,
+ 0x1A62, 0x1A62,
+ 0x1A65, 0x1A6C,
+ 0x1A73, 0x1A7C,
+ 0x1A7F, 0x1A7F,
+ 0x1AB0, 0x1ABD,
+ 0x1ABE, 0x1ABE,
+ 0x1ABF, 0x1ACE,
+ 0x1B00, 0x1B03,
+ 0x1B34, 0x1B34,
+ 0x1B35, 0x1B35,
+ 0x1B36, 0x1B3A,
+ 0x1B3C, 0x1B3C,
+ 0x1B42, 0x1B42,
+ 0x1B6B, 0x1B73,
+ 0x1B80, 0x1B81,
+ 0x1BA2, 0x1BA5,
+ 0x1BA8, 0x1BA9,
+ 0x1BAB, 0x1BAD,
+ 0x1BE6, 0x1BE6,
+ 0x1BE8, 0x1BE9,
+ 0x1BED, 0x1BED,
+ 0x1BEF, 0x1BF1,
+ 0x1C2C, 0x1C33,
+ 0x1C36, 0x1C37,
+ 0x1CD0, 0x1CD2,
+ 0x1CD4, 0x1CE0,
+ 0x1CE2, 0x1CE8,
+ 0x1CED, 0x1CED,
+ 0x1CF4, 0x1CF4,
+ 0x1CF8, 0x1CF9,
+ 0x1DC0, 0x1DFF,
+ 0x200C, 0x200C,
+ 0x20D0, 0x20DC,
+ 0x20DD, 0x20E0,
+ 0x20E1, 0x20E1,
+ 0x20E2, 0x20E4,
+ 0x20E5, 0x20F0,
+ 0x2CEF, 0x2CF1,
+ 0x2D7F, 0x2D7F,
+ 0x2DE0, 0x2DFF,
+ 0x302A, 0x302D,
+ 0x302E, 0x302F,
+ 0x3099, 0x309A,
+ 0xA66F, 0xA66F,
+ 0xA670, 0xA672,
+ 0xA674, 0xA67D,
+ 0xA69E, 0xA69F,
+ 0xA6F0, 0xA6F1,
+ 0xA802, 0xA802,
+ 0xA806, 0xA806,
+ 0xA80B, 0xA80B,
+ 0xA825, 0xA826,
+ 0xA82C, 0xA82C,
+ 0xA8C4, 0xA8C5,
+ 0xA8E0, 0xA8F1,
+ 0xA8FF, 0xA8FF,
+ 0xA926, 0xA92D,
+ 0xA947, 0xA951,
+ 0xA980, 0xA982,
+ 0xA9B3, 0xA9B3,
+ 0xA9B6, 0xA9B9,
+ 0xA9BC, 0xA9BD,
+ 0xA9E5, 0xA9E5,
+ 0xAA29, 0xAA2E,
+ 0xAA31, 0xAA32,
+ 0xAA35, 0xAA36,
+ 0xAA43, 0xAA43,
+ 0xAA4C, 0xAA4C,
+ 0xAA7C, 0xAA7C,
+ 0xAAB0, 0xAAB0,
+ 0xAAB2, 0xAAB4,
+ 0xAAB7, 0xAAB8,
+ 0xAABE, 0xAABF,
+ 0xAAC1, 0xAAC1,
+ 0xAAEC, 0xAAED,
+ 0xAAF6, 0xAAF6,
+ 0xABE5, 0xABE5,
+ 0xABE8, 0xABE8,
+ 0xABED, 0xABED,
+ 0xFB1E, 0xFB1E,
+ 0xFE00, 0xFE0F,
+ 0xFE20, 0xFE2F,
+ 0xFF9E, 0xFF9F,
+ 0x101FD, 0x101FD,
+ 0x102E0, 0x102E0,
+ 0x10376, 0x1037A,
+ 0x10A01, 0x10A03,
+ 0x10A05, 0x10A06,
+ 0x10A0C, 0x10A0F,
+ 0x10A38, 0x10A3A,
+ 0x10A3F, 0x10A3F,
+ 0x10AE5, 0x10AE6,
+ 0x10D24, 0x10D27,
+ 0x10EAB, 0x10EAC,
+ 0x10EFD, 0x10EFF,
+ 0x10F46, 0x10F50,
+ 0x10F82, 0x10F85,
+ 0x11001, 0x11001,
+ 0x11038, 0x11046,
+ 0x11070, 0x11070,
+ 0x11073, 0x11074,
+ 0x1107F, 0x11081,
+ 0x110B3, 0x110B6,
+ 0x110B9, 0x110BA,
+ 0x110C2, 0x110C2,
+ 0x11100, 0x11102,
+ 0x11127, 0x1112B,
+ 0x1112D, 0x11134,
+ 0x11173, 0x11173,
+ 0x11180, 0x11181,
+ 0x111B6, 0x111BE,
+ 0x111C9, 0x111CC,
+ 0x111CF, 0x111CF,
+ 0x1122F, 0x11231,
+ 0x11234, 0x11234,
+ 0x11236, 0x11237,
+ 0x1123E, 0x1123E,
+ 0x11241, 0x11241,
+ 0x112DF, 0x112DF,
+ 0x112E3, 0x112EA,
+ 0x11300, 0x11301,
+ 0x1133B, 0x1133C,
+ 0x1133E, 0x1133E,
+ 0x11340, 0x11340,
+ 0x11357, 0x11357,
+ 0x11366, 0x1136C,
+ 0x11370, 0x11374,
+ 0x11438, 0x1143F,
+ 0x11442, 0x11444,
+ 0x11446, 0x11446,
+ 0x1145E, 0x1145E,
+ 0x114B0, 0x114B0,
+ 0x114B3, 0x114B8,
+ 0x114BA, 0x114BA,
+ 0x114BD, 0x114BD,
+ 0x114BF, 0x114C0,
+ 0x114C2, 0x114C3,
+ 0x115AF, 0x115AF,
+ 0x115B2, 0x115B5,
+ 0x115BC, 0x115BD,
+ 0x115BF, 0x115C0,
+ 0x115DC, 0x115DD,
+ 0x11633, 0x1163A,
+ 0x1163D, 0x1163D,
+ 0x1163F, 0x11640,
+ 0x116AB, 0x116AB,
+ 0x116AD, 0x116AD,
+ 0x116B0, 0x116B5,
+ 0x116B7, 0x116B7,
+ 0x1171D, 0x1171F,
+ 0x11722, 0x11725,
+ 0x11727, 0x1172B,
+ 0x1182F, 0x11837,
+ 0x11839, 0x1183A,
+ 0x11930, 0x11930,
+ 0x1193B, 0x1193C,
+ 0x1193E, 0x1193E,
+ 0x11943, 0x11943,
+ 0x119D4, 0x119D7,
+ 0x119DA, 0x119DB,
+ 0x119E0, 0x119E0,
+ 0x11A01, 0x11A0A,
+ 0x11A33, 0x11A38,
+ 0x11A3B, 0x11A3E,
+ 0x11A47, 0x11A47,
+ 0x11A51, 0x11A56,
+ 0x11A59, 0x11A5B,
+ 0x11A8A, 0x11A96,
+ 0x11A98, 0x11A99,
+ 0x11C30, 0x11C36,
+ 0x11C38, 0x11C3D,
+ 0x11C3F, 0x11C3F,
+ 0x11C92, 0x11CA7,
+ 0x11CAA, 0x11CB0,
+ 0x11CB2, 0x11CB3,
+ 0x11CB5, 0x11CB6,
+ 0x11D31, 0x11D36,
+ 0x11D3A, 0x11D3A,
+ 0x11D3C, 0x11D3D,
+ 0x11D3F, 0x11D45,
+ 0x11D47, 0x11D47,
+ 0x11D90, 0x11D91,
+ 0x11D95, 0x11D95,
+ 0x11D97, 0x11D97,
+ 0x11EF3, 0x11EF4,
+ 0x11F00, 0x11F01,
+ 0x11F36, 0x11F3A,
+ 0x11F40, 0x11F40,
+ 0x11F42, 0x11F42,
+ 0x13440, 0x13440,
+ 0x13447, 0x13455,
+ 0x16AF0, 0x16AF4,
+ 0x16B30, 0x16B36,
+ 0x16F4F, 0x16F4F,
+ 0x16F8F, 0x16F92,
+ 0x16FE4, 0x16FE4,
+ 0x1BC9D, 0x1BC9E,
+ 0x1CF00, 0x1CF2D,
+ 0x1CF30, 0x1CF46,
+ 0x1D165, 0x1D165,
+ 0x1D167, 0x1D169,
+ 0x1D16E, 0x1D172,
+ 0x1D17B, 0x1D182,
+ 0x1D185, 0x1D18B,
+ 0x1D1AA, 0x1D1AD,
+ 0x1D242, 0x1D244,
+ 0x1DA00, 0x1DA36,
+ 0x1DA3B, 0x1DA6C,
+ 0x1DA75, 0x1DA75,
+ 0x1DA84, 0x1DA84,
+ 0x1DA9B, 0x1DA9F,
+ 0x1DAA1, 0x1DAAF,
+ 0x1E000, 0x1E006,
+ 0x1E008, 0x1E018,
+ 0x1E01B, 0x1E021,
+ 0x1E023, 0x1E024,
+ 0x1E026, 0x1E02A,
+ 0x1E08F, 0x1E08F,
+ 0x1E130, 0x1E136,
+ 0x1E2AE, 0x1E2AE,
+ 0x1E2EC, 0x1E2EF,
+ 0x1E4EC, 0x1E4EF,
+ 0x1E8D0, 0x1E8D6,
+ 0x1E944, 0x1E94A,
+ 0xE0020, 0xE007F,
+ 0xE0100, 0xE01EF,
+};
+
+static const int32_t ucg_hangul_syllable_lv_singlets[] = {
+ 0xAC00,
+ 0xAC1C,
+ 0xAC38,
+ 0xAC54,
+ 0xAC70,
+ 0xAC8C,
+ 0xACA8,
+ 0xACC4,
+ 0xACE0,
+ 0xACFC,
+ 0xAD18,
+ 0xAD34,
+ 0xAD50,
+ 0xAD6C,
+ 0xAD88,
+ 0xADA4,
+ 0xADC0,
+ 0xADDC,
+ 0xADF8,
+ 0xAE14,
+ 0xAE30,
+ 0xAE4C,
+ 0xAE68,
+ 0xAE84,
+ 0xAEA0,
+ 0xAEBC,
+ 0xAED8,
+ 0xAEF4,
+ 0xAF10,
+ 0xAF2C,
+ 0xAF48,
+ 0xAF64,
+ 0xAF80,
+ 0xAF9C,
+ 0xAFB8,
+ 0xAFD4,
+ 0xAFF0,
+ 0xB00C,
+ 0xB028,
+ 0xB044,
+ 0xB060,
+ 0xB07C,
+ 0xB098,
+ 0xB0B4,
+ 0xB0D0,
+ 0xB0EC,
+ 0xB108,
+ 0xB124,
+ 0xB140,
+ 0xB15C,
+ 0xB178,
+ 0xB194,
+ 0xB1B0,
+ 0xB1CC,
+ 0xB1E8,
+ 0xB204,
+ 0xB220,
+ 0xB23C,
+ 0xB258,
+ 0xB274,
+ 0xB290,
+ 0xB2AC,
+ 0xB2C8,
+ 0xB2E4,
+ 0xB300,
+ 0xB31C,
+ 0xB338,
+ 0xB354,
+ 0xB370,
+ 0xB38C,
+ 0xB3A8,
+ 0xB3C4,
+ 0xB3E0,
+ 0xB3FC,
+ 0xB418,
+ 0xB434,
+ 0xB450,
+ 0xB46C,
+ 0xB488,
+ 0xB4A4,
+ 0xB4C0,
+ 0xB4DC,
+ 0xB4F8,
+ 0xB514,
+ 0xB530,
+ 0xB54C,
+ 0xB568,
+ 0xB584,
+ 0xB5A0,
+ 0xB5BC,
+ 0xB5D8,
+ 0xB5F4,
+ 0xB610,
+ 0xB62C,
+ 0xB648,
+ 0xB664,
+ 0xB680,
+ 0xB69C,
+ 0xB6B8,
+ 0xB6D4,
+ 0xB6F0,
+ 0xB70C,
+ 0xB728,
+ 0xB744,
+ 0xB760,
+ 0xB77C,
+ 0xB798,
+ 0xB7B4,
+ 0xB7D0,
+ 0xB7EC,
+ 0xB808,
+ 0xB824,
+ 0xB840,
+ 0xB85C,
+ 0xB878,
+ 0xB894,
+ 0xB8B0,
+ 0xB8CC,
+ 0xB8E8,
+ 0xB904,
+ 0xB920,
+ 0xB93C,
+ 0xB958,
+ 0xB974,
+ 0xB990,
+ 0xB9AC,
+ 0xB9C8,
+ 0xB9E4,
+ 0xBA00,
+ 0xBA1C,
+ 0xBA38,
+ 0xBA54,
+ 0xBA70,
+ 0xBA8C,
+ 0xBAA8,
+ 0xBAC4,
+ 0xBAE0,
+ 0xBAFC,
+ 0xBB18,
+ 0xBB34,
+ 0xBB50,
+ 0xBB6C,
+ 0xBB88,
+ 0xBBA4,
+ 0xBBC0,
+ 0xBBDC,
+ 0xBBF8,
+ 0xBC14,
+ 0xBC30,
+ 0xBC4C,
+ 0xBC68,
+ 0xBC84,
+ 0xBCA0,
+ 0xBCBC,
+ 0xBCD8,
+ 0xBCF4,
+ 0xBD10,
+ 0xBD2C,
+ 0xBD48,
+ 0xBD64,
+ 0xBD80,
+ 0xBD9C,
+ 0xBDB8,
+ 0xBDD4,
+ 0xBDF0,
+ 0xBE0C,
+ 0xBE28,
+ 0xBE44,
+ 0xBE60,
+ 0xBE7C,
+ 0xBE98,
+ 0xBEB4,
+ 0xBED0,
+ 0xBEEC,
+ 0xBF08,
+ 0xBF24,
+ 0xBF40,
+ 0xBF5C,
+ 0xBF78,
+ 0xBF94,
+ 0xBFB0,
+ 0xBFCC,
+ 0xBFE8,
+ 0xC004,
+ 0xC020,
+ 0xC03C,
+ 0xC058,
+ 0xC074,
+ 0xC090,
+ 0xC0AC,
+ 0xC0C8,
+ 0xC0E4,
+ 0xC100,
+ 0xC11C,
+ 0xC138,
+ 0xC154,
+ 0xC170,
+ 0xC18C,
+ 0xC1A8,
+ 0xC1C4,
+ 0xC1E0,
+ 0xC1FC,
+ 0xC218,
+ 0xC234,
+ 0xC250,
+ 0xC26C,
+ 0xC288,
+ 0xC2A4,
+ 0xC2C0,
+ 0xC2DC,
+ 0xC2F8,
+ 0xC314,
+ 0xC330,
+ 0xC34C,
+ 0xC368,
+ 0xC384,
+ 0xC3A0,
+ 0xC3BC,
+ 0xC3D8,
+ 0xC3F4,
+ 0xC410,
+ 0xC42C,
+ 0xC448,
+ 0xC464,
+ 0xC480,
+ 0xC49C,
+ 0xC4B8,
+ 0xC4D4,
+ 0xC4F0,
+ 0xC50C,
+ 0xC528,
+ 0xC544,
+ 0xC560,
+ 0xC57C,
+ 0xC598,
+ 0xC5B4,
+ 0xC5D0,
+ 0xC5EC,
+ 0xC608,
+ 0xC624,
+ 0xC640,
+ 0xC65C,
+ 0xC678,
+ 0xC694,
+ 0xC6B0,
+ 0xC6CC,
+ 0xC6E8,
+ 0xC704,
+ 0xC720,
+ 0xC73C,
+ 0xC758,
+ 0xC774,
+ 0xC790,
+ 0xC7AC,
+ 0xC7C8,
+ 0xC7E4,
+ 0xC800,
+ 0xC81C,
+ 0xC838,
+ 0xC854,
+ 0xC870,
+ 0xC88C,
+ 0xC8A8,
+ 0xC8C4,
+ 0xC8E0,
+ 0xC8FC,
+ 0xC918,
+ 0xC934,
+ 0xC950,
+ 0xC96C,
+ 0xC988,
+ 0xC9A4,
+ 0xC9C0,
+ 0xC9DC,
+ 0xC9F8,
+ 0xCA14,
+ 0xCA30,
+ 0xCA4C,
+ 0xCA68,
+ 0xCA84,
+ 0xCAA0,
+ 0xCABC,
+ 0xCAD8,
+ 0xCAF4,
+ 0xCB10,
+ 0xCB2C,
+ 0xCB48,
+ 0xCB64,
+ 0xCB80,
+ 0xCB9C,
+ 0xCBB8,
+ 0xCBD4,
+ 0xCBF0,
+ 0xCC0C,
+ 0xCC28,
+ 0xCC44,
+ 0xCC60,
+ 0xCC7C,
+ 0xCC98,
+ 0xCCB4,
+ 0xCCD0,
+ 0xCCEC,
+ 0xCD08,
+ 0xCD24,
+ 0xCD40,
+ 0xCD5C,
+ 0xCD78,
+ 0xCD94,
+ 0xCDB0,
+ 0xCDCC,
+ 0xCDE8,
+ 0xCE04,
+ 0xCE20,
+ 0xCE3C,
+ 0xCE58,
+ 0xCE74,
+ 0xCE90,
+ 0xCEAC,
+ 0xCEC8,
+ 0xCEE4,
+ 0xCF00,
+ 0xCF1C,
+ 0xCF38,
+ 0xCF54,
+ 0xCF70,
+ 0xCF8C,
+ 0xCFA8,
+ 0xCFC4,
+ 0xCFE0,
+ 0xCFFC,
+ 0xD018,
+ 0xD034,
+ 0xD050,
+ 0xD06C,
+ 0xD088,
+ 0xD0A4,
+ 0xD0C0,
+ 0xD0DC,
+ 0xD0F8,
+ 0xD114,
+ 0xD130,
+ 0xD14C,
+ 0xD168,
+ 0xD184,
+ 0xD1A0,
+ 0xD1BC,
+ 0xD1D8,
+ 0xD1F4,
+ 0xD210,
+ 0xD22C,
+ 0xD248,
+ 0xD264,
+ 0xD280,
+ 0xD29C,
+ 0xD2B8,
+ 0xD2D4,
+ 0xD2F0,
+ 0xD30C,
+ 0xD328,
+ 0xD344,
+ 0xD360,
+ 0xD37C,
+ 0xD398,
+ 0xD3B4,
+ 0xD3D0,
+ 0xD3EC,
+ 0xD408,
+ 0xD424,
+ 0xD440,
+ 0xD45C,
+ 0xD478,
+ 0xD494,
+ 0xD4B0,
+ 0xD4CC,
+ 0xD4E8,
+ 0xD504,
+ 0xD520,
+ 0xD53C,
+ 0xD558,
+ 0xD574,
+ 0xD590,
+ 0xD5AC,
+ 0xD5C8,
+ 0xD5E4,
+ 0xD600,
+ 0xD61C,
+ 0xD638,
+ 0xD654,
+ 0xD670,
+ 0xD68C,
+ 0xD6A8,
+ 0xD6C4,
+ 0xD6E0,
+ 0xD6FC,
+ 0xD718,
+ 0xD734,
+ 0xD750,
+ 0xD76C,
+ 0xD788,
+};
+
+static const int32_t ucg_hangul_syllable_lvt_ranges[] = {
+ 0xAC01, 0xAC1B,
+ 0xAC1D, 0xAC37,
+ 0xAC39, 0xAC53,
+ 0xAC55, 0xAC6F,
+ 0xAC71, 0xAC8B,
+ 0xAC8D, 0xACA7,
+ 0xACA9, 0xACC3,
+ 0xACC5, 0xACDF,
+ 0xACE1, 0xACFB,
+ 0xACFD, 0xAD17,
+ 0xAD19, 0xAD33,
+ 0xAD35, 0xAD4F,
+ 0xAD51, 0xAD6B,
+ 0xAD6D, 0xAD87,
+ 0xAD89, 0xADA3,
+ 0xADA5, 0xADBF,
+ 0xADC1, 0xADDB,
+ 0xADDD, 0xADF7,
+ 0xADF9, 0xAE13,
+ 0xAE15, 0xAE2F,
+ 0xAE31, 0xAE4B,
+ 0xAE4D, 0xAE67,
+ 0xAE69, 0xAE83,
+ 0xAE85, 0xAE9F,
+ 0xAEA1, 0xAEBB,
+ 0xAEBD, 0xAED7,
+ 0xAED9, 0xAEF3,
+ 0xAEF5, 0xAF0F,
+ 0xAF11, 0xAF2B,
+ 0xAF2D, 0xAF47,
+ 0xAF49, 0xAF63,
+ 0xAF65, 0xAF7F,
+ 0xAF81, 0xAF9B,
+ 0xAF9D, 0xAFB7,
+ 0xAFB9, 0xAFD3,
+ 0xAFD5, 0xAFEF,
+ 0xAFF1, 0xB00B,
+ 0xB00D, 0xB027,
+ 0xB029, 0xB043,
+ 0xB045, 0xB05F,
+ 0xB061, 0xB07B,
+ 0xB07D, 0xB097,
+ 0xB099, 0xB0B3,
+ 0xB0B5, 0xB0CF,
+ 0xB0D1, 0xB0EB,
+ 0xB0ED, 0xB107,
+ 0xB109, 0xB123,
+ 0xB125, 0xB13F,
+ 0xB141, 0xB15B,
+ 0xB15D, 0xB177,
+ 0xB179, 0xB193,
+ 0xB195, 0xB1AF,
+ 0xB1B1, 0xB1CB,
+ 0xB1CD, 0xB1E7,
+ 0xB1E9, 0xB203,
+ 0xB205, 0xB21F,
+ 0xB221, 0xB23B,
+ 0xB23D, 0xB257,
+ 0xB259, 0xB273,
+ 0xB275, 0xB28F,
+ 0xB291, 0xB2AB,
+ 0xB2AD, 0xB2C7,
+ 0xB2C9, 0xB2E3,
+ 0xB2E5, 0xB2FF,
+ 0xB301, 0xB31B,
+ 0xB31D, 0xB337,
+ 0xB339, 0xB353,
+ 0xB355, 0xB36F,
+ 0xB371, 0xB38B,
+ 0xB38D, 0xB3A7,
+ 0xB3A9, 0xB3C3,
+ 0xB3C5, 0xB3DF,
+ 0xB3E1, 0xB3FB,
+ 0xB3FD, 0xB417,
+ 0xB419, 0xB433,
+ 0xB435, 0xB44F,
+ 0xB451, 0xB46B,
+ 0xB46D, 0xB487,
+ 0xB489, 0xB4A3,
+ 0xB4A5, 0xB4BF,
+ 0xB4C1, 0xB4DB,
+ 0xB4DD, 0xB4F7,
+ 0xB4F9, 0xB513,
+ 0xB515, 0xB52F,
+ 0xB531, 0xB54B,
+ 0xB54D, 0xB567,
+ 0xB569, 0xB583,
+ 0xB585, 0xB59F,
+ 0xB5A1, 0xB5BB,
+ 0xB5BD, 0xB5D7,
+ 0xB5D9, 0xB5F3,
+ 0xB5F5, 0xB60F,
+ 0xB611, 0xB62B,
+ 0xB62D, 0xB647,
+ 0xB649, 0xB663,
+ 0xB665, 0xB67F,
+ 0xB681, 0xB69B,
+ 0xB69D, 0xB6B7,
+ 0xB6B9, 0xB6D3,
+ 0xB6D5, 0xB6EF,
+ 0xB6F1, 0xB70B,
+ 0xB70D, 0xB727,
+ 0xB729, 0xB743,
+ 0xB745, 0xB75F,
+ 0xB761, 0xB77B,
+ 0xB77D, 0xB797,
+ 0xB799, 0xB7B3,
+ 0xB7B5, 0xB7CF,
+ 0xB7D1, 0xB7EB,
+ 0xB7ED, 0xB807,
+ 0xB809, 0xB823,
+ 0xB825, 0xB83F,
+ 0xB841, 0xB85B,
+ 0xB85D, 0xB877,
+ 0xB879, 0xB893,
+ 0xB895, 0xB8AF,
+ 0xB8B1, 0xB8CB,
+ 0xB8CD, 0xB8E7,
+ 0xB8E9, 0xB903,
+ 0xB905, 0xB91F,
+ 0xB921, 0xB93B,
+ 0xB93D, 0xB957,
+ 0xB959, 0xB973,
+ 0xB975, 0xB98F,
+ 0xB991, 0xB9AB,
+ 0xB9AD, 0xB9C7,
+ 0xB9C9, 0xB9E3,
+ 0xB9E5, 0xB9FF,
+ 0xBA01, 0xBA1B,
+ 0xBA1D, 0xBA37,
+ 0xBA39, 0xBA53,
+ 0xBA55, 0xBA6F,
+ 0xBA71, 0xBA8B,
+ 0xBA8D, 0xBAA7,
+ 0xBAA9, 0xBAC3,
+ 0xBAC5, 0xBADF,
+ 0xBAE1, 0xBAFB,
+ 0xBAFD, 0xBB17,
+ 0xBB19, 0xBB33,
+ 0xBB35, 0xBB4F,
+ 0xBB51, 0xBB6B,
+ 0xBB6D, 0xBB87,
+ 0xBB89, 0xBBA3,
+ 0xBBA5, 0xBBBF,
+ 0xBBC1, 0xBBDB,
+ 0xBBDD, 0xBBF7,
+ 0xBBF9, 0xBC13,
+ 0xBC15, 0xBC2F,
+ 0xBC31, 0xBC4B,
+ 0xBC4D, 0xBC67,
+ 0xBC69, 0xBC83,
+ 0xBC85, 0xBC9F,
+ 0xBCA1, 0xBCBB,
+ 0xBCBD, 0xBCD7,
+ 0xBCD9, 0xBCF3,
+ 0xBCF5, 0xBD0F,
+ 0xBD11, 0xBD2B,
+ 0xBD2D, 0xBD47,
+ 0xBD49, 0xBD63,
+ 0xBD65, 0xBD7F,
+ 0xBD81, 0xBD9B,
+ 0xBD9D, 0xBDB7,
+ 0xBDB9, 0xBDD3,
+ 0xBDD5, 0xBDEF,
+ 0xBDF1, 0xBE0B,
+ 0xBE0D, 0xBE27,
+ 0xBE29, 0xBE43,
+ 0xBE45, 0xBE5F,
+ 0xBE61, 0xBE7B,
+ 0xBE7D, 0xBE97,
+ 0xBE99, 0xBEB3,
+ 0xBEB5, 0xBECF,
+ 0xBED1, 0xBEEB,
+ 0xBEED, 0xBF07,
+ 0xBF09, 0xBF23,
+ 0xBF25, 0xBF3F,
+ 0xBF41, 0xBF5B,
+ 0xBF5D, 0xBF77,
+ 0xBF79, 0xBF93,
+ 0xBF95, 0xBFAF,
+ 0xBFB1, 0xBFCB,
+ 0xBFCD, 0xBFE7,
+ 0xBFE9, 0xC003,
+ 0xC005, 0xC01F,
+ 0xC021, 0xC03B,
+ 0xC03D, 0xC057,
+ 0xC059, 0xC073,
+ 0xC075, 0xC08F,
+ 0xC091, 0xC0AB,
+ 0xC0AD, 0xC0C7,
+ 0xC0C9, 0xC0E3,
+ 0xC0E5, 0xC0FF,
+ 0xC101, 0xC11B,
+ 0xC11D, 0xC137,
+ 0xC139, 0xC153,
+ 0xC155, 0xC16F,
+ 0xC171, 0xC18B,
+ 0xC18D, 0xC1A7,
+ 0xC1A9, 0xC1C3,
+ 0xC1C5, 0xC1DF,
+ 0xC1E1, 0xC1FB,
+ 0xC1FD, 0xC217,
+ 0xC219, 0xC233,
+ 0xC235, 0xC24F,
+ 0xC251, 0xC26B,
+ 0xC26D, 0xC287,
+ 0xC289, 0xC2A3,
+ 0xC2A5, 0xC2BF,
+ 0xC2C1, 0xC2DB,
+ 0xC2DD, 0xC2F7,
+ 0xC2F9, 0xC313,
+ 0xC315, 0xC32F,
+ 0xC331, 0xC34B,
+ 0xC34D, 0xC367,
+ 0xC369, 0xC383,
+ 0xC385, 0xC39F,
+ 0xC3A1, 0xC3BB,
+ 0xC3BD, 0xC3D7,
+ 0xC3D9, 0xC3F3,
+ 0xC3F5, 0xC40F,
+ 0xC411, 0xC42B,
+ 0xC42D, 0xC447,
+ 0xC449, 0xC463,
+ 0xC465, 0xC47F,
+ 0xC481, 0xC49B,
+ 0xC49D, 0xC4B7,
+ 0xC4B9, 0xC4D3,
+ 0xC4D5, 0xC4EF,
+ 0xC4F1, 0xC50B,
+ 0xC50D, 0xC527,
+ 0xC529, 0xC543,
+ 0xC545, 0xC55F,
+ 0xC561, 0xC57B,
+ 0xC57D, 0xC597,
+ 0xC599, 0xC5B3,
+ 0xC5B5, 0xC5CF,
+ 0xC5D1, 0xC5EB,
+ 0xC5ED, 0xC607,
+ 0xC609, 0xC623,
+ 0xC625, 0xC63F,
+ 0xC641, 0xC65B,
+ 0xC65D, 0xC677,
+ 0xC679, 0xC693,
+ 0xC695, 0xC6AF,
+ 0xC6B1, 0xC6CB,
+ 0xC6CD, 0xC6E7,
+ 0xC6E9, 0xC703,
+ 0xC705, 0xC71F,
+ 0xC721, 0xC73B,
+ 0xC73D, 0xC757,
+ 0xC759, 0xC773,
+ 0xC775, 0xC78F,
+ 0xC791, 0xC7AB,
+ 0xC7AD, 0xC7C7,
+ 0xC7C9, 0xC7E3,
+ 0xC7E5, 0xC7FF,
+ 0xC801, 0xC81B,
+ 0xC81D, 0xC837,
+ 0xC839, 0xC853,
+ 0xC855, 0xC86F,
+ 0xC871, 0xC88B,
+ 0xC88D, 0xC8A7,
+ 0xC8A9, 0xC8C3,
+ 0xC8C5, 0xC8DF,
+ 0xC8E1, 0xC8FB,
+ 0xC8FD, 0xC917,
+ 0xC919, 0xC933,
+ 0xC935, 0xC94F,
+ 0xC951, 0xC96B,
+ 0xC96D, 0xC987,
+ 0xC989, 0xC9A3,
+ 0xC9A5, 0xC9BF,
+ 0xC9C1, 0xC9DB,
+ 0xC9DD, 0xC9F7,
+ 0xC9F9, 0xCA13,
+ 0xCA15, 0xCA2F,
+ 0xCA31, 0xCA4B,
+ 0xCA4D, 0xCA67,
+ 0xCA69, 0xCA83,
+ 0xCA85, 0xCA9F,
+ 0xCAA1, 0xCABB,
+ 0xCABD, 0xCAD7,
+ 0xCAD9, 0xCAF3,
+ 0xCAF5, 0xCB0F,
+ 0xCB11, 0xCB2B,
+ 0xCB2D, 0xCB47,
+ 0xCB49, 0xCB63,
+ 0xCB65, 0xCB7F,
+ 0xCB81, 0xCB9B,
+ 0xCB9D, 0xCBB7,
+ 0xCBB9, 0xCBD3,
+ 0xCBD5, 0xCBEF,
+ 0xCBF1, 0xCC0B,
+ 0xCC0D, 0xCC27,
+ 0xCC29, 0xCC43,
+ 0xCC45, 0xCC5F,
+ 0xCC61, 0xCC7B,
+ 0xCC7D, 0xCC97,
+ 0xCC99, 0xCCB3,
+ 0xCCB5, 0xCCCF,
+ 0xCCD1, 0xCCEB,
+ 0xCCED, 0xCD07,
+ 0xCD09, 0xCD23,
+ 0xCD25, 0xCD3F,
+ 0xCD41, 0xCD5B,
+ 0xCD5D, 0xCD77,
+ 0xCD79, 0xCD93,
+ 0xCD95, 0xCDAF,
+ 0xCDB1, 0xCDCB,
+ 0xCDCD, 0xCDE7,
+ 0xCDE9, 0xCE03,
+ 0xCE05, 0xCE1F,
+ 0xCE21, 0xCE3B,
+ 0xCE3D, 0xCE57,
+ 0xCE59, 0xCE73,
+ 0xCE75, 0xCE8F,
+ 0xCE91, 0xCEAB,
+ 0xCEAD, 0xCEC7,
+ 0xCEC9, 0xCEE3,
+ 0xCEE5, 0xCEFF,
+ 0xCF01, 0xCF1B,
+ 0xCF1D, 0xCF37,
+ 0xCF39, 0xCF53,
+ 0xCF55, 0xCF6F,
+ 0xCF71, 0xCF8B,
+ 0xCF8D, 0xCFA7,
+ 0xCFA9, 0xCFC3,
+ 0xCFC5, 0xCFDF,
+ 0xCFE1, 0xCFFB,
+ 0xCFFD, 0xD017,
+ 0xD019, 0xD033,
+ 0xD035, 0xD04F,
+ 0xD051, 0xD06B,
+ 0xD06D, 0xD087,
+ 0xD089, 0xD0A3,
+ 0xD0A5, 0xD0BF,
+ 0xD0C1, 0xD0DB,
+ 0xD0DD, 0xD0F7,
+ 0xD0F9, 0xD113,
+ 0xD115, 0xD12F,
+ 0xD131, 0xD14B,
+ 0xD14D, 0xD167,
+ 0xD169, 0xD183,
+ 0xD185, 0xD19F,
+ 0xD1A1, 0xD1BB,
+ 0xD1BD, 0xD1D7,
+ 0xD1D9, 0xD1F3,
+ 0xD1F5, 0xD20F,
+ 0xD211, 0xD22B,
+ 0xD22D, 0xD247,
+ 0xD249, 0xD263,
+ 0xD265, 0xD27F,
+ 0xD281, 0xD29B,
+ 0xD29D, 0xD2B7,
+ 0xD2B9, 0xD2D3,
+ 0xD2D5, 0xD2EF,
+ 0xD2F1, 0xD30B,
+ 0xD30D, 0xD327,
+ 0xD329, 0xD343,
+ 0xD345, 0xD35F,
+ 0xD361, 0xD37B,
+ 0xD37D, 0xD397,
+ 0xD399, 0xD3B3,
+ 0xD3B5, 0xD3CF,
+ 0xD3D1, 0xD3EB,
+ 0xD3ED, 0xD407,
+ 0xD409, 0xD423,
+ 0xD425, 0xD43F,
+ 0xD441, 0xD45B,
+ 0xD45D, 0xD477,
+ 0xD479, 0xD493,
+ 0xD495, 0xD4AF,
+ 0xD4B1, 0xD4CB,
+ 0xD4CD, 0xD4E7,
+ 0xD4E9, 0xD503,
+ 0xD505, 0xD51F,
+ 0xD521, 0xD53B,
+ 0xD53D, 0xD557,
+ 0xD559, 0xD573,
+ 0xD575, 0xD58F,
+ 0xD591, 0xD5AB,
+ 0xD5AD, 0xD5C7,
+ 0xD5C9, 0xD5E3,
+ 0xD5E5, 0xD5FF,
+ 0xD601, 0xD61B,
+ 0xD61D, 0xD637,
+ 0xD639, 0xD653,
+ 0xD655, 0xD66F,
+ 0xD671, 0xD68B,
+ 0xD68D, 0xD6A7,
+ 0xD6A9, 0xD6C3,
+ 0xD6C5, 0xD6DF,
+ 0xD6E1, 0xD6FB,
+ 0xD6FD, 0xD717,
+ 0xD719, 0xD733,
+ 0xD735, 0xD74F,
+ 0xD751, 0xD76B,
+ 0xD76D, 0xD787,
+ 0xD789, 0xD7A3,
+};
+
+static const int32_t ucg_indic_conjunct_break_consonant_ranges[] = {
+ 0x0915, 0x0939,
+ 0x0958, 0x095F,
+ 0x0978, 0x097F,
+ 0x0995, 0x09A8,
+ 0x09AA, 0x09B0,
+ 0x09B2, 0x09B2,
+ 0x09B6, 0x09B9,
+ 0x09DC, 0x09DD,
+ 0x09DF, 0x09DF,
+ 0x09F0, 0x09F1,
+ 0x0A95, 0x0AA8,
+ 0x0AAA, 0x0AB0,
+ 0x0AB2, 0x0AB3,
+ 0x0AB5, 0x0AB9,
+ 0x0AF9, 0x0AF9,
+ 0x0B15, 0x0B28,
+ 0x0B2A, 0x0B30,
+ 0x0B32, 0x0B33,
+ 0x0B35, 0x0B39,
+ 0x0B5C, 0x0B5D,
+ 0x0B5F, 0x0B5F,
+ 0x0B71, 0x0B71,
+ 0x0C15, 0x0C28,
+ 0x0C2A, 0x0C39,
+ 0x0C58, 0x0C5A,
+ 0x0D15, 0x0D3A,
+};
+
+static const int32_t ucg_indic_conjunct_break_extend_ranges[] = {
+ 0x0300, 0x034E,
+ 0x0350, 0x036F,
+ 0x0483, 0x0487,
+ 0x0591, 0x05BD,
+ 0x05BF, 0x05BF,
+ 0x05C1, 0x05C2,
+ 0x05C4, 0x05C5,
+ 0x05C7, 0x05C7,
+ 0x0610, 0x061A,
+ 0x064B, 0x065F,
+ 0x0670, 0x0670,
+ 0x06D6, 0x06DC,
+ 0x06DF, 0x06E4,
+ 0x06E7, 0x06E8,
+ 0x06EA, 0x06ED,
+ 0x0711, 0x0711,
+ 0x0730, 0x074A,
+ 0x07EB, 0x07F3,
+ 0x07FD, 0x07FD,
+ 0x0816, 0x0819,
+ 0x081B, 0x0823,
+ 0x0825, 0x0827,
+ 0x0829, 0x082D,
+ 0x0859, 0x085B,
+ 0x0898, 0x089F,
+ 0x08CA, 0x08E1,
+ 0x08E3, 0x08FF,
+ 0x093C, 0x093C,
+ 0x0951, 0x0954,
+ 0x09BC, 0x09BC,
+ 0x09FE, 0x09FE,
+ 0x0A3C, 0x0A3C,
+ 0x0ABC, 0x0ABC,
+ 0x0B3C, 0x0B3C,
+ 0x0C3C, 0x0C3C,
+ 0x0C55, 0x0C56,
+ 0x0CBC, 0x0CBC,
+ 0x0D3B, 0x0D3C,
+ 0x0E38, 0x0E3A,
+ 0x0E48, 0x0E4B,
+ 0x0EB8, 0x0EBA,
+ 0x0EC8, 0x0ECB,
+ 0x0F18, 0x0F19,
+ 0x0F35, 0x0F35,
+ 0x0F37, 0x0F37,
+ 0x0F39, 0x0F39,
+ 0x0F71, 0x0F72,
+ 0x0F74, 0x0F74,
+ 0x0F7A, 0x0F7D,
+ 0x0F80, 0x0F80,
+ 0x0F82, 0x0F84,
+ 0x0F86, 0x0F87,
+ 0x0FC6, 0x0FC6,
+ 0x1037, 0x1037,
+ 0x1039, 0x103A,
+ 0x108D, 0x108D,
+ 0x135D, 0x135F,
+ 0x1714, 0x1714,
+ 0x17D2, 0x17D2,
+ 0x17DD, 0x17DD,
+ 0x18A9, 0x18A9,
+ 0x1939, 0x193B,
+ 0x1A17, 0x1A18,
+ 0x1A60, 0x1A60,
+ 0x1A75, 0x1A7C,
+ 0x1A7F, 0x1A7F,
+ 0x1AB0, 0x1ABD,
+ 0x1ABF, 0x1ACE,
+ 0x1B34, 0x1B34,
+ 0x1B6B, 0x1B73,
+ 0x1BAB, 0x1BAB,
+ 0x1BE6, 0x1BE6,
+ 0x1C37, 0x1C37,
+ 0x1CD0, 0x1CD2,
+ 0x1CD4, 0x1CE0,
+ 0x1CE2, 0x1CE8,
+ 0x1CED, 0x1CED,
+ 0x1CF4, 0x1CF4,
+ 0x1CF8, 0x1CF9,
+ 0x1DC0, 0x1DFF,
+ 0x200D, 0x200D,
+ 0x20D0, 0x20DC,
+ 0x20E1, 0x20E1,
+ 0x20E5, 0x20F0,
+ 0x2CEF, 0x2CF1,
+ 0x2D7F, 0x2D7F,
+ 0x2DE0, 0x2DFF,
+ 0x302A, 0x302D,
+ 0x302E, 0x302F,
+ 0x3099, 0x309A,
+ 0xA66F, 0xA66F,
+ 0xA674, 0xA67D,
+ 0xA69E, 0xA69F,
+ 0xA6F0, 0xA6F1,
+ 0xA82C, 0xA82C,
+ 0xA8E0, 0xA8F1,
+ 0xA92B, 0xA92D,
+ 0xA9B3, 0xA9B3,
+ 0xAAB0, 0xAAB0,
+ 0xAAB2, 0xAAB4,
+ 0xAAB7, 0xAAB8,
+ 0xAABE, 0xAABF,
+ 0xAAC1, 0xAAC1,
+ 0xAAF6, 0xAAF6,
+ 0xABED, 0xABED,
+ 0xFB1E, 0xFB1E,
+ 0xFE20, 0xFE2F,
+ 0x101FD, 0x101FD,
+ 0x102E0, 0x102E0,
+ 0x10376, 0x1037A,
+ 0x10A0D, 0x10A0D,
+ 0x10A0F, 0x10A0F,
+ 0x10A38, 0x10A3A,
+ 0x10A3F, 0x10A3F,
+ 0x10AE5, 0x10AE6,
+ 0x10D24, 0x10D27,
+ 0x10EAB, 0x10EAC,
+ 0x10EFD, 0x10EFF,
+ 0x10F46, 0x10F50,
+ 0x10F82, 0x10F85,
+ 0x11070, 0x11070,
+ 0x1107F, 0x1107F,
+ 0x110BA, 0x110BA,
+ 0x11100, 0x11102,
+ 0x11133, 0x11134,
+ 0x11173, 0x11173,
+ 0x111CA, 0x111CA,
+ 0x11236, 0x11236,
+ 0x112E9, 0x112EA,
+ 0x1133B, 0x1133C,
+ 0x11366, 0x1136C,
+ 0x11370, 0x11374,
+ 0x11446, 0x11446,
+ 0x1145E, 0x1145E,
+ 0x114C3, 0x114C3,
+ 0x115C0, 0x115C0,
+ 0x116B7, 0x116B7,
+ 0x1172B, 0x1172B,
+ 0x1183A, 0x1183A,
+ 0x1193E, 0x1193E,
+ 0x11943, 0x11943,
+ 0x11A34, 0x11A34,
+ 0x11A47, 0x11A47,
+ 0x11A99, 0x11A99,
+ 0x11D42, 0x11D42,
+ 0x11D44, 0x11D45,
+ 0x11D97, 0x11D97,
+ 0x11F42, 0x11F42,
+ 0x16AF0, 0x16AF4,
+ 0x16B30, 0x16B36,
+ 0x1BC9E, 0x1BC9E,
+ 0x1D165, 0x1D165,
+ 0x1D167, 0x1D169,
+ 0x1D16E, 0x1D172,
+ 0x1D17B, 0x1D182,
+ 0x1D185, 0x1D18B,
+ 0x1D1AA, 0x1D1AD,
+ 0x1D242, 0x1D244,
+ 0x1E000, 0x1E006,
+ 0x1E008, 0x1E018,
+ 0x1E01B, 0x1E021,
+ 0x1E023, 0x1E024,
+ 0x1E026, 0x1E02A,
+ 0x1E08F, 0x1E08F,
+ 0x1E130, 0x1E136,
+ 0x1E2AE, 0x1E2AE,
+ 0x1E2EC, 0x1E2EF,
+ 0x1E4EC, 0x1E4EF,
+ 0x1E8D0, 0x1E8D6,
+ 0x1E944, 0x1E94A,
+};
+
+// Fullwidth (F) and Wide (W) are counted as 2.
+// Everything else is 1.
+//
+// Derived from: https://unicode.org/Public/15.1.0/ucd/EastAsianWidth.txt
+static const int32_t ucg_normalized_east_asian_width_ranges[] = {
+ 0x0000, 0x10FF, 1,
+ 0x1100, 0x115F, 2,
+ 0x1160, 0x2319, 1,
+ 0x231A, 0x231B, 2,
+ 0x231C, 0x2328, 1,
+ 0x2329, 0x232A, 2,
+ 0x232B, 0x23E8, 1,
+ 0x23E9, 0x23EC, 2,
+ 0x23ED, 0x23EF, 1,
+ 0x23F0, 0x23F0, 2,
+ 0x23F1, 0x23F2, 1,
+ 0x23F3, 0x23F3, 2,
+ 0x23F4, 0x25FC, 1,
+ 0x25FD, 0x25FE, 2,
+ 0x25FF, 0x2613, 1,
+ 0x2614, 0x2615, 2,
+ 0x2616, 0x2647, 1,
+ 0x2648, 0x2653, 2,
+ 0x2654, 0x267E, 1,
+ 0x267F, 0x267F, 2,
+ 0x2680, 0x2692, 1,
+ 0x2693, 0x2693, 2,
+ 0x2694, 0x26A0, 1,
+ 0x26A1, 0x26A1, 2,
+ 0x26A2, 0x26A9, 1,
+ 0x26AA, 0x26AB, 2,
+ 0x26AC, 0x26BC, 1,
+ 0x26BD, 0x26BE, 2,
+ 0x26BF, 0x26C3, 1,
+ 0x26C4, 0x26C5, 2,
+ 0x26C6, 0x26CD, 1,
+ 0x26CE, 0x26CE, 2,
+ 0x26CF, 0x26D3, 1,
+ 0x26D4, 0x26D4, 2,
+ 0x26D5, 0x26E9, 1,
+ 0x26EA, 0x26EA, 2,
+ 0x26EB, 0x26F1, 1,
+ 0x26F2, 0x26F3, 2,
+ 0x26F4, 0x26F4, 1,
+ 0x26F5, 0x26F5, 2,
+ 0x26F6, 0x26F9, 1,
+ 0x26FA, 0x26FA, 2,
+ 0x26FB, 0x26FC, 1,
+ 0x26FD, 0x26FD, 2,
+ 0x26FE, 0x2704, 1,
+ 0x2705, 0x2705, 2,
+ 0x2706, 0x2709, 1,
+ 0x270A, 0x270B, 2,
+ 0x270C, 0x2727, 1,
+ 0x2728, 0x2728, 2,
+ 0x2729, 0x274B, 1,
+ 0x274C, 0x274C, 2,
+ 0x274D, 0x274D, 1,
+ 0x274E, 0x274E, 2,
+ 0x274F, 0x2752, 1,
+ 0x2753, 0x2755, 2,
+ 0x2756, 0x2756, 1,
+ 0x2757, 0x2757, 2,
+ 0x2758, 0x2794, 1,
+ 0x2795, 0x2797, 2,
+ 0x2798, 0x27AF, 1,
+ 0x27B0, 0x27B0, 2,
+ 0x27B1, 0x27BE, 1,
+ 0x27BF, 0x27BF, 2,
+ 0x27C0, 0x2B1A, 1,
+ 0x2B1B, 0x2B1C, 2,
+ 0x2B1D, 0x2B4F, 1,
+ 0x2B50, 0x2B50, 2,
+ 0x2B51, 0x2B54, 1,
+ 0x2B55, 0x2B55, 2,
+ 0x2B56, 0x2E5D, 1,
+ 0x2E80, 0x303E, 2,
+ 0x303F, 0x303F, 1,
+ 0x3041, 0x3247, 2,
+ 0x3248, 0x324F, 1,
+ 0x3250, 0x4DBF, 2,
+ 0x4DC0, 0x4DFF, 1,
+ 0x4E00, 0xA4C6, 2,
+ 0xA4D0, 0xA95F, 1,
+ 0xA960, 0xA97C, 2,
+ 0xA980, 0xABF9, 1,
+ 0xAC00, 0xD7A3, 2,
+ 0xD7B0, 0xF8FF, 1,
+ 0xF900, 0xFAFF, 2,
+ 0xFB00, 0xFE0F, 1,
+ 0xFE10, 0xFE19, 2,
+ 0xFE20, 0xFE2F, 1,
+ 0xFE30, 0xFE6B, 2,
+ 0xFE70, 0xFEFF, 1,
+ 0xFF01, 0xFF60, 2,
+ 0xFF61, 0xFFDC, 1,
+ 0xFFE0, 0xFFE6, 2,
+ 0xFFE8, 0x16F9F, 1,
+ 0x16FE0, 0x1B2FB, 2,
+ 0x1BC00, 0x1F003, 1,
+ 0x1F004, 0x1F004, 2,
+ 0x1F005, 0x1F0CE, 1,
+ 0x1F0CF, 0x1F0CF, 2,
+ 0x1F0D1, 0x1F18D, 1,
+ 0x1F18E, 0x1F18E, 2,
+ 0x1F18F, 0x1F190, 1,
+ 0x1F191, 0x1F19A, 2,
+ 0x1F19B, 0x1F1FF, 1,
+ 0x1F200, 0x1F320, 2,
+ 0x1F321, 0x1F32C, 1,
+ 0x1F32D, 0x1F335, 2,
+ 0x1F336, 0x1F336, 1,
+ 0x1F337, 0x1F37C, 2,
+ 0x1F37D, 0x1F37D, 1,
+ 0x1F37E, 0x1F393, 2,
+ 0x1F394, 0x1F39F, 1,
+ 0x1F3A0, 0x1F3CA, 2,
+ 0x1F3CB, 0x1F3CE, 1,
+ 0x1F3CF, 0x1F3D3, 2,
+ 0x1F3D4, 0x1F3DF, 1,
+ 0x1F3E0, 0x1F3F0, 2,
+ 0x1F3F1, 0x1F3F3, 1,
+ 0x1F3F4, 0x1F3F4, 2,
+ 0x1F3F5, 0x1F3F7, 1,
+ 0x1F3F8, 0x1F43E, 2,
+ 0x1F43F, 0x1F43F, 1,
+ 0x1F440, 0x1F440, 2,
+ 0x1F441, 0x1F441, 1,
+ 0x1F442, 0x1F4FC, 2,
+ 0x1F4FD, 0x1F4FE, 1,
+ 0x1F4FF, 0x1F53D, 2,
+ 0x1F53E, 0x1F54A, 1,
+ 0x1F54B, 0x1F54E, 2,
+ 0x1F54F, 0x1F54F, 1,
+ 0x1F550, 0x1F567, 2,
+ 0x1F568, 0x1F579, 1,
+ 0x1F57A, 0x1F57A, 2,
+ 0x1F57B, 0x1F594, 1,
+ 0x1F595, 0x1F596, 2,
+ 0x1F597, 0x1F5A3, 1,
+ 0x1F5A4, 0x1F5A4, 2,
+ 0x1F5A5, 0x1F5FA, 1,
+ 0x1F5FB, 0x1F64F, 2,
+ 0x1F650, 0x1F67F, 1,
+ 0x1F680, 0x1F6C5, 2,
+ 0x1F6C6, 0x1F6CB, 1,
+ 0x1F6CC, 0x1F6CC, 2,
+ 0x1F6CD, 0x1F6CF, 1,
+ 0x1F6D0, 0x1F6D2, 2,
+ 0x1F6D3, 0x1F6D4, 1,
+ 0x1F6D5, 0x1F6DF, 2,
+ 0x1F6E0, 0x1F6EA, 1,
+ 0x1F6EB, 0x1F6EC, 2,
+ 0x1F6F0, 0x1F6F3, 1,
+ 0x1F6F4, 0x1F6FC, 2,
+ 0x1F700, 0x1F7D9, 1,
+ 0x1F7E0, 0x1F7F0, 2,
+ 0x1F800, 0x1F90B, 1,
+ 0x1F90C, 0x1F93A, 2,
+ 0x1F93B, 0x1F93B, 1,
+ 0x1F93C, 0x1F945, 2,
+ 0x1F946, 0x1F946, 1,
+ 0x1F947, 0x1F9FF, 2,
+ 0x1FA00, 0x1FA6D, 1,
+ 0x1FA70, 0x1FAF8, 2,
+ 0x1FB00, 0x1FBF9, 1,
+ 0x20000, 0x3FFFD, 2,
+ 0xE0001, 0x10FFFD, 1,
+};
+
+//
+// End of Unicode 15.1.0 block.
+//
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _UCG_TABLES_INCLUDED */
diff --git a/src/unicode.cpp b/src/unicode.cpp
index c244a323c..cb9fb12ab 100644
--- a/src/unicode.cpp
+++ b/src/unicode.cpp
@@ -1,10 +1,15 @@
-#pragma warning(push)
-#pragma warning(disable: 4245)
+#if defined(GB_SYSTEM_WINDOWS)
+ #pragma warning(push)
+ #pragma warning(disable: 4245)
+#endif
extern "C" {
#include "utf8proc/utf8proc.c"
}
-#pragma warning(pop)
+
+#if defined(GB_SYSTEM_WINDOWS)
+ #pragma warning(pop)
+#endif
gb_internal bool rune_is_letter(Rune r) {
@@ -109,7 +114,7 @@ gb_internal isize utf8_decode(u8 const *str, isize str_len, Rune *codepoint_out)
u8 b1, b2, b3;
Utf8AcceptRange accept;
if (x >= 0xf0) {
- Rune mask = (cast(Rune)x << 31) >> 31;
+ Rune mask = -cast(Rune)(x & 1);
codepoint = (cast(Rune)s0 & (~mask)) | (GB_RUNE_INVALID & mask);
width = 1;
goto end;
@@ -162,3 +167,8 @@ end:
if (codepoint_out) *codepoint_out = codepoint;
return width;
}
+
+// NOTE(Feoramund): It's down here because I made UCG use the utf8_decode above to avoid duplicating code.
+extern "C" {
+#include "ucg/ucg.c"
+}