diff options
| author | gingerBill <gingerBill@users.noreply.github.com> | 2025-01-06 13:43:01 +0000 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-01-06 13:43:01 +0000 |
| commit | 6e49bbb66853b5d824ac5bbd534ae3e81c4f39aa (patch) | |
| tree | 50886a3be8f2fcfab053e07cfe9e15f50fa5f9f6 /src | |
| parent | bd96cd0af761994210018ca647eb843dfeb71494 (diff) | |
| parent | 98efb03934b464a1b23759b5695a12ff37588357 (diff) | |
Merge branch 'master' into d3d11-annotations
Diffstat (limited to 'src')
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 ®_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 ¶m_value, TokenPos const &pos); +gb_internal lbValue lb_handle_param_value(lbProcedure *p, Type *parameter_type, ParameterValue const ¶m_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(¶meters, 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(¶meters, 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(¶meters, lb_debug_procedure_parameters(m, results->Tuple.variables[i]->type)); + } + } + + if (type->Proc.calling_convention == ProcCC_Odin) { + array_add(¶meters, 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 ¶m_value, TokenPos const &pos) { +gb_internal lbValue lb_handle_param_value(lbProcedure *p, Type *parameter_type, ParameterValue const ¶m_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(¶ms, 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 Binary files differindex 4b3cdb9b0..510569b0e 100644 --- a/src/tilde/tb.lib +++ b/src/tilde/tb.lib 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" +} |