Merge branch 'master' into fix/freebsd-syscall

This commit is contained in:
gingerBill
2022-07-24 22:27:45 +01:00
committed by GitHub
690 changed files with 138305 additions and 37514 deletions
+6
View File
@@ -77,15 +77,21 @@ template <typename T> Slice<T> slice_from_array(Array<T> const &a);
template <typename T>
Slice<T> slice_make(gbAllocator const &allocator, isize count) {
GB_ASSERT(count >= 0);
Slice<T> s = {};
s.data = gb_alloc_array(allocator, T, count);
GB_ASSERT(s.data != nullptr);
s.count = count;
return s;
}
template <typename T>
void slice_init(Slice<T> *s, gbAllocator const &allocator, isize count) {
GB_ASSERT(count >= 0);
s->data = gb_alloc_array(allocator, T, count);
if (count > 0) {
GB_ASSERT(s->data != nullptr);
}
s->count = count;
}
+27 -3
View File
@@ -40,7 +40,7 @@ typedef mp_int BigInt;
void big_int_from_u64(BigInt *dst, u64 x);
void big_int_from_i64(BigInt *dst, i64 x);
void big_int_init (BigInt *dst, BigInt const *src);
void big_int_from_string(BigInt *dst, String const &s);
void big_int_from_string(BigInt *dst, String const &s, bool *success);
void big_int_dealloc(BigInt *dst) {
mp_clear(dst);
@@ -84,7 +84,7 @@ void big_int_quo_eq(BigInt *dst, BigInt const *x);
void big_int_rem_eq(BigInt *dst, BigInt const *x);
bool big_int_is_neg(BigInt const *x);
void big_int_neg(BigInt *dst, BigInt const *x);
void big_int_add_eq(BigInt *dst, BigInt const *x) {
BigInt res = {};
@@ -169,7 +169,11 @@ BigInt big_int_make_i64(i64 x) {
}
void big_int_from_string(BigInt *dst, String const &s) {
void big_int_from_string(BigInt *dst, String const &s, bool *success) {
*success = true;
bool is_negative = false;
u64 base = 10;
bool has_prefix = false;
if (s.len > 2 && s[0] == '0') {
@@ -197,11 +201,26 @@ void big_int_from_string(BigInt *dst, String const &s) {
isize i = 0;
for (; i < len; i++) {
Rune r = cast(Rune)text[i];
if (r == '-') {
if (is_negative) {
// NOTE(Jeroen): Can't have a doubly negative number.
*success = false;
return;
}
is_negative = true;
continue;
}
if (r == '_') {
continue;
}
u64 v = u64_digit_value(r);
if (v >= base) {
// NOTE(Jeroen): Can still be a valid integer if the next character is an `e` or `E`.
if (r != 'e' && r != 'E') {
*success = false;
}
break;
}
BigInt val = big_int_make_u64(v);
@@ -225,6 +244,7 @@ void big_int_from_string(BigInt *dst, String const &s) {
if (gb_char_is_digit(r)) {
v = u64_digit_value(r);
} else {
*success = false;
break;
}
exp *= 10;
@@ -234,6 +254,10 @@ void big_int_from_string(BigInt *dst, String const &s) {
big_int_mul_eq(dst, &b);
}
}
if (is_negative) {
big_int_neg(dst, dst);
}
}
+37 -16
View File
@@ -17,6 +17,11 @@
#include <sys/sysctl.h>
#endif
#if defined(GB_SYSTEM_OPENBSD)
#include <sys/sysctl.h>
#include <sys/utsname.h>
#endif
/*
NOTE(Jeroen): This prints the Windows product edition only, to be called from `print_platform_details`.
*/
@@ -140,7 +145,7 @@ void report_windows_product_type(DWORD ProductType) {
break;
default:
gb_printf("Unknown Edition (%08x)", ProductType);
gb_printf("Unknown Edition (%08x)", cast(unsigned)ProductType);
}
}
#endif
@@ -242,6 +247,14 @@ void report_ram_info() {
if (sysctl(sysctls, 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 sysctls[] = { CTL_HW, HW_PHYSMEM64 };
if (sysctl(sysctls, 2, &ram_amount, &val_size, NULL, 0) != -1) {
gb_printf("%lld MiB\n", ram_amount / gb_megabytes(1));
}
#else
gb_printf("Unknown.\n");
#endif
@@ -316,14 +329,14 @@ void print_bug_report_help() {
}
if (false) {
gb_printf("dwMajorVersion: %d\n", osvi.dwMajorVersion);
gb_printf("dwMinorVersion: %d\n", osvi.dwMinorVersion);
gb_printf("dwBuildNumber: %d\n", osvi.dwBuildNumber);
gb_printf("dwPlatformId: %d\n", osvi.dwPlatformId);
gb_printf("wServicePackMajor: %d\n", osvi.wServicePackMajor);
gb_printf("wServicePackMinor: %d\n", osvi.wServicePackMinor);
gb_printf("wSuiteMask: %d\n", osvi.wSuiteMask);
gb_printf("wProductType: %d\n", osvi.wProductType);
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 ");
@@ -441,18 +454,18 @@ void print_bug_report_help() {
TEXT("DisplayVersion"),
RRF_RT_REG_SZ,
ValueType,
&DisplayVersion,
DisplayVersion,
&ValueSize
);
if (status == 0x0) {
gb_printf(" (version: %s)", &DisplayVersion);
gb_printf(" (version: %s)", DisplayVersion);
}
/*
Now print build number.
*/
gb_printf(", build %d", osvi.dwBuildNumber);
gb_printf(", build %u", cast(unsigned)osvi.dwBuildNumber);
ValueSize = sizeof(UBR);
status = RegGetValue(
@@ -466,18 +479,18 @@ void print_bug_report_help() {
);
if (status == 0x0) {
gb_printf(".%d", UBR);
gb_printf(".%u", cast(unsigned)UBR);
}
gb_printf("\n");
}
#elif defined(GB_SYSTEM_LINUX)
/*
Try to parse `/usr/lib/os-release` for `PRETTY_NAME="Ubuntu 20.04.3 LTS`
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, "/usr/lib/os-release");
gbFileContents release = gb_file_read_contents(a, 1, "/etc/os-release");
defer (gb_file_free_contents(&release));
b32 found = 0;
@@ -643,6 +656,14 @@ void print_bug_report_help() {
} else {
gb_printf("macOS: Unknown\n");
}
#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");
}
#else
gb_printf("Unknown\n");
@@ -657,4 +678,4 @@ void print_bug_report_help() {
And RAM info.
*/
report_ram_info();
}
}
+573 -51
View File
@@ -1,14 +1,13 @@
#if defined(GB_SYSTEM_FREEBSD)
#if defined(GB_SYSTEM_FREEBSD) || defined(GB_SYSTEM_OPENBSD)
#include <sys/types.h>
#include <sys/sysctl.h>
#endif
// #if defined(GB_SYSTEM_WINDOWS)
#define DEFAULT_TO_THREADED_CHECKER
// #define DEFAULT_TO_THREADED_CHECKER
// #endif
enum TargetOsKind {
enum TargetOsKind : u16 {
TargetOs_Invalid,
TargetOs_windows,
@@ -16,6 +15,7 @@ enum TargetOsKind {
TargetOs_linux,
TargetOs_essence,
TargetOs_freebsd,
TargetOs_openbsd,
TargetOs_wasi,
TargetOs_js,
@@ -25,11 +25,12 @@ enum TargetOsKind {
TargetOs_COUNT,
};
enum TargetArchKind {
enum TargetArchKind : u16 {
TargetArch_Invalid,
TargetArch_amd64,
TargetArch_386,
TargetArch_i386,
TargetArch_arm32,
TargetArch_arm64,
TargetArch_wasm32,
TargetArch_wasm64,
@@ -37,7 +38,7 @@ enum TargetArchKind {
TargetArch_COUNT,
};
enum TargetEndianKind {
enum TargetEndianKind : u8 {
TargetEndian_Invalid,
TargetEndian_Little,
@@ -46,6 +47,16 @@ enum TargetEndianKind {
TargetEndian_COUNT,
};
enum TargetABIKind : u16 {
TargetABI_Default,
TargetABI_Win64,
TargetABI_SysV,
TargetABI_COUNT,
};
String target_os_names[TargetOs_COUNT] = {
str_lit(""),
str_lit("windows"),
@@ -53,6 +64,7 @@ String target_os_names[TargetOs_COUNT] = {
str_lit("linux"),
str_lit("essence"),
str_lit("freebsd"),
str_lit("openbsd"),
str_lit("wasi"),
str_lit("js"),
@@ -63,7 +75,8 @@ String target_os_names[TargetOs_COUNT] = {
String target_arch_names[TargetArch_COUNT] = {
str_lit(""),
str_lit("amd64"),
str_lit("386"),
str_lit("i386"),
str_lit("arm32"),
str_lit("arm64"),
str_lit("wasm32"),
str_lit("wasm64"),
@@ -75,12 +88,19 @@ String target_endian_names[TargetEndian_COUNT] = {
str_lit("big"),
};
String target_abi_names[TargetABI_COUNT] = {
str_lit(""),
str_lit("win64"),
str_lit("sysv"),
};
TargetEndianKind target_endians[TargetArch_COUNT] = {
TargetEndian_Invalid,
TargetEndian_Little,
TargetEndian_Little,
TargetEndian_Little,
TargetEndian_Little,
TargetEndian_Little,
};
#ifndef ODIN_VERSION_RAW
@@ -98,6 +118,7 @@ struct TargetMetrics {
isize max_align;
String target_triplet;
String target_data_layout;
TargetABIKind abi;
};
@@ -119,6 +140,8 @@ enum BuildModeKind {
BuildMode_Object,
BuildMode_Assembly,
BuildMode_LLVM_IR,
BuildMode_COUNT,
};
enum CommandKind : u32 {
@@ -163,19 +186,50 @@ enum TimingsExportFormat : i32 {
TimingsExportCSV = 2,
};
enum ErrorPosStyle {
ErrorPosStyle_Default, // path(line:column) msg
ErrorPosStyle_Unix, // path:line:column: msg
ErrorPosStyle_COUNT
};
enum RelocMode : u8 {
RelocMode_Default,
RelocMode_Static,
RelocMode_PIC,
RelocMode_DynamicNoPIC,
};
enum BuildPath : u8 {
BuildPath_Main_Package, // Input Path to the package directory (or file) we're building.
BuildPath_RC, // Input Path for .rc file, can be set with `-resource:`.
BuildPath_RES, // Output Path for .res file, generated from previous.
BuildPath_Win_SDK_Root, // windows_sdk_root
BuildPath_Win_SDK_UM_Lib, // windows_sdk_um_library_path
BuildPath_Win_SDK_UCRT_Lib, // windows_sdk_ucrt_library_path
BuildPath_VS_EXE, // vs_exe_path
BuildPath_VS_LIB, // vs_library_path
BuildPath_Output, // Output Path for .exe, .dll, .so, etc. Can be overridden with `-out:`.
BuildPath_PDB, // Output Path for .pdb file, can be overridden with `-pdb-name:`.
BuildPathCOUNT,
};
// 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_ENDIAN; // target endian
String ODIN_VENDOR; // compiler vendor
String ODIN_VERSION; // compiler version
String ODIN_ROOT; // Odin ROOT
String ODIN_BUILD_MODE;
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_FOREIGN_ERROR_PROCEDURES;
ErrorPosStyle ODIN_ERROR_POS_STYLE;
TargetEndianKind endian_kind;
@@ -190,12 +244,17 @@ struct BuildContext {
bool show_help;
Array<Path> build_paths; // Contains `Path` objects to output filename, pdb, resource and intermediate files.
// BuildPath enum contains the indices of paths we know *before* the work starts.
String out_filepath;
String resource_filepath;
String pdb_filepath;
bool has_resource;
String link_flags;
String extra_linker_flags;
String extra_assembler_flags;
String microarch;
BuildModeKind build_mode;
bool generate_docs;
@@ -242,6 +301,12 @@ struct BuildContext {
bool copy_file_contents;
bool disallow_rtti;
RelocMode reloc_mode;
bool disable_red_zone;
u32 cmd_doc_flags;
Array<String> extra_packages;
@@ -253,10 +318,13 @@ struct BuildContext {
isize thread_count;
PtrMap<char const *, ExactValue> defined_values;
BlockingMutex target_features_mutex;
StringSet target_features_set;
String target_features_string;
};
gb_global BuildContext build_context = {0};
bool global_warnings_as_errors(void) {
@@ -267,9 +335,9 @@ bool global_ignore_warnings(void) {
}
gb_global TargetMetrics target_windows_386 = {
gb_global TargetMetrics target_windows_i386 = {
TargetOs_windows,
TargetArch_386,
TargetArch_i386,
4,
8,
str_lit("i386-pc-windows-msvc"),
@@ -283,9 +351,9 @@ gb_global TargetMetrics target_windows_amd64 = {
str_lit("e-m:w-i64:64-f80:128-n8:16:32:64-S128"),
};
gb_global TargetMetrics target_linux_386 = {
gb_global TargetMetrics target_linux_i386 = {
TargetOs_linux,
TargetArch_386,
TargetArch_i386,
4,
8,
str_lit("i386-pc-linux-gnu"),
@@ -299,6 +367,23 @@ gb_global TargetMetrics target_linux_amd64 = {
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,
16,
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,
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"),
};
gb_global TargetMetrics target_darwin_amd64 = {
TargetOs_darwin,
@@ -318,9 +403,9 @@ gb_global TargetMetrics target_darwin_arm64 = {
str_lit("e-m:o-i64:64-i128:128-n32:64-S128"), // TODO(bill): Is this correct?
};
gb_global TargetMetrics target_freebsd_386 = {
gb_global TargetMetrics target_freebsd_i386 = {
TargetOs_freebsd,
TargetArch_386,
TargetArch_i386,
4,
8,
str_lit("i386-unknown-freebsd-elf"),
@@ -335,6 +420,15 @@ gb_global TargetMetrics target_freebsd_amd64 = {
str_lit("e-m:w-i64:64-f80:128-n8:16:32:64-S128"),
};
gb_global TargetMetrics target_openbsd_amd64 = {
TargetOs_openbsd,
TargetArch_amd64,
8,
16,
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_essence_amd64 = {
TargetOs_essence,
TargetArch_amd64,
@@ -361,6 +455,15 @@ gb_global TargetMetrics target_js_wasm32 = {
str_lit(""),
};
gb_global TargetMetrics target_js_wasm64 = {
TargetOs_js,
TargetArch_wasm64,
8,
16,
str_lit("wasm64-js-js"),
str_lit(""),
};
gb_global TargetMetrics target_wasi_wasm32 = {
TargetOs_wasi,
TargetArch_wasm32,
@@ -380,6 +483,16 @@ gb_global TargetMetrics target_wasi_wasm32 = {
// str_lit(""),
// };
gb_global TargetMetrics target_freestanding_amd64_sysv = {
TargetOs_freestanding,
TargetArch_amd64,
8,
16,
str_lit("x86_64-pc-none-gnu"),
str_lit("e-m:w-i64:64-f80:128-n8:16:32:64-S128"),
TargetABI_SysV,
};
struct NamedTargetMetrics {
@@ -391,16 +504,21 @@ gb_global NamedTargetMetrics named_targets[] = {
{ str_lit("darwin_amd64"), &target_darwin_amd64 },
{ str_lit("darwin_arm64"), &target_darwin_arm64 },
{ str_lit("essence_amd64"), &target_essence_amd64 },
{ str_lit("linux_386"), &target_linux_386 },
{ str_lit("linux_i386"), &target_linux_i386 },
{ str_lit("linux_amd64"), &target_linux_amd64 },
{ str_lit("windows_386"), &target_windows_386 },
{ str_lit("linux_arm64"), &target_linux_arm64 },
{ str_lit("linux_arm32"), &target_linux_arm32 },
{ str_lit("windows_i386"), &target_windows_i386 },
{ str_lit("windows_amd64"), &target_windows_amd64 },
{ str_lit("freebsd_386"), &target_freebsd_386 },
{ str_lit("freebsd_i386"), &target_freebsd_i386 },
{ str_lit("freebsd_amd64"), &target_freebsd_amd64 },
{ str_lit("openbsd_amd64"), &target_openbsd_amd64 },
{ str_lit("freestanding_wasm32"), &target_freestanding_wasm32 },
{ str_lit("wasi_wasm32"), &target_wasi_wasm32 },
{ str_lit("js_wasm32"), &target_js_wasm32 },
// { str_lit("freestanding_wasm64"), &target_freestanding_wasm64 },
{ str_lit("js_wasm64"), &target_js_wasm64 },
{ str_lit("freestanding_amd64_sysv"), &target_freestanding_amd64_sysv },
};
NamedTargetMetrics *selected_target_metrics;
@@ -514,6 +632,15 @@ bool is_arch_wasm(void) {
return false;
}
bool is_arch_x86(void) {
switch (build_context.metrics.arch) {
case TargetArch_i386:
case TargetArch_amd64:
return true;
}
return false;
}
bool allow_check_foreign_filepath(void) {
switch (build_context.metrics.arch) {
case TargetArch_wasm32:
@@ -523,7 +650,6 @@ bool allow_check_foreign_filepath(void) {
return true;
}
// TODO(bill): OS dependent versions for the BuildContext
// join_path
// is_dir
@@ -702,10 +828,38 @@ String internal_odin_root_dir(void) {
len = readlink("/proc/curproc/exe", &path_buf[0], path_buf.count);
#elif defined(GB_SYSTEM_DRAGONFLYBSD)
len = readlink("/proc/curproc/file", &path_buf[0], path_buf.count);
#else
#elif defined(GB_SYSTEM_LINUX)
len = readlink("/proc/self/exe", &path_buf[0], path_buf.count);
#elif defined(GB_SYSTEM_OPENBSD)
int error;
int mib[] = {
CTL_KERN,
KERN_PROC_ARGS,
getpid(),
KERN_PROC_ARGV,
};
// get argv size
error = sysctl(mib, 4, NULL, (size_t *) &len, NULL, 0);
if (error == -1) {
// sysctl error
return make_string(nullptr, 0);
}
// get argv
char **argv = (char **)gb_malloc(len);
error = sysctl(mib, 4, argv, (size_t *) &len, NULL, 0);
if (error == -1) {
// sysctl error
gb_mfree(argv);
return make_string(nullptr, 0);
}
// copy argv[0] to path_buf
len = gb_strlen(argv[0]);
if(len < path_buf.count) {
gb_memmove(&path_buf[0], argv[0], len);
}
gb_mfree(argv);
#endif
if(len == 0) {
if(len == 0 || len == -1) {
return make_string(nullptr, 0);
}
if (len < path_buf.count) {
@@ -821,6 +975,33 @@ bool show_error_line(void) {
return build_context.show_error_line;
}
bool has_asm_extension(String const &path) {
String ext = path_extension(path);
if (ext == ".asm") {
return true;
} else if (ext == ".s") {
return true;
} else if (ext == ".S") {
return true;
}
return false;
}
// temporary
char *token_pos_to_string(TokenPos const &pos) {
gbString s = gb_string_make_reserve(temporary_allocator(), 128);
String file = get_file_path_string(pos.file_id);
switch (build_context.ODIN_ERROR_POS_STYLE) {
default: /*fallthrough*/
case ErrorPosStyle_Default:
s = gb_string_append_fmt(s, "%.*s(%d:%d)", LIT(file), pos.line, pos.column);
break;
case ErrorPosStyle_Unix:
s = gb_string_append_fmt(s, "%.*s:%d:%d:", LIT(file), pos.line, pos.column);
break;
}
return s;
}
void init_build_context(TargetMetrics *cross_target) {
BuildContext *bc = &build_context;
@@ -833,25 +1014,31 @@ void init_build_context(TargetMetrics *cross_target) {
bc->ODIN_VENDOR = str_lit("odin");
bc->ODIN_VERSION = ODIN_VERSION;
bc->ODIN_ROOT = odin_root_dir();
switch (bc->build_mode) {
default:
case BuildMode_Executable:
bc->ODIN_BUILD_MODE = str_lit("executable");
break;
case BuildMode_DynamicLibrary:
bc->ODIN_BUILD_MODE = str_lit("dynamic");
break;
case BuildMode_Object:
bc->ODIN_BUILD_MODE = str_lit("object");
break;
case BuildMode_Assembly:
bc->ODIN_BUILD_MODE = str_lit("assembly");
break;
case BuildMode_LLVM_IR:
bc->ODIN_BUILD_MODE = str_lit("llvm-ir");
break;
{
char const *found = gb_get_env("ODIN_ERROR_POS_STYLE", permanent_allocator());
if (found) {
ErrorPosStyle kind = ErrorPosStyle_Default;
String style = make_string_c(found);
style = string_trim_whitespace(style);
if (style == "" || style == "default" || style == "odin") {
kind = ErrorPosStyle_Default;
} else if (style == "unix" || style == "gcc" || style == "clang" || style == "llvm") {
kind = ErrorPosStyle_Unix;
} else {
gb_printf_err("Invalid ODIN_ERROR_POS_STYLE: got %.*s\n", LIT(style));
gb_printf_err("Valid formats:\n");
gb_printf_err("\t\"default\" or \"odin\"\n");
gb_printf_err("\t\tpath(line:column) message\n");
gb_printf_err("\t\"unix\"\n");
gb_printf_err("\t\tpath:line:column: message\n");
gb_exit(1);
}
build_context.ODIN_ERROR_POS_STYLE = kind;
}
}
bc->copy_file_contents = true;
TargetMetrics *metrics = nullptr;
@@ -867,18 +1054,22 @@ void init_build_context(TargetMetrics *cross_target) {
#endif
#elif defined(GB_SYSTEM_FREEBSD)
metrics = &target_freebsd_amd64;
#elif defined(GB_SYSTEM_OPENBSD)
metrics = &target_openbsd_amd64;
#elif defined(GB_CPU_ARM)
metrics = &target_linux_arm64;
#else
metrics = &target_linux_amd64;
#endif
#else
#if defined(GB_SYSTEM_WINDOWS)
metrics = &target_windows_386;
metrics = &target_windows_i386;
#elif defined(GB_SYSTEM_OSX)
#error "Build Error: Unsupported architecture"
#elif defined(GB_SYSTEM_FREEBSD)
metrics = &target_freebsd_386;
metrics = &target_freebsd_i386;
#else
metrics = &target_linux_386;
metrics = &target_linux_i386;
#endif
#endif
@@ -897,7 +1088,6 @@ void init_build_context(TargetMetrics *cross_target) {
bc->metrics = *metrics;
bc->ODIN_OS = target_os_names[metrics->os];
bc->ODIN_ARCH = target_arch_names[metrics->arch];
bc->ODIN_ENDIAN = target_endian_names[target_endians[metrics->arch]];
bc->endian_kind = target_endians[metrics->arch];
bc->word_size = metrics->word_size;
bc->max_align = metrics->max_align;
@@ -907,6 +1097,21 @@ void init_build_context(TargetMetrics *cross_target) {
bc->threaded_checker = true;
#endif
if (bc->disable_red_zone) {
if (is_arch_wasm() && bc->metrics.os == TargetOs_freestanding) {
gb_printf_err("-disable-red-zone is not support for this target");
gb_exit(1);
}
}
if (bc->metrics.os == TargetOs_freestanding) {
bc->no_entry_point = true;
} else {
if (bc->disallow_rtti) {
gb_printf_err("-disallow-rtti is only allowed on freestanding targets\n");
gb_exit(1);
}
}
// 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
@@ -924,8 +1129,11 @@ void init_build_context(TargetMetrics *cross_target) {
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_386) {
} else if (bc->metrics.arch == TargetArch_i386) {
switch (bc->metrics.os) {
case TargetOs_windows:
bc->link_flags = str_lit("/machine:x86 ");
@@ -941,11 +1149,23 @@ void init_build_context(TargetMetrics *cross_target) {
bc->link_flags = str_lit("-arch x86 ");
break;
}
} else if (bc->metrics.arch == TargetArch_arm32) {
switch (bc->metrics.os) {
case TargetOs_linux:
bc->link_flags = str_lit("-arch arm ");
break;
default:
gb_printf_err("Compiler Error: Unsupported architecture\n");
gb_exit(1);
}
} else if (bc->metrics.arch == TargetArch_arm64) {
switch (bc->metrics.os) {
case TargetOs_darwin:
bc->link_flags = str_lit("-arch arm64 ");
break;
case TargetOs_linux:
bc->link_flags = str_lit("-arch aarch64 ");
break;
}
} else if (is_arch_wasm()) {
gbString link_flags = gb_string_make(heap_allocator(), " ");
@@ -953,16 +1173,16 @@ void init_build_context(TargetMetrics *cross_target) {
// 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, "-mwas64 ");
link_flags = gb_string_appendc(link_flags, "-mwasm64 ");
}
if (bc->metrics.os == TargetOs_freestanding) {
if (bc->no_entry_point) {
link_flags = gb_string_appendc(link_flags, "--no-entry ");
}
bc->link_flags = make_string_c(link_flags);
// Disallow on wasm
build_context.use_separate_modules = false;
bc->use_separate_modules = false;
} else {
gb_printf_err("Compiler Error: Unsupported architecture\n");
gb_exit(1);
@@ -973,3 +1193,305 @@ void init_build_context(TargetMetrics *cross_target) {
#undef LINK_FLAG_X64
#undef LINK_FLAG_386
}
#if defined(GB_SYSTEM_WINDOWS)
// NOTE(IC): In order to find Visual C++ paths without relying on environment variables.
// NOTE(Jeroen): No longer needed in `main.cpp -> linker_stage`. We now resolve those paths in `init_build_paths`.
#include "microsoft_craziness.h"
#endif
Array<String> split_by_comma(String const &list) {
isize n = 1;
for (isize i = 0; i < list.len; i++) {
if (list.text[i] == ',') {
n++;
}
}
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;
}
res[i] = substring(s, 0, m);
s = substring(s, m+1, s.len);
}
return res;
}
bool check_target_feature_is_valid(TokenPos pos, String const &feature) {
// TODO(bill): check_target_feature_is_valid
return true;
}
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_array(i, items) {
String const &item = items.data[i];
if (!check_target_feature_is_valid(pos, item)) {
error(pos, "Target feature '%.*s' is not valid", LIT(item));
return false;
}
if (!string_set_exists(&bc->target_features_set, item)) {
error(pos, "Target feature '%.*s' is not enabled", LIT(item));
return false;
}
}
return true;
}
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);
array_free(&items);
for_array(i, items) {
String const &item = items.data[i];
if (!check_target_feature_is_valid(pos, item)) {
error(pos, "Target feature '%.*s' is not valid", LIT(item));
}
}
}
char const *target_features_set_to_cstring(gbAllocator allocator, bool with_quotes) {
isize len = 0;
for_array(i, build_context.target_features_set.entries) {
if (i != 0) {
len += 1;
}
String feature = build_context.target_features_set.entries[i].value;
len += feature.len;
if (with_quotes) len += 2;
}
char *features = gb_alloc_array(allocator, char, len+1);
len = 0;
for_array(i, build_context.target_features_set.entries) {
if (i != 0) {
features[len++] = ',';
}
if (with_quotes) features[len++] = '"';
String feature = build_context.target_features_set.entries[i].value;
gb_memmove(features, feature.text, feature.len);
len += feature.len;
if (with_quotes) features[len++] = '"';
}
features[len++] = 0;
return features;
}
// NOTE(Jeroen): Set/create the output and other paths and report an error as appropriate.
// We've previously called `parse_build_flags`, so `out_filepath` should be set.
bool init_build_paths(String init_filename) {
gbAllocator ha = heap_allocator();
BuildContext *bc = &build_context;
// NOTE(Jeroen): We're pre-allocating BuildPathCOUNT slots so that certain paths are always at the same enumerated index.
array_init(&bc->build_paths, permanent_allocator(), BuildPathCOUNT);
string_set_init(&bc->target_features_set, heap_allocator(), 1024);
mutex_init(&bc->target_features_mutex);
// [BuildPathMainPackage] Turn given init path into a `Path`, which includes normalizing it into a full path.
bc->build_paths[BuildPath_Main_Package] = path_from_string(ha, init_filename);
bool produces_output_file = false;
if (bc->command_kind == Command_doc && bc->cmd_doc_flags & CmdDocFlag_DocFormat) {
produces_output_file = true;
} else if (bc->command_kind & Command__does_build) {
produces_output_file = true;
}
if (!produces_output_file) {
// Command doesn't produce output files. We're done.
return true;
}
#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"));
}
if (bc->pdb_filepath.len > 0) {
bc->build_paths[BuildPath_PDB] = path_from_string(ha, bc->pdb_filepath);
}
if ((bc->command_kind & Command__does_build) && (!bc->ignore_microsoft_magic)) {
// NOTE(ic): It would be nice to extend this so that we could specify the Visual Studio version that we want instead of defaulting to the latest.
Find_Result_Utf8 find_result = find_visual_studio_and_windows_sdk_utf8();
defer (mc_free_all());
if (find_result.windows_sdk_version == 0) {
gb_printf_err("Windows SDK not found.\n");
return false;
}
if (!build_context.use_lld && find_result.vs_exe_path.len == 0) {
gb_printf_err("link.exe not found.\n");
return false;
}
if (find_result.vs_library_path.len == 0) {
gb_printf_err("VS library path not found.\n");
return false;
}
if (find_result.windows_sdk_um_library_path.len > 0) {
GB_ASSERT(find_result.windows_sdk_ucrt_library_path.len > 0);
if (find_result.windows_sdk_root.len > 0) {
bc->build_paths[BuildPath_Win_SDK_Root] = path_from_string(ha, find_result.windows_sdk_root);
}
if (find_result.windows_sdk_um_library_path.len > 0) {
bc->build_paths[BuildPath_Win_SDK_UM_Lib] = path_from_string(ha, find_result.windows_sdk_um_library_path);
}
if (find_result.windows_sdk_ucrt_library_path.len > 0) {
bc->build_paths[BuildPath_Win_SDK_UCRT_Lib] = path_from_string(ha, find_result.windows_sdk_ucrt_library_path);
}
if (find_result.vs_exe_path.len > 0) {
bc->build_paths[BuildPath_VS_EXE] = path_from_string(ha, find_result.vs_exe_path);
}
if (find_result.vs_library_path.len > 0) {
bc->build_paths[BuildPath_VS_LIB] = path_from_string(ha, find_result.vs_library_path);
}
}
}
}
#endif
// All the build targets and OSes.
String output_extension;
if (bc->command_kind == Command_doc && bc->cmd_doc_flags & CmdDocFlag_DocFormat) {
output_extension = STR_LIT("odin-doc");
} else if (is_arch_wasm()) {
output_extension = STR_LIT("wasm");
} else if (build_context.build_mode == BuildMode_Executable) {
// By default use a .bin executable extension.
output_extension = STR_LIT("bin");
if (build_context.metrics.os == TargetOs_windows) {
output_extension = STR_LIT("exe");
} else if (build_context.cross_compiling && selected_target_metrics->metrics == &target_essence_amd64) {
output_extension = make_string(nullptr, 0);
}
} else if (build_context.build_mode == BuildMode_DynamicLibrary) {
// By default use a .so shared library extension.
output_extension = STR_LIT("so");
if (build_context.metrics.os == TargetOs_windows) {
output_extension = STR_LIT("dll");
} else if (build_context.metrics.os == TargetOs_darwin) {
output_extension = STR_LIT("dylib");
}
} else if (build_context.build_mode == BuildMode_Object) {
// By default use a .o object extension.
output_extension = STR_LIT("o");
if (build_context.metrics.os == TargetOs_windows) {
output_extension = STR_LIT("obj");
}
} else if (build_context.build_mode == BuildMode_Assembly) {
// By default use a .S asm extension.
output_extension = STR_LIT("S");
} else if (build_context.build_mode == BuildMode_LLVM_IR) {
output_extension = STR_LIT("ll");
} else {
GB_PANIC("Unhandled build mode/target combination.\n");
}
if (bc->out_filepath.len > 0) {
bc->build_paths[BuildPath_Output] = path_from_string(ha, bc->out_filepath);
if (build_context.metrics.os == TargetOs_windows) {
String output_file = path_to_string(ha, bc->build_paths[BuildPath_Output]);
defer (gb_free(ha, output_file.text));
if (path_is_directory(bc->build_paths[BuildPath_Output])) {
gb_printf_err("Output path %.*s is a directory.\n", LIT(output_file));
return false;
} else if (bc->build_paths[BuildPath_Output].ext.len == 0) {
gb_printf_err("Output path %.*s must have an appropriate extension.\n", LIT(output_file));
return false;
}
}
} else {
Path output_path;
if (str_eq(init_filename, str_lit("."))) {
// We must name the output file after the current directory.
debugf("Output name will be created from current base name %.*s.\n", LIT(bc->build_paths[BuildPath_Main_Package].basename));
String last_element = last_path_element(bc->build_paths[BuildPath_Main_Package].basename);
if (last_element.len == 0) {
gb_printf_err("The output name is created from the last path element. `%.*s` has none. Use `-out:output_name.ext` to set it.\n", LIT(bc->build_paths[BuildPath_Main_Package].basename));
return false;
}
output_path.basename = copy_string(ha, bc->build_paths[BuildPath_Main_Package].basename);
output_path.name = copy_string(ha, last_element);
} else {
// Init filename was not 'current path'.
// Contruct the output name from the path elements as usual.
String output_name = init_filename;
// If it ends with a trailing (back)slash, strip it before continuing.
while (output_name.len > 0 && (output_name[output_name.len-1] == '/' || output_name[output_name.len-1] == '\\')) {
output_name.len -= 1;
}
output_name = remove_directory_from_path(output_name);
output_name = remove_extension_from_path(output_name);
output_name = copy_string(ha, string_trim_whitespace(output_name));
output_path = path_from_string(ha, output_name);
// Replace extension.
if (output_path.ext.len > 0) {
gb_free(ha, output_path.ext.text);
}
}
output_path.ext = copy_string(ha, output_extension);
bc->build_paths[BuildPath_Output] = output_path;
}
// Do we have an extension? We might not if the output filename was supplied.
if (bc->build_paths[BuildPath_Output].ext.len == 0) {
if (build_context.metrics.os == TargetOs_windows || build_context.build_mode != BuildMode_Executable) {
bc->build_paths[BuildPath_Output].ext = copy_string(ha, output_extension);
}
}
// 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 (bc->target_features_string.len != 0) {
enable_target_feature({}, bc->target_features_string);
}
return true;
}
+1735 -164
View File
File diff suppressed because it is too large Load Diff
+238 -51
View File
@@ -174,6 +174,10 @@ void check_init_constant(CheckerContext *ctx, Entity *e, Operand *operand) {
return;
}
if (is_type_proc(e->type)) {
error(e->token, "Illegal declaration of a constant procedure value");
}
e->parent_proc_decl = ctx->curr_proc_decl;
e->Constant.value = operand->value;
@@ -238,6 +242,51 @@ isize total_attribute_count(DeclInfo *decl) {
return attribute_count;
}
Type *clone_enum_type(CheckerContext *ctx, Type *original_enum_type, Type *named_type) {
// NOTE(bill, 2022-02-05): Stupid edge case for `distinct` declarations
//
// X :: enum {A, B, C}
// Y :: distinct X
//
// To make Y be just like X, it will need to copy the elements of X and change their type
// so that they match Y rather than X.
GB_ASSERT(original_enum_type != nullptr);
GB_ASSERT(named_type != nullptr);
GB_ASSERT(original_enum_type->kind == Type_Enum);
GB_ASSERT(named_type->kind == Type_Named);
Scope *parent = original_enum_type->Enum.scope->parent;
Scope *scope = create_scope(nullptr, parent);
Type *et = alloc_type_enum();
et->Enum.base_type = original_enum_type->Enum.base_type;
et->Enum.min_value = original_enum_type->Enum.min_value;
et->Enum.max_value = original_enum_type->Enum.max_value;
et->Enum.min_value_index = original_enum_type->Enum.min_value_index;
et->Enum.max_value_index = original_enum_type->Enum.max_value_index;
et->Enum.scope = scope;
auto fields = array_make<Entity *>(permanent_allocator(), original_enum_type->Enum.fields.count);
for_array(i, fields) {
Entity *old = original_enum_type->Enum.fields[i];
Entity *e = alloc_entity_constant(scope, old->token, named_type, old->Constant.value);
e->file = old->file;
e->identifier = clone_ast(old->identifier);
e->flags |= EntityFlag_Visited;
e->state = EntityState_Resolved;
e->Constant.flags = old->Constant.flags;
e->Constant.docs = old->Constant.docs;
e->Constant.comment = old->Constant.comment;
fields[i] = e;
add_entity(ctx, scope, nullptr, e);
add_entity_use(ctx, e->identifier, e);
}
et->Enum.fields = fields;
return et;
}
void check_type_decl(CheckerContext *ctx, Entity *e, Ast *init_expr, Type *def) {
GB_ASSERT(e->type == nullptr);
@@ -258,15 +307,25 @@ void check_type_decl(CheckerContext *ctx, Entity *e, Ast *init_expr, Type *def)
Type *bt = check_type_expr(ctx, te, named);
check_type_path_pop(ctx);
named->Named.base = base_type(bt);
if (is_distinct && is_type_typeid(e->type)) {
error(init_expr, "'distinct' cannot be applied to 'typeid'");
is_distinct = false;
Type *base = base_type(bt);
if (is_distinct && bt->kind == Type_Named && base->kind == Type_Enum) {
base = clone_enum_type(ctx, base, named);
}
if (is_distinct && is_type_any(e->type)) {
error(init_expr, "'distinct' cannot be applied to 'any'");
is_distinct = false;
named->Named.base = base;
if (is_distinct) {
if (is_type_typeid(e->type)) {
error(init_expr, "'distinct' cannot be applied to 'typeid'");
is_distinct = false;
} else if (is_type_any(e->type)) {
error(init_expr, "'distinct' cannot be applied to 'any'");
is_distinct = false;
} else if (is_type_simd_vector(e->type)) {
gbString str = type_to_string(e->type);
error(init_expr, "'distinct' cannot be applied to '%s'", str);
gb_string_free(str);
is_distinct = false;
}
}
if (!is_distinct) {
e->type = bt;
@@ -289,6 +348,13 @@ void check_type_decl(CheckerContext *ctx, Entity *e, Ast *init_expr, Type *def)
if (decl != nullptr) {
AttributeContext ac = {};
check_decl_attributes(ctx, decl->attributes, type_decl_attribute, &ac);
if (e->kind == Entity_TypeName && ac.objc_class != "") {
e->TypeName.objc_class_name = ac.objc_class;
if (type_size_of(e->type) > 0) {
error(e->token, "@(objc_class) marked type must be of zero size");
}
}
}
@@ -380,12 +446,56 @@ void check_const_decl(CheckerContext *ctx, Entity *e, Ast *type_expr, Ast *init,
if (type_expr) {
e->type = check_type(ctx, type_expr);
if (are_types_identical(e->type, t_typeid)) {
e->type = nullptr;
e->kind = Entity_TypeName;
check_type_decl(ctx, e, init, named_type);
return;
}
}
Operand operand = {};
if (init != nullptr) {
Entity *entity = 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);
return;
}
entity = nullptr;
if (init->kind == Ast_Ident) {
entity = check_ident(ctx, &operand, init, nullptr, e->type, true);
} else if (init->kind == Ast_SelectorExpr) {
@@ -732,6 +842,75 @@ void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
}
e->Procedure.optimization_mode = cast(ProcedureOptimizationMode)ac.optimization_mode;
if (ac.objc_name.len || ac.objc_is_class_method || ac.objc_type) {
if (ac.objc_name.len == 0 && ac.objc_is_class_method) {
error(e->token, "@(objc_name) is required with @(objc_is_class_method)");
} else if (ac.objc_type == nullptr) {
error(e->token, "@(objc_name) requires that @(objc_type) to be set");
} else if (ac.objc_name.len == 0 && ac.objc_type) {
error(e->token, "@(objc_name) is required with @(objc_type)");
} else {
Type *t = ac.objc_type;
if (t->kind == Type_Named) {
Entity *tn = t->Named.type_name;
GB_ASSERT(tn->kind == Entity_TypeName);
if (tn->scope != e->scope) {
error(e->token, "@(objc_name) attribute may only be applied to procedures and types within the same scope");
} else {
mutex_lock(&global_type_name_objc_metadata_mutex);
defer (mutex_unlock(&global_type_name_objc_metadata_mutex));
if (!tn->TypeName.objc_metadata) {
tn->TypeName.objc_metadata = create_type_name_obj_c_metadata();
}
auto *md = tn->TypeName.objc_metadata;
mutex_lock(md->mutex);
defer (mutex_unlock(md->mutex));
if (!ac.objc_is_class_method) {
bool ok = true;
for (TypeNameObjCMetadataEntry const &entry : md->value_entries) {
if (entry.name == ac.objc_name) {
error(e->token, "Previous declaration of @(objc_name=\"%.*s\")", LIT(ac.objc_name));
ok = false;
break;
}
}
if (ok) {
array_add(&md->value_entries, TypeNameObjCMetadataEntry{ac.objc_name, e});
}
} else {
bool ok = true;
for (TypeNameObjCMetadataEntry const &entry : md->type_entries) {
if (entry.name == ac.objc_name) {
error(e->token, "Previous declaration of @(objc_name=\"%.*s\")", LIT(ac.objc_name));
ok = false;
break;
}
}
if (ok) {
array_add(&md->type_entries, TypeNameObjCMetadataEntry{ac.objc_name, e});
}
}
}
}
}
}
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;
}
} 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:
@@ -777,21 +956,23 @@ void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
if (e->pkg != nullptr && e->token.string == "main") {
if (pt->param_count != 0 ||
pt->result_count != 0) {
gbString str = type_to_string(proc_type);
error(e->token, "Procedure type of 'main' was expected to be 'proc()', got %s", str);
gb_string_free(str);
}
if (pt->calling_convention != default_calling_convention()) {
error(e->token, "Procedure 'main' cannot have a custom calling convention");
}
pt->calling_convention = default_calling_convention();
if (e->pkg->kind == Package_Init) {
if (ctx->info->entry_point != nullptr) {
error(e->token, "Redeclaration of the entry pointer procedure 'main'");
} else {
ctx->info->entry_point = e;
if (e->pkg->kind != Package_Runtime) {
if (pt->param_count != 0 ||
pt->result_count != 0) {
gbString str = type_to_string(proc_type);
error(e->token, "Procedure type of 'main' was expected to be 'proc()', got %s", str);
gb_string_free(str);
}
if (pt->calling_convention != default_calling_convention()) {
error(e->token, "Procedure 'main' cannot have a custom calling convention");
}
pt->calling_convention = default_calling_convention();
if (e->pkg->kind == Package_Init) {
if (ctx->info->entry_point != nullptr) {
error(e->token, "Redeclaration of the entry pointer procedure 'main'");
} else {
ctx->info->entry_point = e;
}
}
}
}
@@ -833,10 +1014,12 @@ void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
}
}
if (pt->result_count == 0 && ac.require_results) {
error(pl->type, "'require_results' is not needed on a procedure with no results");
} else {
pt->require_results = ac.require_results;
if (ac.require_results) {
if (pt->result_count == 0) {
error(pl->type, "'require_results' is not needed on a procedure with no results");
} else {
pt->require_results = true;
}
}
if (ac.link_name.len > 0) {
@@ -855,18 +1038,16 @@ void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
}
Entity *foreign_library = init_entity_foreign_library(ctx, e);
if (is_arch_wasm()) {
if (is_arch_wasm() && foreign_library != nullptr) {
String module_name = str_lit("env");
if (foreign_library != nullptr) {
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];
}
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);
}
@@ -924,7 +1105,9 @@ void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
"\tother at %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 (d->entity->pkg->kind != Package_Runtime) {
error(d->proc_lit, "The link name 'main' is reserved for internal use");
}
} else {
string_map_set(fp, key, e);
}
@@ -971,6 +1154,12 @@ void check_global_variable_decl(CheckerContext *ctx, Entity *&e, Ast *type_expr,
}
ac.link_name = handle_link_name(ctx, e->token, ac.link_name, ac.link_prefix);
if (is_arch_wasm() && e->Variable.thread_local_model.len != 0) {
e->Variable.thread_local_model.len = 0;
// NOTE(bill): ignore this message for the time begin
// error(e->token, "@(thread_local) is not supported for this target platform");
}
String context_name = str_lit("variable declaration");
if (type_expr != nullptr) {
@@ -1046,6 +1235,8 @@ void check_global_variable_decl(CheckerContext *ctx, Entity *&e, Ast *type_expr,
Operand o = {};
check_expr_with_type_hint(ctx, &o, init_expr, e->type);
check_init_variable(ctx, e, &o, str_lit("variable declaration"));
check_rtti_type_disallowed(e->token, e->type, "A variable declaration is using a type, %s, which has been disallowed");
}
void check_proc_group_decl(CheckerContext *ctx, Entity *&pg_entity, DeclInfo *d) {
@@ -1138,20 +1329,20 @@ void check_proc_group_decl(CheckerContext *ctx, Entity *&pg_entity, DeclInfo *d)
if (!both_have_where_clauses) switch (kind) {
case ProcOverload_Identical:
error(p->token, "Overloaded procedure '%.*s' as the same type as another procedure in the procedure group '%.*s'", LIT(name), LIT(proc_group_name));
error(p->token, "Overloaded procedure '%.*s' has the same type as another procedure in the procedure group '%.*s'", LIT(name), LIT(proc_group_name));
is_invalid = true;
break;
// case ProcOverload_CallingConvention:
// error(p->token, "Overloaded procedure '%.*s' as the same type as another procedure in the procedure group '%.*s'", LIT(name), LIT(proc_group_name));
// error(p->token, "Overloaded procedure '%.*s' has the same type as another procedure in the procedure group '%.*s'", LIT(name), LIT(proc_group_name));
// is_invalid = true;
// break;
case ProcOverload_ParamVariadic:
error(p->token, "Overloaded procedure '%.*s' as the same type as another procedure in the procedure group '%.*s'", LIT(name), LIT(proc_group_name));
error(p->token, "Overloaded procedure '%.*s' has the same type as another procedure in the procedure group '%.*s'", LIT(name), LIT(proc_group_name));
is_invalid = true;
break;
case ProcOverload_ResultCount:
case ProcOverload_ResultTypes:
error(p->token, "Overloaded procedure '%.*s' as the same parameters but different results in the procedure group '%.*s'", LIT(name), LIT(proc_group_name));
error(p->token, "Overloaded procedure '%.*s' has the same parameters but different results in the procedure group '%.*s'", LIT(name), LIT(proc_group_name));
is_invalid = true;
break;
case ProcOverload_Polymorphic:
@@ -1302,11 +1493,8 @@ void check_proc_body(CheckerContext *ctx_, Token token, DeclInfo *decl, Type *ty
Type *t = base_type(type_deref(e->type));
if (t->kind == Type_Struct) {
Scope *scope = t->Struct.scope;
if (scope == nullptr) {
scope = scope_of_node(t->Struct.node);
}
GB_ASSERT(scope != nullptr);
for_array(i, scope->elements.entries) {
MUTEX_GUARD_BLOCK(scope->mutex) for_array(i, scope->elements.entries) {
Entity *f = scope->elements.entries[i].value;
if (f->kind == Entity_Variable) {
Entity *uvar = alloc_entity_using_variable(e, f->token, f->type, nullptr);
@@ -1324,11 +1512,10 @@ void check_proc_body(CheckerContext *ctx_, Token token, DeclInfo *decl, Type *ty
}
}
for_array(i, using_entities) {
MUTEX_GUARD_BLOCK(ctx->scope->mutex) for_array(i, using_entities) {
Entity *e = using_entities[i].e;
Entity *uvar = using_entities[i].uvar;
Entity *prev = scope_insert(ctx->scope, uvar);
Entity *prev = scope_insert(ctx->scope, uvar, false);
if (prev != nullptr) {
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));
+2437 -1853
View File
File diff suppressed because it is too large Load Diff
+95 -120
View File
@@ -490,6 +490,14 @@ void check_stmt(CheckerContext *ctx, Ast *node, u32 flags) {
out &= ~StateFlag_no_bounds_check;
}
if (in & StateFlag_no_type_assert) {
out |= StateFlag_no_type_assert;
out &= ~StateFlag_type_assert;
} else if (in & StateFlag_type_assert) {
out |= StateFlag_type_assert;
out &= ~StateFlag_no_type_assert;
}
ctx->state_flags = out;
}
@@ -607,7 +615,7 @@ bool check_using_stmt_entity(CheckerContext *ctx, AstUsingStmt *us, Ast *expr, b
case Entity_ImportName: {
Scope *scope = e->ImportName.scope;
for_array(i, scope->elements.entries) {
MUTEX_GUARD_BLOCK(scope->mutex) for_array(i, scope->elements.entries) {
String name = scope->elements.entries[i].key.string;
Entity *decl = scope->elements.entries[i].value;
if (!is_entity_exported(decl)) continue;
@@ -635,10 +643,7 @@ bool check_using_stmt_entity(CheckerContext *ctx, AstUsingStmt *us, Ast *expr, b
bool is_ptr = is_type_pointer(e->type);
Type *t = base_type(type_deref(e->type));
if (t->kind == Type_Struct) {
Scope *found = scope_of_node(t->Struct.node);
if (found == nullptr) {
found = t->Struct.scope;
}
Scope *found = t->Struct.scope;
GB_ASSERT(found != nullptr);
for_array(i, found->elements.entries) {
Entity *f = found->elements.entries[i].value;
@@ -692,54 +697,6 @@ bool check_using_stmt_entity(CheckerContext *ctx, AstUsingStmt *us, Ast *expr, b
return true;
}
struct TypeAndToken {
Type *type;
Token token;
};
void add_constant_switch_case(CheckerContext *ctx, PtrMap<uintptr, TypeAndToken> *seen, Operand operand, bool use_expr = true) {
if (operand.mode != Addressing_Constant) {
return;
}
if (operand.value.kind == ExactValue_Invalid) {
return;
}
uintptr key = hash_exact_value(operand.value);
TypeAndToken *found = map_get(seen, key);
if (found != nullptr) {
isize count = multi_map_count(seen, key);
TypeAndToken *taps = gb_alloc_array(temporary_allocator(), TypeAndToken, count);
multi_map_get_all(seen, key, taps);
for (isize i = 0; i < count; i++) {
TypeAndToken tap = taps[i];
if (!are_types_identical(operand.type, tap.type)) {
continue;
}
TokenPos pos = tap.token.pos;
if (use_expr) {
gbString expr_str = expr_to_string(operand.expr);
error(operand.expr,
"Duplicate case '%s'\n"
"\tprevious case at %s",
expr_str,
token_pos_to_string(pos));
gb_string_free(expr_str);
} else {
error(operand.expr, "Duplicate case found with previous case at %s", token_pos_to_string(pos));
}
return;
}
}
TypeAndToken tap = {operand.type, ast_token(operand.expr)};
multi_map_insert(seen, key, tap);
}
void check_inline_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
ast_node(irs, UnrollRangeStmt, node);
check_open_scope(ctx, node);
@@ -964,7 +921,7 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
}
}
PtrMap<uintptr, TypeAndToken> seen = {}; // NOTE(bill): Multimap, Key: ExactValue
SeenMap seen = {}; // NOTE(bill): Multimap, Key: ExactValue
map_init(&seen, heap_allocator());
defer (map_destroy(&seen));
@@ -1004,9 +961,9 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
TokenKind upper_op = Token_Invalid;
switch (be->op.kind) {
case Token_Ellipsis: upper_op = Token_GtEq; break;
case Token_RangeFull: upper_op = Token_GtEq; break;
case Token_RangeHalf: upper_op = Token_Gt; break;
case Token_Ellipsis: upper_op = Token_LtEq; break;
case Token_RangeFull: upper_op = Token_LtEq; break;
case Token_RangeHalf: upper_op = Token_Lt; break;
default: GB_PANIC("Invalid range operator"); break;
}
@@ -1027,45 +984,7 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
Operand b1 = rhs;
check_comparison(ctx, &a1, &b1, Token_LtEq);
if (is_type_enum(x.type)) {
// TODO(bill): Fix this logic so it's fast!!!
i64 v0 = exact_value_to_i64(lhs.value);
i64 v1 = exact_value_to_i64(rhs.value);
Operand v = {};
v.mode = Addressing_Constant;
v.type = x.type;
v.expr = x.expr;
Type *bt = base_type(x.type);
GB_ASSERT(bt->kind == Type_Enum);
for (i64 vi = v0; vi <= v1; vi++) {
if (upper_op != Token_GtEq && vi == v1) {
break;
}
bool found = false;
for_array(j, bt->Enum.fields) {
Entity *f = bt->Enum.fields[j];
GB_ASSERT(f->kind == Entity_Constant);
i64 fv = exact_value_to_i64(f->Constant.value);
if (fv == vi) {
found = true;
break;
}
}
if (found) {
v.value = exact_value_i64(vi);
add_constant_switch_case(ctx, &seen, v);
}
}
} else {
add_constant_switch_case(ctx, &seen, lhs);
if (upper_op == Token_GtEq) {
add_constant_switch_case(ctx, &seen, rhs);
}
}
add_to_seen_map(ctx, &seen, upper_op, x, lhs, rhs);
if (is_type_string(x.type)) {
// NOTE(bill): Force dependency for strings here
@@ -1110,7 +1029,7 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
continue;
}
update_untyped_expr_type(ctx, z.expr, x.type, !is_type_untyped(x.type));
add_constant_switch_case(ctx, &seen, y);
add_to_seen_map(ctx, &seen, y);
}
}
}
@@ -1146,7 +1065,7 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
if (unhandled.count == 1) {
error_no_newline(node, "Unhandled switch case: %.*s", LIT(unhandled[0]->token.string));
} else {
error_no_newline(node, "Unhandled switch cases: ");
error(node, "Unhandled switch cases:");
for_array(i, unhandled) {
Entity *f = unhandled[i];
error_line("\t%.*s\n", LIT(f->token.string));
@@ -1399,9 +1318,9 @@ void check_block_stmt_for_errors(CheckerContext *ctx, Ast *body) {
ast_node(bs, BlockStmt, body);
// NOTE(bill, 2020-09-23): This logic is prevent common erros with block statements
// e.g. if cond { x := 123; } // this is an error
if (body->scope != nullptr && body->scope->elements.entries.count > 0) {
if (body->scope->parent->node != nullptr) {
switch (body->scope->parent->node->kind) {
if (bs->scope != nullptr && bs->scope->elements.entries.count > 0) {
if (bs->scope->parent->node != nullptr) {
switch (bs->scope->parent->node->kind) {
case Ast_IfStmt:
case Ast_ForStmt:
case Ast_RangeStmt:
@@ -1462,6 +1381,18 @@ bool all_operands_valid(Array<Operand> const &operands) {
return true;
}
bool check_stmt_internal_builtin_proc_id(Ast *expr, BuiltinProcId *id_) {
BuiltinProcId id = BuiltinProc_Invalid;
Entity *e = entity_of_node(expr);
if (e != nullptr && e->kind == Entity_Builtin) {
if (e->Builtin.id && e->Builtin.id != BuiltinProc_DIRECTIVE) {
id = cast(BuiltinProcId)e->Builtin.id;
}
}
if (id_) *id_ = id;
return id != BuiltinProc_Invalid;
}
void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
u32 mod_flags = flags & (~Stmt_FallthroughAllowed);
switch (node->kind) {
@@ -1486,29 +1417,43 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
if (kind == Expr_Stmt) {
return;
}
Ast *expr = strip_or_return_expr(operand.expr);
Ast *expr = strip_or_return_expr(operand.expr);
if (expr->kind == Ast_CallExpr) {
BuiltinProcId builtin_id = BuiltinProc_Invalid;
bool do_require = false;
AstCallExpr *ce = &expr->CallExpr;
Type *t = type_of_expr(ce->proc);
if (is_type_proc(t)) {
if (t->Proc.require_results) {
gbString expr_str = expr_to_string(ce->proc);
error(node, "'%s' requires that its results must be handled", expr_str);
gb_string_free(expr_str);
}
Type *t = base_type(type_of_expr(ce->proc));
if (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];
do_require = bp.kind == Expr_Expr && !bp.ignore_results;
}
if (do_require) {
gbString expr_str = expr_to_string(ce->proc);
error(node, "'%s' requires that its results must be handled", expr_str);
gb_string_free(expr_str);
}
return;
} else if (expr->kind == Ast_SelectorCallExpr) {
BuiltinProcId builtin_id = BuiltinProc_Invalid;
bool do_require = false;
AstSelectorCallExpr *se = &expr->SelectorCallExpr;
ast_node(ce, CallExpr, se->call);
Type *t = type_of_expr(ce->proc);
if (is_type_proc(t)) {
if (t->Proc.require_results) {
gbString expr_str = expr_to_string(ce->proc);
error(node, "'%s' requires that its results must be handled", expr_str);
gb_string_free(expr_str);
}
Type *t = base_type(type_of_expr(ce->proc));
if (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];
do_require = bp.kind == Expr_Expr && !bp.ignore_results;
}
if (do_require) {
gbString expr_str = expr_to_string(ce->proc);
error(node, "'%s' requires that its results must be handled", expr_str);
gb_string_free(expr_str);
}
return;
}
@@ -1616,7 +1561,7 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
}
Operand lhs = {Addressing_Invalid};
Operand rhs = {Addressing_Invalid};
Ast *binary_expr = alloc_ast_node(node->file, Ast_BinaryExpr);
Ast *binary_expr = alloc_ast_node(node->file(), Ast_BinaryExpr);
ast_node(be, BinaryExpr, binary_expr);
be->op = op;
be->op.kind = cast(TokenKind)(cast(i32)be->op.kind - (Token_AddEq - Token_Add));
@@ -2197,7 +2142,26 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
}
if (new_name_count == 0) {
error(node, "No new declarations on the lhs");
begin_error_block();
error(node, "No new declarations on the left hand side");
bool all_underscore = true;
for_array(i, vd->names) {
Ast *name = vd->names[i];
if (name->kind == Ast_Ident) {
if (!is_blank_ident(name)) {
all_underscore = false;
break;
}
} else {
all_underscore = false;
break;
}
}
if (all_underscore) {
error_line("\tSuggestion: Try changing the declaration (:=) to an assignment (=)\n");
}
end_error_block();
}
Type *init_type = nullptr;
@@ -2233,7 +2197,6 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
e->state = EntityState_Resolved;
}
ac.link_name = handle_link_name(ctx, e->token, ac.link_name, ac.link_prefix);
e->Variable.thread_local_model = ac.thread_local_model;
if (ac.link_name.len > 0) {
e->Variable.link_name = ac.link_name;
@@ -2246,6 +2209,9 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
error(e->token, "The 'static' attribute is not allowed to be applied to '_'");
} else {
e->flags |= EntityFlag_Static;
if (ctx->in_defer) {
error(e->token, "'static' variables cannot be declared within a defer statement");
}
}
}
if (ac.thread_local_model != "") {
@@ -2254,10 +2220,18 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
error(e->token, "The 'thread_local' attribute is not allowed to be applied to '_'");
} else {
e->flags |= EntityFlag_Static;
if (ctx->in_defer) {
error(e->token, "'thread_local' variables cannot be declared within a defer statement");
}
}
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");
}
@@ -2339,7 +2313,8 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
} else if (is_type_struct(t) || is_type_raw_union(t)) {
ERROR_BLOCK();
Scope *scope = scope_of_node(t->Struct.node);
Scope *scope = t->Struct.scope;
GB_ASSERT(scope != nullptr);
for_array(i, scope->elements.entries) {
Entity *f = scope->elements.entries[i].value;
if (f->kind == Entity_Variable) {
+189 -58
View File
@@ -109,14 +109,19 @@ void check_struct_fields(CheckerContext *ctx, Ast *node, Slice<Entity *> *fields
}
i32 field_src_index = 0;
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;
Type *type = nullptr;
CommentGroup *docs = p->docs;
CommentGroup *comment = p->comment;
if (type_expr != nullptr) {
type = check_type_expr(ctx, type_expr, nullptr);
@@ -139,6 +144,7 @@ void check_struct_fields(CheckerContext *ctx, Ast *node, Slice<Entity *> *fields
}
bool is_using = (p->flags&FieldFlag_using) != 0;
bool is_subtype = (p->flags&FieldFlag_subtype) != 0;
for_array(j, p->names) {
Ast *name = p->names[j];
@@ -152,6 +158,18 @@ void check_struct_fields(CheckerContext *ctx, Ast *node, Slice<Entity *> *fields
Entity *field = alloc_entity_field(ctx->scope, name_token, type, is_using, field_src_index);
add_entity(ctx, ctx->scope, name, field);
field->Variable.field_group_index = field_group_index;
if (is_subtype) {
field->flags |= EntityFlag_Subtype;
}
if (j == 0) {
field->Variable.docs = docs;
}
if (j+1 == p->names.count) {
field->Variable.comment = comment;
}
array_add(&fields_array, field);
String tag = p->tag.string;
if (tag.len != 0 && !unquote_string(permanent_allocator(), &tag, 0, tag.text[0] == '`')) {
@@ -180,6 +198,20 @@ void check_struct_fields(CheckerContext *ctx, Ast *node, Slice<Entity *> *fields
populate_using_entity_scope(ctx, node, p, type);
}
if (is_subtype && p->names.count > 0) {
Type *first_type = fields_array[fields_array.count-1]->type;
Type *t = base_type(type_deref(first_type));
if (!does_field_type_allow_using(t) &&
p->names.count >= 1 &&
p->names[0]->kind == Ast_Ident) {
Token name_token = p->names[0]->Ident.token;
gbString type_str = type_to_string(first_type);
error(name_token, "'subtype' cannot be applied to the field '%.*s' of type '%s'", LIT(name_token.string), type_str);
gb_string_free(type_str);
}
}
}
*fields = slice_from_array(fields_array);
@@ -309,6 +341,10 @@ void add_polymorphic_record_entity(CheckerContext *ctx, Ast *node, Type *named_t
}
named_type->Named.type_name = e;
GB_ASSERT(original_type->kind == Type_Named);
e->TypeName.objc_class_name = original_type->Named.type_name->TypeName.objc_class_name;
// TODO(bill): Is this even correct? Or should the metadata be copied?
e->TypeName.objc_metadata = original_type->Named.type_name->TypeName.objc_metadata;
mutex_lock(&ctx->info->gen_types_mutex);
auto *found_gen_types = map_get(&ctx->info->gen_types, original_type);
@@ -639,22 +675,31 @@ void check_union_type(CheckerContext *ctx, Type *union_type, Ast *node, Array<Op
}
if (ok) {
array_add(&variants, t);
if (ut->kind == UnionType_shared_nil) {
if (!type_has_nil(t)) {
gbString s = type_to_string(t);
error(node, "Each variant of a union with #shared_nil must have a 'nil' value, got %s", s);
gb_string_free(s);
}
}
}
}
}
union_type->Union.variants = slice_from_array(variants);
union_type->Union.no_nil = ut->no_nil;
union_type->Union.maybe = ut->maybe;
if (union_type->Union.no_nil) {
union_type->Union.kind = ut->kind;
switch (ut->kind) {
case UnionType_no_nil:
if (variants.count < 2) {
error(ut->align, "A union with #no_nil must have at least 2 variants");
}
}
if (union_type->Union.maybe) {
break;
case UnionType_maybe:
if (variants.count != 1) {
error(ut->align, "A union with #maybe must have at 1 variant, got %lld", cast(long long)variants.count);
}
break;
}
if (ut->align != nullptr) {
@@ -718,20 +763,19 @@ void check_enum_type(CheckerContext *ctx, Type *enum_type, Type *named_type, Ast
Ast *ident = nullptr;
Ast *init = nullptr;
u32 entity_flags = 0;
if (field->kind == Ast_FieldValue) {
ast_node(fv, FieldValue, field);
if (fv->field == nullptr || fv->field->kind != Ast_Ident) {
error(field, "An enum field's name must be an identifier");
continue;
}
ident = fv->field;
init = fv->value;
} else if (field->kind == Ast_Ident) {
ident = field;
} else {
if (field->kind != Ast_EnumFieldValue) {
error(field, "An enum field's name must be an identifier");
continue;
}
ident = field->EnumFieldValue.name;
init = field->EnumFieldValue.value;
if (ident == nullptr || ident->kind != Ast_Ident) {
error(field, "An enum field's name must be an identifier");
continue;
}
CommentGroup *docs = field->EnumFieldValue.docs;
CommentGroup *comment = field->EnumFieldValue.comment;
String name = ident->Ident.token.string;
if (init != nullptr) {
@@ -789,6 +833,8 @@ void check_enum_type(CheckerContext *ctx, Type *enum_type, Type *named_type, Ast
e->flags |= EntityFlag_Visited;
e->state = EntityState_Resolved;
e->Constant.flags |= entity_flags;
e->Constant.docs = docs;
e->Constant.comment = comment;
if (scope_lookup_current(ctx->scope, name) != nullptr) {
error(ident, "'%.*s' is already declared in this enumeration", LIT(name));
@@ -922,20 +968,19 @@ void check_bit_set_type(CheckerContext *c, Type *type, Type *named_type, Ast *no
i64 lower = big_int_to_i64(&i);
i64 upper = big_int_to_i64(&j);
bool lower_changed = false;
i64 actual_lower = lower;
i64 bits = MAX_BITS;
if (type->BitSet.underlying != nullptr) {
bits = 8*type_size_of(type->BitSet.underlying);
if (lower > 0) {
lower = 0;
lower_changed = true;
actual_lower = 0;
} else if (lower < 0) {
error(bs->elem, "bit_set does not allow a negative lower bound (%lld) when an underlying type is set", lower);
}
}
i64 bits_required = upper-lower;
i64 bits_required = upper-actual_lower;
switch (be->op.kind) {
case Token_Ellipsis:
case Token_RangeFull:
@@ -959,7 +1004,7 @@ void check_bit_set_type(CheckerContext *c, Type *type, Type *named_type, Ast *no
break;
}
if (!is_valid) {
if (lower_changed) {
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);
} else {
error(bs->elem, "bit_set range is greater than %lld bits, %lld bits are required", bits, bits_required);
@@ -1189,13 +1234,13 @@ bool check_type_specialization_to(CheckerContext *ctx, Type *specialization, Typ
}
Type *determine_type_from_polymorphic(CheckerContext *ctx, Type *poly_type, Operand operand) {
Type *determine_type_from_polymorphic(CheckerContext *ctx, Type *poly_type, Operand const &operand) {
bool modify_type = !ctx->no_polymorphic_errors;
bool show_error = modify_type && !ctx->hide_polymorphic_errors;
if (!is_operand_value(operand)) {
if (show_error) {
gbString pts = type_to_string(poly_type);
gbString ots = type_to_string(operand.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);
@@ -1208,7 +1253,7 @@ Type *determine_type_from_polymorphic(CheckerContext *ctx, Type *poly_type, Oper
}
if (show_error) {
gbString pts = type_to_string(poly_type);
gbString ots = type_to_string(operand.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);
@@ -1300,7 +1345,9 @@ ParameterValue handle_parameter_value(CheckerContext *ctx, Type *in_type, Type *
param_value.kind = ParameterValue_Constant;
param_value.value = o.value;
} else {
error(expr, "Default parameter must be a constant, %d", o.mode);
gbString s = expr_to_string(o.expr);
error(expr, "Default parameter must be a constant, got %s", s);
gb_string_free(s);
}
}
} else {
@@ -1367,11 +1414,13 @@ Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_params, bool *is
isize variadic_index = -1;
bool is_c_vararg = false;
auto variables = 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 = unparen_expr(p->type);
Type *type = nullptr;
@@ -1564,9 +1613,13 @@ Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_params, bool *is
p->flags &= ~FieldFlag_const;
}
if (p->flags&FieldFlag_any_int) {
error(name, "'#const' can only be applied to variable fields");
error(name, "'#any_int' can only be applied to variable fields");
p->flags &= ~FieldFlag_any_int;
}
if (p->flags&FieldFlag_by_ptr) {
error(name, "'#by_ptr' can only be applied to variable fields");
p->flags &= ~FieldFlag_by_ptr;
}
param = alloc_entity_type_name(scope, name->Ident.token, type, EntityState_Resolved);
param->TypeName.is_type_alias = true;
@@ -1614,7 +1667,7 @@ Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_params, bool *is
ok = false;
}
} else if (p->flags&FieldFlag_any_int) {
if (!is_type_integer(op.type) || !is_type_integer(type)) {
if ((!is_type_integer(op.type) && !is_type_enum(op.type)) || (!is_type_integer(type) && !is_type_enum(type))) {
ok = false;
} else if (!check_is_castable_to(ctx, &op, type)) {
ok = false;
@@ -1643,10 +1696,17 @@ Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_params, bool *is
if (p->flags&FieldFlag_no_alias) {
if (!is_type_pointer(type)) {
error(name, "'#no_alias' can only be applied to fields of pointer type");
error(name, "'#no_alias' can only be applied pointer typed parameters");
p->flags &= ~FieldFlag_no_alias; // Remove the flag
}
}
if (p->flags&FieldFlag_by_ptr) {
if (is_type_internally_pointer_like(type)) {
error(name, "'#by_ptr' can only be applied to non-pointer-like parameters");
p->flags &= ~FieldFlag_by_ptr; // Remove the flag
}
}
if (is_poly_name) {
if (p->flags&FieldFlag_no_alias) {
error(name, "'#no_alias' can only be applied to non constant values");
@@ -1664,6 +1724,10 @@ Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_params, bool *is
error(name, "'#const' can only be applied to variable fields");
p->flags &= ~FieldFlag_const;
}
if (p->flags&FieldFlag_by_ptr) {
error(name, "'#by_ptr' can only be applied to variable fields");
p->flags &= ~FieldFlag_by_ptr;
}
if (!is_type_constant_type(type) && !is_type_polymorphic(type)) {
gbString str = type_to_string(type);
@@ -1672,9 +1736,11 @@ Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_params, bool *is
}
param = alloc_entity_const_param(scope, name->Ident.token, type, poly_const, is_type_polymorphic(type));
param->Constant.field_group_index = field_group_index;
} else {
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;
}
}
if (p->flags&FieldFlag_no_alias) {
@@ -1684,7 +1750,7 @@ Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_params, bool *is
param->flags |= EntityFlag_AutoCast;
}
if (p->flags&FieldFlag_any_int) {
if (!is_type_integer(param->type)) {
if (!is_type_integer(param->type) && !is_type_enum(param->type)) {
gbString str = type_to_string(param->type);
error(name, "A parameter with '#any_int' must be an integer, got %s", str);
gb_string_free(str);
@@ -1694,6 +1760,9 @@ Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_params, bool *is
if (p->flags&FieldFlag_const) {
param->flags |= EntityFlag_ConstInput;
}
if (p->flags&FieldFlag_by_ptr) {
param->flags |= EntityFlag_ByPtr;
}
param->state = EntityState_Resolved; // NOTE(bill): This should have be resolved whilst determining it
add_entity(ctx, scope, name, param);
@@ -1768,7 +1837,10 @@ Type *check_get_results(CheckerContext *ctx, Scope *scope, Ast *_results) {
}
auto variables = array_make<Entity *>(permanent_allocator(), 0, variable_count);
i32 field_group_index = -1;
for_array(i, results) {
field_group_index += 1;
ast_node(field, Field, results[i]);
Ast *default_value = unparen_expr(field->default_value);
ParameterValue param_value = {};
@@ -1799,6 +1871,7 @@ Type *check_get_results(CheckerContext *ctx, Scope *scope, Ast *_results) {
token.string = str_lit("");
Entity *param = alloc_entity_param(scope, token, type, false, false);
param->Variable.param_value = param_value;
param->Variable.field_group_index = -1;
array_add(&variables, param);
} else {
for_array(j, field->names) {
@@ -1822,6 +1895,7 @@ Type *check_get_results(CheckerContext *ctx, Scope *scope, Ast *_results) {
Entity *param = alloc_entity_param(scope, token, type, false, false);
param->flags |= EntityFlag_Result;
param->Variable.param_value = param_value;
param->Variable.field_group_index = field_group_index;
array_add(&variables, param);
add_entity(ctx, scope, name, param);
// NOTE(bill): Removes `declared but not used` when using -vet
@@ -1883,6 +1957,25 @@ bool check_procedure_type(CheckerContext *ctx, Type *type, Ast *proc_type_node,
c->scope->flags &= ~ScopeFlag_ContextDefined;
}
TargetArchKind arch = build_context.metrics.arch;
switch (cc) {
case ProcCC_StdCall:
case ProcCC_FastCall:
if (arch != TargetArch_i386 && arch != TargetArch_amd64) {
error(proc_type_node, "Invalid procedure calling convention \"%s\" for target architecture, expected either i386 or amd64, got %.*s",
proc_calling_convention_strings[cc], LIT(target_arch_names[arch]));
}
break;
case ProcCC_Win64:
case ProcCC_SysV:
if (arch != TargetArch_amd64) {
error(proc_type_node, "Invalid procedure calling convention \"%s\" for target architecture, expected amd64, got %.*s",
proc_calling_convention_strings[cc], LIT(target_arch_names[arch]));
}
break;
}
bool variadic = false;
isize variadic_index = -1;
bool success = true;
@@ -1896,20 +1989,6 @@ bool check_procedure_type(CheckerContext *ctx, Type *type, Ast *proc_type_node,
if (params) param_count = params ->Tuple.variables.count;
if (results) result_count = results->Tuple.variables.count;
if (param_count > 0) {
for_array(i, params->Tuple.variables) {
Entity *param = params->Tuple.variables[i];
if (param->kind == Entity_Variable) {
ParameterValue pv = param->Variable.param_value;
if (pv.kind == ParameterValue_Constant &&
pv.value.kind == ExactValue_Procedure) {
type->Proc.has_proc_default_values = true;
break;
}
}
}
}
if (result_count > 0) {
Entity *first = results->Tuple.variables[0];
type->Proc.has_named_results = first->token.string != "";
@@ -1939,8 +2018,9 @@ bool check_procedure_type(CheckerContext *ctx, Type *type, Ast *proc_type_node,
error(proc_type_node, "A procedure type with the #optional_second tag requires 2 return values, got %td", result_count);
} else {
bool ok = false;
if (proc_type_node->file && proc_type_node->file->pkg) {
ok = proc_type_node->file->pkg->scope == ctx->info->runtime_package->scope;
AstFile *file = proc_type_node->file();
if (file && file->pkg) {
ok = file->pkg->scope == ctx->info->runtime_package->scope;
}
if (!ok) {
@@ -1966,10 +2046,14 @@ bool check_procedure_type(CheckerContext *ctx, Type *type, Ast *proc_type_node,
if (param_count > 0) {
Entity *end = params->Tuple.variables[param_count-1];
if (end->flags&EntityFlag_CVarArg) {
if (cc == ProcCC_StdCall || cc == ProcCC_CDecl) {
switch (cc) {
default:
type->Proc.c_vararg = true;
} else {
break;
case ProcCC_Odin:
case ProcCC_Contextless:
error(end->token, "Calling convention does not support #c_vararg");
break;
}
}
}
@@ -2105,7 +2189,7 @@ void init_map_entry_type(Type *type) {
/*
struct {
hash: runtime.Map_Hash,
hash: uintptr,
next: int,
key: Key,
value: Value,
@@ -2284,10 +2368,21 @@ void check_matrix_type(CheckerContext *ctx, Type **type, Ast *node) {
}
if (!is_type_valid_for_matrix_elems(elem)) {
if (elem == t_typeid) {
Entity *e = entity_of_node(mt->elem);
if (e && e->kind == Entity_TypeName && e->TypeName.is_type_alias) {
// HACK TODO(bill): This is to allow polymorphic parameters for matrix elements
// proc($T: typeid) -> matrix[2, 2]T
//
// THIS IS NEEDS TO BE FIXED AND NOT USE THIS HACK
goto type_assign;
}
}
gbString s = type_to_string(elem);
error(column.expr, "Matrix elements types are limited to integers, floats, and complex, got %s", s);
gb_string_free(s);
}
type_assign:;
*type = alloc_type_matrix(elem, row_count, column_count, generic_row, generic_column);
@@ -2610,7 +2705,28 @@ bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, Type *named_t
case_end;
case_ast_node(pt, PointerType, e);
*type = alloc_type_pointer(check_type(ctx, pt->type));
CheckerContext c = *ctx;
c.type_path = new_checker_type_path();
defer (destroy_checker_type_path(c.type_path));
Type *elem = t_invalid;
Operand o = {};
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);
gb_string_free(s);
}
end_error_block();
} else {
elem = o.type;
}
*type = alloc_type_pointer(elem);
set_base_type(named_type, *type);
return true;
case_end;
@@ -2678,29 +2794,30 @@ bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, Type *named_t
Type *t = alloc_type_enumerated_array(elem, index, bt->Enum.min_value, bt->Enum.max_value, Token_Invalid);
bool is_partial = false;
bool is_sparse = false;
if (at->tag != nullptr) {
GB_ASSERT(at->tag->kind == Ast_BasicDirective);
String name = at->tag->BasicDirective.name.string;
if (name == "partial") {
is_partial = true;
if (name == "sparse") {
is_sparse = true;
} else {
error(at->tag, "Invalid tag applied to an enumerated array, got #%.*s", LIT(name));
}
}
if (!is_partial && t->EnumeratedArray.count > bt->Enum.fields.count) {
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 #partial to the enumerated array to allow for non-named elements\n");
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 #partial is applied\n");
error_line("\t this warning will be removed if #sparse is applied\n");
}
}
t->EnumeratedArray.is_sparse = is_sparse;
*type = t;
@@ -2719,15 +2836,27 @@ bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, Type *named_t
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)) {
if (!is_type_valid_vector_elem(elem) && !is_type_polymorphic(elem)) {
gbString str = type_to_string(elem);
error(at->elem, "Invalid element type for 'intrinsics.simd_vector', expected an integer or float with no specific endianness, got '%s'", str);
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;
}
*type = alloc_type_simd_vector(count, elem);
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);
@@ -2950,5 +3079,7 @@ Type *check_type_expr(CheckerContext *ctx, Ast *e, Type *named_type) {
}
set_base_type(named_type, type);
check_rtti_type_disallowed(e, type, "Use of a type, %s, which has been disallowed");
return type;
}
+647 -162
View File
File diff suppressed because it is too large Load Diff
+26 -2
View File
@@ -60,6 +60,7 @@ struct BuiltinProc {
ExprKind kind;
BuiltinProcPkg pkg;
bool diverging;
bool ignore_results; // ignores require results handling
};
@@ -118,6 +119,15 @@ struct AttributeContext {
bool init : 1;
bool set_cold : 1;
u32 optimization_mode; // ProcedureOptimizationMode
i64 foreign_import_priority_index;
String objc_class;
String objc_name;
bool objc_is_class_method;
Type * objc_type;
String require_target_feature; // required by the target micro-architecture
String enable_target_feature; // will be enabled for the procedure only
};
AttributeContext make_attribute_context(String link_prefix) {
@@ -267,6 +277,17 @@ struct UntypedExprInfo {
typedef PtrMap<Ast *, ExprInfo *> UntypedExprInfoMap;
typedef MPMCQueue<ProcInfo *> ProcBodyQueue;
enum ObjcMsgKind : u32 {
ObjcMsg_normal,
ObjcMsg_fpret,
ObjcMsg_fp2ret,
ObjcMsg_stret,
};
struct ObjcMsgData {
ObjcMsgKind kind;
Type *proc_type;
};
// CheckerInfo stores all the symbol information for a type-checked program
struct CheckerInfo {
Checker *checker;
@@ -338,6 +359,10 @@ struct CheckerInfo {
MPMCQueue<Entity *> required_global_variable_queue;
MPMCQueue<Entity *> required_foreign_imports_through_force_queue;
MPMCQueue<Ast *> intrinsics_entry_point_usage;
BlockingMutex objc_types_mutex;
PtrMap<Ast *, ObjcMsgData> objc_msgSend_types;
};
struct CheckerContext {
@@ -410,7 +435,6 @@ gb_global AstPackage *config_pkg = nullptr;
TypeAndValue type_and_value_of_expr (Ast *expr);
Type * type_of_expr (Ast *expr);
Entity * implicit_entity_of_node(Ast *clause);
Scope * scope_of_node (Ast *node);
DeclInfo * decl_info_of_ident (Ast *ident);
DeclInfo * decl_info_of_entity (Entity * e);
AstFile * ast_file_of_filename (CheckerInfo *i, String filename);
@@ -424,7 +448,7 @@ Entity *entity_of_node(Ast *expr);
Entity *scope_lookup_current(Scope *s, String const &name);
Entity *scope_lookup (Scope *s, String const &name);
void scope_lookup_parent (Scope *s, String const &name, Scope **scope_, Entity **entity_);
Entity *scope_insert (Scope *s, Entity *entity);
Entity *scope_insert (Scope *s, Entity *entity, bool use_mutex=true);
void add_type_and_value (CheckerInfo *i, Ast *expression, AddressingMode mode, Type *type, ExactValue value);
+223 -144
View File
@@ -45,7 +45,6 @@ enum BuiltinProcId {
// "Intrinsics"
BuiltinProc_is_package_imported,
BuiltinProc_simd_vector,
BuiltinProc_soa_struct,
BuiltinProc_alloca,
@@ -66,6 +65,7 @@ enum BuiltinProcId {
BuiltinProc_overflow_mul,
BuiltinProc_sqrt,
BuiltinProc_fused_mul_add,
BuiltinProc_mem_copy,
BuiltinProc_mem_copy_non_overlapping,
@@ -80,83 +80,39 @@ enum BuiltinProcId {
BuiltinProc_unaligned_store,
BuiltinProc_unaligned_load,
BuiltinProc_non_temporal_store,
BuiltinProc_non_temporal_load,
BuiltinProc_prefetch_read_instruction,
BuiltinProc_prefetch_read_data,
BuiltinProc_prefetch_write_instruction,
BuiltinProc_prefetch_write_data,
BuiltinProc_atomic_fence,
BuiltinProc_atomic_fence_acq,
BuiltinProc_atomic_fence_rel,
BuiltinProc_atomic_fence_acqrel,
BuiltinProc_atomic_type_is_lock_free,
BuiltinProc_atomic_thread_fence,
BuiltinProc_atomic_signal_fence,
BuiltinProc_atomic_store,
BuiltinProc_atomic_store_rel,
BuiltinProc_atomic_store_relaxed,
BuiltinProc_atomic_store_unordered,
BuiltinProc_atomic_store_explicit,
BuiltinProc_atomic_load,
BuiltinProc_atomic_load_acq,
BuiltinProc_atomic_load_relaxed,
BuiltinProc_atomic_load_unordered,
BuiltinProc_atomic_load_explicit,
BuiltinProc_atomic_add,
BuiltinProc_atomic_add_acq,
BuiltinProc_atomic_add_rel,
BuiltinProc_atomic_add_acqrel,
BuiltinProc_atomic_add_relaxed,
BuiltinProc_atomic_add_explicit,
BuiltinProc_atomic_sub,
BuiltinProc_atomic_sub_acq,
BuiltinProc_atomic_sub_rel,
BuiltinProc_atomic_sub_acqrel,
BuiltinProc_atomic_sub_relaxed,
BuiltinProc_atomic_sub_explicit,
BuiltinProc_atomic_and,
BuiltinProc_atomic_and_acq,
BuiltinProc_atomic_and_rel,
BuiltinProc_atomic_and_acqrel,
BuiltinProc_atomic_and_relaxed,
BuiltinProc_atomic_and_explicit,
BuiltinProc_atomic_nand,
BuiltinProc_atomic_nand_acq,
BuiltinProc_atomic_nand_rel,
BuiltinProc_atomic_nand_acqrel,
BuiltinProc_atomic_nand_relaxed,
BuiltinProc_atomic_nand_explicit,
BuiltinProc_atomic_or,
BuiltinProc_atomic_or_acq,
BuiltinProc_atomic_or_rel,
BuiltinProc_atomic_or_acqrel,
BuiltinProc_atomic_or_relaxed,
BuiltinProc_atomic_or_explicit,
BuiltinProc_atomic_xor,
BuiltinProc_atomic_xor_acq,
BuiltinProc_atomic_xor_rel,
BuiltinProc_atomic_xor_acqrel,
BuiltinProc_atomic_xor_relaxed,
BuiltinProc_atomic_xchg,
BuiltinProc_atomic_xchg_acq,
BuiltinProc_atomic_xchg_rel,
BuiltinProc_atomic_xchg_acqrel,
BuiltinProc_atomic_xchg_relaxed,
BuiltinProc_atomic_cxchg,
BuiltinProc_atomic_cxchg_acq,
BuiltinProc_atomic_cxchg_rel,
BuiltinProc_atomic_cxchg_acqrel,
BuiltinProc_atomic_cxchg_relaxed,
BuiltinProc_atomic_cxchg_failrelaxed,
BuiltinProc_atomic_cxchg_failacq,
BuiltinProc_atomic_cxchg_acq_failrelaxed,
BuiltinProc_atomic_cxchg_acqrel_failrelaxed,
BuiltinProc_atomic_cxchgweak,
BuiltinProc_atomic_cxchgweak_acq,
BuiltinProc_atomic_cxchgweak_rel,
BuiltinProc_atomic_cxchgweak_acqrel,
BuiltinProc_atomic_cxchgweak_relaxed,
BuiltinProc_atomic_cxchgweak_failrelaxed,
BuiltinProc_atomic_cxchgweak_failacq,
BuiltinProc_atomic_cxchgweak_acq_failrelaxed,
BuiltinProc_atomic_cxchgweak_acqrel_failrelaxed,
BuiltinProc_atomic_xor_explicit,
BuiltinProc_atomic_exchange,
BuiltinProc_atomic_exchange_explicit,
BuiltinProc_atomic_compare_exchange_strong,
BuiltinProc_atomic_compare_exchange_strong_explicit,
BuiltinProc_atomic_compare_exchange_weak,
BuiltinProc_atomic_compare_exchange_weak_explicit,
BuiltinProc_fixed_point_mul,
BuiltinProc_fixed_point_div,
@@ -164,10 +120,76 @@ enum BuiltinProcId {
BuiltinProc_fixed_point_div_sat,
BuiltinProc_expect,
BuiltinProc__simd_begin,
BuiltinProc_simd_add,
BuiltinProc_simd_sub,
BuiltinProc_simd_mul,
BuiltinProc_simd_div,
BuiltinProc_simd_rem,
BuiltinProc_simd_shl, // Odin logic
BuiltinProc_simd_shr, // Odin logic
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_and,
BuiltinProc_simd_or,
BuiltinProc_simd_xor,
BuiltinProc_simd_and_not,
BuiltinProc_simd_neg,
BuiltinProc_simd_abs,
BuiltinProc_simd_min,
BuiltinProc_simd_max,
BuiltinProc_simd_clamp,
BuiltinProc_simd_lanes_eq,
BuiltinProc_simd_lanes_ne,
BuiltinProc_simd_lanes_lt,
BuiltinProc_simd_lanes_le,
BuiltinProc_simd_lanes_gt,
BuiltinProc_simd_lanes_ge,
BuiltinProc_simd_extract,
BuiltinProc_simd_replace,
BuiltinProc_simd_reduce_add_ordered,
BuiltinProc_simd_reduce_mul_ordered,
BuiltinProc_simd_reduce_min,
BuiltinProc_simd_reduce_max,
BuiltinProc_simd_reduce_and,
BuiltinProc_simd_reduce_or,
BuiltinProc_simd_reduce_xor,
BuiltinProc_simd_shuffle,
BuiltinProc_simd_select,
BuiltinProc_simd_ceil,
BuiltinProc_simd_floor,
BuiltinProc_simd_trunc,
BuiltinProc_simd_nearest,
BuiltinProc_simd_to_bits,
BuiltinProc_simd_lanes_reverse,
BuiltinProc_simd_lanes_rotate_left,
BuiltinProc_simd_lanes_rotate_right,
// Platform specific SIMD intrinsics
BuiltinProc_simd_x86__MM_SHUFFLE,
BuiltinProc__simd_end,
// Platform specific intrinsics
BuiltinProc_syscall,
BuiltinProc_x86_cpuid,
BuiltinProc_x86_xgetbv,
// Constant type tests
BuiltinProc__type_begin,
@@ -204,6 +226,7 @@ BuiltinProc__type_simple_boolean_begin,
BuiltinProc_type_is_named,
BuiltinProc_type_is_pointer,
BuiltinProc_type_is_multi_pointer,
BuiltinProc_type_is_array,
BuiltinProc_type_is_enumerated_array,
BuiltinProc_type_is_slice,
@@ -213,8 +236,6 @@ BuiltinProc__type_simple_boolean_begin,
BuiltinProc_type_is_union,
BuiltinProc_type_is_enum,
BuiltinProc_type_is_proc,
BuiltinProc_type_is_bit_field,
BuiltinProc_type_is_bit_field_value,
BuiltinProc_type_is_bit_set,
BuiltinProc_type_is_simd_vector,
BuiltinProc_type_is_matrix,
@@ -227,6 +248,7 @@ BuiltinProc__type_simple_boolean_begin,
BuiltinProc__type_simple_boolean_end,
BuiltinProc_type_has_field,
BuiltinProc_type_field_type,
BuiltinProc_type_is_specialization_of,
@@ -243,6 +265,8 @@ BuiltinProc__type_simple_boolean_end,
BuiltinProc_type_polymorphic_record_parameter_count,
BuiltinProc_type_polymorphic_record_parameter_value,
BuiltinProc_type_is_subtype_of,
BuiltinProc_type_field_index_of,
BuiltinProc_type_equal_proc,
@@ -250,6 +274,21 @@ BuiltinProc__type_simple_boolean_end,
BuiltinProc__type_end,
BuiltinProc___entry_point,
BuiltinProc_objc_send,
BuiltinProc_objc_find_selector,
BuiltinProc_objc_find_class,
BuiltinProc_objc_register_selector,
BuiltinProc_objc_register_class,
BuiltinProc_constant_utf16_cstring,
BuiltinProc_wasm_memory_grow,
BuiltinProc_wasm_memory_size,
BuiltinProc_wasm_memory_atomic_wait32,
BuiltinProc_wasm_memory_atomic_notify32,
BuiltinProc_COUNT,
};
gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
@@ -297,7 +336,6 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
// "Intrinsics"
{STR_LIT("is_package_imported"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_vector"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, // Type
{STR_LIT("soa_struct"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, // Type
{STR_LIT("alloca"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
@@ -319,6 +357,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT("overflow_mul"), 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},
{STR_LIT("mem_copy"), 3, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
{STR_LIT("mem_copy_non_overlapping"), 3, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
@@ -333,84 +372,39 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT("unaligned_store"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
{STR_LIT("unaligned_load"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("non_temporal_store"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
{STR_LIT("non_temporal_load"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("prefetch_read_instruction"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
{STR_LIT("prefetch_read_data"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
{STR_LIT("prefetch_write_instruction"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
{STR_LIT("prefetch_write_data"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_fence"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_fence_acq"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_fence_rel"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_fence_acqrel"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_store"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_store_rel"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_store_relaxed"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_store_unordered"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_load"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_load_acq"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_load_relaxed"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_load_unordered"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_add"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_add_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_add_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_add_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_add_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_sub"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_sub_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_sub_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_sub_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_sub_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_and"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_and_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_and_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_and_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_and_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_nand"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_nand_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_nand_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_nand_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_nand_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_or"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_or_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_or_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_or_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_or_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_xor"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_xor_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_xor_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_xor_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_xor_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_xchg"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_xchg_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_xchg_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_xchg_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_xchg_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_cxchg"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_cxchg_acq"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_cxchg_rel"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_cxchg_acqrel"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_cxchg_relaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_cxchg_failrelaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_cxchg_failacq"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_cxchg_acq_failrelaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_cxchg_acqrel_failrelaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_cxchgweak"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_cxchgweak_acq"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_cxchgweak_rel"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_cxchgweak_acqrel"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_cxchgweak_relaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_cxchgweak_failrelaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_cxchgweak_failacq"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_cxchgweak_acq_failrelaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_cxchgweak_acqrel_failrelaxed"), 3, false, Expr_Expr, 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},
{STR_LIT("atomic_store"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_store_explicit"), 3, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_load"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
{STR_LIT("atomic_load_explicit"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
{STR_LIT("atomic_add"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
{STR_LIT("atomic_add_explicit"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
{STR_LIT("atomic_sub"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
{STR_LIT("atomic_sub_explicit"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
{STR_LIT("atomic_and"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
{STR_LIT("atomic_and_explicit"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
{STR_LIT("atomic_nand"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
{STR_LIT("atomic_nand_explicit"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
{STR_LIT("atomic_or"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
{STR_LIT("atomic_or_explicit"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
{STR_LIT("atomic_xor"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
{STR_LIT("atomic_xor_explicit"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
{STR_LIT("atomic_exchange"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
{STR_LIT("atomic_exchange_explicit"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
{STR_LIT("atomic_compare_exchange_strong"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
{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("fixed_point_mul"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("fixed_point_div"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
@@ -418,8 +412,74 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT("fixed_point_div_sat"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("expect"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("syscall"), 1, true, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_add"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_sub"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_mul"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_div"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_rem"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_shl"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_shr"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{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_and"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_or"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_xor"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_and_not"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_neg"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_abs"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_min"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_max"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_clamp"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_lanes_eq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_lanes_ne"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_lanes_lt"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_lanes_le"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_lanes_gt"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_lanes_ge"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_extract"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_replace"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_reduce_add_ordered"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_reduce_mul_ordered"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_reduce_min"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_reduce_max"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_reduce_and"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{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_shuffle"), 2, true, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_select"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_ceil") , 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_floor"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_trunc"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_nearest"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_to_bits"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_lanes_reverse"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{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_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("x86_cpuid"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("x86_xgetbv"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
@@ -455,6 +515,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT("type_is_named"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_is_pointer"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_is_multi_pointer"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_is_array"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_is_enumerated_array"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_is_slice"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
@@ -464,8 +525,6 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT("type_is_union"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_is_enum"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_is_proc"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_is_bit_field"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_is_bit_field_value"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_is_bit_set"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_is_simd_vector"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_is_matrix"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
@@ -477,6 +536,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT(""), 0, false, Expr_Stmt, 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},
@@ -493,10 +553,29 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT("type_polymorphic_record_parameter_count"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_polymorphic_record_parameter_value"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_is_subtype_of"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_field_index_of"), 2, 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(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
{STR_LIT("__entry_point"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
{STR_LIT("objc_send"), 3, true, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
{STR_LIT("objc_find_selector"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("objc_find_class"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("objc_register_selector"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
{STR_LIT("objc_register_class"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
{STR_LIT("constant_utf16_cstring"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("wasm_memory_grow"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("wasm_memory_size"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("wasm_memory_atomic_wait32"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("wasm_memory_atomic_notify32"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
};
+60 -269
View File
@@ -47,6 +47,13 @@ void debugf(char const *fmt, ...);
#include "range_cache.cpp"
bool is_power_of_two(i64 x) {
if (x <= 0) {
return false;
}
return !(x & (x-1));
}
int isize_cmp(isize x, isize y) {
if (x < y) {
return -1;
@@ -83,9 +90,20 @@ int i32_cmp(i32 x, i32 y) {
u32 fnv32a(void const *data, isize len) {
u8 const *bytes = cast(u8 const *)data;
u32 h = 0x811c9dc5;
for (isize i = 0; i < len; i++) {
u32 b = cast(u32)bytes[i];
h = (h ^ b) * 0x01000193;
for (; len >= 8; len -= 8, bytes += 8) {
h = (h ^ bytes[0]) * 0x01000193;
h = (h ^ bytes[1]) * 0x01000193;
h = (h ^ bytes[2]) * 0x01000193;
h = (h ^ bytes[3]) * 0x01000193;
h = (h ^ bytes[4]) * 0x01000193;
h = (h ^ bytes[5]) * 0x01000193;
h = (h ^ bytes[6]) * 0x01000193;
h = (h ^ bytes[7]) * 0x01000193;
}
while (len--) {
h = (h ^ *bytes++) * 0x01000193;
}
return h;
}
@@ -93,20 +111,48 @@ u32 fnv32a(void const *data, isize len) {
u64 fnv64a(void const *data, isize len) {
u8 const *bytes = cast(u8 const *)data;
u64 h = 0xcbf29ce484222325ull;
for (isize i = 0; i < len; i++) {
u64 b = cast(u64)bytes[i];
h = (h ^ b) * 0x100000001b3ull;
for (; len >= 8; len -= 8, bytes += 8) {
h = (h ^ bytes[0]) * 0x100000001b3ull;
h = (h ^ bytes[1]) * 0x100000001b3ull;
h = (h ^ bytes[2]) * 0x100000001b3ull;
h = (h ^ bytes[3]) * 0x100000001b3ull;
h = (h ^ bytes[4]) * 0x100000001b3ull;
h = (h ^ bytes[5]) * 0x100000001b3ull;
h = (h ^ bytes[6]) * 0x100000001b3ull;
h = (h ^ bytes[7]) * 0x100000001b3ull;
}
while (len--) {
h = (h ^ *bytes++) * 0x100000001b3ull;
}
return h;
}
u64 u64_digit_value(Rune r) {
if ('0' <= r && r <= '9') {
return r - '0';
} else if ('a' <= r && r <= 'f') {
return r - 'a' + 10;
} else if ('A' <= r && r <= 'F') {
return r - 'A' + 10;
switch (r) {
case '0': return 0;
case '1': return 1;
case '2': return 2;
case '3': return 3;
case '4': return 4;
case '5': return 5;
case '6': return 6;
case '7': return 7;
case '8': return 8;
case '9': return 9;
case 'a': return 10;
case 'b': return 11;
case 'c': return 12;
case 'd': return 13;
case 'e': return 14;
case 'f': return 15;
case 'A': return 10;
case 'B': return 11;
case 'C': return 12;
case 'D': return 13;
case 'E': return 14;
case 'F': return 15;
}
return 16; // NOTE(bill): Larger than highest possible
}
@@ -636,262 +682,7 @@ wchar_t **command_line_to_wargv(wchar_t *cmd_line, int *_argc) {
#endif
#if defined(GB_SYSTEM_WINDOWS)
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
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
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 res;
}
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,
};
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)
ReadDirectoryError read_directory(String path, Array<FileInfo> *fi) {
GB_ASSERT(fi != nullptr);
gbAllocator a = heap_allocator();
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(a, path);
defer (gb_free(a, c_str));
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;
}
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;
i64 size = cast(i64)file_data.nFileSizeLow;
size |= (cast(i64)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 = 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)
#include <dirent.h>
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
#include "path.cpp"
struct LoadedFile {
void *handle;
@@ -982,7 +773,7 @@ LoadedFileError load_file_32(char const *fullpath, LoadedFile *memory_mapped_fil
#endif
}
gbFileContents fc = gb_file_read_contents(heap_allocator(), true, fullpath);
gbFileContents fc = gb_file_read_contents(permanent_allocator(), true, fullpath);
if (fc.size > I32_MAX) {
err = LoadedFile_FileTooLarge;
+41 -12
View File
@@ -139,6 +139,7 @@ struct PlatformMemoryBlock {
};
gb_global std::atomic<isize> global_platform_memory_total_usage;
gb_global PlatformMemoryBlock global_platform_memory_block_sentinel;
PlatformMemoryBlock *platform_virtual_memory_alloc(isize total_size);
@@ -158,10 +159,17 @@ void platform_virtual_memory_protect(void *memory, isize size);
PlatformMemoryBlock *platform_virtual_memory_alloc(isize total_size) {
PlatformMemoryBlock *pmblock = (PlatformMemoryBlock *)VirtualAlloc(0, total_size, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE);
GB_ASSERT_MSG(pmblock != nullptr, "Out of Virtual Memory, oh no...");
if (pmblock == nullptr) {
gb_printf_err("Out of Virtual memory, oh no...\n");
gb_printf_err("Requested: %lld bytes\n", cast(long long)total_size);
gb_printf_err("Total Usage: %lld bytes\n", cast(long long)global_platform_memory_total_usage);
GB_ASSERT_MSG(pmblock != nullptr, "Out of Virtual Memory, oh no...");
}
global_platform_memory_total_usage += total_size;
return pmblock;
}
void platform_virtual_memory_free(PlatformMemoryBlock *block) {
global_platform_memory_total_usage -= block->total_size;
GB_ASSERT(VirtualFree(block, 0, MEM_RELEASE));
}
void platform_virtual_memory_protect(void *memory, isize size) {
@@ -180,11 +188,18 @@ void platform_virtual_memory_protect(void *memory, isize size);
PlatformMemoryBlock *platform_virtual_memory_alloc(isize total_size) {
PlatformMemoryBlock *pmblock = (PlatformMemoryBlock *)mmap(nullptr, total_size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
GB_ASSERT_MSG(pmblock != nullptr, "Out of Virtual Memory, oh no...");
if (pmblock == nullptr) {
gb_printf_err("Out of Virtual memory, oh no...\n");
gb_printf_err("Requested: %lld bytes\n", cast(long long)total_size);
gb_printf_err("Total Usage: %lld bytes\n", cast(long long)global_platform_memory_total_usage);
GB_ASSERT_MSG(pmblock != nullptr, "Out of Virtual Memory, oh no...");
}
global_platform_memory_total_usage += total_size;
return pmblock;
}
void platform_virtual_memory_free(PlatformMemoryBlock *block) {
isize size = block->total_size;
global_platform_memory_total_usage -= size;
munmap(block, size);
}
void platform_virtual_memory_protect(void *memory, isize size) {
@@ -325,18 +340,32 @@ GB_ALLOCATOR_PROC(heap_allocator_proc) {
// TODO(bill): Throughly test!
switch (type) {
#if defined(GB_COMPILER_MSVC)
case gbAllocation_Alloc: {
isize aligned_size = align_formula_isize(size, alignment);
// TODO(bill): Make sure this is aligned correctly
ptr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, aligned_size);
} break;
case gbAllocation_Alloc:
if (size == 0) {
return NULL;
} else {
isize aligned_size = align_formula_isize(size, alignment);
// TODO(bill): Make sure this is aligned correctly
ptr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, aligned_size);
}
break;
case gbAllocation_Free:
HeapFree(GetProcessHeap(), 0, old_memory);
if (old_memory != nullptr) {
HeapFree(GetProcessHeap(), 0, old_memory);
}
break;
case gbAllocation_Resize:
if (old_memory != nullptr && size > 0) {
isize aligned_size = align_formula_isize(size, alignment);
ptr = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, old_memory, aligned_size);
} else if (old_memory != nullptr) {
HeapFree(GetProcessHeap(), 0, old_memory);
} else if (size != 0) {
isize aligned_size = align_formula_isize(size, alignment);
// TODO(bill): Make sure this is aligned correctly
ptr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, aligned_size);
}
break;
case gbAllocation_Resize: {
isize aligned_size = align_formula_isize(size, alignment);
ptr = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, old_memory, aligned_size);
} break;
#elif defined(GB_SYSTEM_LINUX)
// TODO(bill): *nix version that's decent
case gbAllocation_Alloc: {
+22 -6
View File
@@ -67,6 +67,14 @@ GB_COMPARE_PROC(cmp_ast_package_by_name) {
#include "docs_format.cpp"
#include "docs_writer.cpp"
void print_doc_line(i32 indent, String const &data) {
while (indent --> 0) {
gb_printf("\t");
}
gb_file_write(gb_file_get_standard(gbFileStandard_Output), data.text, data.len);
gb_printf("\n");
}
void print_doc_line(i32 indent, char const *fmt, ...) {
while (indent --> 0) {
gb_printf("\t");
@@ -86,6 +94,13 @@ void print_doc_line_no_newline(i32 indent, char const *fmt, ...) {
gb_printf_va(fmt, va);
va_end(va);
}
void print_doc_line_no_newline(i32 indent, String const &data) {
while (indent --> 0) {
gb_printf("\t");
}
gb_file_write(gb_file_get_standard(gbFileStandard_Output), data.text, data.len);
}
bool print_doc_comment_group_string(i32 indent, CommentGroup *g) {
if (g == nullptr) {
@@ -106,8 +121,9 @@ bool print_doc_comment_group_string(i32 indent, CommentGroup *g) {
String comment = g->list[i].string;
String original_comment = comment;
bool slash_slash = comment[1] == '/';
bool slash_slash = false;
if (comment[1] == '/') {
slash_slash = true;
comment.text += 2;
comment.len -= 2;
} else if (comment[1] == '*') {
@@ -131,7 +147,7 @@ bool print_doc_comment_group_string(i32 indent, CommentGroup *g) {
}
if (slash_slash) {
print_doc_line(indent, "%.*s", LIT(comment));
print_doc_line(indent, comment);
count += 1;
} else {
isize pos = 0;
@@ -143,7 +159,7 @@ bool print_doc_comment_group_string(i32 indent, CommentGroup *g) {
}
}
String line = substring(comment, pos, end);
pos = end+1;
pos = end;
String trimmed_line = string_trim_whitespace(line);
if (trimmed_line.len == 0) {
if (count == 0) {
@@ -159,7 +175,7 @@ bool print_doc_comment_group_string(i32 indent, CommentGroup *g) {
line = substring(line, 2, line.len);
}
print_doc_line(indent, "%.*s", LIT(line));
print_doc_line(indent, line);
count += 1;
}
}
@@ -263,7 +279,7 @@ void print_doc_package(CheckerInfo *info, AstPackage *pkg) {
}
GB_ASSERT(type_expr != nullptr || init_expr != nullptr);
print_doc_line_no_newline(2, "%.*s", LIT(e->token.string));
print_doc_line_no_newline(2, e->token.string);
if (type_expr != nullptr) {
gbString t = expr_to_string(type_expr);
gb_printf(": %s ", t);
@@ -298,7 +314,7 @@ void print_doc_package(CheckerInfo *info, AstPackage *pkg) {
for_array(i, pkg->files) {
AstFile *f = pkg->files[i];
String filename = remove_directory_from_path(f->fullpath);
print_doc_line(2, "%.*s", LIT(filename));
print_doc_line(2, filename);
}
}
+22 -6
View File
@@ -15,7 +15,7 @@ struct OdinDocVersionType {
#define OdinDocVersionType_Major 0
#define OdinDocVersionType_Minor 2
#define OdinDocVersionType_Patch 0
#define OdinDocVersionType_Patch 4
struct OdinDocHeaderBase {
u8 magic[8];
@@ -99,6 +99,7 @@ enum OdinDocTypeFlag_Union : u32 {
OdinDocTypeFlag_Union_polymorphic = 1<<0,
OdinDocTypeFlag_Union_no_nil = 1<<1,
OdinDocTypeFlag_Union_maybe = 1<<2,
OdinDocTypeFlag_Union_shared_nil = 1<<3,
};
enum OdinDocTypeFlag_Proc : u32 {
@@ -137,6 +138,7 @@ struct OdinDocType {
OdinDocArray<OdinDocEntityIndex> entities;
OdinDocTypeIndex polmorphic_params;
OdinDocArray<OdinDocString> where_clauses;
OdinDocArray<OdinDocString> tags; // struct field tags
};
struct OdinDocAttribute {
@@ -153,6 +155,7 @@ enum OdinDocEntityKind : u32 {
OdinDocEntity_ProcGroup = 5,
OdinDocEntity_ImportName = 6,
OdinDocEntity_LibraryName = 7,
OdinDocEntity_Builtin = 8,
};
enum OdinDocEntityFlag : u64 {
@@ -169,20 +172,27 @@ enum OdinDocEntityFlag : u64 {
OdinDocEntityFlag_Type_Alias = 1ull<<20,
OdinDocEntityFlag_Builtin_Pkg_Builtin = 1ull<<30,
OdinDocEntityFlag_Builtin_Pkg_Intrinsics = 1ull<<31,
OdinDocEntityFlag_Var_Thread_Local = 1ull<<40,
OdinDocEntityFlag_Var_Static = 1ull<<41,
OdinDocEntityFlag_Private = 1ull<<50,
};
struct OdinDocEntity {
OdinDocEntityKind kind;
u32 flags;
u32 reserved;
u64 flags;
OdinDocPosition pos;
OdinDocString name;
OdinDocTypeIndex type;
OdinDocString init_string;
u32 reserved_for_init;
OdinDocString comment;
OdinDocString docs;
OdinDocString comment; // line comment
OdinDocString docs; // preceding comment
i32 field_group_index;
OdinDocEntityIndex foreign_library;
OdinDocString link_name;
OdinDocArray<OdinDocAttribute> attributes;
@@ -196,15 +206,21 @@ enum OdinDocPkgFlags : u32 {
OdinDocPkgFlag_Init = 1<<2,
};
struct OdinDocScopeEntry {
OdinDocString name;
OdinDocEntityIndex entity;
};
struct OdinDocPkg {
OdinDocString fullpath;
OdinDocString name;
u32 flags;
OdinDocString docs;
OdinDocArray<OdinDocFileIndex> files;
OdinDocArray<OdinDocEntityIndex> entities;
OdinDocArray<OdinDocFileIndex> files;
OdinDocArray<OdinDocScopeEntry> entries;
};
struct OdinDocHeader {
OdinDocHeaderBase base;
+85 -69
View File
@@ -257,7 +257,7 @@ OdinDocArray<T> odin_write_item_as_slice(OdinDocWriter *w, T data) {
OdinDocPosition odin_doc_token_pos_cast(OdinDocWriter *w, TokenPos const &pos) {
OdinDocFileIndex file_index = 0;
if (pos.file_id != 0) {
AstFile *file = get_ast_file_from_id(pos.file_id);
AstFile *file = global_files[pos.file_id];
if (file != nullptr) {
OdinDocFileIndex *file_index_found = map_get(&w->file_cache, file);
GB_ASSERT(file_index_found != nullptr);
@@ -292,8 +292,9 @@ bool odin_doc_append_comment_group_string(Array<u8> *buf, CommentGroup *g) {
String comment = g->list[i].string;
String original_comment = comment;
bool slash_slash = comment[1] == '/';
bool slash_slash = false;
if (comment[1] == '/') {
slash_slash = true;
comment.text += 2;
comment.len -= 2;
} else if (comment[1] == '*') {
@@ -330,7 +331,7 @@ bool odin_doc_append_comment_group_string(Array<u8> *buf, CommentGroup *g) {
}
}
String line = substring(comment, pos, end);
pos = end+1;
pos = end;
String trimmed_line = string_trim_whitespace(line);
if (trimmed_line.len == 0) {
if (count == 0) {
@@ -482,7 +483,7 @@ OdinDocTypeIndex odin_doc_type(OdinDocWriter *w, Type *type) {
for_array(i, w->type_cache.entries) {
// NOTE(bill): THIS IS SLOW
Type *other = w->type_cache.entries[i].key;
if (are_types_identical(type, other)) {
if (are_types_identical_unique_tuples(type, other)) {
OdinDocTypeIndex index = w->type_cache.entries[i].value;
map_set(&w->type_cache, type, index);
return index;
@@ -511,10 +512,16 @@ OdinDocTypeIndex odin_doc_type(OdinDocWriter *w, Type *type) {
doc_type.entities = odin_doc_add_entity_as_slice(w, type->Named.type_name);
break;
case Type_Generic:
doc_type.kind = OdinDocType_Generic;
doc_type.name = odin_doc_write_string(w, type->Generic.name);
if (type->Generic.specialized) {
doc_type.types = odin_doc_type_as_slice(w, type->Generic.specialized);
{
String name = type->Generic.name;
if (type->Generic.entity) {
name = type->Generic.entity->token.string;
}
doc_type.kind = OdinDocType_Generic;
doc_type.name = odin_doc_write_string(w, name);
if (type->Generic.specialized) {
doc_type.types = odin_doc_type_as_slice(w, type->Generic.specialized);
}
}
break;
case Type_Pointer:
@@ -598,14 +605,24 @@ OdinDocTypeIndex odin_doc_type(OdinDocWriter *w, Type *type) {
}
doc_type.where_clauses = odin_doc_where_clauses(w, st->where_clauses);
}
auto tags = array_make<OdinDocString>(heap_allocator(), type->Struct.fields.count);
defer (array_free(&tags));
for_array(i, type->Struct.fields) {
tags[i] = odin_doc_write_string(w, type->Struct.tags[i]);
}
doc_type.tags = odin_write_slice(w, tags.data, tags.count);
}
break;
case Type_Union:
doc_type.kind = OdinDocType_Union;
if (type->Union.is_polymorphic) { doc_type.flags |= OdinDocTypeFlag_Union_polymorphic; }
if (type->Union.no_nil) { doc_type.flags |= OdinDocTypeFlag_Union_no_nil; }
if (type->Union.maybe) { doc_type.flags |= OdinDocTypeFlag_Union_maybe; }
switch (type->Union.kind) {
case UnionType_no_nil: doc_type.flags |= OdinDocTypeFlag_Union_no_nil; break;
case UnionType_shared_nil: doc_type.flags |= OdinDocTypeFlag_Union_shared_nil; break;
}
{
auto variants = array_make<OdinDocTypeIndex>(heap_allocator(), type->Union.variants.count);
defer (array_free(&variants));
@@ -667,40 +684,7 @@ OdinDocTypeIndex odin_doc_type(OdinDocWriter *w, Type *type) {
types[1] = odin_doc_type(w, type->Proc.results);
doc_type.types = odin_write_slice(w, types, gb_count_of(types));
String calling_convention = {};
switch (type->Proc.calling_convention) {
case ProcCC_Invalid:
// no need
break;
case ProcCC_Odin:
if (default_calling_convention() != ProcCC_Odin) {
calling_convention = str_lit("odin");
}
break;
case ProcCC_Contextless:
if (default_calling_convention() != ProcCC_Contextless) {
calling_convention = str_lit("contextless");
}
break;
case ProcCC_CDecl:
calling_convention = str_lit("cdecl");
break;
case ProcCC_StdCall:
calling_convention = str_lit("stdcall");
break;
case ProcCC_FastCall:
calling_convention = str_lit("fastcall");
break;
case ProcCC_None:
calling_convention = str_lit("none");
break;
case ProcCC_Naked:
calling_convention = str_lit("naked");
break;
case ProcCC_InlineAsm:
calling_convention = str_lit("inline-assembly");
break;
}
String calling_convention = make_string_c(proc_calling_convention_strings[type->Proc.calling_convention]);
doc_type.calling_convention = odin_doc_write_string(w, calling_convention);
}
break;
@@ -795,11 +779,21 @@ OdinDocEntityIndex odin_doc_add_entity(OdinDocWriter *w, Entity *e) {
comment = e->decl_info->comment;
docs = e->decl_info->docs;
}
if (e->kind == Entity_Variable) {
if (!comment) { comment = e->Variable.comment; }
if (!docs) { docs = e->Variable.docs; }
} else if (e->kind == Entity_Constant) {
if (!comment) { comment = e->Constant.comment; }
if (!docs) { docs = e->Constant.docs; }
}
String name = e->token.string;
String link_name = {};
TokenPos pos = e->token.pos;
OdinDocEntityKind kind = OdinDocEntity_Invalid;
u32 flags = 0;
u64 flags = 0;
i32 field_group_index = -1;
switch (e->kind) {
case Entity_Invalid: kind = OdinDocEntity_Invalid; break;
@@ -810,6 +804,7 @@ OdinDocEntityIndex odin_doc_add_entity(OdinDocWriter *w, Entity *e) {
case Entity_ProcGroup: kind = OdinDocEntity_ProcGroup; break;
case Entity_ImportName: kind = OdinDocEntity_ImportName; break;
case Entity_LibraryName: kind = OdinDocEntity_LibraryName; break;
case Entity_Builtin: kind = OdinDocEntity_Builtin; break;
}
switch (e->kind) {
@@ -826,12 +821,36 @@ OdinDocEntityIndex odin_doc_add_entity(OdinDocWriter *w, Entity *e) {
}
if (e->flags & EntityFlag_Static) { flags |= OdinDocEntityFlag_Var_Static; }
link_name = e->Variable.link_name;
if (init_expr == nullptr) {
init_expr = e->Variable.init_expr;
}
field_group_index = e->Variable.field_group_index;
break;
case Entity_Constant:
field_group_index = e->Constant.field_group_index;
break;
case Entity_Procedure:
if (e->Procedure.is_foreign) { flags |= OdinDocEntityFlag_Foreign; }
if (e->Procedure.is_export) { flags |= OdinDocEntityFlag_Export; }
link_name = e->Procedure.link_name;
break;
case Entity_Builtin:
{
auto bp = builtin_procs[e->Builtin.id];
pos = {};
name = bp.name;
switch (bp.pkg) {
case BuiltinProcPkg_builtin:
flags |= OdinDocEntityFlag_Builtin_Pkg_Builtin;
break;
case BuiltinProcPkg_intrinsics:
flags |= OdinDocEntityFlag_Builtin_Pkg_Intrinsics;
break;
default:
GB_PANIC("Unhandled BuiltinProcPkg");
}
}
break;
}
if (e->flags & EntityFlag_Param) {
@@ -842,6 +861,9 @@ OdinDocEntityIndex odin_doc_add_entity(OdinDocWriter *w, Entity *e) {
if (e->flags & EntityFlag_NoAlias) { flags |= OdinDocEntityFlag_Param_NoAlias; }
if (e->flags & EntityFlag_AnyInt) { flags |= OdinDocEntityFlag_Param_AnyInt; }
}
if (e->scope && (e->scope->flags & (ScopeFlag_File|ScopeFlag_Pkg)) && !is_entity_exported(e)) {
flags |= OdinDocEntityFlag_Private;
}
OdinDocString init_string = {};
if (init_expr) {
@@ -856,20 +878,21 @@ OdinDocEntityIndex odin_doc_add_entity(OdinDocWriter *w, Entity *e) {
init_string = odin_doc_write_string(w, make_string_c(exact_value_to_string(e->Constant.value)));
}
} else if (e->kind == Entity_Variable) {
if (e->Variable.param_expr) {
init_string = odin_doc_expr_string(w, e->Variable.param_expr);
if (e->Variable.param_value.original_ast_expr) {
init_string = odin_doc_expr_string(w, e->Variable.param_value.original_ast_expr);
}
}
}
doc_entity.kind = kind;
doc_entity.flags = flags;
doc_entity.pos = odin_doc_token_pos_cast(w, e->token.pos);
doc_entity.name = odin_doc_write_string(w, e->token.string);
doc_entity.pos = odin_doc_token_pos_cast(w, pos);
doc_entity.name = odin_doc_write_string(w, name);
doc_entity.type = 0; // Set later
doc_entity.init_string = init_string;
doc_entity.comment = odin_doc_comment_group_string(w, comment);
doc_entity.docs = odin_doc_comment_group_string(w, docs);
doc_entity.field_group_index = field_group_index;
doc_entity.foreign_library = 0; // Set later
doc_entity.link_name = odin_doc_write_string(w, link_name);
if (e->decl_info != nullptr) {
@@ -941,7 +964,7 @@ void odin_doc_update_entities(OdinDocWriter *w) {
OdinDocArray<OdinDocEntityIndex> odin_doc_add_pkg_entities(OdinDocWriter *w, AstPackage *pkg) {
OdinDocArray<OdinDocScopeEntry> odin_doc_add_pkg_entries(OdinDocWriter *w, AstPackage *pkg) {
if (pkg->scope == nullptr) {
return {};
}
@@ -949,14 +972,14 @@ OdinDocArray<OdinDocEntityIndex> odin_doc_add_pkg_entities(OdinDocWriter *w, Ast
return {};
}
auto entities = array_make<Entity *>(heap_allocator(), 0, pkg->scope->elements.entries.count);
defer (array_free(&entities));
auto entries = array_make<OdinDocScopeEntry>(heap_allocator(), 0, w->entity_cache.entries.count);
defer (array_free(&entries));
for_array(i, pkg->scope->elements.entries) {
String name = pkg->scope->elements.entries[i].key.string;
Entity *e = pkg->scope->elements.entries[i].value;
switch (e->kind) {
case Entity_Invalid:
case Entity_Builtin:
case Entity_Nil:
case Entity_Label:
continue;
@@ -967,34 +990,27 @@ OdinDocArray<OdinDocEntityIndex> odin_doc_add_pkg_entities(OdinDocWriter *w, Ast
case Entity_ProcGroup:
case Entity_ImportName:
case Entity_LibraryName:
case Entity_Builtin:
// Fine
break;
}
array_add(&entities, e);
}
gb_sort_array(entities.data, entities.count, cmp_entities_for_printing);
auto entity_indices = array_make<OdinDocEntityIndex>(heap_allocator(), 0, w->entity_cache.entries.count);
defer (array_free(&entity_indices));
for_array(i, entities) {
Entity *e = entities[i];
if (e->pkg != pkg) {
continue;
}
if (!is_entity_exported(e)) {
if (!is_entity_exported(e, true)) {
continue;
}
if (e->token.string.len == 0) {
continue;
}
OdinDocEntityIndex doc_entity_index = 0;
doc_entity_index = odin_doc_add_entity(w, e);
array_add(&entity_indices, doc_entity_index);
OdinDocScopeEntry entry = {};
entry.name = odin_doc_write_string(w, name);
entry.entity = odin_doc_add_entity(w, e);
array_add(&entries, entry);
}
return odin_write_slice(w, entity_indices.data, entity_indices.count);
return odin_write_slice(w, entries.data, entries.count);
}
@@ -1062,7 +1078,7 @@ void odin_doc_write_docs(OdinDocWriter *w) {
}
doc_pkg.files = odin_write_slice(w, file_indices.data, file_indices.count);
doc_pkg.entities = odin_doc_add_pkg_entities(w, pkg);
doc_pkg.entries = odin_doc_add_pkg_entries(w, pkg);
if (dst) {
*dst = doc_pkg;
+56 -8
View File
@@ -45,9 +45,9 @@ enum EntityFlag : u64 {
EntityFlag_NoAlias = 1ull<<9,
EntityFlag_TypeField = 1ull<<10,
EntityFlag_Value = 1ull<<11,
EntityFlag_Sret = 1ull<<12,
EntityFlag_ByVal = 1ull<<13,
EntityFlag_BitFieldValue = 1ull<<14,
EntityFlag_PolyConst = 1ull<<15,
EntityFlag_NotExported = 1ull<<16,
EntityFlag_ConstInput = 1ull<<17,
@@ -74,6 +74,7 @@ enum EntityFlag : u64 {
EntityFlag_Test = 1ull<<30,
EntityFlag_Init = 1ull<<31,
EntityFlag_Subtype = 1ull<<32,
EntityFlag_CustomLinkName = 1ull<<40,
EntityFlag_CustomLinkage_Internal = 1ull<<41,
@@ -82,10 +83,15 @@ enum EntityFlag : u64 {
EntityFlag_CustomLinkage_LinkOnce = 1ull<<44,
EntityFlag_Require = 1ull<<50,
EntityFlag_ByPtr = 1ull<<51, // enforce parameter is passed by pointer
EntityFlag_Overridden = 1ull<<63,
};
enum : u64 {
EntityFlags_IsSubtype = EntityFlag_Using|EntityFlag_Subtype,
};
enum EntityState : u32 {
EntityState_Unresolved = 0,
EntityState_InProgress = 1,
@@ -110,6 +116,16 @@ struct ParameterValue {
};
};
bool has_parameter_value(ParameterValue const &param_value) {
if (param_value.kind != ParameterValue_Invalid) {
return true;
}
if (param_value.original_ast_expr != nullptr) {
return true;
}
return false;
}
enum EntityConstantFlags : u32 {
EntityConstantFlag_ImplicitEnumValue = 1<<0,
};
@@ -122,6 +138,28 @@ enum ProcedureOptimizationMode : u32 {
ProcedureOptimizationMode_Speed,
};
BlockingMutex global_type_name_objc_metadata_mutex;
struct TypeNameObjCMetadataEntry {
String name;
Entity *entity;
};
struct TypeNameObjCMetadata {
BlockingMutex *mutex;
Array<TypeNameObjCMetadataEntry> type_entries;
Array<TypeNameObjCMetadataEntry> value_entries;
};
TypeNameObjCMetadata *create_type_name_obj_c_metadata() {
TypeNameObjCMetadata *md = gb_alloc_item(permanent_allocator(), TypeNameObjCMetadata);
md->mutex = gb_alloc_item(permanent_allocator(), BlockingMutex);
mutex_init(md->mutex);
array_init(&md->type_entries, heap_allocator());
array_init(&md->value_entries, heap_allocator());
return md;
}
// An Entity is a named "thing" in the language
struct Entity {
EntityKind kind;
@@ -160,13 +198,16 @@ struct Entity {
ExactValue value;
ParameterValue param_value;
u32 flags;
i32 field_group_index;
CommentGroup *docs;
CommentGroup *comment;
} Constant;
struct {
Ast *init_expr; // only used for some variables within procedure bodies
i32 field_index;
i32 field_group_index;
ParameterValue param_value;
Ast * param_expr;
String thread_local_model;
Entity * foreign_library;
@@ -174,6 +215,8 @@ struct Entity {
String link_name;
String link_prefix;
String link_section;
CommentGroup *docs;
CommentGroup *comment;
bool is_foreign;
bool is_export;
} Variable;
@@ -181,6 +224,8 @@ struct Entity {
Type * type_parameter_specialization;
String ir_mangled_name;
bool is_type_alias;
String objc_class_name;
TypeNameObjCMetadata *objc_metadata;
} TypeName;
struct {
u64 tags;
@@ -189,10 +234,12 @@ struct Entity {
String link_name;
String link_prefix;
DeferredProcedure deferred_procedure;
bool is_foreign;
bool is_export;
bool generated_from_polymorphic;
ProcedureOptimizationMode optimization_mode;
bool is_foreign : 1;
bool is_export : 1;
bool generated_from_polymorphic : 1;
bool target_feature_disabled : 1;
String target_feature;
} Procedure;
struct {
Array<Entity *> entities;
@@ -208,6 +255,7 @@ struct Entity {
struct {
Slice<String> paths;
String name;
i64 priority_index;
} LibraryName;
i32 Nil;
struct {
@@ -240,7 +288,7 @@ bool is_entity_exported(Entity *e, bool allow_builtin = false) {
if (e->flags & EntityFlag_NotExported) {
return false;
}
if (e->file != nullptr && (e->file->flags & AstFile_IsPrivate) != 0) {
if (e->file != nullptr && (e->file->flags & (AstFile_IsPrivatePkg|AstFile_IsPrivateFile)) != 0) {
return false;
}
+416
View File
@@ -0,0 +1,416 @@
struct ErrorCollector {
TokenPos prev;
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;
Array<u8> error_buffer;
Array<String> errors;
};
gb_global ErrorCollector global_error_collector;
#define MAX_ERROR_COLLECTOR_COUNT (36)
bool any_errors(void) {
return global_error_collector.count.load() != 0;
}
void init_global_error_collector(void) {
mutex_init(&global_error_collector.mutex);
mutex_init(&global_error_collector.block_mutex);
mutex_init(&global_error_collector.error_out_mutex);
mutex_init(&global_error_collector.string_mutex);
array_init(&global_error_collector.errors, heap_allocator());
array_init(&global_error_collector.error_buffer, heap_allocator());
array_init(&global_file_path_strings, heap_allocator(), 1, 4096);
array_init(&global_files, heap_allocator(), 1, 4096);
}
// temporary
// defined in build_settings.cpp
char *token_pos_to_string(TokenPos const &pos);
bool set_file_path_string(i32 index, String const &path) {
bool ok = false;
GB_ASSERT(index >= 0);
mutex_lock(&global_error_collector.string_mutex);
if (index >= global_file_path_strings.count) {
array_resize(&global_file_path_strings, index+1);
}
String prev = global_file_path_strings[index];
if (prev.len == 0) {
global_file_path_strings[index] = path;
ok = true;
}
mutex_unlock(&global_error_collector.string_mutex);
return ok;
}
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);
if (index >= global_files.count) {
array_resize(&global_files, index+1);
}
AstFile *prev = global_files[index];
if (prev == nullptr) {
global_files[index] = file;
ok = true;
}
mutex_unlock(&global_error_collector.string_mutex);
return ok;
}
String get_file_path_string(i32 index) {
GB_ASSERT(index >= 0);
mutex_lock(&global_error_collector.string_mutex);
String path = {};
if (index < global_file_path_strings.count) {
path = global_file_path_strings[index];
}
mutex_unlock(&global_error_collector.string_mutex);
return path;
}
AstFile *thread_safe_get_ast_file_from_id(i32 index) {
GB_ASSERT(index >= 0);
mutex_lock(&global_error_collector.string_mutex);
AstFile *file = nullptr;
if (index < global_files.count) {
file = global_files[index];
}
mutex_unlock(&global_error_collector.string_mutex);
return file;
}
void begin_error_block(void) {
mutex_lock(&global_error_collector.block_mutex);
global_error_collector.in_block.store(true);
}
void end_error_block(void) {
if (global_error_collector.error_buffer.count > 0) {
isize n = global_error_collector.error_buffer.count;
u8 *text = gb_alloc_array(permanent_allocator(), u8, n+1);
gb_memmove(text, global_error_collector.error_buffer.data, n);
text[n] = 0;
String s = {text, n};
array_add(&global_error_collector.errors, s);
global_error_collector.error_buffer.count = 0;
}
global_error_collector.in_block.store(false);
mutex_unlock(&global_error_collector.block_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);
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) {
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;
} 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);
}
ErrorOutProc *error_out_va = default_error_out_va;
// NOTE: defined in build_settings.cpp
bool global_warnings_as_errors(void);
bool global_ignore_warnings(void);
bool show_error_line(void);
gbString get_file_line_as_string(TokenPos const &pos, i32 *offset);
void error_out(char const *fmt, ...) {
va_list va;
va_start(va, fmt);
error_out_va(fmt, va);
va_end(va);
}
bool show_error_on_line(TokenPos const &pos, TokenPos end) {
if (!show_error_line()) {
return false;
}
i32 offset = 0;
gbString the_line = get_file_line_as_string(pos, &offset);
defer (gb_string_free(the_line));
if (the_line != nullptr) {
String line = make_string(cast(u8 const *)the_line, gb_string_length(the_line));
// TODO(bill): This assumes ASCII
enum {
MAX_LINE_LENGTH = 76,
MAX_TAB_WIDTH = 8,
ELLIPSIS_PADDING = 8
};
error_out("\n\t");
if (line.len+MAX_TAB_WIDTH+ELLIPSIS_PADDING > MAX_LINE_LENGTH) {
i32 const half_width = MAX_LINE_LENGTH/2;
i32 left = cast(i32)(offset);
i32 right = cast(i32)(line.len - offset);
left = gb_min(left, half_width);
right = gb_min(right, half_width);
line.text += offset-left;
line.len -= offset+right-left;
line = string_trim_whitespace(line);
offset = left + ELLIPSIS_PADDING/2;
error_out("... %.*s ...", LIT(line));
} else {
error_out("%.*s", LIT(line));
}
error_out("\n\t");
for (i32 i = 0; i < offset; i++) {
error_out(" ");
}
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) {
i32 length = gb_min(end.offset - pos.offset, cast(i32)(line.len-offset));
for (i32 i = 1; i < length-1; i++) {
error_out("~");
}
if (length > 1) {
error_out("^");
}
}
}
error_out("\n\n");
return true;
}
return false;
}
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);
// NOTE(bill): Duplicate error, skip it
if (pos.line == 0) {
error_out("Error: %s\n", gb_bprintf_va(fmt, va));
} else if (global_error_collector.prev != pos) {
global_error_collector.prev = pos;
error_out("%s %s\n",
token_pos_to_string(pos),
gb_bprintf_va(fmt, va));
show_error_on_line(pos, end);
}
mutex_unlock(&global_error_collector.mutex);
if (global_error_collector.count > MAX_ERROR_COLLECTOR_COUNT) {
gb_exit(1);
}
}
void warning_va(TokenPos const &pos, TokenPos end, char const *fmt, va_list va) {
if (global_warnings_as_errors()) {
error_va(pos, end, fmt, va);
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("Warning: %s\n", gb_bprintf_va(fmt, va));
} else if (global_error_collector.prev != pos) {
global_error_collector.prev = pos;
error_out("%s Warning: %s\n",
token_pos_to_string(pos),
gb_bprintf_va(fmt, va));
show_error_on_line(pos, end);
}
}
mutex_unlock(&global_error_collector.mutex);
}
void error_line_va(char const *fmt, va_list va) {
error_out_va(fmt, va);
}
void error_no_newline_va(TokenPos const &pos, char const *fmt, va_list va) {
mutex_lock(&global_error_collector.mutex);
global_error_collector.count++;
// NOTE(bill): Duplicate error, skip it
if (pos.line == 0) {
error_out("Error: %s", gb_bprintf_va(fmt, va));
} else if (global_error_collector.prev != pos) {
global_error_collector.prev = pos;
error_out("%s %s",
token_pos_to_string(pos),
gb_bprintf_va(fmt, va));
}
mutex_unlock(&global_error_collector.mutex);
if (global_error_collector.count > MAX_ERROR_COLLECTOR_COUNT) {
gb_exit(1);
}
}
void syntax_error_va(TokenPos const &pos, TokenPos end, char const *fmt, va_list va) {
mutex_lock(&global_error_collector.mutex);
global_error_collector.count++;
// NOTE(bill): Duplicate error, skip it
if (global_error_collector.prev != pos) {
global_error_collector.prev = pos;
error_out("%s Syntax Error: %s\n",
token_pos_to_string(pos),
gb_bprintf_va(fmt, va));
show_error_on_line(pos, end);
} else if (pos.line == 0) {
error_out("Syntax Error: %s\n", gb_bprintf_va(fmt, va));
}
mutex_unlock(&global_error_collector.mutex);
if (global_error_collector.count > MAX_ERROR_COLLECTOR_COUNT) {
gb_exit(1);
}
}
void syntax_warning_va(TokenPos const &pos, TokenPos end, char const *fmt, va_list va) {
if (global_warnings_as_errors()) {
syntax_error_va(pos, end, fmt, va);
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;
error_out("%s Syntax Warning: %s\n",
token_pos_to_string(pos),
gb_bprintf_va(fmt, va));
show_error_on_line(pos, end);
} else if (pos.line == 0) {
error_out("Warning: %s\n", gb_bprintf_va(fmt, va));
}
}
mutex_unlock(&global_error_collector.mutex);
}
void warning(Token const &token, char const *fmt, ...) {
va_list va;
va_start(va, fmt);
warning_va(token.pos, {}, fmt, va);
va_end(va);
}
void error(Token const &token, char const *fmt, ...) {
va_list va;
va_start(va, fmt);
error_va(token.pos, {}, fmt, va);
va_end(va);
}
void error(TokenPos pos, char const *fmt, ...) {
va_list va;
va_start(va, fmt);
Token token = {};
token.pos = pos;
error_va(pos, {}, fmt, va);
va_end(va);
}
void error_line(char const *fmt, ...) {
va_list va;
va_start(va, fmt);
error_line_va(fmt, va);
va_end(va);
}
void syntax_error(Token const &token, char const *fmt, ...) {
va_list va;
va_start(va, fmt);
syntax_error_va(token.pos, {}, fmt, va);
va_end(va);
}
void syntax_error(TokenPos pos, char const *fmt, ...) {
va_list va;
va_start(va, fmt);
syntax_error_va(pos, {}, fmt, va);
va_end(va);
}
void syntax_warning(Token const &token, char const *fmt, ...) {
va_list va;
va_start(va, fmt);
syntax_warning_va(token.pos, {}, fmt, va);
va_end(va);
}
void compiler_error(char const *fmt, ...) {
va_list va;
va_start(va, fmt);
gb_printf_err("Internal Compiler Error: %s\n",
gb_bprintf_va(fmt, va));
va_end(va);
GB_DEBUG_TRAP();
gb_exit(1);
}
+22 -8
View File
@@ -50,9 +50,9 @@ struct ExactValue {
union {
bool value_bool;
String value_string;
BigInt value_integer; // NOTE(bill): This must be an integer and not a pointer
BigInt value_integer;
f64 value_float;
i64 value_pointer;
i64 value_pointer; // NOTE(bill): This must be an integer and not a pointer
Complex128 *value_complex;
Quaternion256 *value_quaternion;
Ast * value_compound;
@@ -177,7 +177,11 @@ ExactValue exact_value_typeid(Type *type) {
ExactValue exact_value_integer_from_string(String const &string) {
ExactValue result = {ExactValue_Integer};
big_int_from_string(&result.value_integer, string);
bool success;
big_int_from_string(&result.value_integer, string, &success);
if (!success) {
result = {ExactValue_Invalid};
}
return result;
}
@@ -591,6 +595,7 @@ failure:
i32 exact_value_order(ExactValue const &v) {
switch (v.kind) {
case ExactValue_Invalid:
case ExactValue_Compound:
return 0;
case ExactValue_Bool:
case ExactValue_String:
@@ -607,8 +612,6 @@ i32 exact_value_order(ExactValue const &v) {
return 6;
case ExactValue_Procedure:
return 7;
// case ExactValue_Compound:
// return 8;
default:
GB_PANIC("How'd you get here? Invalid Value.kind %d", v.kind);
@@ -630,6 +633,9 @@ void match_exact_values(ExactValue *x, ExactValue *y) {
case ExactValue_Bool:
case ExactValue_String:
case ExactValue_Quaternion:
case ExactValue_Pointer:
case ExactValue_Procedure:
case ExactValue_Typeid:
return;
case ExactValue_Integer:
@@ -671,9 +677,6 @@ void match_exact_values(ExactValue *x, ExactValue *y) {
return;
}
break;
case ExactValue_Procedure:
return;
}
compiler_error("match_exact_values: How'd you get here? Invalid ExactValueKind %d", x->kind);
@@ -932,6 +935,17 @@ bool compare_exact_values(TokenKind op, ExactValue x, ExactValue y) {
break;
}
case ExactValue_Pointer: {
switch (op) {
case Token_CmpEq: return x.value_pointer == y.value_pointer;
case Token_NotEq: return x.value_pointer != y.value_pointer;
case Token_Lt: return x.value_pointer < y.value_pointer;
case Token_LtEq: return x.value_pointer <= y.value_pointer;
case Token_Gt: return x.value_pointer > y.value_pointer;
case Token_GtEq: return x.value_pointer >= y.value_pointer;
}
}
case ExactValue_Typeid:
switch (op) {
case Token_CmpEq: return are_types_identical(x.value_typeid, y.value_typeid);
+127 -16
View File
@@ -79,6 +79,10 @@ extern "C" {
#ifndef GB_SYSTEM_FREEBSD
#define GB_SYSTEM_FREEBSD 1
#endif
#elif defined(__OpenBSD__)
#ifndef GB_SYSTEM_OPENBSD
#define GB_SYSTEM_OPENBSD 1
#endif
#else
#error This UNIX operating system is not supported
#endif
@@ -199,7 +203,7 @@ extern "C" {
#endif
#include <stdlib.h> // NOTE(bill): malloc on linux
#include <sys/mman.h>
#if !defined(GB_SYSTEM_OSX) && !defined(__FreeBSD__)
#if !defined(GB_SYSTEM_OSX) && !defined(__FreeBSD__) && !defined(__OpenBSD__)
#include <sys/sendfile.h>
#endif
#include <sys/stat.h>
@@ -235,6 +239,12 @@ extern "C" {
#define sendfile(out, in, offset, count) sendfile(out, in, offset, count, NULL, NULL, 0)
#endif
#if defined(GB_SYSTEM_OPENBSD)
#include <stdio.h>
#include <pthread_np.h>
#define lseek64 lseek
#endif
#if defined(GB_SYSTEM_UNIX)
#include <semaphore.h>
#endif
@@ -783,6 +793,13 @@ typedef struct gbAffinity {
isize thread_count;
isize threads_per_core;
} gbAffinity;
#elif defined(GB_SYSTEM_OPENBSD)
typedef struct gbAffinity {
b32 is_accurate;
isize core_count;
isize thread_count;
isize threads_per_core;
} gbAffinity;
#else
#error TODO(bill): Unknown system
#endif
@@ -1663,7 +1680,7 @@ GB_DEF gbFileContents gb_file_read_contents(gbAllocator a, b32 zero_terminate, c
GB_DEF void gb_file_free_contents(gbFileContents *fc);
// TODO(bill): Should these have different na,es as they do not take in a gbFile * ???
// TODO(bill): Should these have different names as they do not take in a gbFile * ???
GB_DEF b32 gb_file_exists (char const *filepath);
GB_DEF gbFileTime gb_file_last_write_time(char const *filepath);
GB_DEF b32 gb_file_copy (char const *existing_filename, char const *new_filename, b32 fail_if_exists);
@@ -3355,6 +3372,8 @@ gb_inline u32 gb_thread_current_id(void) {
__asm__("mov %%gs:0x08,%0" : "=r"(thread_id));
#elif defined(GB_ARCH_64_BIT) && defined(GB_CPU_X86)
__asm__("mov %%fs:0x10,%0" : "=r"(thread_id));
#elif defined(GB_SYSTEM_LINUX)
thread_id = gettid();
#else
#error Unsupported architecture for gb_thread_current_id()
#endif
@@ -3676,6 +3695,30 @@ 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_OPENBSD)
#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;
@@ -6023,7 +6066,7 @@ gbFileTime gb_file_last_write_time(char const *filepath) {
gb_inline b32 gb_file_copy(char const *existing_filename, char const *new_filename, b32 fail_if_exists) {
#if defined(GB_SYSTEM_OSX)
return copyfile(existing_filename, new_filename, NULL, COPYFILE_DATA) == 0;
#else
#elif defined(GB_SYSTEM_LINUX) || defined(GB_SYSTEM_FREEBSD)
isize size;
int existing_fd = open(existing_filename, O_RDONLY, 0);
int new_fd = open(new_filename, O_WRONLY|O_CREAT, 0666);
@@ -6039,6 +6082,49 @@ gb_inline b32 gb_file_copy(char const *existing_filename, char const *new_filena
close(new_fd);
close(existing_fd);
return size == stat_existing.st_size;
#else
int new_flags = O_WRONLY | O_CREAT;
if (fail_if_exists) {
new_flags |= O_EXCL;
}
int existing_fd = open(existing_filename, O_RDONLY, 0);
int new_fd = open(new_filename, new_flags, 0666);
struct stat stat_existing;
if (fstat(existing_fd, &stat_existing) == -1) {
return 0;
}
size_t bsize = stat_existing.st_blksize > BUFSIZ ? stat_existing.st_blksize : BUFSIZ;
char *buf = (char *)malloc(bsize);
if (buf == NULL) {
close(new_fd);
close(existing_fd);
return 0;
}
isize size = 0;
ssize_t nread, nwrite, offset;
while ((nread = read(existing_fd, buf, bsize)) != -1 && nread != 0) {
for (offset = 0; nread; nread -= nwrite, offset += nwrite) {
if ((nwrite = write(new_fd, buf + offset, nread)) == -1 || nwrite == 0) {
free(buf);
close(new_fd);
close(existing_fd);
return 0;
}
size += nwrite;
}
}
free(buf);
close(new_fd);
close(existing_fd);
if (nread == -1) {
return 0;
}
return size == stat_existing.st_size;
#endif
}
@@ -6091,6 +6177,7 @@ gbFileContents gb_file_read_contents(gbAllocator a, b32 zero_terminate, char con
}
void gb_file_free_contents(gbFileContents *fc) {
if (fc == NULL || fc->size == 0) return;
GB_ASSERT_NOT_NULL(fc->data);
gb_free(fc->allocator, fc->data);
fc->data = NULL;
@@ -6186,20 +6273,44 @@ char *gb_path_get_full_name(gbAllocator a, char const *path) {
#else
char *p, *result, *fullpath = NULL;
isize len;
p = realpath(path, NULL);
fullpath = p;
if (p == NULL) {
// NOTE(bill): File does not exist
fullpath = cast(char *)path;
fullpath = realpath(path, NULL);
if (fullpath == NULL) {
// NOTE(Jeroen): Path doesn't exist.
if (gb_strlen(path) > 0 && path[0] == '/') {
// But it is an absolute path, so return as-is.
fullpath = (char *)path;
len = gb_strlen(fullpath) + 1;
result = gb_alloc_array(a, char, len + 1);
gb_memmove(result, fullpath, len);
result[len] = 0;
} else {
// Appears to be a relative path, so construct an absolute one relative to <cwd>.
char cwd[4096];
getcwd(&cwd[0], 4096);
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);
gb_memmove(result, (void *)cwd, cwd_len);
result[cwd_len] = '/';
gb_memmove(result + cwd_len + 1, (void *)path, gb_strlen(path));
result[len] = 0;
}
} else {
len = gb_strlen(fullpath) + 1;
result = gb_alloc_array(a, char, len + 1);
gb_memmove(result, fullpath, len);
result[len] = 0;
free(fullpath);
}
len = gb_strlen(fullpath);
result = gb_alloc_array(a, char, len + 1);
gb_memmove(result, fullpath, len);
result[len] = 0;
free(p);
return result;
#endif
}
+162 -40
View File
@@ -516,6 +516,10 @@ namespace lbAbiAmd64SysV {
bool is_register(LLVMTypeRef type) {
LLVMTypeKind kind = LLVMGetTypeKind(type);
i64 sz = lb_sizeof(type);
if (sz == 0) {
return false;
}
switch (kind) {
case LLVMIntegerTypeKind:
case LLVMHalfTypeKind:
@@ -965,6 +969,10 @@ namespace lbAbiArm64 {
}
return false;
}
unsigned is_homogenous_aggregate_small_enough(LLVMTypeRef *base_type_, unsigned member_count_) {
return (member_count_ <= 4);
}
lbArgType compute_return_type(LLVMContextRef c, LLVMTypeRef type, bool return_is_defined) {
LLVMTypeRef homo_base_type = {};
@@ -975,22 +983,31 @@ namespace lbAbiArm64 {
} else if (is_register(type)) {
return non_struct(c, type);
} else if (is_homogenous_aggregate(c, type, &homo_base_type, &homo_member_count)) {
return lb_arg_type_direct(type, LLVMArrayType(homo_base_type, homo_member_count), nullptr, nullptr);
if(is_homogenous_aggregate_small_enough(&homo_base_type, homo_member_count)) {
return lb_arg_type_direct(type, LLVMArrayType(homo_base_type, homo_member_count), nullptr, nullptr);
} else {
//TODO(Platin): do i need to create stuff that can handle the diffrent return type?
// else this needs a fix in llvm_backend_proc as we would need to cast it to the correct array type
//LLVMTypeRef array_type = LLVMArrayType(homo_base_type, homo_member_count);
LLVMAttributeRef attr = lb_create_enum_attribute_with_type(c, "sret", type);
return lb_arg_type_indirect(type, attr);
}
} else {
i64 size = lb_sizeof(type);
if (size <= 16) {
LLVMTypeRef cast_type = nullptr;
if (size <= 1) {
cast_type = LLVMIntTypeInContext(c, 8);
cast_type = LLVMInt8TypeInContext(c);
} else if (size <= 2) {
cast_type = LLVMIntTypeInContext(c, 16);
cast_type = LLVMInt16TypeInContext(c);
} else if (size <= 4) {
cast_type = LLVMIntTypeInContext(c, 32);
cast_type = LLVMInt32TypeInContext(c);
} else if (size <= 8) {
cast_type = LLVMIntTypeInContext(c, 64);
cast_type = LLVMInt64TypeInContext(c);
} else {
unsigned count = cast(unsigned)((size+7)/8);
cast_type = LLVMArrayType(LLVMIntTypeInContext(c, 64), count);
cast_type = LLVMArrayType(LLVMInt64TypeInContext(c), count);
}
return lb_arg_type_direct(type, cast_type, nullptr, nullptr);
} else {
@@ -999,7 +1016,7 @@ namespace lbAbiArm64 {
}
}
}
Array<lbArgType> compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count) {
auto args = array_make<lbArgType>(heap_allocator(), arg_count);
@@ -1039,10 +1056,18 @@ namespace lbAbiArm64 {
}
}
namespace lbAbiWasm32 {
namespace lbAbiWasm {
/*
NOTE(bill): All of this is custom since there is not an "official"
ABI definition for WASM, especially for Odin.
The approach taken optimizes for passing things in multiple
registers/arguments if possible rather than by pointer.
*/
Array<lbArgType> compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count);
lbArgType compute_return_type(LLVMContextRef c, LLVMTypeRef return_type, bool return_is_defined);
enum {MAX_DIRECT_STRUCT_SIZE = 32};
LB_ABI_INFO(abi_info) {
lbFunctionType *ft = gb_alloc_item(permanent_allocator(), lbFunctionType);
ft->ctx = c;
@@ -1070,7 +1095,7 @@ namespace lbAbiWasm32 {
return lb_arg_type_direct(type, nullptr, nullptr, attr);
}
bool is_struct_valid_elem_type(LLVMTypeRef type) {
bool is_basic_register_type(LLVMTypeRef type) {
switch (LLVMGetTypeKind(type)) {
case LLVMHalfTypeKind:
case LLVMFloatTypeKind:
@@ -1082,7 +1107,33 @@ namespace lbAbiWasm32 {
}
return false;
}
bool type_can_be_direct(LLVMTypeRef type) {
LLVMTypeKind kind = LLVMGetTypeKind(type);
i64 sz = lb_sizeof(type);
if (sz == 0) {
return false;
}
if (sz <= MAX_DIRECT_STRUCT_SIZE) {
if (kind == LLVMArrayTypeKind) {
if (is_basic_register_type(LLVMGetElementType(type))) {
return true;
}
} else if (kind == LLVMStructTypeKind) {
unsigned count = LLVMCountStructElementTypes(type);
for (unsigned i = 0; i < count; i++) {
LLVMTypeRef elem = LLVMStructGetTypeAtIndex(type, i);
if (!is_basic_register_type(elem)) {
return false;
}
}
return true;
}
}
return false;
}
lbArgType is_struct(LLVMContextRef c, LLVMTypeRef type) {
LLVMTypeKind kind = LLVMGetTypeKind(type);
GB_ASSERT(kind == LLVMArrayTypeKind || kind == LLVMStructTypeKind);
@@ -1091,29 +1142,9 @@ namespace lbAbiWasm32 {
if (sz == 0) {
return lb_arg_type_ignore(type);
}
if (sz <= 16) {
if (kind == LLVMArrayTypeKind) {
LLVMTypeRef elem = LLVMGetElementType(type);
if (is_struct_valid_elem_type(elem)) {
return lb_arg_type_direct(type);
}
} else if (kind == LLVMStructTypeKind) {
bool can_be_direct = true;
unsigned count = LLVMCountStructElementTypes(type);
for (unsigned i = 0; i < count; i++) {
LLVMTypeRef elem = LLVMStructGetTypeAtIndex(type, i);
if (!is_struct_valid_elem_type(elem)) {
can_be_direct = false;
break;
}
}
if (can_be_direct) {
return lb_arg_type_direct(type);
}
}
if (type_can_be_direct(type)) {
return lb_arg_type_direct(type);
}
return lb_arg_type_indirect(type, nullptr);
}
@@ -1137,6 +1168,10 @@ namespace lbAbiWasm32 {
if (!return_is_defined) {
return lb_arg_type_direct(LLVMVoidTypeInContext(c));
} else if (lb_is_type_kind(return_type, LLVMStructTypeKind) || lb_is_type_kind(return_type, LLVMArrayTypeKind)) {
if (type_can_be_direct(return_type)) {
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);
@@ -1151,6 +1186,88 @@ namespace lbAbiWasm32 {
}
}
namespace lbAbiArm32 {
Array<lbArgType> compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count, ProcCallingConvention calling_convention);
lbArgType compute_return_type(LLVMContextRef c, LLVMTypeRef return_type, bool return_is_defined);
LB_ABI_INFO(abi_info) {
lbFunctionType *ft = gb_alloc_item(permanent_allocator(), lbFunctionType);
ft->ctx = c;
ft->args = compute_arg_types(c, arg_types, arg_count, calling_convention);
ft->ret = compute_return_type(c, return_type, return_is_defined);
ft->calling_convention = calling_convention;
return ft;
}
bool is_register(LLVMTypeRef type, bool is_return) {
LLVMTypeKind kind = LLVMGetTypeKind(type);
switch (kind) {
case LLVMHalfTypeKind:
case LLVMFloatTypeKind:
case LLVMDoubleTypeKind:
return true;
case LLVMIntegerTypeKind:
return lb_sizeof(type) <= 8;
case LLVMFunctionTypeKind:
return true;
case LLVMPointerTypeKind:
return true;
case LLVMVectorTypeKind:
return true;
}
return false;
}
lbArgType non_struct(LLVMContextRef c, LLVMTypeRef type, bool is_return) {
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);
}
Array<lbArgType> compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count, ProcCallingConvention calling_convention) {
auto args = array_make<lbArgType>(heap_allocator(), arg_count);
for (unsigned i = 0; i < arg_count; i++) {
LLVMTypeRef t = arg_types[i];
if (is_register(t, false)) {
args[i] = non_struct(c, t, false);
} else {
i64 sz = lb_sizeof(t);
i64 a = lb_alignof(t);
if (is_calling_convention_odin(calling_convention) && sz > 8) {
// Minor change to improve performance using the Odin calling conventions
args[i] = lb_arg_type_indirect(t, nullptr);
} else if (a <= 4) {
unsigned n = cast(unsigned)((sz + 3) / 4);
args[i] = lb_arg_type_direct(LLVMArrayType(LLVMIntTypeInContext(c, 32), n));
} else {
unsigned n = cast(unsigned)((sz + 7) / 8);
args[i] = lb_arg_type_direct(LLVMArrayType(LLVMIntTypeInContext(c, 64), n));
}
}
}
return args;
}
lbArgType compute_return_type(LLVMContextRef c, LLVMTypeRef return_type, bool return_is_defined) {
if (!return_is_defined) {
return lb_arg_type_direct(LLVMVoidTypeInContext(c));
} else if (!is_register(return_type, true)) {
switch (lb_sizeof(return_type)) {
case 1: return lb_arg_type_direct(LLVMIntTypeInContext(c, 8), return_type, nullptr, nullptr);
case 2: return lb_arg_type_direct(LLVMIntTypeInContext(c, 16), return_type, nullptr, nullptr);
case 3: case 4: return lb_arg_type_direct(LLVMIntTypeInContext(c, 32), return_type, nullptr, nullptr);
}
LLVMAttributeRef attr = lb_create_enum_attribute_with_type(c, "sret", return_type);
return lb_arg_type_indirect(return_type, attr);
}
return non_struct(c, return_type, true);
}
};
LB_ABI_INFO(lb_get_abi_info) {
switch (calling_convention) {
@@ -1171,27 +1288,32 @@ LB_ABI_INFO(lb_get_abi_info) {
ft->calling_convention = calling_convention;
return ft;
}
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, calling_convention);
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, calling_convention);
}
switch (build_context.metrics.arch) {
case TargetArch_amd64:
if (build_context.metrics.os == TargetOs_windows) {
if (build_context.metrics.os == TargetOs_windows || build_context.metrics.abi == TargetABI_Win64) {
return lbAbiAmd64Win64::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention);
} else if (build_context.metrics.abi == TargetABI_SysV) {
return lbAbiAmd64SysV::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention);
} else {
return lbAbiAmd64SysV::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention);
}
case TargetArch_386:
case TargetArch_i386:
return lbAbi386::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention);
case TargetArch_arm32:
return lbAbiArm32::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention);
case TargetArch_arm64:
return lbAbiArm64::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention);
case TargetArch_wasm32:
// TODO(bill): implement wasm32's ABI correct
// NOTE(bill): this ABI is only an issue for WASI compatibility
return lbAbiWasm32::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention);
case TargetArch_wasm64:
// TODO(bill): implement wasm64's ABI correct
// NOTE(bill): this ABI is only an issue for WASI compatibility
return lbAbiAmd64SysV::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention);
return lbAbiWasm::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention);
}
GB_PANIC("Unsupported ABI");
+198 -57
View File
@@ -29,29 +29,53 @@ void lb_add_foreign_library_path(lbModule *m, Entity *e) {
GB_ASSERT(e->kind == Entity_LibraryName);
GB_ASSERT(e->flags & EntityFlag_Used);
for_array(i, e->LibraryName.paths) {
String library_path = e->LibraryName.paths[i];
if (library_path.len == 0) {
continue;
}
mutex_lock(&m->gen->foreign_mutex);
if (!ptr_set_update(&m->gen->foreign_libraries_set, e)) {
array_add(&m->gen->foreign_libraries, e);
}
mutex_unlock(&m->gen->foreign_mutex);
}
bool ok = true;
for_array(path_index, m->foreign_library_paths) {
String path = m->foreign_library_paths[path_index];
#if defined(GB_SYSTEM_WINDOWS)
if (str_eq_ignore_case(path, library_path)) {
#else
if (str_eq(path, library_path)) {
#endif
ok = false;
break;
}
}
GB_COMPARE_PROC(foreign_library_cmp) {
int cmp = 0;
Entity *x = *(Entity **)a;
Entity *y = *(Entity **)b;
if (x == y) {
return 0;
}
GB_ASSERT(x->kind == Entity_LibraryName);
GB_ASSERT(y->kind == Entity_LibraryName);
if (ok) {
array_add(&m->foreign_library_paths, library_path);
cmp = i64_cmp(x->LibraryName.priority_index, y->LibraryName.priority_index);
if (cmp) {
return cmp;
}
if (x->pkg != y->pkg) {
isize order_x = x->pkg ? x->pkg->order : 0;
isize order_y = y->pkg ? y->pkg->order : 0;
cmp = isize_cmp(order_x, order_y);
if (cmp) {
return cmp;
}
}
if (x->file != y->file) {
String fullpath_x = x->file ? x->file->fullpath : (String{});
String fullpath_y = y->file ? y->file->fullpath : (String{});
String file_x = filename_from_path(fullpath_x);
String file_y = filename_from_path(fullpath_y);
cmp = string_compare(file_x, file_y);
if (cmp) {
return cmp;
}
}
cmp = u64_cmp(x->order_in_src, y->order_in_src);
if (cmp) {
return cmp;
}
return i32_cmp(x->token.pos.offset, y->token.pos.offset);
}
void lb_set_entity_from_other_modules_linkage_correctly(lbModule *other_module, Entity *e, String const &name) {
@@ -454,7 +478,7 @@ lbValue lb_generate_anonymous_proc_lit(lbModule *m, String const &prefix_name, A
token.kind = Token_Ident;
token.string = name;
Entity *e = alloc_entity_procedure(nullptr, token, type, pl->tags);
e->file = expr->file;
e->file = expr->file();
e->decl_info = pl->decl;
e->code_gen_module = m;
e->flags |= EntityFlag_ProcBodyChecked;
@@ -624,6 +648,9 @@ struct lbGlobalVariable {
};
lbProcedure *lb_create_startup_type_info(lbModule *m) {
if (build_context.disallow_rtti) {
return nullptr;
}
LLVMPassManagerRef default_function_pass_manager = LLVMCreateFunctionPassManagerForModule(m->mod);
lb_populate_function_pass_manager(m, default_function_pass_manager, false, build_context.optimization_level);
LLVMFinalizeFunctionPassManager(default_function_pass_manager);
@@ -652,7 +679,54 @@ lbProcedure *lb_create_startup_type_info(lbModule *m) {
return p;
}
lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProcedure *startup_type_info, Array<lbGlobalVariable> &global_variables) { // Startup Runtime
lbProcedure *lb_create_objc_names(lbModule *main_module) {
if (build_context.metrics.os != TargetOs_darwin) {
return nullptr;
}
Type *proc_type = alloc_type_proc(nullptr, nullptr, 0, nullptr, 0, false, ProcCC_CDecl);
lbProcedure *p = lb_create_dummy_procedure(main_module, str_lit("__$init_objc_names"), proc_type);
p->is_startup = true;
return p;
}
void lb_finalize_objc_names(lbProcedure *p) {
if (p == nullptr) {
return;
}
lbModule *m = p->module;
LLVMPassManagerRef default_function_pass_manager = LLVMCreateFunctionPassManagerForModule(m->mod);
lb_populate_function_pass_manager(m, default_function_pass_manager, false, build_context.optimization_level);
LLVMFinalizeFunctionPassManager(default_function_pass_manager);
auto args = array_make<lbValue>(permanent_allocator(), 1);
LLVMSetLinkage(p->value, LLVMInternalLinkage);
lb_begin_procedure_body(p);
for_array(i, m->objc_classes.entries) {
auto const &entry = m->objc_classes.entries[i];
String name = entry.key.string;
args[0] = lb_const_value(m, t_cstring, exact_value_string(name));
lbValue ptr = lb_emit_runtime_call(p, "objc_lookUpClass", args);
lb_addr_store(p, entry.value, ptr);
}
for_array(i, m->objc_selectors.entries) {
auto const &entry = m->objc_selectors.entries[i];
String name = entry.key.string;
args[0] = lb_const_value(m, t_cstring, exact_value_string(name));
lbValue ptr = lb_emit_runtime_call(p, "sel_registerName", args);
lb_addr_store(p, entry.value, ptr);
}
lb_end_procedure_body(p);
lb_run_function_pass_manager(default_function_pass_manager, p);
}
lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProcedure *startup_type_info, lbProcedure *objc_names, Array<lbGlobalVariable> &global_variables) { // Startup Runtime
LLVMPassManagerRef default_function_pass_manager = LLVMCreateFunctionPassManagerForModule(main_module->mod);
lb_populate_function_pass_manager(main_module, default_function_pass_manager, false, build_context.optimization_level);
LLVMFinalizeFunctionPassManager(default_function_pass_manager);
@@ -664,7 +738,13 @@ lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProcedure *start
lb_begin_procedure_body(p);
LLVMBuildCall2(p->builder, LLVMGetElementType(lb_type(main_module, startup_type_info->type)), startup_type_info->value, nullptr, 0, "");
if (startup_type_info) {
LLVMBuildCall2(p->builder, LLVMGetElementType(lb_type(main_module, startup_type_info->type)), startup_type_info->value, nullptr, 0, "");
}
if (objc_names) {
LLVMBuildCall2(p->builder, LLVMGetElementType(lb_type(main_module, objc_names->type)), objc_names->value, nullptr, 0, "");
}
for_array(i, global_variables) {
auto *var = &global_variables[i];
@@ -783,7 +863,7 @@ lbProcedure *lb_create_main_procedure(lbModule *m, lbProcedure *startup_runtime)
params->Tuple.variables[1] = alloc_entity_param(nullptr, make_token_ident("fdwReason"), t_u32, false, true);
params->Tuple.variables[2] = alloc_entity_param(nullptr, make_token_ident("lpReserved"), t_rawptr, false, true);
call_cleanup = false;
} else if (build_context.metrics.os == TargetOs_windows && (build_context.metrics.arch == TargetArch_386 || build_context.no_crt)) {
} else if (build_context.metrics.os == TargetOs_windows && (build_context.metrics.arch == TargetArch_i386 || build_context.no_crt)) {
name = str_lit("mainCRTStartup");
} else if (is_arch_wasm()) {
name = str_lit("_start");
@@ -873,7 +953,7 @@ lbProcedure *lb_create_main_procedure(lbModule *m, lbProcedure *startup_runtime)
} else {
if (m->info->entry_point != nullptr) {
lbValue entry_point = lb_find_procedure_value_from_entity(m, m->info->entry_point);
lb_emit_call(p, entry_point, {});
lb_emit_call(p, entry_point, {}, ProcInlining_no_inline);
}
}
@@ -911,7 +991,12 @@ lbProcedure *lb_create_main_procedure(lbModule *m, lbProcedure *startup_runtime)
}
String lb_filepath_ll_for_module(lbModule *m) {
String path = m->gen->output_base;
String path = concatenate3_strings(permanent_allocator(),
build_context.build_paths[BuildPath_Output].basename,
STR_LIT("/"),
build_context.build_paths[BuildPath_Output].name
);
if (m->pkg) {
path = concatenate3_strings(permanent_allocator(), path, STR_LIT("-"), m->pkg->name);
} else if (USE_SEPARATE_MODULES) {
@@ -922,7 +1007,12 @@ String lb_filepath_ll_for_module(lbModule *m) {
return path;
}
String lb_filepath_obj_for_module(lbModule *m) {
String path = m->gen->output_base;
String path = concatenate3_strings(permanent_allocator(),
build_context.build_paths[BuildPath_Output].basename,
STR_LIT("/"),
build_context.build_paths[BuildPath_Output].name
);
if (m->pkg) {
path = concatenate3_strings(permanent_allocator(), path, STR_LIT("-"), m->pkg->name);
}
@@ -945,6 +1035,19 @@ String lb_filepath_obj_for_module(lbModule *m) {
case TargetOs_essence:
ext = STR_LIT(".o");
break;
case TargetOs_freestanding:
switch (build_context.metrics.abi) {
default:
case TargetABI_Default:
case TargetABI_SysV:
ext = STR_LIT(".o");
break;
case TargetABI_Win64:
ext = STR_LIT(".obj");
break;
}
break;
}
}
}
@@ -1140,7 +1243,7 @@ void lb_generate_code(lbGenerator *gen) {
switch (build_context.metrics.arch) {
case TargetArch_amd64:
case TargetArch_386:
case TargetArch_i386:
LLVMInitializeX86TargetInfo();
LLVMInitializeX86Target();
LLVMInitializeX86TargetMC();
@@ -1197,6 +1300,8 @@ void lb_generate_code(lbGenerator *gen) {
LLVMCodeModel code_mode = LLVMCodeModelDefault;
if (is_arch_wasm()) {
code_mode = LLVMCodeModelJITDefault;
} else if (build_context.metrics.os == TargetOs_freestanding) {
code_mode = LLVMCodeModelKernel;
}
char const *host_cpu_name = LLVMGetHostCPUName();
@@ -1219,10 +1324,18 @@ void lb_generate_code(lbGenerator *gen) {
// 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) {
llvm_cpu = "x86-64-v2";
if (build_context.metrics.os == TargetOs_freestanding) {
llvm_cpu = "x86-64";
} else {
llvm_cpu = "x86-64-v2";
}
}
}
if (build_context.target_features_set.entries.count != 0) {
llvm_features = target_features_set_to_cstring(permanent_allocator(), false);
}
// GB_ASSERT_MSG(LLVMTargetHasAsmBackend(target));
LLVMCodeGenOptLevel code_gen_level = LLVMCodeGenLevelNone;
@@ -1238,12 +1351,36 @@ void lb_generate_code(lbGenerator *gen) {
// NOTE(bill, 2021-05-04): Target machines must be unique to each module because they are not thread safe
auto target_machines = array_make<LLVMTargetMachineRef>(permanent_allocator(), gen->modules.entries.count);
// NOTE(dweiler): Dynamic libraries require position-independent code.
LLVMRelocMode reloc_mode = LLVMRelocDefault;
if (build_context.build_mode == BuildMode_DynamicLibrary) {
reloc_mode = LLVMRelocPIC;
}
switch (build_context.reloc_mode) {
case RelocMode_Default:
if (build_context.metrics.os == TargetOs_openbsd) {
// Always use PIC for OpenBSD: it defaults to PIE
reloc_mode = LLVMRelocPIC;
}
break;
case RelocMode_Static:
reloc_mode = LLVMRelocStatic;
break;
case RelocMode_PIC:
reloc_mode = LLVMRelocPIC;
break;
case RelocMode_DynamicNoPIC:
reloc_mode = LLVMRelocDynamicNoPic;
break;
}
for_array(i, gen->modules.entries) {
target_machines[i] = LLVMCreateTargetMachine(
target, target_triple, llvm_cpu,
llvm_features,
code_gen_level,
LLVMRelocDefault,
reloc_mode,
code_mode);
LLVMSetModuleDataLayout(gen->modules.entries[i].value->mod, LLVMCreateTargetDataLayout(target_machines[i]));
}
@@ -1278,8 +1415,8 @@ void lb_generate_code(lbGenerator *gen) {
if (Entity *entry_point = m->info->entry_point) {
if (Ast *ident = entry_point->identifier.load()) {
if (ident->file) {
init_file = ident->file;
if (ident->file_id) {
init_file = ident->file();
}
}
}
@@ -1304,7 +1441,7 @@ void lb_generate_code(lbGenerator *gen) {
TIME_SECTION("LLVM Global Variables");
{
if (!build_context.disallow_rtti) {
lbModule *m = default_module;
{ // Add type info data
@@ -1399,30 +1536,30 @@ void lb_generate_code(lbGenerator *gen) {
isize global_variable_max_count = 0;
Entity *entry_point = info->entry_point;
bool has_dll_main = false;
bool has_win_main = false;
bool already_has_entry_point = false;
for_array(i, info->entities) {
Entity *e = info->entities[i];
String name = e->token.string;
bool is_global = e->pkg != nullptr;
if (e->kind == Entity_Variable) {
global_variable_max_count++;
} else if (e->kind == Entity_Procedure && !is_global) {
} else if (e->kind == Entity_Procedure) {
if ((e->scope->flags&ScopeFlag_Init) && name == "main") {
GB_ASSERT(e == entry_point);
// entry_point = e;
GB_ASSERT(e == info->entry_point);
}
if (e->Procedure.is_export ||
(e->Procedure.link_name.len > 0) ||
((e->scope->flags&ScopeFlag_File) && e->Procedure.link_name.len > 0)) {
if (!has_dll_main && name == "DllMain") {
has_dll_main = true;
} else if (!has_win_main && name == "WinMain") {
has_win_main = true;
if (build_context.command_kind == Command_test &&
(e->Procedure.is_export || e->Procedure.link_name.len > 0)) {
String link_name = e->Procedure.link_name;
if (e->pkg->kind == Package_Runtime) {
if (link_name == "main" ||
link_name == "DllMain" ||
link_name == "WinMain" ||
link_name == "wWinMain" ||
link_name == "mainCRTStartup" ||
link_name == "_start") {
already_has_entry_point = true;
}
}
}
}
@@ -1560,6 +1697,14 @@ void lb_generate_code(lbGenerator *gen) {
}
}
TIME_SECTION("LLVM Runtime Type Information Creation");
lbProcedure *startup_type_info = lb_create_startup_type_info(default_module);
lbProcedure *objc_names = lb_create_objc_names(default_module);
TIME_SECTION("LLVM Runtime Startup Creation (Global Variables)");
lbProcedure *startup_runtime = lb_create_startup_runtime(default_module, startup_type_info, objc_names, global_variables);
gb_unused(startup_runtime);
TIME_SECTION("LLVM Global Procedures and Types");
for_array(i, info->entities) {
@@ -1619,14 +1764,6 @@ void lb_generate_code(lbGenerator *gen) {
}
}
TIME_SECTION("LLVM Runtime Type Information Creation");
lbProcedure *startup_type_info = lb_create_startup_type_info(default_module);
TIME_SECTION("LLVM Runtime Startup Creation (Global Variables)");
lbProcedure *startup_runtime = lb_create_startup_runtime(default_module, startup_type_info, global_variables);
TIME_SECTION("LLVM Procedure Generation");
for_array(j, gen->modules.entries) {
lbModule *m = gen->modules.entries[j].value;
@@ -1636,8 +1773,7 @@ void lb_generate_code(lbGenerator *gen) {
}
}
if (!(build_context.build_mode == BuildMode_DynamicLibrary && !has_dll_main)) {
if (build_context.command_kind == Command_test && !already_has_entry_point) {
TIME_SECTION("LLVM main");
lb_create_main_procedure(default_module, startup_runtime);
}
@@ -1651,6 +1787,8 @@ void lb_generate_code(lbGenerator *gen) {
}
}
lb_finalize_objc_names(objc_names);
if (build_context.ODIN_DEBUG) {
TIME_SECTION("LLVM Debug Info Complete Types and Finalize");
for_array(j, gen->modules.entries) {
@@ -1663,6 +1801,7 @@ void lb_generate_code(lbGenerator *gen) {
}
TIME_SECTION("LLVM Function Pass");
for_array(i, gen->modules.entries) {
lbModule *m = gen->modules.entries[i].value;
@@ -1807,4 +1946,6 @@ void lb_generate_code(lbGenerator *gen) {
}
}
}
gb_sort_array(gen->foreign_libraries.data, gen->foreign_libraries.count, foreign_library_cmp);
}
+22 -7
View File
@@ -135,7 +135,6 @@ struct lbModule {
u32 nested_type_name_guid;
Array<lbProcedure *> procedures_to_generate;
Array<String> foreign_library_paths;
lbProcedure *curr_procedure;
@@ -144,6 +143,9 @@ struct lbModule {
PtrMap<void *, LLVMMetadataRef> debug_values;
Array<lbIncompleteDebugType> debug_incomplete_types;
StringMap<lbAddr> objc_classes;
StringMap<lbAddr> objc_selectors;
};
struct lbGenerator {
@@ -159,6 +161,10 @@ struct lbGenerator {
PtrMap<Ast *, lbProcedure *> anonymous_proc_lits;
BlockingMutex foreign_mutex;
PtrSet<Entity *> foreign_libraries_set;
Array<Entity *> foreign_libraries;
std::atomic<u32> global_array_index;
std::atomic<u32> global_generated_index;
};
@@ -204,7 +210,6 @@ enum lbDeferExitKind {
enum lbDeferKind {
lbDefer_Node,
lbDefer_Instr,
lbDefer_Proc,
};
@@ -215,8 +220,6 @@ struct lbDefer {
lbBlock * block;
union {
Ast *stmt;
// NOTE(bill): 'instr' will be copied every time to create a new one
lbValue instr;
struct {
lbValue deferred;
Array<lbValue> result_as_args;
@@ -235,6 +238,7 @@ struct lbTargetList {
enum lbProcedureFlag : u32 {
lbProcedureFlag_WithoutMemcpyPass = 1<<0,
lbProcedureFlag_DebugAllocaCopy = 1<<1,
};
struct lbCopyElisionHint {
@@ -270,7 +274,6 @@ struct lbProcedure {
bool is_done;
lbAddr return_ptr;
Array<lbValue> params;
Array<lbDefer> defer_stmts;
Array<lbBlock *> blocks;
Array<lbBranchBlocks> branch_blocks;
@@ -289,6 +292,9 @@ struct lbProcedure {
LLVMMetadataRef debug_info;
lbCopyElisionHint copy_elision_hint;
PtrMap<Ast *, lbValue> selector_values;
PtrMap<Ast *, lbAddr> selector_addr;
};
@@ -296,7 +302,6 @@ struct lbProcedure {
bool lb_init_generator(lbGenerator *gen, Checker *c);
void lb_generate_module(lbGenerator *gen);
String lb_mangle_name(lbModule *m, Entity *e);
String lb_get_entity_name(lbModule *m, Entity *e, String name = {});
@@ -369,7 +374,7 @@ lbContextData *lb_push_context_onto_stack(lbProcedure *p, lbAddr ctx);
lbContextData *lb_push_context_onto_stack_from_implicit_parameter(lbProcedure *p);
lbAddr lb_add_global_generated(lbModule *m, Type *type, lbValue value={});
lbAddr lb_add_global_generated(lbModule *m, Type *type, lbValue value={}, Entity **entity_=nullptr);
lbAddr lb_add_local(lbProcedure *p, Type *type, Entity *e=nullptr, bool zero_init=true, i32 param_index=0, bool force_no_init=false);
void lb_add_foreign_library_path(lbModule *m, Entity *e);
@@ -459,6 +464,8 @@ void lb_set_entity_from_other_modules_linkage_correctly(lbModule *other_module,
lbValue lb_expr_untyped_const_to_typed(lbModule *m, Ast *expr, Type *t);
bool lb_is_expr_untyped_const(Ast *expr);
LLVMValueRef llvm_alloca(lbProcedure *p, LLVMTypeRef llvm_type, isize alignment, char const *name = "");
void lb_mem_zero_ptr(lbProcedure *p, LLVMValueRef ptr, Type *type, unsigned alignment);
void lb_emit_init_context(lbProcedure *p, lbAddr addr);
@@ -475,7 +482,11 @@ LLVMValueRef llvm_basic_shuffle(lbProcedure *p, LLVMValueRef vector, LLVMValueRe
void lb_mem_copy_overlapping(lbProcedure *p, lbValue dst, lbValue src, lbValue len, bool is_volatile=false);
void lb_mem_copy_non_overlapping(lbProcedure *p, lbValue dst, lbValue src, lbValue len, bool is_volatile=false);
LLVMValueRef lb_mem_zero_ptr_internal(lbProcedure *p, LLVMValueRef ptr, LLVMValueRef len, unsigned alignment, bool is_volatile);
i64 lb_max_zero_init_size(void) {
return cast(i64)(4*build_context.word_size);
}
#define LB_STARTUP_RUNTIME_PROC_NAME "__$startup_runtime"
#define LB_STARTUP_TYPE_INFO_PROC_NAME "__$startup_type_info"
@@ -549,6 +560,10 @@ lbCallingConventionKind const lb_calling_convention_map[ProcCC_MAX] = {
lbCallingConvention_C, // ProcCC_None,
lbCallingConvention_C, // ProcCC_Naked,
lbCallingConvention_C, // ProcCC_InlineAsm,
lbCallingConvention_Win64, // ProcCC_Win64,
lbCallingConvention_X86_64_SysV, // ProcCC_SysV,
};
enum : LLVMDWARFTypeEncoding {
+109 -41
View File
@@ -115,8 +115,8 @@ LLVMValueRef llvm_const_cast(LLVMValueRef val, LLVMTypeRef dst) {
lbValue lb_const_ptr_cast(lbModule *m, lbValue value, Type *t) {
GB_ASSERT(is_type_pointer(value.type));
GB_ASSERT(is_type_pointer(t));
GB_ASSERT(is_type_internally_pointer_like(value.type));
GB_ASSERT(is_type_internally_pointer_like(t));
GB_ASSERT(lb_is_const(value));
lbValue res = {};
@@ -175,7 +175,7 @@ LLVMValueRef llvm_const_array(LLVMTypeRef elem_type, LLVMValueRef *values, isize
}
LLVMValueRef llvm_const_slice(lbModule *m, lbValue data, lbValue len) {
GB_ASSERT(is_type_pointer(data.type));
GB_ASSERT(is_type_pointer(data.type) || is_type_multi_pointer(data.type));
GB_ASSERT(are_types_identical(len.type, t_int));
LLVMValueRef vals[2] = {
data.value,
@@ -410,12 +410,10 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc
// NOTE(bill, 2020-06-08): This is a bit of a hack but a "constant" slice needs
// its backing data on the stack
lbProcedure *p = m->curr_procedure;
LLVMPositionBuilderAtEnd(p->builder, p->decl_block->block);
LLVMTypeRef llvm_type = lb_type(m, t);
array_data = LLVMBuildAlloca(p->builder, llvm_type, "");
LLVMSetAlignment(array_data, 16); // TODO(bill): Make this configurable
LLVMPositionBuilderAtEnd(p->builder, p->curr_block->block);
array_data = llvm_alloca(p, llvm_type, 16);
LLVMBuildStore(p->builder, backing_array.value, array_data);
{
@@ -495,9 +493,9 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc
res.value = data;
return res;
} else if (is_type_array(type) &&
value.kind != ExactValue_Invalid &&
value.kind != ExactValue_String &&
value.kind != ExactValue_Compound) {
value.kind != ExactValue_Invalid &&
value.kind != ExactValue_String &&
value.kind != ExactValue_Compound) {
i64 count = type->Array.count;
Type *elem = type->Array.elem;
@@ -513,8 +511,8 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc
res.value = llvm_const_array(lb_type(m, elem), elems, cast(unsigned)count);
return res;
} else if (is_type_matrix(type) &&
value.kind != ExactValue_Invalid &&
value.kind != ExactValue_Compound) {
value.kind != ExactValue_Invalid &&
value.kind != ExactValue_Compound) {
i64 row = type->Matrix.row_count;
i64 column = type->Matrix.column_count;
GB_ASSERT(row == column);
@@ -537,6 +535,22 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc
res.value = LLVMConstArray(lb_type(m, elem), elems, cast(unsigned)total_elem_count);
return res;
} else if (is_type_simd_vector(type) &&
value.kind != ExactValue_Invalid &&
value.kind != ExactValue_Compound) {
i64 count = type->SimdVector.count;
Type *elem = type->SimdVector.elem;
lbValue single_elem = lb_const_value(m, elem, value, allow_local);
single_elem.value = llvm_const_cast(single_elem.value, lb_type(m, elem));
LLVMValueRef *elems = gb_alloc_array(permanent_allocator(), LLVMValueRef, count);
for (i64 i = 0; i < count; i++) {
elems[i] = single_elem.value;
}
res.value = LLVMConstVector(elems, cast(unsigned)count);
return res;
}
switch (value.kind) {
@@ -568,7 +582,7 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc
}
case ExactValue_Integer:
if (is_type_pointer(type)) {
if (is_type_pointer(type) || is_type_multi_pointer(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);
@@ -819,26 +833,81 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc
return lb_const_nil(m, original_type);
}
GB_ASSERT(elem_type_can_be_constant(elem_type));
isize total_elem_count = cast(isize)type->SimdVector.count;
LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, total_elem_count);
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;
}
LLVMTypeRef et = lb_type(m, elem_type);
if (cl->elems[0]->kind == Ast_FieldValue) {
// TODO(bill): This is O(N*M) and will be quite slow; it should probably be sorted before hand
isize value_index = 0;
for (i64 i = 0; i < total_elem_count; i++) {
bool found = false;
for (isize i = elem_count; i < type->SimdVector.count; i++) {
values[i] = LLVMConstNull(et);
}
for (isize i = 0; i < total_elem_count; i++) {
values[i] = llvm_const_cast(values[i], et);
}
for (isize j = 0; j < elem_count; j++) {
Ast *elem = cl->elems[j];
ast_node(fv, FieldValue, elem);
if (is_ast_range(fv->field)) {
ast_node(ie, BinaryExpr, fv->field);
TypeAndValue lo_tav = ie->left->tav;
TypeAndValue hi_tav = ie->right->tav;
GB_ASSERT(lo_tav.mode == Addressing_Constant);
GB_ASSERT(hi_tav.mode == Addressing_Constant);
res.value = LLVMConstVector(values, cast(unsigned)total_elem_count);
return res;
TokenKind op = ie->op.kind;
i64 lo = exact_value_to_i64(lo_tav.value);
i64 hi = exact_value_to_i64(hi_tav.value);
if (op != Token_RangeHalf) {
hi += 1;
}
if (lo == i) {
TypeAndValue tav = fv->value->tav;
LLVMValueRef val = lb_const_value(m, elem_type, tav.value, allow_local).value;
for (i64 k = lo; k < hi; k++) {
values[value_index++] = val;
}
found = true;
i += (hi-lo-1);
break;
}
} else {
TypeAndValue index_tav = fv->field->tav;
GB_ASSERT(index_tav.mode == Addressing_Constant);
i64 index = exact_value_to_i64(index_tav.value);
if (index == i) {
TypeAndValue tav = fv->value->tav;
LLVMValueRef val = lb_const_value(m, elem_type, tav.value, allow_local).value;
values[value_index++] = val;
found = true;
break;
}
}
}
if (!found) {
values[value_index++] = LLVMConstNull(lb_type(m, elem_type));
}
}
res.value = LLVMConstVector(values, cast(unsigned)total_elem_count);
return res;
} else {
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;
}
LLVMTypeRef et = lb_type(m, elem_type);
for (isize i = elem_count; i < total_elem_count; i++) {
values[i] = LLVMConstNull(et);
}
for (isize i = 0; i < total_elem_count; i++) {
values[i] = llvm_const_cast(values[i], et);
}
res.value = LLVMConstVector(values, cast(unsigned)total_elem_count);
return res;
}
} else if (is_type_struct(type)) {
ast_node(cl, CompoundLit, value.value_compound);
@@ -956,7 +1025,10 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc
return lb_const_nil(m, original_type);
}
u64 bits = 0;
BigInt bits = {};
BigInt one = {};
big_int_from_u64(&one, 1);
for_array(i, cl->elems) {
Ast *e = cl->elems[i];
GB_ASSERT(e->kind != Ast_FieldValue);
@@ -968,18 +1040,14 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc
GB_ASSERT(tav.value.kind == ExactValue_Integer);
i64 v = big_int_to_i64(&tav.value.value_integer);
i64 lower = type->BitSet.lower;
bits |= 1ull<<cast(u64)(v-lower);
u64 index = cast(u64)(v-lower);
gb_printf_err("index: %llu\n", index);
BigInt bit = {};
big_int_from_u64(&bit, index);
big_int_shl(&bit, &one, &bit);
big_int_or(&bits, &bits, &bit);
}
if (is_type_different_to_arch_endianness(type)) {
i64 size = type_size_of(type);
switch (size) {
case 2: bits = cast(u64)gb_endian_swap16(cast(u16)bits); break;
case 4: bits = cast(u64)gb_endian_swap32(cast(u32)bits); break;
case 8: bits = cast(u64)gb_endian_swap64(cast(u64)bits); break;
}
}
res.value = LLVMConstInt(lb_type(m, original_type), bits, false);
res.value = lb_big_int_to_llvm(m, original_type, &bits);
return res;
} else if (is_type_matrix(type)) {
ast_node(cl, CompoundLit, value.value_compound);
+96 -10
View File
@@ -18,7 +18,7 @@ LLVMMetadataRef lb_get_llvm_file_metadata_from_node(lbModule *m, Ast *node) {
if (node == nullptr) {
return nullptr;
}
return lb_get_llvm_metadata(m, node->file);
return lb_get_llvm_metadata(m, node->file());
}
LLVMMetadataRef lb_get_current_debug_scope(lbProcedure *p) {
@@ -43,6 +43,10 @@ LLVMMetadataRef lb_debug_location_from_ast(lbProcedure *p, Ast *node) {
GB_ASSERT(node != nullptr);
return lb_debug_location_from_token_pos(p, ast_token(node).pos);
}
LLVMMetadataRef lb_debug_end_location_from_ast(lbProcedure *p, Ast *node) {
GB_ASSERT(node != nullptr);
return lb_debug_location_from_token_pos(p, ast_end_token(node).pos);
}
LLVMMetadataRef lb_debug_type_internal_proc(lbModule *m, Type *type) {
i64 size = type_size_of(type); // Check size
@@ -419,7 +423,18 @@ LLVMMetadataRef lb_debug_type_internal(lbModule *m, Type *type) {
break;
case Type_SimdVector:
return LLVMDIBuilderCreateVectorType(m->debug_builder, cast(unsigned)type->SimdVector.count, 8*cast(unsigned)type_align_of(type), lb_debug_type(m, type->SimdVector.elem), nullptr, 0);
{
LLVMMetadataRef elem = lb_debug_type(m, type->SimdVector.elem);
LLVMMetadataRef subscripts[1] = {};
subscripts[0] = LLVMDIBuilderGetOrCreateSubrange(m->debug_builder,
0ll,
type->SimdVector.count
);
return LLVMDIBuilderCreateVectorType(
m->debug_builder,
8*cast(unsigned)type_size_of(type), 8*cast(unsigned)type_align_of(type),
elem, subscripts, gb_count_of(subscripts));
}
case Type_RelativePointer: {
LLVMMetadataRef base_integer = lb_debug_type(m, type->RelativePointer.base_integer);
@@ -660,7 +675,7 @@ void lb_debug_complete_types(lbModule *m) {
case Type_Struct:
if (file == nullptr) {
if (bt->Struct.node) {
file = lb_get_llvm_metadata(m, bt->Struct.node->file);
file = lb_get_llvm_metadata(m, bt->Struct.node->file());
line_number = cast(unsigned)ast_token(bt->Struct.node).pos.line;
}
}
@@ -741,7 +756,7 @@ void lb_debug_complete_types(lbModule *m) {
{
if (file == nullptr) {
GB_ASSERT(bt->Union.node != nullptr);
file = lb_get_llvm_metadata(m, bt->Union.node->file);
file = lb_get_llvm_metadata(m, bt->Union.node->file());
line_number = cast(unsigned)ast_token(bt->Union.node).pos.line;
}
@@ -801,7 +816,7 @@ void lb_debug_complete_types(lbModule *m) {
{
if (file == nullptr) {
GB_ASSERT(bt->BitSet.node != nullptr);
file = lb_get_llvm_metadata(m, bt->BitSet.node->file);
file = lb_get_llvm_metadata(m, bt->BitSet.node->file());
line_number = cast(unsigned)ast_token(bt->BitSet.node).pos.line;
}
@@ -929,7 +944,7 @@ void lb_add_debug_local_variable(lbProcedure *p, LLVMValueRef ptr, Type *type, T
}
AstFile *file = p->body->file;
AstFile *file = p->body->file();
LLVMMetadataRef llvm_scope = lb_get_current_debug_scope(p);
LLVMMetadataRef llvm_file = lb_get_llvm_metadata(m, file);
@@ -958,13 +973,79 @@ void lb_add_debug_local_variable(lbProcedure *p, LLVMValueRef ptr, Type *type, T
);
LLVMValueRef storage = ptr;
LLVMValueRef instr = ptr;
LLVMBasicBlockRef block = p->curr_block->block;
LLVMMetadataRef llvm_debug_loc = lb_debug_location_from_token_pos(p, token.pos);
LLVMMetadataRef llvm_expr = LLVMDIBuilderCreateExpression(m->debug_builder, nullptr, 0);
lb_set_llvm_metadata(m, ptr, llvm_expr);
LLVMDIBuilderInsertDeclareBefore(m->debug_builder, storage, var_info, llvm_expr, llvm_debug_loc, instr);
LLVMDIBuilderInsertDeclareAtEnd(m->debug_builder, storage, var_info, llvm_expr, llvm_debug_loc, block);
}
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;
}
if (type == nullptr) {
return;
}
if (type == t_invalid) {
return;
}
if (p->body == nullptr) {
return;
}
lbModule *m = p->module;
String const &name = token.string;
if (name == "" || name == "_") {
return;
}
if (lb_get_llvm_metadata(m, ptr) != nullptr) {
// Already been set
return;
}
AstFile *file = p->body->file();
LLVMMetadataRef llvm_scope = lb_get_current_debug_scope(p);
LLVMMetadataRef llvm_file = lb_get_llvm_metadata(m, file);
GB_ASSERT(llvm_scope != nullptr);
if (llvm_file == nullptr) {
llvm_file = LLVMDIScopeGetFile(llvm_scope);
}
if (llvm_file == nullptr) {
return;
}
LLVMDIFlags flags = LLVMDIFlagZero;
LLVMBool always_preserve = build_context.optimization_level == 0;
LLVMMetadataRef debug_type = lb_debug_type(m, type);
LLVMMetadataRef var_info = LLVMDIBuilderCreateParameterVariable(
m->debug_builder, llvm_scope,
cast(char const *)name.text, cast(size_t)name.len,
arg_number,
llvm_file, token.pos.line,
debug_type,
always_preserve, flags
);
LLVMValueRef storage = ptr;
LLVMMetadataRef llvm_debug_loc = lb_debug_location_from_token_pos(p, token.pos);
LLVMMetadataRef llvm_expr = LLVMDIBuilderCreateExpression(m->debug_builder, nullptr, 0);
lb_set_llvm_metadata(m, ptr, llvm_expr);
// 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.
LLVMDIBuilderInsertDbgValueAtEnd(m->debug_builder, storage, var_info, llvm_expr, llvm_debug_loc, block->block);
}
void lb_add_debug_context_variable(lbProcedure *p, lbAddr const &ctx) {
if (!p->debug_info || !p->body) {
return;
@@ -975,7 +1056,7 @@ void lb_add_debug_context_variable(lbProcedure *p, lbAddr const &ctx) {
}
TokenPos pos = {};
pos.file_id = p->body->file ? p->body->file->id : 0;
pos.file_id = p->body->file_id;
pos.line = LLVMDILocationGetLine(loc);
pos.column = LLVMDILocationGetColumn(loc);
@@ -984,5 +1065,10 @@ void lb_add_debug_context_variable(lbProcedure *p, lbAddr const &ctx) {
token.string = str_lit("context");
token.pos = pos;
lb_add_debug_local_variable(p, ctx.addr.value, t_context, token);
LLVMValueRef ptr = ctx.addr.value;
while (LLVMIsABitCastInst(ptr)) {
ptr = LLVMGetOperand(ptr, 0);
}
lb_add_debug_local_variable(p, ptr, t_context, token);
}
+425 -97
View File
@@ -1,3 +1,4 @@
lbValue lb_emit_arith_matrix(lbProcedure *p, TokenKind op, lbValue lhs, lbValue rhs, Type *type, bool component_wise=false);
lbValue lb_emit_logical_binary_expr(lbProcedure *p, TokenKind op, Ast *left, Ast *right, Type *type) {
lbModule *m = p->module;
@@ -258,7 +259,18 @@ lbValue lb_emit_unary_arith(lbProcedure *p, TokenKind op, lbValue x, Type *type)
LLVMBuildStore(p->builder, v2, LLVMBuildStructGEP(p->builder, addr.addr.value, 2, ""));
LLVMBuildStore(p->builder, v3, LLVMBuildStructGEP(p->builder, addr.addr.value, 3, ""));
return lb_addr_load(p, addr);
} else if (is_type_simd_vector(x.type)) {
Type *elem = base_array_type(x.type);
if (is_type_float(elem)) {
res.value = LLVMBuildFNeg(p->builder, x.value, "");
} else {
res.value = LLVMBuildNeg(p->builder, x.value, "");
}
} else if (is_type_matrix(x.type)) {
lbValue zero = {};
zero.value = LLVMConstNull(lb_type(p->module, type));
zero.type = type;
return lb_emit_arith_matrix(p, Token_Sub, zero, x, type, true);
} else {
GB_PANIC("Unhandled type %s", type_to_string(x.type));
}
@@ -508,7 +520,7 @@ bool lb_is_matrix_simdable(Type *t) {
case TargetArch_arm64:
// TODO(bill): determine when this is fine
return true;
case TargetArch_386:
case TargetArch_i386:
case TargetArch_wasm32:
case TargetArch_wasm64:
return false;
@@ -580,6 +592,27 @@ LLVMValueRef lb_matrix_to_trimmed_vector(lbProcedure *p, lbValue m) {
lbValue lb_emit_matrix_tranpose(lbProcedure *p, lbValue m, Type *type) {
if (is_type_array(m.type)) {
i32 rank = type_math_rank(m.type);
if (rank == 2) {
lbAddr addr = lb_add_local_generated(p, type, false);
lbValue dst = addr.addr;
lbValue src = m;
i32 n = cast(i32)get_array_type_count(m.type);
i32 m = cast(i32)get_array_type_count(type);
// m.type == [n][m]T
// type == [m][n]T
for (i32 j = 0; j < m; j++) {
lbValue dst_col = lb_emit_struct_ep(p, dst, j);
for (i32 i = 0; i < n; i++) {
lbValue dst_row = lb_emit_struct_ep(p, dst_col, i);
lbValue src_col = lb_emit_struct_ev(p, src, i);
lbValue src_row = lb_emit_struct_ev(p, src_col, j);
lb_emit_store(p, dst_row, src_row);
}
}
return lb_addr_load(p, addr);
}
// no-op
m.type = type;
return m;
@@ -949,7 +982,7 @@ lbValue lb_emit_vector_mul_matrix(lbProcedure *p, lbValue lhs, lbValue rhs, Type
lbValue lb_emit_arith_matrix(lbProcedure *p, TokenKind op, lbValue lhs, lbValue rhs, Type *type, bool component_wise=false) {
lbValue lb_emit_arith_matrix(lbProcedure *p, TokenKind op, lbValue lhs, lbValue rhs, Type *type, bool component_wise) {
GB_ASSERT(is_type_matrix(lhs.type) || is_type_matrix(rhs.type));
@@ -1369,7 +1402,7 @@ lbValue lb_build_binary_expr(lbProcedure *p, Ast *expr) {
Type *rt = base_type(right.type);
if (is_type_pointer(rt)) {
right = lb_emit_load(p, right);
rt = type_deref(rt);
rt = base_type(type_deref(rt));
}
switch (rt->kind) {
@@ -1398,10 +1431,13 @@ lbValue lb_build_binary_expr(lbProcedure *p, Ast *expr) {
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, it, exact_value_i64(rt->BitSet.lower));
lbValue key = lb_emit_arith(p, Token_Sub, left, lower, it);
lbValue bit = lb_emit_arith(p, Token_Shl, lb_const_int(p->module, it, 1), key, 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);
@@ -1655,26 +1691,10 @@ lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
return res;
}
if (is_type_float(src) && is_type_complex(dst)) {
Type *ft = base_complex_elem_type(dst);
lbAddr gen = lb_add_local_generated(p, dst, false);
lbValue gp = lb_addr_get_ptr(p, gen);
lbValue real = lb_emit_conv(p, value, ft);
lb_emit_store(p, lb_emit_struct_ep(p, gp, 0), real);
return lb_addr_load(p, gen);
}
if (is_type_float(src) && is_type_quaternion(dst)) {
Type *ft = base_complex_elem_type(dst);
lbAddr gen = lb_add_local_generated(p, dst, false);
lbValue gp = lb_addr_get_ptr(p, gen);
lbValue real = lb_emit_conv(p, value, ft);
lb_emit_store(p, lb_emit_struct_ep(p, gp, 0), real);
return lb_addr_load(p, gen);
}
if (is_type_complex(src) && is_type_complex(dst)) {
Type *ft = base_complex_elem_type(dst);
lbAddr gen = lb_add_local_generated(p, dst, false);
lbAddr gen = lb_add_local_generated(p, t, false);
lbValue gp = lb_addr_get_ptr(p, gen);
lbValue real = lb_emit_conv(p, lb_emit_struct_ev(p, value, 0), ft);
lbValue imag = lb_emit_conv(p, lb_emit_struct_ev(p, value, 1), ft);
@@ -1686,7 +1706,7 @@ lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
if (is_type_quaternion(src) && is_type_quaternion(dst)) {
// @QuaternionLayout
Type *ft = base_complex_elem_type(dst);
lbAddr gen = lb_add_local_generated(p, dst, false);
lbAddr gen = lb_add_local_generated(p, t, false);
lbValue gp = lb_addr_get_ptr(p, gen);
lbValue q0 = lb_emit_conv(p, lb_emit_struct_ev(p, value, 0), ft);
lbValue q1 = lb_emit_conv(p, lb_emit_struct_ev(p, value, 1), ft);
@@ -1701,7 +1721,7 @@ lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
if (is_type_integer(src) && is_type_complex(dst)) {
Type *ft = base_complex_elem_type(dst);
lbAddr gen = lb_add_local_generated(p, dst, true);
lbAddr gen = lb_add_local_generated(p, t, true);
lbValue gp = lb_addr_get_ptr(p, gen);
lbValue real = lb_emit_conv(p, value, ft);
lb_emit_store(p, lb_emit_struct_ep(p, gp, 0), real);
@@ -1709,7 +1729,7 @@ lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
}
if (is_type_float(src) && is_type_complex(dst)) {
Type *ft = base_complex_elem_type(dst);
lbAddr gen = lb_add_local_generated(p, dst, true);
lbAddr gen = lb_add_local_generated(p, t, true);
lbValue gp = lb_addr_get_ptr(p, gen);
lbValue real = lb_emit_conv(p, value, ft);
lb_emit_store(p, lb_emit_struct_ep(p, gp, 0), real);
@@ -1719,7 +1739,7 @@ lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
if (is_type_integer(src) && is_type_quaternion(dst)) {
Type *ft = base_complex_elem_type(dst);
lbAddr gen = lb_add_local_generated(p, dst, true);
lbAddr gen = lb_add_local_generated(p, t, true);
lbValue gp = lb_addr_get_ptr(p, gen);
lbValue real = lb_emit_conv(p, value, ft);
// @QuaternionLayout
@@ -1728,7 +1748,7 @@ lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
}
if (is_type_float(src) && is_type_quaternion(dst)) {
Type *ft = base_complex_elem_type(dst);
lbAddr gen = lb_add_local_generated(p, dst, true);
lbAddr gen = lb_add_local_generated(p, t, true);
lbValue gp = lb_addr_get_ptr(p, gen);
lbValue real = lb_emit_conv(p, value, ft);
// @QuaternionLayout
@@ -1737,7 +1757,7 @@ lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
}
if (is_type_complex(src) && is_type_quaternion(dst)) {
Type *ft = base_complex_elem_type(dst);
lbAddr gen = lb_add_local_generated(p, dst, true);
lbAddr gen = lb_add_local_generated(p, t, true);
lbValue gp = lb_addr_get_ptr(p, gen);
lbValue real = lb_emit_conv(p, lb_emit_struct_ev(p, value, 0), ft);
lbValue imag = lb_emit_conv(p, lb_emit_struct_ev(p, value, 1), ft);
@@ -1815,6 +1835,59 @@ lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
return res;
}
if (is_type_simd_vector(dst)) {
Type *et = base_array_type(dst);
if (is_type_simd_vector(src)) {
Type *src_elem = core_array_type(src);
Type *dst_elem = core_array_type(dst);
GB_ASSERT(src->SimdVector.count == dst->SimdVector.count);
lbValue res = {};
res.type = t;
if (are_types_identical(src_elem, dst_elem)) {
res.value = value.value;
} else if (is_type_float(src_elem) && is_type_integer(dst_elem)) {
if (is_type_unsigned(dst_elem)) {
res.value = LLVMBuildFPToUI(p->builder, value.value, lb_type(m, t), "");
} else {
res.value = LLVMBuildFPToSI(p->builder, value.value, lb_type(m, t), "");
}
} else if (is_type_integer(src_elem) && is_type_float(dst_elem)) {
if (is_type_unsigned(src_elem)) {
res.value = LLVMBuildUIToFP(p->builder, value.value, lb_type(m, t), "");
} else {
res.value = LLVMBuildSIToFP(p->builder, value.value, lb_type(m, t), "");
}
} else if ((is_type_integer(src_elem) || is_type_boolean(src_elem)) && is_type_integer(dst_elem)) {
res.value = LLVMBuildIntCast2(p->builder, value.value, lb_type(m, t), !is_type_unsigned(src_elem), "");
} else if (is_type_float(src_elem) && is_type_float(dst_elem)) {
res.value = LLVMBuildFPCast(p->builder, value.value, lb_type(m, 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 {
GB_PANIC("Unhandled simd vector conversion: %s -> %s", type_to_string(src), type_to_string(dst));
}
return res;
} else {
i64 count = get_array_type_count(dst);
LLVMTypeRef vt = lb_type(m, t);
LLVMTypeRef llvm_u32 = lb_type(m, t_u32);
LLVMValueRef elem = lb_emit_conv(p, value, et).value;
LLVMValueRef vector = LLVMConstNull(vt);
for (i64 i = 0; i < count; i++) {
LLVMValueRef idx = LLVMConstInt(llvm_u32, i, false);
vector = LLVMBuildInsertElement(p->builder, vector, elem, idx, "");
}
lbValue res = {};
res.type = t;
res.value = vector;
return res;
}
}
// Pointer <-> uintptr
if (is_type_pointer(src) && is_type_uintptr(dst)) {
lbValue res = {};
@@ -1841,7 +1914,6 @@ lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
return res;
}
#if 1
if (is_type_union(dst)) {
for_array(i, dst->Union.variants) {
Type *vt = dst->Union.variants[i];
@@ -1851,8 +1923,16 @@ lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
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, 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);
}
}
}
#endif
// NOTE(bill): This has to be done before 'Pointer <-> Pointer' as it's
// subtype polymorphism casting
@@ -2190,6 +2270,21 @@ lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left, lbValue ri
}
}
if (is_type_matrix(a) && (op_kind == Token_CmpEq || op_kind == Token_NotEq)) {
Type *tl = base_type(a);
lbValue lhs = lb_address_from_load_or_generate_local(p, left);
lbValue rhs = lb_address_from_load_or_generate_local(p, right);
// TODO(bill): Test to see if this is actually faster!!!!
auto args = array_make<lbValue>(permanent_allocator(), 3);
args[0] = lb_emit_conv(p, lhs, t_rawptr);
args[1] = lb_emit_conv(p, rhs, t_rawptr);
args[2] = lb_const_int(p->module, t_int, type_size_of(tl));
lbValue val = lb_emit_runtime_call(p, "memory_compare", args);
lbValue res = lb_emit_comp(p, op_kind, val, lb_const_nil(p->module, val.type));
return lb_emit_conv(p, res, t_bool);
}
if (is_type_array(a) || is_type_enumerated_array(a)) {
Type *tl = base_type(a);
lbValue lhs = lb_address_from_load_or_generate_local(p, left);
@@ -2479,6 +2574,57 @@ lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left, lbValue ri
case Token_NotEq: pred = LLVMIntNE; break;
}
res.value = LLVMBuildICmp(p->builder, pred, left.value, right.value, "");
} else if (is_type_simd_vector(a)) {
LLVMValueRef mask = nullptr;
Type *elem = base_array_type(a);
if (is_type_float(elem)) {
LLVMRealPredicate pred = {};
switch (op_kind) {
case Token_CmpEq: pred = LLVMRealOEQ; break;
case Token_NotEq: pred = LLVMRealONE; break;
}
mask = LLVMBuildFCmp(p->builder, pred, left.value, right.value, "");
} else {
LLVMIntPredicate pred = {};
switch (op_kind) {
case Token_CmpEq: pred = LLVMIntEQ; break;
case Token_NotEq: pred = LLVMIntNE; break;
}
mask = LLVMBuildICmp(p->builder, pred, left.value, right.value, "");
}
GB_ASSERT_MSG(mask != nullptr, "Unhandled comparison kind %s (%s) %.*s %s (%s)", type_to_string(left.type), type_to_string(base_type(left.type)), LIT(token_strings[op_kind]), type_to_string(right.type), type_to_string(base_type(right.type)));
/* NOTE(bill, 2022-05-28):
Thanks to Per Vognsen, sign extending <N x i1> to
a vector of the same width as the input vector, bit casting to an integer,
and then comparing against zero is the better option
See: https://lists.llvm.org/pipermail/llvm-dev/2012-September/053046.html
// Example assuming 128-bit vector
%1 = <4 x float> ...
%2 = <4 x float> ...
%3 = fcmp oeq <4 x float> %1, %2
%4 = sext <4 x i1> %3 to <4 x i32>
%5 = bitcast <4 x i32> %4 to i128
%6 = icmp ne i128 %5, 0
br i1 %6, label %true1, label %false2
This will result in 1 cmpps + 1 ptest + 1 br
(even without SSE4.1, contrary to what the mail list states, because of pmovmskb)
*/
unsigned count = cast(unsigned)get_array_type_count(a);
unsigned elem_sz = cast(unsigned)(type_size_of(elem)*8);
LLVMTypeRef mask_type = LLVMVectorType(LLVMIntTypeInContext(p->module->ctx, elem_sz), count);
mask = LLVMBuildSExtOrBitCast(p->builder, mask, mask_type, "");
LLVMTypeRef mask_int_type = LLVMIntTypeInContext(p->module->ctx, cast(unsigned)(8*type_size_of(a)));
LLVMValueRef mask_int = LLVMBuildBitCast(p->builder, mask, mask_int_type, "");
res.value = LLVMBuildICmp(p->builder, LLVMIntNE, mask_int, LLVMConstNull(LLVMTypeOf(mask_int)), "");
return res;
} else {
GB_PANIC("Unhandled comparison kind %s (%s) %.*s %s (%s)", type_to_string(left.type), type_to_string(base_type(left.type)), LIT(token_strings[op_kind]), type_to_string(right.type), type_to_string(base_type(right.type)));
}
@@ -2786,28 +2932,39 @@ lbValue lb_build_unary_and(lbProcedure *p, Ast *expr) {
Type *src_type = type_deref(v.type);
Type *dst_type = type;
lbValue src_tag = {};
lbValue dst_tag = {};
if (is_type_union_maybe_pointer(src_type)) {
src_tag = lb_emit_comp_against_nil(p, Token_NotEq, v);
dst_tag = lb_const_bool(p->module, t_bool, true);
} else {
src_tag = lb_emit_load(p, lb_emit_union_tag_ptr(p, v));
dst_tag = lb_const_union_tag(p->module, src_type, dst_type);
if ((p->state_flags & StateFlag_no_type_assert) == 0) {
lbValue src_tag = {};
lbValue dst_tag = {};
if (is_type_union_maybe_pointer(src_type)) {
src_tag = lb_emit_comp_against_nil(p, Token_NotEq, v);
dst_tag = lb_const_bool(p->module, t_bool, true);
} else {
src_tag = lb_emit_load(p, lb_emit_union_tag_ptr(p, v));
dst_tag = lb_const_union_tag(p->module, src_type, dst_type);
}
isize arg_count = 6;
if (build_context.disallow_rtti) {
arg_count = 4;
}
lbValue ok = lb_emit_comp(p, Token_CmpEq, src_tag, dst_tag);
auto args = array_make<lbValue>(permanent_allocator(), arg_count);
args[0] = ok;
args[1] = lb_find_or_add_entity_string(p->module, get_file_path_string(pos.file_id));
args[2] = lb_const_int(p->module, t_i32, pos.line);
args[3] = lb_const_int(p->module, t_i32, pos.column);
if (!build_context.disallow_rtti) {
args[4] = lb_typeid(p->module, src_type);
args[5] = lb_typeid(p->module, dst_type);
}
lb_emit_runtime_call(p, "type_assertion_check", args);
}
lbValue ok = lb_emit_comp(p, Token_CmpEq, src_tag, dst_tag);
auto args = array_make<lbValue>(permanent_allocator(), 6);
args[0] = ok;
args[1] = lb_find_or_add_entity_string(p->module, get_file_path_string(pos.file_id));
args[2] = lb_const_int(p->module, t_i32, pos.line);
args[3] = lb_const_int(p->module, t_i32, pos.column);
args[4] = lb_typeid(p->module, src_type);
args[5] = lb_typeid(p->module, dst_type);
lb_emit_runtime_call(p, "type_assertion_check", args);
lbValue data_ptr = v;
return lb_emit_conv(p, data_ptr, tv.type);
} else if (is_type_any(t)) {
@@ -2815,23 +2972,25 @@ lbValue lb_build_unary_and(lbProcedure *p, Ast *expr) {
if (is_type_pointer(v.type)) {
v = lb_emit_load(p, v);
}
lbValue data_ptr = lb_emit_struct_ev(p, v, 0);
lbValue any_id = lb_emit_struct_ev(p, v, 1);
lbValue id = lb_typeid(p->module, type);
if ((p->state_flags & StateFlag_no_type_assert) == 0) {
GB_ASSERT(!build_context.disallow_rtti);
lbValue any_id = lb_emit_struct_ev(p, v, 1);
lbValue ok = lb_emit_comp(p, Token_CmpEq, any_id, id);
auto args = array_make<lbValue>(permanent_allocator(), 6);
args[0] = ok;
lbValue id = lb_typeid(p->module, type);
lbValue ok = lb_emit_comp(p, Token_CmpEq, any_id, id);
auto args = array_make<lbValue>(permanent_allocator(), 6);
args[0] = ok;
args[1] = lb_find_or_add_entity_string(p->module, get_file_path_string(pos.file_id));
args[2] = lb_const_int(p->module, t_i32, pos.line);
args[3] = lb_const_int(p->module, t_i32, pos.column);
args[1] = lb_find_or_add_entity_string(p->module, get_file_path_string(pos.file_id));
args[2] = lb_const_int(p->module, t_i32, pos.line);
args[3] = lb_const_int(p->module, t_i32, pos.column);
args[4] = any_id;
args[5] = id;
lb_emit_runtime_call(p, "type_assertion_check", args);
args[4] = any_id;
args[5] = id;
lb_emit_runtime_call(p, "type_assertion_check", args);
}
return lb_emit_conv(p, data_ptr, tv.type);
} else {
@@ -2843,9 +3002,8 @@ lbValue lb_build_unary_and(lbProcedure *p, Ast *expr) {
return lb_build_addr_ptr(p, ue->expr);
}
lbValue lb_build_expr_internal(lbProcedure *p, Ast *expr);
lbValue lb_build_expr(lbProcedure *p, Ast *expr) {
lbModule *m = p->module;
u16 prev_state_flags = p->state_flags;
defer (p->state_flags = prev_state_flags);
@@ -2861,9 +3019,49 @@ lbValue lb_build_expr(lbProcedure *p, Ast *expr) {
out &= ~StateFlag_bounds_check;
}
if (in & StateFlag_type_assert) {
out |= StateFlag_type_assert;
out &= ~StateFlag_no_type_assert;
} else if (in & StateFlag_no_type_assert) {
out |= StateFlag_no_type_assert;
out &= ~StateFlag_type_assert;
}
p->state_flags = out;
}
// IMPORTANT NOTE(bill):
// Selector Call Expressions (foo->bar(...))
// must only evaluate `foo` once as it gets transformed into
// `foo.bar(foo, ...)`
// And if `foo` is a procedure call or something more complex, storing the value
// once is a very good idea
// If a stored value is found, it must be removed from the cache
if (expr->state_flags & StateFlag_SelectorCallExpr) {
lbValue *pp = map_get(&p->selector_values, expr);
if (pp != nullptr) {
lbValue res = *pp;
map_remove(&p->selector_values, expr);
return res;
}
lbAddr *pa = map_get(&p->selector_addr, expr);
if (pa != nullptr) {
lbAddr res = *pa;
map_remove(&p->selector_addr, expr);
return lb_addr_load(p, res);
}
}
lbValue res = lb_build_expr_internal(p, expr);
if (expr->state_flags & StateFlag_SelectorCallExpr) {
map_set(&p->selector_values, expr, res);
}
return res;
}
lbValue lb_build_expr_internal(lbProcedure *p, Ast *expr) {
lbModule *m = p->module;
expr = unparen_expr(expr);
TokenPos expr_pos = ast_token(expr).pos;
@@ -2882,17 +3080,6 @@ lbValue lb_build_expr(lbProcedure *p, Ast *expr) {
return lb_const_value(p->module, type, tv.value);
}
#if 0
LLVMMetadataRef prev_debug_location = nullptr;
if (p->debug_info != nullptr) {
prev_debug_location = LLVMGetCurrentDebugLocation2(p->builder);
LLVMSetCurrentDebugLocation2(p->builder, lb_debug_location_from_ast(p, expr));
}
defer (if (prev_debug_location != nullptr) {
LLVMSetCurrentDebugLocation2(p->builder, prev_debug_location);
});
#endif
switch (expr->kind) {
case_ast_node(bl, BasicLit, expr);
TokenPos pos = bl->token.pos;
@@ -2961,14 +3148,7 @@ lbValue lb_build_expr(lbProcedure *p, Ast *expr) {
case_ast_node(se, SelectorCallExpr, expr);
GB_ASSERT(se->modified_call);
TypeAndValue tav = type_and_value_of_expr(expr);
GB_ASSERT(tav.mode != Addressing_Invalid);
lbValue res = lb_build_call_expr(p, se->call);
ast_node(ce, CallExpr, se->call);
ce->sce_temp_data = gb_alloc_copy(permanent_allocator(), &res, gb_size_of(res));
return res;
return lb_build_call_expr(p, se->call);
case_end;
case_ast_node(te, TernaryIfExpr, expr);
@@ -2980,23 +3160,31 @@ lbValue lb_build_expr(lbProcedure *p, Ast *expr) {
lbBlock *done = lb_create_block(p, "if.done"); // NOTE(bill): Append later
lbBlock *else_ = lb_create_block(p, "if.else");
lbValue cond = lb_build_cond(p, te->cond, then, else_);
lb_build_cond(p, te->cond, then, else_);
lb_start_block(p, then);
Type *type = default_type(type_of_expr(expr));
LLVMTypeRef llvm_type = lb_type(p->module, type);
incoming_values[0] = lb_emit_conv(p, lb_build_expr(p, te->x), type).value;
if (is_type_internally_pointer_like(type)) {
incoming_values[0] = LLVMBuildBitCast(p->builder, incoming_values[0], llvm_type, "");
}
lb_emit_jump(p, done);
lb_start_block(p, else_);
incoming_values[1] = lb_emit_conv(p, lb_build_expr(p, te->y), type).value;
if (is_type_internally_pointer_like(type)) {
incoming_values[1] = LLVMBuildBitCast(p->builder, incoming_values[1], llvm_type, "");
}
lb_emit_jump(p, done);
lb_start_block(p, done);
lbValue res = {};
res.value = LLVMBuildPhi(p->builder, lb_type(p->module, type), "");
res.value = LLVMBuildPhi(p->builder, llvm_type, "");
res.type = type;
GB_ASSERT(p->curr_block->preds.count >= 2);
@@ -3254,9 +3442,34 @@ lbAddr lb_build_array_swizzle_addr(lbProcedure *p, AstCallExpr *ce, TypeAndValue
}
lbAddr lb_build_addr_internal(lbProcedure *p, Ast *expr);
lbAddr lb_build_addr(lbProcedure *p, Ast *expr) {
expr = unparen_expr(expr);
// IMPORTANT NOTE(bill):
// Selector Call Expressions (foo->bar(...))
// must only evaluate `foo` once as it gets transformed into
// `foo.bar(foo, ...)`
// And if `foo` is a procedure call or something more complex, storing the value
// once is a very good idea
// If a stored value is found, it must be removed from the cache
if (expr->state_flags & StateFlag_SelectorCallExpr) {
lbAddr *pp = map_get(&p->selector_addr, expr);
if (pp != nullptr) {
lbAddr res = *pp;
map_remove(&p->selector_addr, expr);
return res;
}
}
lbAddr addr = lb_build_addr_internal(p, expr);
if (expr->state_flags & StateFlag_SelectorCallExpr) {
map_set(&p->selector_addr, expr, addr);
}
return addr;
}
lbAddr lb_build_addr_internal(lbProcedure *p, Ast *expr) {
switch (expr->kind) {
case_ast_node(i, Implicit, expr);
lbAddr v = {};
@@ -3281,9 +3494,9 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) {
case_end;
case_ast_node(se, SelectorExpr, expr);
Ast *sel = unparen_expr(se->selector);
if (sel->kind == Ast_Ident) {
String selector = sel->Ident.token.string;
Ast *sel_node = unparen_expr(se->selector);
if (sel_node->kind == Ast_Ident) {
String selector = sel_node->Ident.token.string;
TypeAndValue tav = type_and_value_of_expr(se->expr);
if (tav.mode == Addressing_Invalid) {
@@ -3298,7 +3511,12 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) {
Type *type = base_type(tav.type);
if (tav.mode == Addressing_Type) { // Addressing_Type
GB_PANIC("Unreachable");
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_PANIC("Unreachable %.*s", LIT(selector));
}
if (se->swizzle_count > 0) {
@@ -3325,6 +3543,11 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) {
Selection sel = lookup_field(type, selector, false);
GB_ASSERT(sel.entity != nullptr);
if (sel.pseudo_field) {
GB_ASSERT(sel.entity->kind == Entity_Procedure);
Entity *e = entity_of_node(sel_node);
return lb_addr(lb_find_value_from_entity(p->module, e));
}
{
lbAddr addr = lb_build_addr(p, se->expr);
@@ -3388,9 +3611,6 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) {
case_end;
case_ast_node(se, SelectorCallExpr, expr);
GB_ASSERT(se->modified_call);
TypeAndValue tav = type_and_value_of_expr(expr);
GB_ASSERT(tav.mode != Addressing_Invalid);
lbValue e = lb_build_expr(p, expr);
return lb_addr(lb_address_from_load_or_generate_local(p, e));
case_end;
@@ -3478,7 +3698,8 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) {
GB_ASSERT_MSG(is_type_indexable(t), "%s %s", type_to_string(t), expr_to_string(expr));
if (is_type_map(t)) {
lbValue map_val = lb_build_addr_ptr(p, ie->expr);
lbAddr map_addr = lb_build_addr(p, ie->expr);
lbValue map_val = lb_addr_load(p, map_addr);
if (deref) {
map_val = lb_emit_load(p, map_val);
}
@@ -3487,7 +3708,8 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) {
key = lb_emit_conv(p, key, t->Map.key);
Type *result_type = type_of_expr(expr);
return lb_addr_map(map_val, key, t, result_type);
lbValue map_ptr = lb_address_from_load_or_generate_local(p, map_val);
return lb_addr_map(map_ptr, key, t, result_type);
}
switch (t->kind) {
@@ -4549,6 +4771,102 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) {
break;
}
case Type_SimdVector: {
if (cl->elems.count > 0) {
lbValue vector_value = lb_const_value(p->module, type, exact_value_compound(expr));
defer (lb_addr_store(p, v, vector_value));
auto temp_data = array_make<lbCompoundLitElemTempData>(temporary_allocator(), 0, cl->elems.count);
// NOTE(bill): Separate value, store into their own chunks
for_array(i, cl->elems) {
Ast *elem = cl->elems[i];
if (elem->kind == Ast_FieldValue) {
ast_node(fv, FieldValue, elem);
if (lb_is_elem_const(fv->value, et)) {
continue;
}
if (is_ast_range(fv->field)) {
ast_node(ie, BinaryExpr, fv->field);
TypeAndValue lo_tav = ie->left->tav;
TypeAndValue hi_tav = ie->right->tav;
GB_ASSERT(lo_tav.mode == Addressing_Constant);
GB_ASSERT(hi_tav.mode == Addressing_Constant);
TokenKind op = ie->op.kind;
i64 lo = exact_value_to_i64(lo_tav.value);
i64 hi = exact_value_to_i64(hi_tav.value);
if (op != Token_RangeHalf) {
hi += 1;
}
lbValue value = lb_build_expr(p, fv->value);
for (i64 k = lo; k < hi; k++) {
lbCompoundLitElemTempData data = {};
data.value = value;
data.elem_index = cast(i32)k;
array_add(&temp_data, data);
}
} else {
auto tav = fv->field->tav;
GB_ASSERT(tav.mode == Addressing_Constant);
i64 index = exact_value_to_i64(tav.value);
lbValue value = lb_build_expr(p, fv->value);
lbCompoundLitElemTempData data = {};
data.value = lb_emit_conv(p, value, et);
data.expr = fv->value;
data.elem_index = cast(i32)index;
array_add(&temp_data, data);
}
} else {
if (lb_is_elem_const(elem, et)) {
continue;
}
lbCompoundLitElemTempData data = {};
data.expr = elem;
data.elem_index = cast(i32)i;
array_add(&temp_data, data);
}
}
for_array(i, temp_data) {
lbValue field_expr = temp_data[i].value;
Ast *expr = temp_data[i].expr;
auto prev_hint = lb_set_copy_elision_hint(p, lb_addr(temp_data[i].gep), expr);
if (field_expr.value == nullptr) {
field_expr = lb_build_expr(p, expr);
}
Type *t = field_expr.type;
GB_ASSERT(t->kind != Type_Tuple);
lbValue ev = lb_emit_conv(p, field_expr, et);
if (!p->copy_elision_hint.used) {
temp_data[i].value = ev;
}
lb_reset_copy_elision_hint(p, prev_hint);
}
// TODO(bill): reduce the need for individual `insertelement` if a `shufflevector`
// might be a better option
for_array(i, temp_data) {
if (temp_data[i].value.value != nullptr) {
LLVMValueRef index = lb_const_int(p->module, t_u32, temp_data[i].elem_index).value;
vector_value.value = LLVMBuildInsertElement(p->builder, vector_value.value, temp_data[i].value.value, index, "");
}
}
}
break;
}
}
return v;
@@ -4586,7 +4904,7 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) {
lbBlock *done = lb_create_block(p, "if.done"); // NOTE(bill): Append later
lbBlock *else_ = lb_create_block(p, "if.else");
lbValue cond = lb_build_cond(p, te->cond, then, else_);
lb_build_cond(p, te->cond, then, else_);
lb_start_block(p, then);
Type *ptr_type = alloc_type_pointer(default_type(type_of_expr(expr)));
@@ -4613,6 +4931,16 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) {
return lb_addr(res);
case_end;
case_ast_node(oe, OrElseExpr, 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(oe, OrReturnExpr, expr);
lbValue ptr = lb_address_from_load_or_generate_local(p, lb_build_expr(p, expr));
return lb_addr(ptr);
case_end;
}
TokenPos token_pos = ast_token(expr).pos;
+125 -33
View File
@@ -67,11 +67,12 @@ void lb_init_module(lbModule *m, Checker *c) {
map_init(&m->equal_procs, a);
map_init(&m->hasher_procs, a);
array_init(&m->procedures_to_generate, a, 0, 1024);
array_init(&m->foreign_library_paths, a, 0, 1024);
array_init(&m->missing_procedures_to_check, a, 0, 16);
map_init(&m->debug_values, a);
array_init(&m->debug_incomplete_types, a, 0, 1024);
string_map_init(&m->objc_classes, a);
string_map_init(&m->objc_selectors, a);
}
bool lb_init_generator(lbGenerator *gen, Checker *c) {
@@ -84,7 +85,6 @@ bool lb_init_generator(lbGenerator *gen, Checker *c) {
return false;
}
String init_fullpath = c->parser->init_fullpath;
if (build_context.out_filepath.len == 0) {
@@ -124,6 +124,11 @@ bool lb_init_generator(lbGenerator *gen, Checker *c) {
map_init(&gen->modules_through_ctx, permanent_allocator(), gen->info->packages.entries.count*2);
map_init(&gen->anonymous_proc_lits, heap_allocator(), 1024);
mutex_init(&gen->foreign_mutex);
array_init(&gen->foreign_libraries, heap_allocator(), 0, 1024);
ptr_set_init(&gen->foreign_libraries_set, heap_allocator(), 1024);
if (USE_SEPARATE_MODULES) {
for_array(i, gen->info->packages.entries) {
AstPackage *pkg = gen->info->packages.entries[i].value;
@@ -216,6 +221,17 @@ LLVMValueRef llvm_one(lbModule *m) {
return LLVMConstInt(lb_type(m, t_i32), 1, false);
}
LLVMValueRef llvm_alloca(lbProcedure *p, LLVMTypeRef llvm_type, isize alignment, char const *name) {
LLVMPositionBuilderAtEnd(p->builder, p->decl_block->block);
LLVMValueRef val = LLVMBuildAlloca(p->builder, llvm_type, name);
LLVMSetAlignment(val, cast(unsigned int)alignment);
LLVMPositionBuilderAtEnd(p->builder, p->curr_block->block);
return val;
}
lbValue lb_zero(lbModule *m, Type *t) {
lbValue v = {};
v.value = LLVMConstInt(lb_type(m, t), 0, false);
@@ -271,6 +287,10 @@ lbAddr lb_addr(lbValue addr) {
lbAddr lb_addr_map(lbValue addr, lbValue map_key, Type *map_type, Type *map_result) {
GB_ASSERT(is_type_pointer(addr.type));
Type *mt = type_deref(addr.type);
GB_ASSERT(is_type_map(mt));
lbAddr v = {lbAddr_Map, addr};
v.map.key = map_key;
v.map.type = map_type;
@@ -832,6 +852,16 @@ bool lb_is_type_proc_recursive(Type *t) {
void lb_emit_store(lbProcedure *p, lbValue ptr, lbValue value) {
GB_ASSERT(value.value != nullptr);
Type *a = type_deref(ptr.type);
if (LLVMIsNull(value.value)) {
LLVMTypeRef src_t = LLVMGetElementType(LLVMTypeOf(ptr.value));
if (lb_sizeof(src_t) <= lb_max_zero_init_size()) {
LLVMBuildStore(p->builder, LLVMConstNull(src_t), ptr.value);
} else {
lb_mem_zero_ptr(p, ptr.value, a, 1);
}
return;
}
if (is_type_boolean(a)) {
// NOTE(bill): There are multiple sized booleans, thus force a conversion (if necessarily)
value = lb_emit_conv(p, value, a);
@@ -841,6 +871,20 @@ void lb_emit_store(lbProcedure *p, lbValue ptr, lbValue value) {
GB_ASSERT_MSG(are_types_identical(ca, core_type(value.type)), "%s != %s", type_to_string(a), type_to_string(value.type));
}
enum {MAX_STORE_SIZE = 64};
if (LLVMIsALoadInst(value.value) && lb_sizeof(LLVMTypeOf(value.value)) > MAX_STORE_SIZE) {
LLVMValueRef dst_ptr = ptr.value;
LLVMValueRef src_ptr = LLVMGetOperand(value.value, 0);
src_ptr = LLVMBuildPointerCast(p->builder, src_ptr, LLVMTypeOf(dst_ptr), "");
LLVMBuildMemMove(p->builder,
dst_ptr, 1,
src_ptr, 1,
LLVMConstInt(LLVMInt64TypeInContext(p->module->ctx), lb_sizeof(LLVMTypeOf(value.value)), false));
return;
}
if (lb_is_type_proc_recursive(a)) {
// NOTE(bill, 2020-11-11): Because of certain LLVM rules, a procedure value may be
// stored as regular pointer with no procedure information
@@ -1142,7 +1186,7 @@ lbValue lb_emit_union_tag_ptr(lbProcedure *p, lbValue u) {
LLVMTypeRef uvt = LLVMGetElementType(LLVMTypeOf(u.value));
unsigned element_count = LLVMCountStructElementTypes(uvt);
GB_ASSERT_MSG(element_count == 2, "element_count=%u (%s) != (%s)", element_count, type_to_string(ut), LLVMPrintTypeToString(uvt));
GB_ASSERT_MSG(element_count >= 2, "element_count=%u (%s) != (%s)", element_count, type_to_string(ut), LLVMPrintTypeToString(uvt));
lbValue tag_ptr = {};
tag_ptr.value = LLVMBuildStructGEP(p->builder, u.value, 1, "");
@@ -1169,10 +1213,35 @@ void lb_emit_store_union_variant_tag(lbProcedure *p, lbValue parent, Type *varia
}
void lb_emit_store_union_variant(lbProcedure *p, lbValue parent, lbValue variant, Type *variant_type) {
lbValue underlying = lb_emit_conv(p, parent, alloc_type_pointer(variant_type));
Type *pt = base_type(type_deref(parent.type));
GB_ASSERT(pt->kind == Type_Union);
if (pt->Union.kind == UnionType_shared_nil) {
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");
lb_emit_store(p, underlying, variant);
lb_emit_store_union_variant_tag(p, parent, variant_type);
lbValue cond_is_nil = lb_emit_comp_against_nil(p, Token_CmpEq, variant);
lb_emit_if(p, cond_is_nil, if_nil, if_not_nil);
lb_start_block(p, if_nil);
lb_emit_store(p, parent, lb_const_nil(p->module, type_deref(parent.type)));
lb_emit_jump(p, done);
lb_start_block(p, if_not_nil);
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);
lb_emit_jump(p, done);
lb_start_block(p, done);
} 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);
}
}
@@ -1598,8 +1667,9 @@ LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
return llvm_type;
}
llvm_type = LLVMStructCreateNamed(ctx, name);
LLVMTypeRef found_val = *found;
map_set(&m->types, type, llvm_type);
lb_clone_struct_type(llvm_type, *found);
lb_clone_struct_type(llvm_type, found_val);
return llvm_type;
}
}
@@ -1795,7 +1865,7 @@ LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
unsigned block_size = cast(unsigned)type->Union.variant_block_size;
auto fields = array_make<LLVMTypeRef>(temporary_allocator(), 0, 2);
auto fields = array_make<LLVMTypeRef>(temporary_allocator(), 0, 3);
if (is_type_union_maybe_pointer(type)) {
LLVMTypeRef variant = lb_type(m, type->Union.variants[0]);
array_add(&fields, variant);
@@ -1804,7 +1874,12 @@ LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
LLVMTypeRef tag_type = lb_type(m, union_tag_type(type));
array_add(&fields, block_type);
array_add(&fields, tag_type);
i64 used_size = lb_sizeof(block_type) + lb_sizeof(tag_type);
i64 padding = size - used_size;
if (padding > 0) {
LLVMTypeRef padding_type = lb_type_padding_filler(m, padding, align);
array_add(&fields, padding_type);
}
}
return LLVMStructTypeInContext(ctx, fields.data, cast(unsigned)fields.count, false);
@@ -1887,11 +1962,12 @@ LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
if (e->flags & EntityFlag_CVarArg) {
continue;
}
Type *e_type = reduce_tuple_to_single_type(e->type);
LLVMTypeRef param_type = nullptr;
if (is_type_boolean(e_type) &&
if (e->flags & EntityFlag_ByPtr) {
param_type = lb_type(m, alloc_type_pointer(e_type));
} else if (is_type_boolean(e_type) &&
type_size_of(e_type) <= 1) {
param_type = LLVMInt1TypeInContext(m->ctx);
} else {
@@ -2260,13 +2336,11 @@ general_end:;
return loaded_val;
} else {
GB_ASSERT(p->decl_block != p->curr_block);
LLVMPositionBuilderAtEnd(p->builder, p->decl_block->block);
LLVMValueRef ptr = LLVMBuildAlloca(p->builder, dst_type, "");
LLVMPositionBuilderAtEnd(p->builder, p->curr_block->block);
i64 max_align = gb_max(lb_alignof(src_type), lb_alignof(dst_type));
max_align = gb_max(max_align, 4);
LLVMSetAlignment(ptr, cast(unsigned)max_align);
LLVMValueRef ptr = llvm_alloca(p, dst_type, max_align);
LLVMValueRef nptr = LLVMBuildPointerCast(p->builder, ptr, LLVMPointerType(src_type, 0), "");
LLVMBuildStore(p->builder, val, nptr);
@@ -2299,7 +2373,10 @@ LLVMValueRef lb_find_or_add_entity_string_ptr(lbModule *m, String const &str) {
LLVMValueRef global_data = LLVMAddGlobal(m->mod, LLVMTypeOf(data), name);
LLVMSetInitializer(global_data, data);
LLVMSetLinkage(global_data, LLVMInternalLinkage);
LLVMSetLinkage(global_data, LLVMPrivateLinkage);
LLVMSetUnnamedAddress(global_data, LLVMGlobalUnnamedAddr);
LLVMSetAlignment(global_data, 1);
LLVMSetGlobalConstant(global_data, true);
LLVMValueRef ptr = LLVMConstInBoundsGEP(global_data, indices, 2);
string_map_set(&m->const_strings, key, ptr);
@@ -2341,7 +2418,10 @@ lbValue lb_find_or_add_entity_string_byte_slice(lbModule *m, String const &str)
}
LLVMValueRef global_data = LLVMAddGlobal(m->mod, LLVMTypeOf(data), name);
LLVMSetInitializer(global_data, data);
LLVMSetLinkage(global_data, LLVMInternalLinkage);
LLVMSetLinkage(global_data, LLVMPrivateLinkage);
LLVMSetUnnamedAddress(global_data, LLVMGlobalUnnamedAddr);
LLVMSetAlignment(global_data, 1);
LLVMSetGlobalConstant(global_data, true);
LLVMValueRef ptr = nullptr;
if (str.len != 0) {
@@ -2440,7 +2520,7 @@ lbValue lb_find_procedure_value_from_entity(lbModule *m, Entity *e) {
return {};
}
lbAddr lb_add_global_generated(lbModule *m, Type *type, lbValue value) {
lbAddr lb_add_global_generated(lbModule *m, Type *type, lbValue value, Entity **entity_) {
GB_ASSERT(type != nullptr);
type = default_type(type);
@@ -2466,6 +2546,9 @@ lbAddr lb_add_global_generated(lbModule *m, Type *type, lbValue value) {
lb_add_entity(m, e, g);
lb_add_member(m, name, g);
if (entity_) *entity_ = e;
return lb_addr(g);
}
@@ -2574,7 +2657,8 @@ lbValue lb_generate_global_array(lbModule *m, Type *elem_type, i64 count, String
g.value = LLVMAddGlobal(m->mod, lb_type(m, t), text);
g.type = alloc_type_pointer(t);
LLVMSetInitializer(g.value, LLVMConstNull(lb_type(m, t)));
LLVMSetLinkage(g.value, LLVMInternalLinkage);
LLVMSetLinkage(g.value, LLVMPrivateLinkage);
LLVMSetUnnamedAddress(g.value, LLVMGlobalUnnamedAddr);
string_map_set(&m->members, s, g);
return g;
}
@@ -2586,6 +2670,9 @@ lbValue lb_build_cond(lbProcedure *p, Ast *cond, lbBlock *true_block, lbBlock *f
GB_ASSERT(true_block != nullptr);
GB_ASSERT(false_block != nullptr);
// Use to signal not to do compile time short circuit for consts
lbValue no_comptime_short_circuit = {};
switch (cond->kind) {
case_ast_node(pe, ParenExpr, cond);
return lb_build_cond(p, pe->expr, true_block, false_block);
@@ -2593,7 +2680,11 @@ lbValue lb_build_cond(lbProcedure *p, Ast *cond, lbBlock *true_block, lbBlock *f
case_ast_node(ue, UnaryExpr, cond);
if (ue->op.kind == Token_Not) {
return lb_build_cond(p, ue->expr, false_block, true_block);
lbValue cond_val = lb_build_cond(p, ue->expr, false_block, true_block);
if (cond_val.value && LLVMIsConstant(cond_val.value)) {
return lb_const_bool(p->module, cond_val.type, LLVMConstIntGetZExtValue(cond_val.value) == 0);
}
return no_comptime_short_circuit;
}
case_end;
@@ -2602,12 +2693,14 @@ lbValue lb_build_cond(lbProcedure *p, Ast *cond, lbBlock *true_block, lbBlock *f
lbBlock *block = lb_create_block(p, "cmp.and");
lb_build_cond(p, be->left, block, false_block);
lb_start_block(p, block);
return lb_build_cond(p, be->right, true_block, false_block);
lb_build_cond(p, be->right, true_block, false_block);
return no_comptime_short_circuit;
} else if (be->op.kind == Token_CmpOr) {
lbBlock *block = lb_create_block(p, "cmp.or");
lb_build_cond(p, be->left, true_block, block);
lb_start_block(p, block);
return lb_build_cond(p, be->right, true_block, false_block);
lb_build_cond(p, be->right, true_block, false_block);
return no_comptime_short_circuit;
}
case_end;
}
@@ -2637,34 +2730,29 @@ lbAddr lb_add_local(lbProcedure *p, Type *type, Entity *e, bool zero_init, i32 p
}
LLVMTypeRef llvm_type = lb_type(p->module, type);
LLVMValueRef ptr = LLVMBuildAlloca(p->builder, llvm_type, name);
unsigned alignment = cast(unsigned)gb_max(type_align_of(type), lb_alignof(llvm_type));
if (is_type_matrix(type)) {
alignment *= 2; // NOTE(bill): Just in case
}
LLVMSetAlignment(ptr, alignment);
LLVMPositionBuilderAtEnd(p->builder, p->curr_block->block);
LLVMValueRef ptr = llvm_alloca(p, llvm_type, alignment, name);
if (!zero_init && !force_no_init) {
// If there is any padding of any kind, just zero init regardless of zero_init parameter
LLVMTypeKind kind = LLVMGetTypeKind(llvm_type);
if (kind == LLVMArrayTypeKind) {
kind = LLVMGetTypeKind(lb_type(p->module, core_array_type(type)));
}
if (kind == LLVMStructTypeKind) {
i64 sz = type_size_of(type);
if (type_size_of_struct_pretend_is_packed(type) != sz) {
zero_init = true;
}
} else if (kind == LLVMArrayTypeKind) {
zero_init = true;
}
}
if (zero_init) {
lb_mem_zero_ptr(p, ptr, type, alignment);
}
lbValue val = {};
val.value = ptr;
val.type = alloc_type_pointer(type);
@@ -2674,6 +2762,10 @@ lbAddr lb_add_local(lbProcedure *p, Type *type, Entity *e, bool zero_init, i32 p
lb_add_debug_local_variable(p, ptr, type, e->token);
}
if (zero_init) {
lb_mem_zero_ptr(p, ptr, type, alignment);
}
return lb_addr(val);
}
+54 -24
View File
@@ -48,12 +48,6 @@ LLVMBool lb_must_preserve_predicate_callback(LLVMValueRef value, void *user_data
return LLVMIsAAllocaInst(value) != nullptr;
}
void lb_add_must_preserve_predicate_pass(lbModule *m, LLVMPassManagerRef fpm, i32 optimization_level) {
if (false && optimization_level == 0 && m->debug_builder) {
// LLVMAddInternalizePassWithMustPreservePredicate(fpm, m, lb_must_preserve_predicate_callback);
}
}
#if LLVM_VERSION_MAJOR < 12
#define LLVM_ADD_CONSTANT_VALUE_PASS(fpm) LLVMAddConstantPropagationPass(fpm)
@@ -61,16 +55,15 @@ void lb_add_must_preserve_predicate_pass(lbModule *m, LLVMPassManagerRef fpm, i3
#define LLVM_ADD_CONSTANT_VALUE_PASS(fpm)
#endif
void lb_basic_populate_function_pass_manager(LLVMPassManagerRef fpm) {
LLVMAddPromoteMemoryToRegisterPass(fpm);
LLVMAddMergedLoadStoreMotionPass(fpm);
LLVM_ADD_CONSTANT_VALUE_PASS(fpm);
LLVMAddEarlyCSEPass(fpm);
// LLVM_ADD_CONSTANT_VALUE_PASS(fpm);
// LLVMAddMergedLoadStoreMotionPass(fpm);
// LLVMAddPromoteMemoryToRegisterPass(fpm);
// LLVMAddCFGSimplificationPass(fpm);
void lb_basic_populate_function_pass_manager(LLVMPassManagerRef fpm, i32 optimization_level) {
if (false && optimization_level == 0 && build_context.ODIN_DEBUG) {
LLVMAddMergedLoadStoreMotionPass(fpm);
} else {
LLVMAddPromoteMemoryToRegisterPass(fpm);
LLVMAddMergedLoadStoreMotionPass(fpm);
LLVM_ADD_CONSTANT_VALUE_PASS(fpm);
LLVMAddEarlyCSEPass(fpm);
}
}
void lb_populate_function_pass_manager(lbModule *m, LLVMPassManagerRef fpm, bool ignore_memcpy_pass, i32 optimization_level) {
@@ -78,14 +71,12 @@ void lb_populate_function_pass_manager(lbModule *m, LLVMPassManagerRef fpm, bool
// TODO(bill): Determine which opt definitions should exist in the first place
optimization_level = gb_clamp(optimization_level, 0, 2);
lb_add_must_preserve_predicate_pass(m, fpm, optimization_level);
if (ignore_memcpy_pass) {
lb_basic_populate_function_pass_manager(fpm);
lb_basic_populate_function_pass_manager(fpm, optimization_level);
return;
} else if (optimization_level == 0) {
LLVMAddMemCpyOptPass(fpm);
lb_basic_populate_function_pass_manager(fpm);
lb_basic_populate_function_pass_manager(fpm, optimization_level);
return;
}
@@ -96,7 +87,7 @@ void lb_populate_function_pass_manager(lbModule *m, LLVMPassManagerRef fpm, bool
LLVMPassManagerBuilderPopulateFunctionPassManager(pmb, fpm);
#else
LLVMAddMemCpyOptPass(fpm);
lb_basic_populate_function_pass_manager(fpm);
lb_basic_populate_function_pass_manager(fpm, optimization_level);
LLVMAddSCCPPass(fpm);
@@ -114,11 +105,9 @@ void lb_populate_function_pass_manager_specific(lbModule *m, LLVMPassManagerRef
// TODO(bill): Determine which opt definitions should exist in the first place
optimization_level = gb_clamp(optimization_level, 0, 2);
lb_add_must_preserve_predicate_pass(m, fpm, optimization_level);
if (optimization_level == 0) {
LLVMAddMemCpyOptPass(fpm);
lb_basic_populate_function_pass_manager(fpm);
lb_basic_populate_function_pass_manager(fpm, optimization_level);
return;
}
@@ -191,6 +180,9 @@ void lb_populate_module_pass_manager(LLVMTargetMachineRef target_machine, LLVMPa
// NOTE(bill): Treat -opt:3 as if it was -opt:2
// TODO(bill): Determine which opt definitions should exist in the first place
optimization_level = gb_clamp(optimization_level, 0, 2);
if (optimization_level == 0 && build_context.ODIN_DEBUG) {
return;
}
LLVMAddAlwaysInlinerPass(mpm);
LLVMAddStripDeadPrototypesPass(mpm);
@@ -388,6 +380,43 @@ void llvm_delete_function(LLVMValueRef func) {
LLVMDeleteFunction(func);
}
void lb_append_to_compiler_used(lbModule *m, LLVMValueRef func) {
LLVMValueRef global = LLVMGetNamedGlobal(m->mod, "llvm.compiler.used");
LLVMValueRef *constants;
int operands = 1;
if (global != NULL) {
GB_ASSERT(LLVMIsAGlobalVariable(global));
LLVMValueRef initializer = LLVMGetInitializer(global);
GB_ASSERT(LLVMIsAConstantArray(initializer));
operands = LLVMGetNumOperands(initializer) + 1;
constants = gb_alloc_array(temporary_allocator(), LLVMValueRef, operands);
for (int i = 0; i < operands - 1; i++) {
LLVMValueRef operand = LLVMGetOperand(initializer, i);
GB_ASSERT(LLVMIsAConstant(operand));
constants[i] = operand;
}
LLVMDeleteGlobal(global);
} else {
constants = gb_alloc_array(temporary_allocator(), LLVMValueRef, 1);
}
LLVMTypeRef Int8PtrTy = LLVMPointerType(LLVMInt8TypeInContext(m->ctx), 0);
LLVMTypeRef ATy = LLVMArrayType(Int8PtrTy, operands);
constants[operands - 1] = LLVMConstBitCast(func, Int8PtrTy);
LLVMValueRef initializer = LLVMConstArray(Int8PtrTy, constants, operands);
global = LLVMAddGlobal(m->mod, ATy, "llvm.compiler.used");
LLVMSetLinkage(global, LLVMAppendingLinkage);
LLVMSetSection(global, "llvm.metadata");
LLVMSetInitializer(global, initializer);
}
void lb_run_remove_unused_function_pass(lbModule *m) {
isize removal_count = 0;
isize pass_count = 0;
@@ -423,6 +452,7 @@ void lb_run_remove_unused_function_pass(lbModule *m) {
Entity *e = *found;
bool is_required = (e->flags & EntityFlag_Require) == EntityFlag_Require;
if (is_required) {
lb_append_to_compiler_used(m, curr_func);
continue;
}
}
+1007 -281
View File
File diff suppressed because it is too large Load Diff
+53 -76
View File
@@ -212,8 +212,9 @@ void lb_open_scope(lbProcedure *p, Scope *s) {
unsigned column = cast(unsigned)token.pos.column;
LLVMMetadataRef file = nullptr;
if (s->node->file != nullptr) {
file = lb_get_llvm_metadata(m, s->node->file);
AstFile *ast_file = s->node->file();
if (ast_file != nullptr) {
file = lb_get_llvm_metadata(m, ast_file);
}
LLVMMetadataRef scope = nullptr;
if (p->scope_stack.count > 0) {
@@ -1209,8 +1210,8 @@ void lb_build_switch_stmt(lbProcedure *p, AstSwitchStmt *ss, Scope *scope) {
}
lb_emit_jump(p, done);
lb_close_scope(p, lbDeferExit_Default, done);
lb_start_block(p, done);
lb_close_scope(p, lbDeferExit_Default, done);
}
void lb_store_type_case_implicit(lbProcedure *p, Ast *clause, lbValue value) {
@@ -1252,7 +1253,6 @@ void lb_type_case_body(lbProcedure *p, Ast *label, Ast *clause, lbBlock *body, l
ast_node(cc, CaseClause, clause);
lb_push_target_list(p, label, done, nullptr, nullptr);
lb_open_scope(p, body->scope);
lb_build_stmt_list(p, cc->stmts);
lb_close_scope(p, lbDeferExit_Default, body);
lb_pop_target_list(p);
@@ -1262,6 +1262,7 @@ void lb_type_case_body(lbProcedure *p, Ast *label, Ast *clause, lbBlock *body, l
void lb_build_type_switch_stmt(lbProcedure *p, AstTypeSwitchStmt *ss) {
lbModule *m = p->module;
lb_open_scope(p, ss->scope);
ast_node(as, AssignStmt, ss->tag);
GB_ASSERT(as->lhs.count == 1);
@@ -1320,6 +1321,7 @@ void lb_build_type_switch_stmt(lbProcedure *p, AstTypeSwitchStmt *ss) {
for_array(i, body->stmts) {
Ast *clause = body->stmts[i];
ast_node(cc, CaseClause, 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);
@@ -1328,6 +1330,9 @@ void lb_build_type_switch_stmt(lbProcedure *p, AstTypeSwitchStmt *ss) {
}
lbBlock *body = lb_create_block(p, "typeswitch.body");
if (p->debug_info != nullptr) {
LLVMSetCurrentDebugLocation2(p->builder, lb_debug_location_from_ast(p, clause));
}
Type *case_type = nullptr;
for_array(type_index, cc->list) {
case_type = type_of_expr(cc->list[type_index]);
@@ -1374,6 +1379,7 @@ 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);
}
@@ -1484,7 +1490,14 @@ void lb_build_return_stmt_internal(lbProcedure *p, lbValue const &res) {
if (return_by_pointer) {
if (res.value != nullptr) {
LLVMBuildStore(p->builder, res.value, p->return_ptr.addr.value);
LLVMValueRef res_val = res.value;
i64 sz = type_size_of(res.type);
if (LLVMIsALoadInst(res_val) && sz > build_context.word_size) {
lbValue ptr = lb_address_from_load_or_generate_local(p, res);
lb_mem_copy_non_overlapping(p, p->return_ptr.addr, ptr, lb_const_int(p->module, t_int, sz));
} else {
LLVMBuildStore(p->builder, res_val, p->return_ptr.addr.value);
}
} else {
LLVMBuildStore(p->builder, LLVMConstNull(p->abi_function_type->ret.type), p->return_ptr.addr.value);
}
@@ -1624,7 +1637,7 @@ void lb_build_return_stmt(lbProcedure *p, Slice<Ast *> const &return_results) {
void lb_build_if_stmt(lbProcedure *p, Ast *node) {
ast_node(is, IfStmt, node);
lb_open_scope(p, node->scope); // Scope #1
lb_open_scope(p, is->scope); // Scope #1
defer (lb_close_scope(p, lbDeferExit_Default, nullptr));
if (is->init != nullptr) {
@@ -1644,13 +1657,16 @@ void lb_build_if_stmt(lbProcedure *p, Ast *node) {
}
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 (LLVMIsConstant(cond.value)) {
if (cond.value && LLVMIsConstant(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.
@@ -1674,7 +1690,7 @@ void lb_build_if_stmt(lbProcedure *p, Ast *node) {
lb_emit_jump(p, else_);
lb_start_block(p, else_);
lb_open_scope(p, is->else_stmt->scope);
lb_open_scope(p, scope_of_node(is->else_stmt));
lb_build_stmt(p, is->else_stmt);
lb_close_scope(p, lbDeferExit_Default, nullptr);
}
@@ -1691,7 +1707,7 @@ void lb_build_if_stmt(lbProcedure *p, Ast *node) {
if (is->else_stmt != nullptr) {
lb_start_block(p, else_);
lb_open_scope(p, is->else_stmt->scope);
lb_open_scope(p, scope_of_node(is->else_stmt));
lb_build_stmt(p, is->else_stmt);
lb_close_scope(p, lbDeferExit_Default, nullptr);
@@ -1709,7 +1725,10 @@ void lb_build_if_stmt(lbProcedure *p, Ast *node) {
void lb_build_for_stmt(lbProcedure *p, Ast *node) {
ast_node(fs, ForStmt, node);
lb_open_scope(p, node->scope); // Open Scope here
lb_open_scope(p, fs->scope); // Open Scope here
if (p->debug_info != nullptr) {
LLVMSetCurrentDebugLocation2(p->builder, lb_debug_location_from_ast(p, node));
}
if (fs->init != nullptr) {
#if 1
@@ -1730,11 +1749,14 @@ void lb_build_for_stmt(lbProcedure *p, Ast *node) {
post = lb_create_block(p, "for.post");
}
lb_emit_jump(p, loop);
lb_start_block(p, loop);
if (loop != body) {
// right now the condition (all expressions) will not set it's debug location, so we will do it here
if (p->debug_info != nullptr) {
LLVMSetCurrentDebugLocation2(p->builder, lb_debug_location_from_ast(p, fs->cond));
}
lb_build_cond(p, fs->cond, body, done);
lb_start_block(p, body);
}
@@ -1742,10 +1764,12 @@ void lb_build_for_stmt(lbProcedure *p, Ast *node) {
lb_push_target_list(p, fs->label, done, post, nullptr);
lb_build_stmt(p, fs->body);
lb_close_scope(p, lbDeferExit_Default, nullptr);
lb_pop_target_list(p);
if (p->debug_info != nullptr) {
LLVMSetCurrentDebugLocation2(p->builder, lb_debug_end_location_from_ast(p, fs->body));
}
lb_emit_jump(p, post);
if (fs->post != nullptr) {
@@ -1755,9 +1779,12 @@ void lb_build_for_stmt(lbProcedure *p, Ast *node) {
}
lb_start_block(p, done);
lb_close_scope(p, lbDeferExit_Default, nullptr);
}
void lb_build_assign_stmt_array(lbProcedure *p, TokenKind op, lbAddr const &lhs, lbValue const &value) {
GB_ASSERT(op != Token_Eq);
Type *lhs_type = lb_addr_type(lhs);
Type *array_type = base_type(lhs_type);
GB_ASSERT(is_type_array_like(array_type));
@@ -1787,7 +1814,6 @@ void lb_build_assign_stmt_array(lbProcedure *p, TokenKind op, lbAddr const &lhs,
}
indices[index_count++] = index;
}
gb_sort_array(indices, index_count, gb_i32_cmp(0));
lbValue lhs_ptrs[4] = {};
lbValue x_loads[4] = {};
@@ -1832,7 +1858,6 @@ void lb_build_assign_stmt_array(lbProcedure *p, TokenKind op, lbAddr const &lhs,
}
indices[index_count++] = index;
}
gb_sort_array(indices.data, index_count, gb_i32_cmp(0));
lbValue lhs_ptrs[4] = {};
lbValue x_loads[4] = {};
@@ -1860,11 +1885,7 @@ void lb_build_assign_stmt_array(lbProcedure *p, TokenKind op, lbAddr const &lhs,
lbValue x = lb_addr_get_ptr(p, lhs);
if (inline_array_arith) {
#if 1
#if 1
unsigned n = cast(unsigned)count;
auto lhs_ptrs = slice_make<lbValue>(temporary_allocator(), n);
@@ -1888,50 +1909,6 @@ void lb_build_assign_stmt_array(lbProcedure *p, TokenKind op, lbAddr const &lhs,
for (unsigned i = 0; i < n; i++) {
lb_emit_store(p, lhs_ptrs[i], ops[i]);
}
#else
lbValue y = lb_address_from_load_or_generate_local(p, rhs);
unsigned n = cast(unsigned)count;
auto lhs_ptrs = slice_make<lbValue>(temporary_allocator(), n);
auto rhs_ptrs = slice_make<lbValue>(temporary_allocator(), n);
auto x_loads = slice_make<lbValue>(temporary_allocator(), n);
auto y_loads = slice_make<lbValue>(temporary_allocator(), n);
auto ops = slice_make<lbValue>(temporary_allocator(), n);
for (unsigned i = 0; i < n; i++) {
lhs_ptrs[i] = lb_emit_array_epi(p, x, i);
}
for (unsigned i = 0; i < n; i++) {
rhs_ptrs[i] = lb_emit_array_epi(p, y, i);
}
for (unsigned i = 0; i < n; i++) {
x_loads[i] = lb_emit_load(p, lhs_ptrs[i]);
}
for (unsigned i = 0; i < n; i++) {
y_loads[i] = lb_emit_load(p, rhs_ptrs[i]);
}
for (unsigned i = 0; i < n; i++) {
ops[i] = lb_emit_arith(p, op, x_loads[i], y_loads[i], elem_type);
}
for (unsigned i = 0; i < n; i++) {
lb_emit_store(p, lhs_ptrs[i], ops[i]);
}
#endif
#else
lbValue y = lb_address_from_load_or_generate_local(p, rhs);
for (i64 i = 0; i < count; i++) {
lbValue a_ptr = lb_emit_array_epi(p, x, i);
lbValue b_ptr = lb_emit_array_epi(p, y, i);
lbValue a = lb_emit_load(p, a_ptr);
lbValue b = lb_emit_load(p, b_ptr);
lbValue c = lb_emit_arith(p, op, a, b, elem_type);
lb_emit_store(p, a_ptr, c);
}
#endif
} else {
lbValue y = lb_address_from_load_or_generate_local(p, rhs);
@@ -2008,14 +1985,9 @@ void lb_build_stmt(lbProcedure *p, Ast *node) {
}
}
LLVMMetadataRef prev_debug_location = nullptr;
if (p->debug_info != nullptr) {
prev_debug_location = LLVMGetCurrentDebugLocation2(p->builder);
LLVMSetCurrentDebugLocation2(p->builder, lb_debug_location_from_ast(p, node));
}
defer (if (prev_debug_location != nullptr) {
LLVMSetCurrentDebugLocation2(p->builder, prev_debug_location);
});
u16 prev_state_flags = p->state_flags;
defer (p->state_flags = prev_state_flags);
@@ -2031,6 +2003,13 @@ void lb_build_stmt(lbProcedure *p, Ast *node) {
out |= StateFlag_no_bounds_check;
out &= ~StateFlag_bounds_check;
}
if (in & StateFlag_no_type_assert) {
out |= StateFlag_no_type_assert;
out &= ~StateFlag_type_assert;
} else if (in & StateFlag_type_assert) {
out |= StateFlag_type_assert;
out &= ~StateFlag_no_type_assert;
}
p->state_flags = out;
}
@@ -2055,7 +2034,7 @@ void lb_build_stmt(lbProcedure *p, Ast *node) {
tl->is_block = true;
}
lb_open_scope(p, node->scope);
lb_open_scope(p, bs->scope);
lb_build_stmt_list(p, bs->stmts);
lb_close_scope(p, lbDeferExit_Default, nullptr);
@@ -2103,7 +2082,8 @@ void lb_build_stmt(lbProcedure *p, Ast *node) {
lbAddr lval = {};
if (!is_blank_ident(name)) {
Entity *e = entity_of_node(name);
bool zero_init = true; // Always do it
// bool zero_init = true; // Always do it
bool zero_init = vd->values.count == 0;
lval = lb_add_local(p, e->type, e, zero_init);
}
array_add(&lvals, lval);
@@ -2136,15 +2116,15 @@ void lb_build_stmt(lbProcedure *p, Ast *node) {
case_end;
case_ast_node(rs, RangeStmt, node);
lb_build_range_stmt(p, rs, node->scope);
lb_build_range_stmt(p, rs, rs->scope);
case_end;
case_ast_node(rs, UnrollRangeStmt, node);
lb_build_unroll_range_stmt(p, rs, node->scope);
lb_build_unroll_range_stmt(p, rs, rs->scope);
case_end;
case_ast_node(ss, SwitchStmt, node);
lb_build_switch_stmt(p, ss, node->scope);
lb_build_switch_stmt(p, ss, ss->scope);
case_end;
case_ast_node(ss, TypeSwitchStmt, node);
@@ -2180,6 +2160,7 @@ void lb_build_stmt(lbProcedure *p, Ast *node) {
lb_emit_defer_stmts(p, lbDeferExit_Branch, block);
}
lb_emit_jump(p, block);
lb_start_block(p, lb_create_block(p, "unreachable"));
case_end;
}
}
@@ -2211,10 +2192,6 @@ void lb_build_defer_stmt(lbProcedure *p, lbDefer const &d) {
lb_start_block(p, b);
if (d.kind == lbDefer_Node) {
lb_build_stmt(p, d.stmt);
} else if (d.kind == lbDefer_Instr) {
// NOTE(bill): Need to make a new copy
LLVMValueRef instr = LLVMInstructionClone(d.instr.value);
LLVMInsertIntoBuilder(p->builder, instr);
} else if (d.kind == lbDefer_Proc) {
lb_emit_call(p, d.proc.deferred, d.proc.result_as_args);
}
+20 -9
View File
@@ -1,11 +1,10 @@
isize lb_type_info_index(CheckerInfo *info, Type *type, bool err_on_not_found=true) {
isize index = type_info_index(info, type, false);
auto *set = &info->minimum_dependency_type_info_set;
isize index = type_info_index(info, type, err_on_not_found);
if (index >= 0) {
auto *set = &info->minimum_dependency_type_info_set;
for_array(i, set->entries) {
if (set->entries[i].ptr == index) {
return i+1;
}
isize i = ptr_entry_index(set, index);
if (i >= 0) {
return i+1;
}
}
if (err_on_not_found) {
@@ -15,6 +14,8 @@ isize lb_type_info_index(CheckerInfo *info, Type *type, bool err_on_not_found=tr
}
lbValue lb_typeid(lbModule *m, Type *type) {
GB_ASSERT(!build_context.disallow_rtti);
type = default_type(type);
u64 id = cast(u64)lb_type_info_index(m->info, type);
@@ -89,6 +90,8 @@ lbValue lb_typeid(lbModule *m, Type *type) {
}
lbValue lb_type_info(lbModule *m, Type *type) {
GB_ASSERT(!build_context.disallow_rtti);
type = default_type(type);
isize index = lb_type_info_index(m->info, type);
@@ -107,6 +110,8 @@ lbValue lb_type_info(lbModule *m, Type *type) {
}
lbValue lb_get_type_info_ptr(lbModule *m, Type *type) {
GB_ASSERT(!build_context.disallow_rtti);
i32 index = cast(i32)lb_type_info_index(m->info, type);
GB_ASSERT(index >= 0);
// gb_printf_err("%d %s\n", index, type_to_string(type));
@@ -156,6 +161,10 @@ lbValue lb_type_info_member_tags_offset(lbProcedure *p, isize count) {
void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info data
if (build_context.disallow_rtti) {
return;
}
lbModule *m = p->module;
CheckerInfo *info = m->info;
@@ -455,7 +464,7 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da
case Type_EnumeratedArray: {
tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_enumerated_array_ptr);
LLVMValueRef vals[6] = {
LLVMValueRef vals[7] = {
lb_get_type_info_ptr(m, t->EnumeratedArray.elem).value,
lb_get_type_info_ptr(m, t->EnumeratedArray.index).value,
lb_const_int(m, t_int, type_size_of(t->EnumeratedArray.elem)).value,
@@ -464,6 +473,8 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da
// 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 = {};
@@ -664,8 +675,8 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da
}
vals[4] = lb_const_bool(m, t_bool, t->Union.custom_align != 0).value;
vals[5] = lb_const_bool(m, t_bool, t->Union.no_nil).value;
vals[6] = lb_const_bool(m, t_bool, t->Union.maybe).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) {
+276 -45
View File
@@ -1,3 +1,5 @@
lbValue lb_lookup_runtime_procedure(lbModule *m, String const &name);
bool lb_is_type_aggregate(Type *t) {
t = base_type(t);
switch (t->kind) {
@@ -48,18 +50,19 @@ lbValue lb_correct_endianness(lbProcedure *p, lbValue value) {
return value;
}
void lb_mem_zero_ptr_internal(lbProcedure *p, LLVMValueRef ptr, LLVMValueRef len, unsigned alignment, bool is_volatile) {
LLVMValueRef lb_mem_zero_ptr_internal(lbProcedure *p, LLVMValueRef ptr, LLVMValueRef len, unsigned alignment, bool is_volatile) {
bool is_inlinable = false;
i64 const_len = 0;
if (LLVMIsConstant(len)) {
const_len = cast(i64)LLVMConstIntGetSExtValue(len);
// TODO(bill): Determine when it is better to do the `*.inline` versions
if (const_len <= 4*build_context.word_size) {
if (const_len <= lb_max_zero_init_size()) {
is_inlinable = true;
}
}
char const *name = "llvm.memset";
if (is_inlinable) {
name = "llvm.memset.inline";
@@ -69,17 +72,29 @@ void lb_mem_zero_ptr_internal(lbProcedure *p, LLVMValueRef ptr, LLVMValueRef len
lb_type(p->module, t_rawptr),
lb_type(p->module, t_int)
};
unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name));
GB_ASSERT_MSG(id != 0, "Unable to find %s.%s.%s", name, LLVMPrintTypeToString(types[0]), LLVMPrintTypeToString(types[1]));
LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, gb_count_of(types));
if (true || is_inlinable) {
unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name));
GB_ASSERT_MSG(id != 0, "Unable to find %s.%s.%s", name, LLVMPrintTypeToString(types[0]), LLVMPrintTypeToString(types[1]));
LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, gb_count_of(types));
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 LLVMBuildCall(p->builder, ip, args, gb_count_of(args), "");
} else {
LLVMValueRef ip = lb_lookup_runtime_procedure(p->module, str_lit("memset")).value;
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, "");
return LLVMBuildCall(p->builder, ip, args, gb_count_of(args), "");
}
LLVMBuildCall(p->builder, ip, args, gb_count_of(args), "");
}
void lb_mem_zero_ptr(lbProcedure *p, LLVMValueRef ptr, Type *type, unsigned alignment) {
@@ -179,36 +194,33 @@ lbValue lb_emit_transmute(lbProcedure *p, lbValue value, Type *t) {
GB_ASSERT_MSG(sz == dz, "Invalid transmute conversion: '%s' to '%s'", type_to_string(src_type), type_to_string(t));
// NOTE(bill): Casting between an integer and a pointer cannot be done through a bitcast
if (is_type_uintptr(src) && is_type_pointer(dst)) {
if (is_type_uintptr(src) && is_type_internally_pointer_like(dst)) {
res.value = LLVMBuildIntToPtr(p->builder, value.value, lb_type(m, t), "");
return res;
}
if (is_type_pointer(src) && is_type_uintptr(dst)) {
res.value = LLVMBuildPtrToInt(p->builder, value.value, lb_type(m, t), "");
return res;
}
if (is_type_uintptr(src) && is_type_proc(dst)) {
res.value = LLVMBuildIntToPtr(p->builder, value.value, lb_type(m, t), "");
return res;
}
if (is_type_proc(src) && is_type_uintptr(dst)) {
if (is_type_internally_pointer_like(src) && is_type_uintptr(dst)) {
res.value = LLVMBuildPtrToInt(p->builder, value.value, lb_type(m, t), "");
return res;
}
if (is_type_integer(src) && (is_type_pointer(dst) || is_type_cstring(dst))) {
if (is_type_integer(src) && is_type_internally_pointer_like(dst)) {
res.value = LLVMBuildIntToPtr(p->builder, value.value, lb_type(m, t), "");
return res;
} else if ((is_type_pointer(src) || is_type_cstring(src)) && is_type_integer(dst)) {
} else if (is_type_internally_pointer_like(src) && is_type_integer(dst)) {
res.value = LLVMBuildPtrToInt(p->builder, value.value, lb_type(m, t), "");
return res;
}
if (is_type_pointer(src) && is_type_pointer(dst)) {
if (is_type_internally_pointer_like(src) && is_type_internally_pointer_like(dst)) {
res.value = LLVMBuildPointerCast(p->builder, value.value, lb_type(p->module, t), "");
return res;
}
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;
}
if (lb_is_type_aggregate(src) || lb_is_type_aggregate(dst)) {
lbValue s = lb_address_from_load_or_generate_local(p, value);
lbValue d = lb_emit_transmute(p, s, alloc_type_pointer(t));
@@ -488,8 +500,10 @@ lbValue lb_emit_count_ones(lbProcedure *p, lbValue x, Type *type) {
}
lbValue lb_emit_count_zeros(lbProcedure *p, lbValue x, Type *type) {
i64 sz = 8*type_size_of(type);
lbValue size = lb_const_int(p->module, type, cast(u64)sz);
Type *elem = base_array_type(type);
i64 sz = 8*type_size_of(elem);
lbValue size = lb_const_int(p->module, elem, cast(u64)sz);
size = lb_emit_conv(p, size, type);
lbValue count = lb_emit_count_ones(p, x, type);
return lb_emit_arith(p, Token_Sub, size, count, type);
}
@@ -634,6 +648,12 @@ lbValue lb_emit_union_cast(lbProcedure *p, lbValue value, Type *type, TokenPos p
lbValue value_ = lb_address_from_load_or_generate_local(p, value);
if ((p->state_flags & StateFlag_no_type_assert) != 0 && !is_tuple) {
// just do a bit cast of the data at the front
lbValue ptr = lb_emit_conv(p, value_, alloc_type_pointer(type));
return lb_emit_load(p, ptr);
}
lbValue tag = {};
lbValue dst_tag = {};
lbValue cond = {};
@@ -674,23 +694,29 @@ lbValue lb_emit_union_cast(lbProcedure *p, lbValue value, Type *type, TokenPos p
lb_start_block(p, end_block);
if (!is_tuple) {
{
// NOTE(bill): Panic on invalid conversion
Type *dst_type = tuple->Tuple.variables[0]->type;
GB_ASSERT((p->state_flags & StateFlag_no_type_assert) == 0);
// NOTE(bill): Panic on invalid conversion
Type *dst_type = tuple->Tuple.variables[0]->type;
lbValue ok = lb_emit_load(p, lb_emit_struct_ep(p, v.addr, 1));
auto args = array_make<lbValue>(permanent_allocator(), 7);
args[0] = ok;
isize arg_count = 7;
if (build_context.disallow_rtti) {
arg_count = 4;
}
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);
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);
if (!build_context.disallow_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));
}
@@ -714,6 +740,13 @@ lbAddr lb_emit_any_cast_addr(lbProcedure *p, lbValue value, Type *type, TokenPos
}
Type *dst_type = tuple->Tuple.variables[0]->type;
if ((p->state_flags & StateFlag_no_type_assert) != 0 && !is_tuple) {
// just do a bit cast of the data at the front
lbValue ptr = lb_emit_struct_ev(p, value, 0);
ptr = lb_emit_conv(p, ptr, alloc_type_pointer(type));
return lb_addr(ptr);
}
lbAddr v = lb_add_local_generated(p, tuple, true);
lbValue dst_typeid = lb_typeid(m, dst_type);
@@ -739,18 +772,24 @@ lbAddr lb_emit_any_cast_addr(lbProcedure *p, lbValue value, Type *type, TokenPos
if (!is_tuple) {
// NOTE(bill): Panic on invalid conversion
lbValue ok = lb_emit_load(p, lb_emit_struct_ep(p, v.addr, 1));
auto args = array_make<lbValue>(permanent_allocator(), 7);
isize arg_count = 7;
if (build_context.disallow_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[4] = any_typeid;
args[5] = dst_typeid;
args[6] = lb_emit_struct_ev(p, value, 0);;
if (!build_context.disallow_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);
return lb_addr(lb_emit_struct_ep(p, v.addr, 0));
@@ -1208,7 +1247,7 @@ lbValue lb_emit_array_epi(lbProcedure *p, lbValue s, isize index) {
}
lbValue lb_emit_ptr_offset(lbProcedure *p, lbValue ptr, lbValue index) {
index = lb_correct_endianness(p, index);
index = lb_emit_conv(p, index, t_int);
LLVMValueRef indices[1] = {index.value};
lbValue res = {};
res.type = ptr.type;
@@ -1370,7 +1409,7 @@ lbValue lb_slice_elem(lbProcedure *p, lbValue slice) {
return lb_emit_struct_ev(p, slice, 0);
}
lbValue lb_slice_len(lbProcedure *p, lbValue slice) {
GB_ASSERT(is_type_slice(slice.type));
GB_ASSERT(is_type_slice(slice.type) || is_type_relative_slice(slice.type));
return lb_emit_struct_ev(p, slice, 1);
}
lbValue lb_dynamic_array_elem(lbProcedure *p, lbValue da) {
@@ -1502,7 +1541,7 @@ lbValue lb_emit_mul_add(lbProcedure *p, lbValue a, lbValue b, lbValue c, Type *t
case TargetArch_arm64:
// possible
break;
case TargetArch_386:
case TargetArch_i386:
case TargetArch_wasm32:
case TargetArch_wasm64:
is_possible = false;
@@ -1776,7 +1815,7 @@ LLVMValueRef llvm_get_inline_asm(LLVMTypeRef func_type, String const &str, Strin
return LLVMGetInlineAsm(func_type,
cast(char *)str.text, cast(size_t)str.len,
cast(char *)clobbers.text, cast(size_t)clobbers.len,
/*HasSideEffects*/true, /*IsAlignStack*/false,
has_side_effects, is_align_stack,
dialect
#if LLVM_VERSION_MAJOR >= 13
, /*CanThrow*/false
@@ -1816,3 +1855,195 @@ void lb_set_wasm_export_attributes(LLVMValueRef value, String export_name) {
LLVMSetVisibility(value, LLVMDefaultVisibility);
LLVMAddTargetDependentFunctionAttr(value, "wasm-export-name", alloc_cstring(permanent_allocator(), export_name));
}
lbAddr lb_handle_objc_find_or_register_selector(lbProcedure *p, String const &name) {
lbAddr *found = string_map_get(&p->module->objc_selectors, name);
if (found) {
return *found;
} else {
lbModule *default_module = &p->module->gen->default_module;
Entity *e = nullptr;
lbAddr default_addr = lb_add_global_generated(default_module, t_objc_SEL, {}, &e);
lbValue ptr = lb_find_value_from_entity(p->module, e);
lbAddr local_addr = lb_addr(ptr);
string_map_set(&default_module->objc_selectors, name, default_addr);
if (default_module != p->module) {
string_map_set(&p->module->objc_selectors, name, local_addr);
}
return local_addr;
}
}
lbValue lb_handle_objc_find_selector(lbProcedure *p, Ast *expr) {
ast_node(ce, CallExpr, expr);
auto tav = ce->args[0]->tav;
GB_ASSERT(tav.value.kind == ExactValue_String);
String name = tav.value.value_string;
return lb_addr_load(p, lb_handle_objc_find_or_register_selector(p, name));
}
lbValue lb_handle_objc_register_selector(lbProcedure *p, Ast *expr) {
ast_node(ce, CallExpr, expr);
lbModule *m = p->module;
auto tav = ce->args[0]->tav;
GB_ASSERT(tav.value.kind == ExactValue_String);
String name = tav.value.value_string;
lbAddr dst = lb_handle_objc_find_or_register_selector(p, name);
auto args = array_make<lbValue>(permanent_allocator(), 1);
args[0] = lb_const_value(m, t_cstring, exact_value_string(name));
lbValue ptr = lb_emit_runtime_call(p, "sel_registerName", args);
lb_addr_store(p, dst, ptr);
return lb_addr_load(p, dst);
}
lbAddr lb_handle_objc_find_or_register_class(lbProcedure *p, String const &name) {
lbAddr *found = string_map_get(&p->module->objc_classes, name);
if (found) {
return *found;
} else {
lbModule *default_module = &p->module->gen->default_module;
Entity *e = nullptr;
lbAddr default_addr = lb_add_global_generated(default_module, t_objc_SEL, {}, &e);
lbValue ptr = lb_find_value_from_entity(p->module, e);
lbAddr local_addr = lb_addr(ptr);
string_map_set(&default_module->objc_classes, name, default_addr);
if (default_module != p->module) {
string_map_set(&p->module->objc_classes, name, local_addr);
}
return local_addr;
}
}
lbValue lb_handle_objc_find_class(lbProcedure *p, Ast *expr) {
ast_node(ce, CallExpr, expr);
auto tav = ce->args[0]->tav;
GB_ASSERT(tav.value.kind == ExactValue_String);
String name = tav.value.value_string;
return lb_addr_load(p, lb_handle_objc_find_or_register_class(p, name));
}
lbValue lb_handle_objc_register_class(lbProcedure *p, Ast *expr) {
ast_node(ce, CallExpr, expr);
lbModule *m = p->module;
auto tav = ce->args[0]->tav;
GB_ASSERT(tav.value.kind == ExactValue_String);
String name = tav.value.value_string;
lbAddr dst = lb_handle_objc_find_or_register_class(p, name);
auto args = array_make<lbValue>(permanent_allocator(), 3);
args[0] = lb_const_nil(m, t_objc_Class);
args[1] = lb_const_nil(m, t_objc_Class);
args[2] = lb_const_int(m, t_uint, 0);
lbValue ptr = lb_emit_runtime_call(p, "objc_allocateClassPair", args);
lb_addr_store(p, dst, ptr);
return lb_addr_load(p, dst);
}
lbValue lb_handle_objc_id(lbProcedure *p, Ast *expr) {
TypeAndValue const &tav = type_and_value_of_expr(expr);
if (tav.mode == Addressing_Type) {
Type *type = tav.type;
GB_ASSERT_MSG(type->kind == Type_Named, "%s", type_to_string(type));
Entity *e = type->Named.type_name;
GB_ASSERT(e->kind == Entity_TypeName);
String name = e->TypeName.objc_class_name;
lbAddr *found = string_map_get(&p->module->objc_classes, name);
if (found) {
return lb_addr_load(p, *found);
} else {
lbModule *default_module = &p->module->gen->default_module;
Entity *e = nullptr;
lbAddr default_addr = lb_add_global_generated(default_module, t_objc_Class, {}, &e);
lbValue ptr = lb_find_value_from_entity(p->module, e);
lbAddr local_addr = lb_addr(ptr);
string_map_set(&default_module->objc_classes, name, default_addr);
if (default_module != p->module) {
string_map_set(&p->module->objc_classes, name, local_addr);
}
return lb_addr_load(p, local_addr);
}
}
return lb_build_expr(p, expr);
}
lbValue lb_handle_objc_send(lbProcedure *p, Ast *expr) {
ast_node(ce, CallExpr, expr);
lbModule *m = p->module;
CheckerInfo *info = m->info;
ObjcMsgData data = map_must_get(&info->objc_msgSend_types, expr);
GB_ASSERT(data.proc_type != nullptr);
GB_ASSERT(ce->args.count >= 3);
auto args = array_make<lbValue>(permanent_allocator(), 0, ce->args.count-1);
lbValue id = lb_handle_objc_id(p, ce->args[1]);
Ast *sel_expr = ce->args[2];
GB_ASSERT(sel_expr->tav.value.kind == ExactValue_String);
lbValue sel = lb_addr_load(p, lb_handle_objc_find_or_register_selector(p, sel_expr->tav.value.value_string));
array_add(&args, id);
array_add(&args, sel);
for (isize i = 3; i < ce->args.count; i++) {
lbValue arg = lb_build_expr(p, ce->args[i]);
array_add(&args, arg);
}
lbValue the_proc = {};
switch (data.kind) {
default:
GB_PANIC("unhandled ObjcMsgKind %u", data.kind);
break;
case ObjcMsg_normal: the_proc = lb_lookup_runtime_procedure(m, str_lit("objc_msgSend")); break;
case ObjcMsg_fpret: the_proc = lb_lookup_runtime_procedure(m, str_lit("objc_msgSend_fpret")); break;
case ObjcMsg_fp2ret: the_proc = lb_lookup_runtime_procedure(m, str_lit("objc_msgSend_fp2ret")); break;
case ObjcMsg_stret: the_proc = lb_lookup_runtime_procedure(m, str_lit("objc_msgSend_stret")); break;
}
the_proc = lb_emit_conv(p, the_proc, data.proc_type);
return lb_emit_call(p, the_proc, args);
}
LLVMAtomicOrdering llvm_atomic_ordering_from_odin(ExactValue const &value) {
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_acquire: return LLVMAtomicOrderingAcquire;
case OdinAtomicMemoryOrder_release: return LLVMAtomicOrderingRelease;
case OdinAtomicMemoryOrder_acq_rel: return LLVMAtomicOrderingAcquireRelease;
case OdinAtomicMemoryOrder_seq_cst: return LLVMAtomicOrderingSequentiallyConsistent;
}
GB_PANIC("Unknown atomic ordering");
return LLVMAtomicOrderingSequentiallyConsistent;
}
LLVMAtomicOrdering llvm_atomic_ordering_from_odin(Ast *expr) {
ExactValue value = type_and_value_of_expr(expr).value;
return llvm_atomic_ordering_from_odin(value);
}
+670 -472
View File
File diff suppressed because it is too large Load Diff
+588 -427
View File
File diff suppressed because it is too large Load Diff
+28
View File
@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
<Type Name="String">
<DisplayString>{text,[len]s8}</DisplayString>
<StringView>text,[len]s8</StringView>
</Type>
<Type Name="Array&lt;*&gt;">
<DisplayString>{{ size={count} capacity={capacity} }}</DisplayString>
<Expand>
<ArrayItems>
<Size>count</Size>
<ValuePointer>data</ValuePointer>
</ArrayItems>
</Expand>
</Type>
<Type Name="Slice&lt;*&gt;">
<DisplayString>{{ size={count} }}</DisplayString>
<Expand>
<ArrayItems>
<Size>count</Size>
<ValuePointer>data</ValuePointer>
</ArrayItems>
</Expand>
</Type>
<Type Name="lbProcedure">
<DisplayString>Procedure {name}</DisplayString>
</Type>
</AutoVisualizer>
+409 -221
View File
File diff suppressed because it is too large Load Diff
+90 -30
View File
@@ -46,6 +46,7 @@ enum ParseFileError {
ParseFile_InvalidToken,
ParseFile_GeneralError,
ParseFile_FileTooLarge,
ParseFile_DirectoryAlreadyExists,
ParseFile_Count,
};
@@ -78,9 +79,11 @@ struct ImportedFile {
};
enum AstFileFlag : u32 {
AstFile_IsPrivate = 1<<0,
AstFile_IsTest = 1<<1,
AstFile_IsLazy = 1<<2,
AstFile_IsPrivatePkg = 1<<0,
AstFile_IsPrivateFile = 1<<1,
AstFile_IsTest = 1<<3,
AstFile_IsLazy = 1<<4,
};
enum AstDelayQueueKind {
@@ -95,8 +98,6 @@ struct AstFile {
AstPackage * pkg;
Scope * scope;
Arena arena;
Ast * pkg_decl;
String fullpath;
Tokenizer tokenizer;
@@ -226,6 +227,8 @@ enum ProcInlining {
enum ProcTag {
ProcTag_bounds_check = 1<<0,
ProcTag_no_bounds_check = 1<<1,
ProcTag_type_assert = 1<<2,
ProcTag_no_type_assert = 1<<3,
ProcTag_require_results = 1<<4,
ProcTag_optional_ok = 1<<5,
@@ -245,24 +248,46 @@ enum ProcCallingConvention : i32 {
ProcCC_InlineAsm = 8,
ProcCC_Win64 = 9,
ProcCC_SysV = 10,
ProcCC_MAX,
ProcCC_ForeignBlockDefault = -1,
};
char const *proc_calling_convention_strings[ProcCC_MAX] = {
"",
"odin",
"contextless",
"cdecl",
"stdcall",
"fastcall",
"none",
"naked",
"inlineasm",
"win64",
"sysv",
};
ProcCallingConvention default_calling_convention(void) {
return ProcCC_Odin;
}
enum StateFlag : u16 {
enum StateFlag : u8 {
StateFlag_bounds_check = 1<<0,
StateFlag_no_bounds_check = 1<<1,
StateFlag_type_assert = 1<<2,
StateFlag_no_type_assert = 1<<3,
StateFlag_BeenHandled = 1<<15,
StateFlag_SelectorCallExpr = 1<<6,
StateFlag_BeenHandled = 1<<7,
};
enum ViralStateFlag : u16 {
enum ViralStateFlag : u8 {
ViralStateFlag_ContainsDeferredProcedure = 1<<0,
};
@@ -276,14 +301,16 @@ enum FieldFlag : u32 {
FieldFlag_auto_cast = 1<<4,
FieldFlag_const = 1<<5,
FieldFlag_any_int = 1<<6,
FieldFlag_subtype = 1<<7,
FieldFlag_by_ptr = 1<<8,
// Internal use by the parser only
FieldFlag_Tags = 1<<10,
FieldFlag_Results = 1<<16,
// Parameter List Restrictions
FieldFlag_Signature = FieldFlag_ellipsis|FieldFlag_using|FieldFlag_no_alias|FieldFlag_c_vararg|FieldFlag_auto_cast|FieldFlag_const|FieldFlag_any_int,
FieldFlag_Struct = FieldFlag_using|FieldFlag_Tags,
FieldFlag_Signature = FieldFlag_ellipsis|FieldFlag_using|FieldFlag_no_alias|FieldFlag_c_vararg|FieldFlag_auto_cast|FieldFlag_const|FieldFlag_any_int|FieldFlag_by_ptr,
FieldFlag_Struct = FieldFlag_using|FieldFlag_subtype|FieldFlag_Tags,
};
enum StmtAllowFlag {
@@ -306,6 +333,13 @@ char const *inline_asm_dialect_strings[InlineAsmDialect_COUNT] = {
"intel",
};
enum UnionTypeKind : u8 {
UnionType_Normal = 0,
UnionType_maybe = 1, // removed
UnionType_no_nil = 2,
UnionType_shared_nil = 3,
};
#define AST_KINDS \
AST_KIND(Ident, "identifier", struct { \
Token token; \
@@ -344,6 +378,7 @@ char const *inline_asm_dialect_strings[InlineAsmDialect_COUNT] = {
Slice<Ast *> elems; \
Token open, close; \
i64 max_count; \
Ast *tag; \
}) \
AST_KIND(_ExprBegin, "", bool) \
AST_KIND(BadExpr, "bad expression", struct { Token begin, end; }) \
@@ -379,10 +414,15 @@ AST_KIND(_ExprBegin, "", bool) \
Token ellipsis; \
ProcInlining inlining; \
bool optional_ok_one; \
i32 builtin_id; \
void *sce_temp_data; \
bool was_selector; \
}) \
AST_KIND(FieldValue, "field value", struct { Token eq; Ast *field, *value; }) \
AST_KIND(EnumFieldValue, "enum field value", struct { \
Ast *name; \
Ast *value; \
CommentGroup *docs; \
CommentGroup *comment; \
}) \
AST_KIND(TernaryIfExpr, "ternary if expression", struct { Ast *x, *cond, *y; }) \
AST_KIND(TernaryWhenExpr, "ternary when expression", struct { Ast *x, *cond, *y; }) \
AST_KIND(OrElseExpr, "or_else expression", struct { Ast *x; Token token; Ast *y; }) \
@@ -424,11 +464,13 @@ AST_KIND(_StmtBegin, "", bool) \
}) \
AST_KIND(_ComplexStmtBegin, "", bool) \
AST_KIND(BlockStmt, "block statement", struct { \
Scope *scope; \
Slice<Ast *> stmts; \
Ast *label; \
Token open, close; \
}) \
AST_KIND(IfStmt, "if statement", struct { \
Scope *scope; \
Token token; \
Ast *label; \
Ast * init; \
@@ -449,6 +491,7 @@ AST_KIND(_ComplexStmtBegin, "", bool) \
Slice<Ast *> results; \
}) \
AST_KIND(ForStmt, "for statement", struct { \
Scope *scope; \
Token token; \
Ast *label; \
Ast *init; \
@@ -457,6 +500,7 @@ AST_KIND(_ComplexStmtBegin, "", bool) \
Ast *body; \
}) \
AST_KIND(RangeStmt, "range statement", struct { \
Scope *scope; \
Token token; \
Ast *label; \
Slice<Ast *> vals; \
@@ -465,6 +509,7 @@ AST_KIND(_ComplexStmtBegin, "", bool) \
Ast *body; \
}) \
AST_KIND(UnrollRangeStmt, "#unroll range statement", struct { \
Scope *scope; \
Token unroll_token; \
Token for_token; \
Ast *val0; \
@@ -474,12 +519,14 @@ AST_KIND(_ComplexStmtBegin, "", bool) \
Ast *body; \
}) \
AST_KIND(CaseClause, "case clause", struct { \
Scope *scope; \
Token token; \
Slice<Ast *> list; \
Slice<Ast *> stmts; \
Entity *implicit_entity; \
}) \
AST_KIND(SwitchStmt, "switch statement", struct { \
Scope *scope; \
Token token; \
Ast *label; \
Ast *init; \
@@ -488,6 +535,7 @@ AST_KIND(_ComplexStmtBegin, "", bool) \
bool partial; \
}) \
AST_KIND(TypeSwitchStmt, "type switch statement", struct { \
Scope *scope; \
Token token; \
Ast *label; \
Ast *tag; \
@@ -539,7 +587,6 @@ AST_KIND(_DeclBegin, "", bool) \
Token import_name; \
CommentGroup *docs; \
CommentGroup *comment; \
bool is_using; \
}) \
AST_KIND(ForeignImportDecl, "foreign import declaration", struct { \
Token token; \
@@ -589,6 +636,7 @@ AST_KIND(_TypeBegin, "", bool) \
Ast * specialization; \
}) \
AST_KIND(ProcType, "procedure type", struct { \
Scope *scope; \
Token token; \
Ast *params; \
Ast *results; \
@@ -621,6 +669,7 @@ AST_KIND(_TypeBegin, "", bool) \
Ast *tag; \
}) \
AST_KIND(StructType, "struct type", struct { \
Scope *scope; \
Token token; \
Slice<Ast *> fields; \
isize field_count; \
@@ -632,16 +681,17 @@ AST_KIND(_TypeBegin, "", bool) \
bool is_raw_union; \
}) \
AST_KIND(UnionType, "union type", struct { \
Scope *scope; \
Token token; \
Slice<Ast *> variants; \
Ast *polymorphic_params; \
Ast * align; \
bool maybe; \
bool no_nil; \
UnionTypeKind kind; \
Token where_token; \
Slice<Ast *> where_clauses; \
}) \
AST_KIND(EnumType, "enum type", struct { \
Scope *scope; \
Token token; \
Ast * base_type; \
Slice<Ast *> fields; /* FieldValue */ \
@@ -666,7 +716,7 @@ AST_KIND(_TypeBegin, "", bool) \
}) \
AST_KIND(_TypeEnd, "", bool)
enum AstKind {
enum AstKind : u16 {
Ast_Invalid,
#define AST_KIND(_kind_name_, ...) GB_JOIN2(Ast_, _kind_name_),
AST_KINDS
@@ -695,21 +745,19 @@ isize const ast_variant_sizes[] = {
};
struct AstCommonStuff {
AstKind kind;
u16 state_flags;
u16 viral_state_flags;
AstFile * file;
Scope * scope;
TypeAndValue tav; // TODO(bill): Make this a pointer to minimize pointer size
AstKind kind; // u16
u8 state_flags;
u8 viral_state_flags;
i32 file_id;
TypeAndValue tav; // TODO(bill): Make this a pointer to minimize 'Ast' size
};
struct Ast {
AstKind kind;
u16 state_flags;
u16 viral_state_flags;
AstFile * file;
Scope * scope;
TypeAndValue tav; // TODO(bill): Make this a pointer to minimize pointer size
AstKind kind; // u16
u8 state_flags;
u8 viral_state_flags;
i32 file_id;
TypeAndValue tav; // TODO(bill): Make this a pointer to minimize 'Ast' size
// IMPORTANT NOTE(bill): This must be at the end since the AST is allocated to be size of the variant
union {
@@ -717,6 +765,17 @@ struct Ast {
AST_KINDS
#undef AST_KIND
};
// NOTE(bill): I know I dislike methods but this is hopefully a temporary thing
// for refactoring purposes
gb_inline AstFile *file() const {
// NOTE(bill): This doesn't need to call get_ast_file_from_id which
return global_files[this->file_id];
}
gb_inline AstFile *thread_safe_file() const {
return thread_safe_get_ast_file_from_id(this->file_id);
}
};
@@ -748,13 +807,14 @@ gb_inline bool is_ast_when_stmt(Ast *node) {
return node->kind == Ast_WhenStmt;
}
gb_global gb_thread_local Arena global_ast_arena = {};
gb_global gb_thread_local Arena global_thread_local_ast_arena = {};
gbAllocator ast_allocator(AstFile *f) {
Arena *arena = f ? &f->arena : &global_ast_arena;
Arena *arena = &global_thread_local_ast_arena;
return arena_allocator(arena);
}
Ast *alloc_ast_node(AstFile *f, AstKind kind);
gbString expr_to_string(Ast *expression);
bool allow_field_separator(AstFile *f);
+6
View File
@@ -39,6 +39,7 @@ Token ast_token(Ast *node) {
case Ast_SliceExpr: return node->SliceExpr.open;
case Ast_Ellipsis: return node->Ellipsis.token;
case Ast_FieldValue: return node->FieldValue.eq;
case Ast_EnumFieldValue: return ast_token(node->EnumFieldValue.name);
case Ast_DerefExpr: return node->DerefExpr.op;
case Ast_TernaryIfExpr: return ast_token(node->TernaryIfExpr.x);
case Ast_TernaryWhenExpr: return ast_token(node->TernaryWhenExpr.x);
@@ -178,6 +179,11 @@ Token ast_end_token(Ast *node) {
}
return node->Ellipsis.token;
case Ast_FieldValue: return ast_end_token(node->FieldValue.value);
case Ast_EnumFieldValue:
if (node->EnumFieldValue.value) {
return ast_end_token(node->EnumFieldValue.value);
}
return ast_end_token(node->EnumFieldValue.name);
case Ast_DerefExpr: return node->DerefExpr.op;
case Ast_TernaryIfExpr: return ast_end_token(node->TernaryIfExpr.y);
case Ast_TernaryWhenExpr: return ast_end_token(node->TernaryWhenExpr.y);
+394
View File
@@ -0,0 +1,394 @@
/*
Path handling utilities.
*/
String remove_extension_from_path(String const &s) {
for (isize i = s.len-1; i >= 0; i--) {
if (s[i] == '.') {
return substring(s, 0, i);
}
}
return s;
}
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);
}
bool path_is_directory(String path);
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)
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
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
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.
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`.
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.
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;
}
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.
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("");
}
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,
};
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)
ReadDirectoryError read_directory(String path, Array<FileInfo> *fi) {
GB_ASSERT(fi != nullptr);
gbAllocator a = heap_allocator();
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(a, path);
defer (gb_free(a, c_str));
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;
}
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;
i64 size = cast(i64)file_data.nFileSizeLow;
size |= (cast(i64)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 = 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>
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
+7 -5
View File
@@ -28,11 +28,13 @@ struct PtrMap {
u32 ptr_map_hash_key(uintptr key) {
#if defined(GB_ARCH_64_BIT)
u64 x = (u64)key;
u8 count = (u8)(x >> 59);
x ^= x >> (5 + count);
x *= 12605985483714917081ull;
return (u32)(x ^ (x >> 43));
key = (~key) + (key << 21);
key = key ^ (key >> 24);
key = (key + (key << 3)) + (key << 8);
key = key ^ (key >> 14);
key = (key + (key << 2)) + (key << 4);
key = key ^ (key << 28);
return cast(u32)key;
#elif defined(GB_ARCH_32_BIT)
u32 state = ((u32)key) * 747796405u + 2891336453u;
u32 word = ((state >> ((state >> 28u) + 4u)) ^ state) * 277803737u;
+13 -2
View File
@@ -13,7 +13,7 @@ struct PtrSet {
template <typename T> void ptr_set_init (PtrSet<T> *s, gbAllocator a, isize capacity = 16);
template <typename T> void ptr_set_destroy(PtrSet<T> *s);
template <typename T> T ptr_set_add (PtrSet<T> *s, T ptr);
template <typename T> bool ptr_set_update (PtrSet<T> *s, T ptr); // returns true if it previously existsed
template <typename T> bool ptr_set_update (PtrSet<T> *s, T ptr); // returns true if it previously existed
template <typename T> bool ptr_set_exists (PtrSet<T> *s, T ptr);
template <typename T> void ptr_set_remove (PtrSet<T> *s, T ptr);
template <typename T> void ptr_set_clear (PtrSet<T> *s);
@@ -24,7 +24,9 @@ template <typename T> void ptr_set_reserve(PtrSet<T> *h, isize cap);
template <typename T>
void ptr_set_init(PtrSet<T> *s, gbAllocator a, isize capacity) {
capacity = next_pow2_isize(gb_max(16, capacity));
if (capacity != 0) {
capacity = next_pow2_isize(gb_max(16, capacity));
}
slice_init(&s->hashes, a, capacity);
array_init(&s->entries, a, 0, capacity);
@@ -136,6 +138,15 @@ gb_inline bool ptr_set_exists(PtrSet<T> *s, T ptr) {
return index != MAP_SENTINEL;
}
template <typename T>
gb_inline isize ptr_entry_index(PtrSet<T> *s, T ptr) {
isize index = ptr_set__find(s, ptr).entry_index;
if (index != MAP_SENTINEL) {
return index;
}
return -1;
}
// Returns true if it already exists
template <typename T>
T ptr_set_add(PtrSet<T> *s, T ptr) {
+27 -18
View File
@@ -71,6 +71,29 @@ void mpmc_destroy(MPMCQueue<T> *q) {
}
template <typename T>
bool mpmc_internal_grow(MPMCQueue<T> *q) {
mutex_lock(&q->mutex);
i32 old_size = q->mask+1;
i32 new_size = old_size*2;
resize_array_raw(&q->nodes, q->allocator, old_size, new_size);
if (q->nodes == nullptr) {
GB_PANIC("Unable to resize enqueue: %td -> %td", old_size, new_size);
mutex_unlock(&q->mutex);
return false;
}
resize_array_raw(&q->indices, q->allocator, old_size, new_size);
if (q->indices == nullptr) {
GB_PANIC("Unable to resize enqueue: %td -> %td", old_size, new_size);
mutex_unlock(&q->mutex);
return false;
}
mpmc_internal_init_indices(q->indices, old_size, new_size);
q->mask = new_size-1;
mutex_unlock(&q->mutex);
return true;
}
template <typename T>
i32 mpmc_enqueue(MPMCQueue<T> *q, T const &data) {
GB_ASSERT(q->mask != 0);
@@ -78,8 +101,9 @@ i32 mpmc_enqueue(MPMCQueue<T> *q, T const &data) {
i32 head_idx = q->head_idx.load(std::memory_order_relaxed);
for (;;) {
auto node = &q->nodes[head_idx & q->mask];
auto node_idx_ptr = &q->indices[head_idx & q->mask];
i32 index = head_idx & q->mask;
auto node = &q->nodes[index];
auto node_idx_ptr = &q->indices[index];
i32 node_idx = node_idx_ptr->load(std::memory_order_acquire);
i32 diff = node_idx - head_idx;
@@ -91,24 +115,9 @@ i32 mpmc_enqueue(MPMCQueue<T> *q, T const &data) {
return q->count.fetch_add(1, std::memory_order_release);
}
} else if (diff < 0) {
mutex_lock(&q->mutex);
i32 old_size = q->mask+1;
i32 new_size = old_size*2;
resize_array_raw(&q->nodes, q->allocator, old_size, new_size);
if (q->nodes == nullptr) {
GB_PANIC("Unable to resize enqueue: %td -> %td", old_size, new_size);
mutex_unlock(&q->mutex);
if (!mpmc_internal_grow(q)) {
return -1;
}
resize_array_raw(&q->indices, q->allocator, old_size, new_size);
if (q->indices == nullptr) {
GB_PANIC("Unable to resize enqueue: %td -> %td", old_size, new_size);
mutex_unlock(&q->mutex);
return -1;
}
mpmc_internal_init_indices(q->indices, old_size, new_size);
q->mask = new_size-1;
mutex_unlock(&q->mutex);
} else {
head_idx = q->head_idx.load(std::memory_order_relaxed);
}
+52 -45
View File
@@ -10,10 +10,6 @@ struct String {
u8 * text;
isize len;
// u8 &operator[](isize i) {
// GB_ASSERT_MSG(0 <= i && i < len, "[%td]", i);
// return text[i];
// }
u8 const &operator[](isize i) const {
GB_ASSERT_MSG(0 <= i && i < len, "[%td]", i);
return text[i];
@@ -33,10 +29,6 @@ struct String {
struct String16 {
wchar_t *text;
isize len;
wchar_t &operator[](isize i) {
GB_ASSERT_MSG(0 <= i && i < len, "[%td]", i);
return text[i];
}
wchar_t const &operator[](isize i) const {
GB_ASSERT_MSG(0 <= i && i < len, "[%td]", i);
return text[i];
@@ -165,6 +157,15 @@ int string_compare(String const &x, String const &y) {
return 0;
}
isize string_index_byte(String const &s, u8 x) {
for (isize i = 0; i < s.len; i++) {
if (s.text[i] == x) {
return i;
}
}
return -1;
}
GB_COMPARE_PROC(string_cmp_proc) {
String x = *(String *)a;
String y = *(String *)b;
@@ -195,8 +196,6 @@ template <isize N> bool operator > (String const &a, char const (&b)[N]) { retu
template <isize N> bool operator <= (String const &a, char const (&b)[N]) { return str_le(a, make_string(cast(u8 *)b, N-1)); }
template <isize N> bool operator >= (String const &a, char const (&b)[N]) { return str_ge(a, make_string(cast(u8 *)b, N-1)); }
gb_inline bool string_starts_with(String const &s, String const &prefix) {
if (prefix.len > s.len) {
return false;
@@ -230,6 +229,16 @@ gb_inline bool string_ends_with(String const &s, u8 suffix) {
return s[s.len-1] == suffix;
}
gb_inline String string_trim_starts_with(String const &s, String const &prefix) {
if (string_starts_with(s, prefix)) {
return substring(s, prefix.len, s.len);
}
return s;
}
gb_inline isize string_extension_position(String const &str) {
isize dot_pos = -1;
isize i = str.len;
@@ -245,15 +254,14 @@ gb_inline isize string_extension_position(String const &str) {
return dot_pos;
}
String path_extension(String const &str) {
String path_extension(String const &str, bool include_dot = true) {
isize pos = string_extension_position(str);
if (pos < 0) {
return make_string(nullptr, 0);
}
return substring(str, pos, str.len);
return substring(str, include_dot ? pos : pos + 1, str.len);
}
String string_trim_whitespace(String str) {
while (str.len > 0 && rune_is_whitespace(str[str.len-1])) {
str.len--;
@@ -299,38 +307,6 @@ String filename_from_path(String s) {
return make_string(nullptr, 0);
}
String remove_extension_from_path(String const &s) {
for (isize i = s.len-1; i >= 0; i--) {
if (s[i] == '.') {
return substring(s, 0, i);
}
}
return s;
}
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);
}
String directory_from_path(String const &s) {
isize i = s.len-1;
for (; i >= 0; i--) {
if (s[i] == '/' ||
s[i] == '\\') {
break;
}
}
return substring(s, 0, i);
}
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);
@@ -773,3 +749,34 @@ i32 unquote_string(gbAllocator a, String *s_, u8 quote=0, bool has_carriage_retu
return 2;
}
bool string_is_valid_identifier(String str) {
if (str.len <= 0) return false;
isize rune_count = 0;
isize w = 0;
isize offset = 0;
while (offset < str.len) {
Rune r = 0;
w = utf8_decode(str.text, str.len, &r);
if (r == GB_RUNE_INVALID) {
return false;
}
if (rune_count == 0) {
if (!rune_is_letter(r)) {
return false;
}
} else {
if (!rune_is_letter(r) && !rune_is_digit(r)) {
return false;
}
}
rune_count += 1;
offset += w;
}
return true;
}
+29
View File
@@ -13,6 +13,7 @@ struct StringSet {
void string_set_init (StringSet *s, gbAllocator a, isize capacity = 16);
void string_set_destroy(StringSet *s);
void string_set_add (StringSet *s, String const &str);
bool string_set_update (StringSet *s, String const &str); // returns true if it previously existed
bool string_set_exists (StringSet *s, String const &str);
void string_set_remove (StringSet *s, String const &str);
void string_set_clear (StringSet *s);
@@ -149,6 +150,34 @@ void string_set_add(StringSet *s, String const &str) {
}
}
bool string_set_update(StringSet *s, String const &str) {
bool exists = false;
MapIndex index;
MapFindResult fr;
StringHashKey key = string_hash_string(str);
if (s->hashes.count == 0) {
string_set_grow(s);
}
fr = string_set__find(s, key);
if (fr.entry_index != MAP_SENTINEL) {
index = fr.entry_index;
exists = true;
} else {
index = string_set__add_entry(s, key);
if (fr.entry_prev != MAP_SENTINEL) {
s->entries[fr.entry_prev].next = index;
} else {
s->hashes[fr.hash_index] = index;
}
}
s->entries[index].value = str;
if (string_set__full(s)) {
string_set_grow(s);
}
return exists;
}
void string_set__erase(StringSet *s, MapFindResult fr) {
MapFindResult last;
+1 -1
View File
@@ -95,7 +95,7 @@ bool thread_pool_add_task(ThreadPool *pool, WorkerTaskProc *proc, void *data) {
thread_pool_queue_push(pool, task);
GB_ASSERT(pool->ready >= 0);
pool->ready++;
condition_signal(&pool->task_cond);
condition_broadcast(&pool->task_cond);
mutex_unlock(&pool->mutex);
return true;
}
+39 -1
View File
@@ -68,6 +68,40 @@ void yield_thread(void);
void yield_process(void);
struct MutexGuard {
MutexGuard() = delete;
MutexGuard(MutexGuard const &) = delete;
MutexGuard(BlockingMutex *bm) : bm{bm} {
mutex_lock(this->bm);
}
MutexGuard(RecursiveMutex *rm) : rm{rm} {
mutex_lock(this->rm);
}
MutexGuard(BlockingMutex &bm) : bm{&bm} {
mutex_lock(this->bm);
}
MutexGuard(RecursiveMutex &rm) : rm{&rm} {
mutex_lock(this->rm);
}
~MutexGuard() {
if (this->bm) {
mutex_unlock(this->bm);
} else if (this->rm) {
mutex_unlock(this->rm);
}
}
operator bool() const { return true; }
BlockingMutex *bm;
RecursiveMutex *rm;
};
#define MUTEX_GUARD_BLOCK(m) if (MutexGuard GB_DEFER_3(_mutex_guard_){m})
#define MUTEX_GUARD(m) MutexGuard GB_DEFER_3(_mutex_guard_){m}
#if defined(GB_SYSTEM_WINDOWS)
struct BlockingMutex {
SRWLOCK srwlock;
@@ -296,6 +330,8 @@ u32 thread_current_id(void) {
__asm__("mov %%gs:0x08,%0" : "=r"(thread_id));
#elif defined(GB_ARCH_64_BIT) && defined(GB_CPU_X86)
__asm__("mov %%fs:0x10,%0" : "=r"(thread_id));
#elif defined(GB_SYSTEM_LINUX)
thread_id = gettid();
#else
#error Unsupported architecture for thread_current_id()
#endif
@@ -315,6 +351,8 @@ gb_inline void yield_thread(void) {
#endif
#elif defined(GB_CPU_X86)
_mm_pause();
#elif defined(GB_CPU_ARM)
__asm__ volatile ("yield" : : : "memory");
#else
#error Unknown architecture
#endif
@@ -448,7 +486,7 @@ void thread_set_name(Thread *t, char const *name) {
#elif defined(GB_SYSTEM_OSX)
// TODO(bill): Test if this works
pthread_setname_np(name);
#elif defined(GB_SYSTEM_FREEBSD)
#elif defined(GB_SYSTEM_FREEBSD) || defined(GB_SYSTEM_OPENBSD)
pthread_set_name_np(t->posix_handle, name);
#else
// TODO(bill): Test if this works
+3 -422
View File
@@ -192,7 +192,7 @@ gb_global Array<String> global_file_path_strings; // index is file id
gb_global Array<struct AstFile *> global_files; // index is file id
String get_file_path_string(i32 index);
struct AstFile *get_ast_file_from_id(i32 index);
struct AstFile *thread_safe_get_ast_file_from_id(i32 index);
struct TokenPos {
i32 file_id;
@@ -201,14 +201,6 @@ struct TokenPos {
i32 column; // starting at 1
};
// temporary
char *token_pos_to_string(TokenPos const &pos) {
gbString s = gb_string_make_reserve(temporary_allocator(), 128);
String file = get_file_path_string(pos.file_id);
s = gb_string_append_fmt(s, "%.*s(%d:%d)", LIT(file), pos.line, pos.column);
return s;
}
i32 token_pos_cmp(TokenPos const &a, TokenPos const &b) {
if (a.offset != b.offset) {
return (a.offset < b.offset) ? -1 : +1;
@@ -264,419 +256,6 @@ bool token_is_newline(Token const &tok) {
return tok.kind == Token_Semicolon && tok.string == "\n";
}
struct ErrorCollector {
TokenPos prev;
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;
Array<u8> error_buffer;
Array<String> errors;
};
gb_global ErrorCollector global_error_collector;
#define MAX_ERROR_COLLECTOR_COUNT (36)
bool any_errors(void) {
return global_error_collector.count.load() != 0;
}
void init_global_error_collector(void) {
mutex_init(&global_error_collector.mutex);
mutex_init(&global_error_collector.block_mutex);
mutex_init(&global_error_collector.error_out_mutex);
mutex_init(&global_error_collector.string_mutex);
array_init(&global_error_collector.errors, heap_allocator());
array_init(&global_error_collector.error_buffer, heap_allocator());
array_init(&global_file_path_strings, heap_allocator(), 4096);
array_init(&global_files, heap_allocator(), 4096);
}
bool set_file_path_string(i32 index, String const &path) {
bool ok = false;
GB_ASSERT(index >= 0);
mutex_lock(&global_error_collector.string_mutex);
if (index >= global_file_path_strings.count) {
array_resize(&global_file_path_strings, index);
}
String prev = global_file_path_strings[index];
if (prev.len == 0) {
global_file_path_strings[index] = path;
ok = true;
}
mutex_unlock(&global_error_collector.string_mutex);
return ok;
}
bool set_ast_file_from_id(i32 index, AstFile *file) {
bool ok = false;
GB_ASSERT(index >= 0);
mutex_lock(&global_error_collector.string_mutex);
if (index >= global_files.count) {
array_resize(&global_files, index);
}
AstFile *prev = global_files[index];
if (prev == nullptr) {
global_files[index] = file;
ok = true;
}
mutex_unlock(&global_error_collector.string_mutex);
return ok;
}
String get_file_path_string(i32 index) {
GB_ASSERT(index >= 0);
mutex_lock(&global_error_collector.string_mutex);
String path = {};
if (index < global_file_path_strings.count) {
path = global_file_path_strings[index];
}
mutex_unlock(&global_error_collector.string_mutex);
return path;
}
AstFile *get_ast_file_from_id(i32 index) {
GB_ASSERT(index >= 0);
mutex_lock(&global_error_collector.string_mutex);
AstFile *file = nullptr;
if (index < global_files.count) {
file = global_files[index];
}
mutex_unlock(&global_error_collector.string_mutex);
return file;
}
void begin_error_block(void) {
mutex_lock(&global_error_collector.block_mutex);
global_error_collector.in_block.store(true);
}
void end_error_block(void) {
if (global_error_collector.error_buffer.count > 0) {
isize n = global_error_collector.error_buffer.count;
u8 *text = gb_alloc_array(permanent_allocator(), u8, n+1);
gb_memmove(text, global_error_collector.error_buffer.data, n);
text[n] = 0;
String s = {text, n};
array_add(&global_error_collector.errors, s);
global_error_collector.error_buffer.count = 0;
}
global_error_collector.in_block.store(false);
mutex_unlock(&global_error_collector.block_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);
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) {
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;
} 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);
}
ErrorOutProc *error_out_va = default_error_out_va;
// NOTE: defined in build_settings.cpp
bool global_warnings_as_errors(void);
bool global_ignore_warnings(void);
bool show_error_line(void);
gbString get_file_line_as_string(TokenPos const &pos, i32 *offset);
void error_out(char const *fmt, ...) {
va_list va;
va_start(va, fmt);
error_out_va(fmt, va);
va_end(va);
}
bool show_error_on_line(TokenPos const &pos, TokenPos end) {
if (!show_error_line()) {
return false;
}
i32 offset = 0;
gbString the_line = get_file_line_as_string(pos, &offset);
defer (gb_string_free(the_line));
if (the_line != nullptr) {
String line = make_string(cast(u8 const *)the_line, gb_string_length(the_line));
// TODO(bill): This assumes ASCII
enum {
MAX_LINE_LENGTH = 76,
MAX_TAB_WIDTH = 8,
ELLIPSIS_PADDING = 8
};
error_out("\n\t");
if (line.len+MAX_TAB_WIDTH+ELLIPSIS_PADDING > MAX_LINE_LENGTH) {
i32 const half_width = MAX_LINE_LENGTH/2;
i32 left = cast(i32)(offset);
i32 right = cast(i32)(line.len - offset);
left = gb_min(left, half_width);
right = gb_min(right, half_width);
line.text += offset-left;
line.len -= offset+right-left;
line = string_trim_whitespace(line);
offset = left + ELLIPSIS_PADDING/2;
error_out("... %.*s ...", LIT(line));
} else {
error_out("%.*s", LIT(line));
}
error_out("\n\t");
for (i32 i = 0; i < offset; i++) {
error_out(" ");
}
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) {
i32 length = gb_min(end.offset - pos.offset, cast(i32)(line.len-offset));
for (i32 i = 1; i < length-1; i++) {
error_out("~");
}
if (length > 1) {
error_out("^");
}
}
}
error_out("\n\n");
return true;
}
return false;
}
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);
// NOTE(bill): Duplicate error, skip it
if (pos.line == 0) {
error_out("Error: %s\n", gb_bprintf_va(fmt, va));
} else if (global_error_collector.prev != pos) {
global_error_collector.prev = pos;
error_out("%s %s\n",
token_pos_to_string(pos),
gb_bprintf_va(fmt, va));
show_error_on_line(pos, end);
}
mutex_unlock(&global_error_collector.mutex);
if (global_error_collector.count > MAX_ERROR_COLLECTOR_COUNT) {
gb_exit(1);
}
}
void warning_va(TokenPos const &pos, TokenPos end, char const *fmt, va_list va) {
if (global_warnings_as_errors()) {
error_va(pos, end, fmt, va);
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("Warning: %s\n", gb_bprintf_va(fmt, va));
} else if (global_error_collector.prev != pos) {
global_error_collector.prev = pos;
error_out("%s Warning: %s\n",
token_pos_to_string(pos),
gb_bprintf_va(fmt, va));
show_error_on_line(pos, end);
}
}
mutex_unlock(&global_error_collector.mutex);
}
void error_line_va(char const *fmt, va_list va) {
error_out_va(fmt, va);
}
void error_no_newline_va(TokenPos const &pos, char const *fmt, va_list va) {
mutex_lock(&global_error_collector.mutex);
global_error_collector.count++;
// NOTE(bill): Duplicate error, skip it
if (pos.line == 0) {
error_out("Error: %s", gb_bprintf_va(fmt, va));
} else if (global_error_collector.prev != pos) {
global_error_collector.prev = pos;
error_out("%s %s",
token_pos_to_string(pos),
gb_bprintf_va(fmt, va));
}
mutex_unlock(&global_error_collector.mutex);
if (global_error_collector.count > MAX_ERROR_COLLECTOR_COUNT) {
gb_exit(1);
}
}
void syntax_error_va(TokenPos const &pos, TokenPos end, char const *fmt, va_list va) {
mutex_lock(&global_error_collector.mutex);
global_error_collector.count++;
// NOTE(bill): Duplicate error, skip it
if (global_error_collector.prev != pos) {
global_error_collector.prev = pos;
error_out("%s Syntax Error: %s\n",
token_pos_to_string(pos),
gb_bprintf_va(fmt, va));
show_error_on_line(pos, end);
} else if (pos.line == 0) {
error_out("Syntax Error: %s\n", gb_bprintf_va(fmt, va));
}
mutex_unlock(&global_error_collector.mutex);
if (global_error_collector.count > MAX_ERROR_COLLECTOR_COUNT) {
gb_exit(1);
}
}
void syntax_warning_va(TokenPos const &pos, TokenPos end, char const *fmt, va_list va) {
if (global_warnings_as_errors()) {
syntax_error_va(pos, end, fmt, va);
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;
error_out("%s Syntax Warning: %s\n",
token_pos_to_string(pos),
gb_bprintf_va(fmt, va));
show_error_on_line(pos, end);
} else if (pos.line == 0) {
error_out("Warning: %s\n", gb_bprintf_va(fmt, va));
}
}
mutex_unlock(&global_error_collector.mutex);
}
void warning(Token const &token, char const *fmt, ...) {
va_list va;
va_start(va, fmt);
warning_va(token.pos, {}, fmt, va);
va_end(va);
}
void error(Token const &token, char const *fmt, ...) {
va_list va;
va_start(va, fmt);
error_va(token.pos, {}, fmt, va);
va_end(va);
}
void error(TokenPos pos, char const *fmt, ...) {
va_list va;
va_start(va, fmt);
Token token = {};
token.pos = pos;
error_va(pos, {}, fmt, va);
va_end(va);
}
void error_line(char const *fmt, ...) {
va_list va;
va_start(va, fmt);
error_line_va(fmt, va);
va_end(va);
}
void syntax_error(Token const &token, char const *fmt, ...) {
va_list va;
va_start(va, fmt);
syntax_error_va(token.pos, {}, fmt, va);
va_end(va);
}
void syntax_error(TokenPos pos, char const *fmt, ...) {
va_list va;
va_start(va, fmt);
syntax_error_va(pos, {}, fmt, va);
va_end(va);
}
void syntax_warning(Token const &token, char const *fmt, ...) {
va_list va;
va_start(va, fmt);
syntax_warning_va(token.pos, {}, fmt, va);
va_end(va);
}
void compiler_error(char const *fmt, ...) {
va_list va;
va_start(va, fmt);
gb_printf_err("Internal Compiler Error: %s\n",
gb_bprintf_va(fmt, va));
va_end(va);
gb_exit(1);
}
gb_inline bool token_is_literal(TokenKind t) {
return gb_is_between(t, Token__LiteralBegin+1, Token__LiteralEnd-1);
}
@@ -695,6 +274,8 @@ gb_inline bool token_is_shift(TokenKind t) {
gb_inline void print_token(Token t) { gb_printf("%.*s\n", LIT(t.string)); }
#include "error.cpp"
enum TokenizerInitError {
TokenizerInit_None,
+292 -56
View File
@@ -165,9 +165,8 @@ struct TypeUnion {
i16 tag_size;
bool is_polymorphic;
bool is_poly_specialized : 1;
bool no_nil : 1;
bool maybe : 1;
bool is_poly_specialized;
UnionTypeKind kind;
};
struct TypeProc {
@@ -186,7 +185,6 @@ struct TypeProc {
bool c_vararg;
bool is_polymorphic;
bool is_poly_specialized;
bool has_proc_default_values;
bool has_named_results;
bool diverging; // no return
bool return_by_pointer;
@@ -221,6 +219,7 @@ struct TypeProc {
ExactValue *max_value; \
i64 count; \
TokenKind op; \
bool is_sparse; \
}) \
TYPE_KIND(Slice, struct { Type *elem; }) \
TYPE_KIND(DynamicArray, struct { Type *elem; }) \
@@ -262,6 +261,7 @@ struct TypeProc {
TYPE_KIND(SimdVector, struct { \
i64 count; \
Type *elem; \
Type *generic_count; \
}) \
TYPE_KIND(RelativePointer, struct { \
Type *pointer_type; \
@@ -362,6 +362,10 @@ enum TypeInfoFlag : u32 {
enum : int {
MATRIX_ELEMENT_COUNT_MIN = 1,
MATRIX_ELEMENT_COUNT_MAX = 16,
MATRIX_ELEMENT_MAX_SIZE = MATRIX_ELEMENT_COUNT_MAX * (2 * 8), // complex128
SIMD_ELEMENT_COUNT_MIN = 1,
SIMD_ELEMENT_COUNT_MAX = 64,
};
@@ -391,6 +395,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 pseudo_field;
};
Selection empty_selection = {0};
@@ -683,20 +688,54 @@ gb_global Type *t_map_header = nullptr;
gb_global Type *t_equal_proc = nullptr;
gb_global Type *t_hasher_proc = nullptr;
gb_global Type *t_objc_object = nullptr;
gb_global Type *t_objc_selector = nullptr;
gb_global Type *t_objc_class = nullptr;
gb_global Type *t_objc_id = nullptr;
gb_global Type *t_objc_SEL = nullptr;
gb_global Type *t_objc_Class = nullptr;
enum OdinAtomicMemoryOrder : i32 {
OdinAtomicMemoryOrder_relaxed = 0, // unordered
OdinAtomicMemoryOrder_consume = 1, // monotonic
OdinAtomicMemoryOrder_acquire = 2,
OdinAtomicMemoryOrder_release = 3,
OdinAtomicMemoryOrder_acq_rel = 4,
OdinAtomicMemoryOrder_seq_cst = 5,
OdinAtomicMemoryOrder_COUNT,
};
char const *OdinAtomicMemoryOrder_strings[OdinAtomicMemoryOrder_COUNT] = {
"Relaxed",
"Consume",
"Acquire",
"Release",
"Acq_Rel",
"Seq_Cst",
};
gb_global Type *t_atomic_memory_order = nullptr;
gb_global RecursiveMutex g_type_mutex;
struct TypePath;
i64 type_size_of (Type *t);
i64 type_align_of (Type *t);
i64 type_offset_of (Type *t, i32 index);
gbString type_to_string (Type *type);
i64 type_size_of (Type *t);
i64 type_align_of (Type *t);
i64 type_offset_of (Type *t, i32 index);
gbString type_to_string (Type *type, bool shorthand=true);
gbString type_to_string (Type *type, gbAllocator allocator, bool shorthand=true);
i64 type_size_of_internal(Type *t, TypePath *path);
void init_map_internal_types(Type *type);
Type * bit_set_to_int(Type *t);
bool are_types_identical(Type *x, Type *y);
bool is_type_pointer(Type *t);
bool is_type_proc(Type *t);
bool is_type_slice(Type *t);
bool is_type_integer(Type *t);
bool type_set_offsets(Type *t);
@@ -1051,10 +1090,11 @@ Type *alloc_type_bit_set() {
Type *alloc_type_simd_vector(i64 count, Type *elem) {
Type *alloc_type_simd_vector(i64 count, Type *elem, Type *generic_count=nullptr) {
Type *t = alloc_type(Type_SimdVector);
t->SimdVector.count = count;
t->SimdVector.elem = elem;
t->SimdVector.generic_count = generic_count;
return t;
}
@@ -1252,6 +1292,13 @@ bool is_type_quaternion(Type *t) {
}
return false;
}
bool is_type_complex_or_quaternion(Type *t) {
t = core_type(t);
if (t->kind == Type_Basic) {
return (t->Basic.flags & (BasicFlag_Complex|BasicFlag_Quaternion)) != 0;
}
return false;
}
bool is_type_f16(Type *t) {
t = core_type(t);
if (t->kind == Type_Basic) {
@@ -1284,6 +1331,10 @@ bool is_type_multi_pointer(Type *t) {
t = base_type(t);
return t->kind == Type_MultiPointer;
}
bool is_type_internally_pointer_like(Type *t) {
return is_type_pointer(t) || is_type_multi_pointer(t) || is_type_cstring(t) || is_type_proc(t);
}
bool is_type_tuple(Type *t) {
t = base_type(t);
return t->kind == Type_Tuple;
@@ -1548,6 +1599,8 @@ i64 get_array_type_count(Type *t) {
return bt->Array.count;
} else if (bt->kind == Type_EnumeratedArray) {
return bt->EnumeratedArray.count;
} else if (bt->kind == Type_SimdVector) {
return bt->SimdVector.count;
}
GB_ASSERT(is_type_array_like(t));
return -1;
@@ -1570,6 +1623,24 @@ Type *core_array_type(Type *t) {
}
}
i32 type_math_rank(Type *t) {
i32 rank = 0;
for (;;) {
t = base_type(t);
switch (t->kind) {
case Type_Array:
rank += 1;
t = t->Array.elem;
break;
case Type_Matrix:
rank += 2;
t = t->Matrix.elem;
break;
default:
return rank;
}
}
}
Type *base_complex_elem_type(Type *t) {
@@ -1622,11 +1693,9 @@ bool is_type_map(Type *t) {
bool is_type_union_maybe_pointer(Type *t) {
t = base_type(t);
if (t->kind == Type_Union && t->Union.maybe) {
if (t->Union.variants.count == 1) {
Type *v = t->Union.variants[0];
return is_type_pointer(v) || is_type_multi_pointer(v);
}
if (t->kind == Type_Union && t->Union.variants.count == 1) {
Type *v = t->Union.variants[0];
return is_type_internally_pointer_like(v);
}
return false;
}
@@ -1634,12 +1703,10 @@ bool is_type_union_maybe_pointer(Type *t) {
bool is_type_union_maybe_pointer_original_alignment(Type *t) {
t = base_type(t);
if (t->kind == Type_Union && t->Union.maybe) {
if (t->Union.variants.count == 1) {
Type *v = t->Union.variants[0];
if (is_type_pointer(v) || is_type_multi_pointer(v)) {
return type_align_of(v) == type_align_of(t);
}
if (t->kind == Type_Union && t->Union.variants.count == 1) {
Type *v = t->Union.variants[0];
if (is_type_internally_pointer_like(v)) {
return type_align_of(v) == type_align_of(t);
}
}
return false;
@@ -1873,11 +1940,14 @@ bool is_type_valid_vector_elem(Type *t) {
return false;
}
if (is_type_integer(t)) {
return true;
return !is_type_integer_128bit(t);
}
if (is_type_float(t)) {
return true;
}
if (is_type_boolean(t)) {
return true;
}
}
return false;
}
@@ -2019,6 +2089,11 @@ bool is_type_polymorphic(Type *t, bool or_specialized=false) {
return true;
}
return is_type_polymorphic(t->Array.elem, or_specialized);
case Type_SimdVector:
if (t->SimdVector.generic_count != nullptr) {
return true;
}
return is_type_polymorphic(t->SimdVector.elem, or_specialized);
case Type_DynamicArray:
return is_type_polymorphic(t->DynamicArray.elem, or_specialized);
case Type_Slice:
@@ -2126,7 +2201,7 @@ bool type_has_nil(Type *t) {
case Type_Map:
return true;
case Type_Union:
return !t->Union.no_nil;
return t->Union.kind != UnionType_no_nil;
case Type_Struct:
if (is_type_soa_struct(t)) {
switch (t->Struct.soa_kind) {
@@ -2155,6 +2230,17 @@ bool elem_type_can_be_constant(Type *t) {
return true;
}
bool is_type_lock_free(Type *t) {
t = core_type(t);
if (t == t_invalid) {
return false;
}
i64 sz = type_size_of(t);
// TODO(bill): Figure this out correctly
return sz <= build_context.max_align;
}
bool is_type_comparable(Type *t) {
t = base_type(t);
@@ -2221,6 +2307,9 @@ bool is_type_comparable(Type *t) {
}
}
return true;
case Type_SimdVector:
return true;
}
return false;
}
@@ -2291,7 +2380,7 @@ String lookup_subtype_polymorphic_field(Type *dst, Type *src) {
GB_ASSERT(is_type_struct(src) || is_type_union(src));
for_array(i, src->Struct.fields) {
Entity *f = src->Struct.fields[i];
if (f->kind == Entity_Variable && f->flags & EntityFlag_Using) {
if (f->kind == Entity_Variable && f->flags & EntityFlags_IsSubtype) {
if (are_types_identical(dst, f->type)) {
return f->token.string;
}
@@ -2300,7 +2389,7 @@ String lookup_subtype_polymorphic_field(Type *dst, Type *src) {
return f->token.string;
}
}
if (is_type_struct(f->type)) {
if ((f->flags & EntityFlag_Using) != 0 && is_type_struct(f->type)) {
String name = lookup_subtype_polymorphic_field(dst, f->type);
if (name.len > 0) {
return name;
@@ -2326,7 +2415,17 @@ Type *strip_type_aliasing(Type *x) {
return x;
}
bool are_types_identical_internal(Type *x, Type *y, bool check_tuple_names);
bool are_types_identical(Type *x, Type *y) {
return are_types_identical_internal(x, y, false);
}
bool are_types_identical_unique_tuples(Type *x, Type *y) {
return are_types_identical_internal(x, y, true);
}
bool are_types_identical_internal(Type *x, Type *y, bool check_tuple_names) {
if (x == y) {
return true;
}
@@ -2402,7 +2501,7 @@ bool are_types_identical(Type *x, Type *y) {
if (y->kind == Type_Union) {
if (x->Union.variants.count == y->Union.variants.count &&
x->Union.custom_align == y->Union.custom_align &&
x->Union.no_nil == y->Union.no_nil) {
x->Union.kind == y->Union.kind) {
// NOTE(bill): zeroth variant is nullptr
for_array(i, x->Union.variants) {
if (!are_types_identical(x->Union.variants[i], y->Union.variants[i])) {
@@ -2436,9 +2535,9 @@ bool are_types_identical(Type *x, Type *y) {
if (xf->token.string != yf->token.string) {
return false;
}
bool xf_is_using = (xf->flags&EntityFlag_Using) != 0;
bool yf_is_using = (yf->flags&EntityFlag_Using) != 0;
if (xf_is_using ^ yf_is_using) {
u64 xf_flags = (xf->flags&EntityFlags_IsSubtype);
u64 yf_flags = (yf->flags&EntityFlags_IsSubtype);
if (xf_flags != yf_flags) {
return false;
}
}
@@ -2475,6 +2574,11 @@ bool are_types_identical(Type *x, Type *y) {
if (xe->kind != ye->kind || !are_types_identical(xe->type, ye->type)) {
return false;
}
if (check_tuple_names) {
if (xe->token.string != ye->token.string) {
return false;
}
}
if (xe->kind == Entity_Constant && !compare_exact_values(Token_CmpEq, xe->Constant.value, ye->Constant.value)) {
// NOTE(bill): This is needed for polymorphic procedures
return false;
@@ -2541,7 +2645,7 @@ i64 union_variant_index(Type *u, Type *v) {
for_array(i, u->Union.variants) {
Type *vt = u->Union.variants[i];
if (are_types_identical(v, vt)) {
if (u->Union.no_nil) {
if (u->Union.kind == UnionType_no_nil) {
return cast(i64)(i+0);
} else {
return cast(i64)(i+1);
@@ -2565,6 +2669,17 @@ i64 union_tag_size(Type *u) {
// TODO(bill): Is this an okay approach?
i64 max_align = 1;
if (u->Union.variants.count < 1ull<<8) {
max_align = 1;
} else if (u->Union.variants.count < 1ull<<16) {
max_align = 2;
} else if (u->Union.variants.count < 1ull<<32) {
max_align = 4;
} else {
GB_PANIC("how many variants do you have?!");
}
for_array(i, u->Union.variants) {
Type *variant_type = u->Union.variants[i];
i64 align = type_align_of(variant_type);
@@ -2725,6 +2840,7 @@ Selection lookup_field_from_index(Type *type, i64 index) {
}
Entity *scope_lookup_current(Scope *s, String const &name);
bool has_type_got_objc_class_attribute(Type *t);
Selection lookup_field_with_selection(Type *type_, String field_name, bool is_type, Selection sel, bool allow_blank_ident) {
GB_ASSERT(type_ != nullptr);
@@ -2737,9 +2853,40 @@ Selection lookup_field_with_selection(Type *type_, String field_name, bool is_ty
bool is_ptr = type != type_;
sel.indirect = sel.indirect || is_ptr;
Type *original_type = type;
type = base_type(type);
if (is_type) {
if (has_type_got_objc_class_attribute(original_type) && original_type->kind == Type_Named) {
Entity *e = original_type->Named.type_name;
GB_ASSERT(e->kind == Entity_TypeName);
if (e->TypeName.objc_metadata) {
auto *md = e->TypeName.objc_metadata;
mutex_lock(md->mutex);
defer (mutex_unlock(md->mutex));
for (TypeNameObjCMetadataEntry const &entry : md->type_entries) {
GB_ASSERT(entry.entity->kind == Entity_Procedure);
if (entry.name == field_name) {
sel.entity = entry.entity;
sel.pseudo_field = true;
return sel;
}
}
}
if (type->kind == Type_Struct) {
for_array(i, type->Struct.fields) {
Entity *f = type->Struct.fields[i];
if (f->flags&EntityFlag_Using) {
sel = lookup_field_with_selection(f->type, field_name, is_type, sel, allow_blank_ident);
if (sel.entity) {
return sel;
}
}
}
}
}
if (is_type_enum(type)) {
// NOTE(bill): These may not have been added yet, so check in case
for_array(i, type->Enum.fields) {
@@ -2786,6 +2933,24 @@ Selection lookup_field_with_selection(Type *type_, String field_name, bool is_ty
} else if (type->kind == Type_Union) {
} else if (type->kind == Type_Struct) {
if (has_type_got_objc_class_attribute(original_type) && original_type->kind == Type_Named) {
Entity *e = original_type->Named.type_name;
GB_ASSERT(e->kind == Entity_TypeName);
if (e->TypeName.objc_metadata) {
auto *md = e->TypeName.objc_metadata;
mutex_lock(md->mutex);
defer (mutex_unlock(md->mutex));
for (TypeNameObjCMetadataEntry const &entry : md->value_entries) {
GB_ASSERT(entry.entity->kind == Entity_Procedure);
if (entry.name == field_name) {
sel.entity = entry.entity;
sel.pseudo_field = true;
return sel;
}
}
}
}
for_array(i, type->Struct.fields) {
Entity *f = type->Struct.fields[i];
if (f->kind != Entity_Variable || (f->flags & EntityFlag_Field) == 0) {
@@ -3300,7 +3465,7 @@ i64 type_align_of_internal(Type *t, TypePath *path) {
case Type_SimdVector: {
// IMPORTANT TODO(bill): Figure out the alignment of vector types
return gb_clamp(next_pow2(type_size_of_internal(t, path)), 1, build_context.max_align);
return gb_clamp(next_pow2(type_size_of_internal(t, path)), 1, build_context.max_align*2);
}
case Type_Matrix:
@@ -3691,6 +3856,61 @@ i64 type_offset_of_from_selection(Type *type, Selection sel) {
return offset;
}
isize check_is_assignable_to_using_subtype(Type *src, Type *dst, isize level = 0, bool src_is_ptr = false) {
Type *prev_src = src;
src = type_deref(src);
if (!src_is_ptr) {
src_is_ptr = src != prev_src;
}
src = base_type(src);
if (!is_type_struct(src)) {
return 0;
}
for_array(i, src->Struct.fields) {
Entity *f = src->Struct.fields[i];
if (f->kind != Entity_Variable || (f->flags&EntityFlags_IsSubtype) == 0) {
continue;
}
if (are_types_identical(f->type, dst)) {
return level+1;
}
if (src_is_ptr && is_type_pointer(dst)) {
if (are_types_identical(f->type, type_deref(dst))) {
return level+1;
}
}
isize nested_level = check_is_assignable_to_using_subtype(f->type, dst, level+1, src_is_ptr);
if (nested_level > 0) {
return nested_level;
}
}
return 0;
}
bool is_type_subtype_of(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));
}
bool has_type_got_objc_class_attribute(Type *t) {
return t->kind == Type_Named && t->Named.type_name != nullptr && t->Named.type_name->TypeName.objc_class_name != "";
}
bool is_type_objc_object(Type *t) {
bool internal_check_is_assignable_to(Type *src, Type *dst);
return internal_check_is_assignable_to(t, t_objc_object);
}
Type *get_struct_field_type(Type *t, isize index) {
t = base_type(type_deref(t));
@@ -3762,7 +3982,7 @@ Type *alloc_type_proc_from_types(Type **param_types, unsigned param_count, Type
gbString write_type_to_string(gbString str, Type *type) {
gbString write_type_to_string(gbString str, Type *type, bool shorthand=false) {
if (type == nullptr) {
return gb_string_appendc(str, "<no type>");
}
@@ -3803,6 +4023,9 @@ gbString write_type_to_string(gbString str, Type *type) {
break;
case Type_EnumeratedArray:
if (type->EnumeratedArray.is_sparse) {
str = gb_string_appendc(str, "#sparse");
}
str = gb_string_append_rune(str, '[');
str = write_type_to_string(str, type->EnumeratedArray.index);
str = gb_string_append_rune(str, ']');
@@ -3845,8 +4068,10 @@ gbString write_type_to_string(gbString str, Type *type) {
case Type_Union:
str = gb_string_appendc(str, "union");
if (type->Union.no_nil != 0) str = gb_string_appendc(str, " #no_nil");
if (type->Union.maybe != 0) str = gb_string_appendc(str, " #maybe");
switch (type->Union.kind) {
case UnionType_no_nil: str = gb_string_appendc(str, " #no_nil"); break;
case UnionType_shared_nil: str = gb_string_appendc(str, " #shared_nil"); break;
}
if (type->Union.custom_align != 0) str = gb_string_append_fmt(str, " #align %d", cast(int)type->Union.custom_align);
str = gb_string_appendc(str, " {");
for_array(i, type->Union.variants) {
@@ -3874,15 +4099,21 @@ gbString write_type_to_string(gbString str, Type *type) {
if (type->Struct.is_raw_union) str = gb_string_appendc(str, " #raw_union");
if (type->Struct.custom_align != 0) str = gb_string_append_fmt(str, " #align %d", cast(int)type->Struct.custom_align);
str = gb_string_appendc(str, " {");
for_array(i, type->Struct.fields) {
Entity *f = type->Struct.fields[i];
GB_ASSERT(f->kind == Entity_Variable);
if (i > 0) {
str = gb_string_appendc(str, ", ");
if (shorthand && type->Struct.fields.count > 16) {
str = gb_string_append_fmt(str, "%lld fields...", cast(long long)type->Struct.fields.count);
} else {
for_array(i, type->Struct.fields) {
Entity *f = type->Struct.fields[i];
GB_ASSERT(f->kind == Entity_Variable);
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_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_rune(str, '}');
} break;
@@ -3921,7 +4152,7 @@ gbString write_type_to_string(gbString str, Type *type) {
str = gb_string_appendc(str, " = ");
str = write_exact_value_to_string(str, var->Constant.value);
} else {
str = gb_string_appendc(str, "=");
str = gb_string_appendc(str, " := ");
str = write_exact_value_to_string(str, var->Constant.value);
}
continue;
@@ -3949,14 +4180,10 @@ gbString write_type_to_string(gbString str, Type *type) {
str = gb_string_appendc(str, "typeid/");
str = write_type_to_string(str, var->type);
} else {
if (var->kind == Entity_TypeName) {
str = gb_string_appendc(str, "$");
str = gb_string_append_length(str, name.text, name.len);
str = gb_string_appendc(str, "=");
str = write_type_to_string(str, var->type);
} else {
str = gb_string_appendc(str, "typeid");
}
str = gb_string_appendc(str, "$");
str = gb_string_append_length(str, name.text, name.len);
str = gb_string_appendc(str, "=");
str = write_type_to_string(str, var->type);
}
}
}
@@ -4019,7 +4246,13 @@ gbString write_type_to_string(gbString str, Type *type) {
case Type_BitSet:
str = gb_string_appendc(str, "bit_set[");
str = write_type_to_string(str, type->BitSet.elem);
if (is_type_enum(type->BitSet.elem)) {
str = write_type_to_string(str, type->BitSet.elem);
} else {
str = gb_string_append_fmt(str, "%lld", type->BitSet.lower);
str = gb_string_append_fmt(str, "..=");
str = gb_string_append_fmt(str, "%lld", type->BitSet.upper);
}
if (type->BitSet.underlying != nullptr) {
str = gb_string_appendc(str, "; ");
str = write_type_to_string(str, type->BitSet.underlying);
@@ -4055,13 +4288,16 @@ gbString write_type_to_string(gbString str, Type *type) {
}
gbString type_to_string(Type *type, gbAllocator allocator) {
return write_type_to_string(gb_string_make(allocator, ""), type);
gbString type_to_string(Type *type, gbAllocator allocator, bool shorthand) {
return write_type_to_string(gb_string_make(allocator, ""), type, shorthand);
}
gbString type_to_string(Type *type) {
return write_type_to_string(gb_string_make(heap_allocator(), ""), type);
gbString type_to_string(Type *type, bool shorthand) {
return write_type_to_string(gb_string_make(heap_allocator(), ""), type, shorthand);
}
gbString type_to_string_shorthand(Type *type) {
return type_to_string(type, true);
}