Merge remote-tracking branch 'offical/bill/raddebugger-custom-section'

This commit is contained in:
2025-05-25 09:25:29 -04:00
266 changed files with 20393 additions and 4512 deletions
+8 -2
View File
@@ -527,7 +527,7 @@ gb_internal void report_os_info() {
#elif defined(GB_SYSTEM_OSX)
gbString sw_vers = gb_string_make(heap_allocator(), "");
if (!system_exec_command_line_app_output("sw_vers --productVersion", &sw_vers)) {
if (!system_exec_command_line_app_output("sw_vers -productVersion", &sw_vers)) {
gb_printf("macOS Unknown\n");
return;
}
@@ -667,8 +667,14 @@ gb_internal void print_bug_report_help() {
gb_printf("-nightly");
#endif
String version = {};
#ifdef GIT_SHA
gb_printf(":%s", GIT_SHA);
version.text = cast(u8 *)GIT_SHA;
version.len = gb_strlen(GIT_SHA);
if (version != "") {
gb_printf(":%.*s", LIT(version));
}
#endif
gb_printf("\n");
+83 -49
View File
@@ -459,6 +459,7 @@ struct BuildContext {
bool ignore_unknown_attributes;
bool no_bounds_check;
bool no_type_assert;
bool dynamic_literals; // Opt-in to `#+feature dynamic-literals` project-wide.
bool no_output_files;
bool no_crt;
bool no_rpath;
@@ -551,10 +552,9 @@ struct BuildContext {
String ODIN_ANDROID_NDK_TOOLCHAIN_LIB_LEVEL;
String ODIN_ANDROID_NDK_TOOLCHAIN_SYSROOT;
String ODIN_ANDROID_JAR_SIGNER;
String android_keystore;
String android_keystore_alias;
String android_manifest;
String android_keystore_password;
};
gb_global BuildContext build_context = {0};
@@ -848,13 +848,39 @@ gb_global NamedTargetMetrics *selected_target_metrics;
gb_global Subtarget selected_subtarget;
gb_internal TargetOsKind get_target_os_from_string(String str) {
gb_internal TargetOsKind get_target_os_from_string(String str, Subtarget *subtarget_ = nullptr) {
String os_name = str;
String subtarget = {};
auto part = string_partition(str, str_lit(":"));
if (part.match.len == 1) {
os_name = part.head;
subtarget = part.tail;
}
TargetOsKind kind = TargetOs_Invalid;
for (isize i = 0; i < TargetOs_COUNT; i++) {
if (str_eq_ignore_case(target_os_names[i], str)) {
return cast(TargetOsKind)i;
if (str_eq_ignore_case(target_os_names[i], os_name)) {
kind = cast(TargetOsKind)i;
break;
}
}
return TargetOs_Invalid;
if (subtarget_) *subtarget_ = Subtarget_Default;
if (subtarget.len != 0) {
if (str_eq_ignore_case(subtarget, "generic") || str_eq_ignore_case(subtarget, "default")) {
if (subtarget_) *subtarget_ = Subtarget_Default;
} else {
for (isize i = 1; i < Subtarget_COUNT; i++) {
if (str_eq_ignore_case(subtarget_strings[i], subtarget)) {
if (subtarget_) *subtarget_ = cast(Subtarget)i;
break;
}
}
}
}
return kind;
}
gb_internal TargetArchKind get_target_arch_from_string(String str) {
@@ -1573,24 +1599,15 @@ gb_internal void init_android_values(bool with_sdk) {
bc->ODIN_ANDROID_NDK_TOOLCHAIN_SYSROOT = concatenate_strings(permanent_allocator(), bc->ODIN_ANDROID_NDK_TOOLCHAIN, str_lit("sysroot/"));
bc->ODIN_ANDROID_JAR_SIGNER = normalize_path(permanent_allocator(), make_string_c(gb_get_env("ODIN_ANDROID_JAR_SIGNER", permanent_allocator())), NIX_SEPARATOR_STRING);
if (with_sdk) {
if (bc->ODIN_ANDROID_SDK.len == 0) {
gb_printf_err("Error: ODIN_ANDROID_SDK not set, which is required for -build-mode:executable for -subtarget:android");
gb_exit(1);
}
if (bc->ODIN_ANDROID_JAR_SIGNER.len == 0) {
gb_printf_err("Error: ODIN_ANDROID_JAR_SIGNER not set, which is required for -build-mode:executable for -subtarget:android");
gb_exit(1);
}
if (bc->android_keystore.len == 0) {
gb_printf_err("Error: -android-keystore:<string> has not been set\n");
gb_exit(1);
}
if (bc->android_keystore_alias.len == 0) {
gb_printf_err("Error: -android-keystore_alias:<string> has not been set\n");
gb_exit(1);
}
}
}
@@ -1774,6 +1791,30 @@ gb_internal void init_build_context(TargetMetrics *cross_target, Subtarget subta
bc->ODIN_WINDOWS_SUBSYSTEM = windows_subsystem_names[Windows_Subsystem_CONSOLE];
}
if (subtarget == Subtarget_Android) {
switch (build_context.build_mode) {
case BuildMode_DynamicLibrary:
case BuildMode_Object:
case BuildMode_Assembly:
case BuildMode_LLVM_IR:
break;
default:
case BuildMode_Executable:
case BuildMode_StaticLibrary:
if ((build_context.command_kind & Command__does_build) != 0) {
gb_printf_err("Unsupported -build-mode for -subtarget:android\n");
gb_printf_err("\tCurrently only supporting: \n");
// gb_printf_err("\t\texe\n");
gb_printf_err("\t\tshared\n");
gb_printf_err("\t\tobject\n");
gb_printf_err("\t\tassembly\n");
gb_printf_err("\t\tllvm-ir\n");
gb_exit(1);
}
break;
}
}
if (metrics->os == TargetOs_darwin && subtarget == Subtarget_iOS) {
switch (metrics->arch) {
case TargetArch_arm64:
@@ -1849,7 +1890,7 @@ gb_internal void init_build_context(TargetMetrics *cross_target, Subtarget subta
bc->metrics.target_triplet = concatenate_strings(permanent_allocator(), bc->metrics.target_triplet, bc->minimum_os_version_string);
}
} else if (selected_subtarget == Subtarget_Android) {
init_android_values(bc->build_mode == BuildMode_Executable);
init_android_values(bc->build_mode == BuildMode_Executable && (bc->command_kind & Command__does_build) != 0);
}
if (!bc->custom_optimization_level) {
@@ -1875,12 +1916,6 @@ gb_internal void init_build_context(TargetMetrics *cross_target, Subtarget subta
}
// TODO: Static map calls are bugged on `amd64sysv` abi.
if (bc->metrics.os != TargetOs_windows && bc->metrics.arch == TargetArch_amd64) {
// ENFORCE DYNAMIC MAP CALLS
bc->dynamic_map_calls = true;
}
bc->ODIN_VALGRIND_SUPPORT = false;
if (build_context.metrics.os != TargetOs_windows) {
switch (bc->metrics.arch) {
@@ -1893,30 +1928,6 @@ gb_internal void init_build_context(TargetMetrics *cross_target, Subtarget subta
if (bc->metrics.os == TargetOs_freestanding) {
bc->ODIN_DEFAULT_TO_NIL_ALLOCATOR = !bc->ODIN_DEFAULT_TO_PANIC_ALLOCATOR;
}
if (subtarget == Subtarget_Android) {
switch (build_context.build_mode) {
case BuildMode_DynamicLibrary:
case BuildMode_Object:
case BuildMode_Assembly:
case BuildMode_LLVM_IR:
break;
default:
case BuildMode_Executable:
case BuildMode_StaticLibrary:
if ((build_context.command_kind & Command__does_build) != 0) {
gb_printf_err("Unsupported -build-mode for -subtarget:android\n");
gb_printf_err("\tCurrently only supporting: \n");
// gb_printf_err("\t\texe\n");
gb_printf_err("\t\tshared\n");
gb_printf_err("\t\tobject\n");
gb_printf_err("\t\tassembly\n");
gb_printf_err("\t\tllvm-ir\n");
gb_exit(1);
}
break;
}
}
}
#if defined(GB_SYSTEM_WINDOWS)
@@ -2198,11 +2209,34 @@ gb_internal bool init_build_paths(String init_filename) {
while (output_name.len > 0 && (output_name[output_name.len-1] == '/' || output_name[output_name.len-1] == '\\')) {
output_name.len -= 1;
}
// Only trim the extension if it's an Odin source file.
// This lets people build folders with extensions or files beginning with dots.
if (path_extension(output_name) == ".odin" && !path_is_directory(output_name)) {
output_name = remove_extension_from_path(output_name);
}
output_name = remove_directory_from_path(output_name);
output_name = remove_extension_from_path(output_name);
output_name = copy_string(ha, string_trim_whitespace(output_name));
output_path = path_from_string(ha, output_name);
// This is `path_from_string` without the extension trimming.
Path res = {};
if (output_name.len > 0) {
String fullpath = path_to_full_path(ha, output_name);
defer (gb_free(ha, fullpath.text));
res.basename = directory_from_path(fullpath);
res.basename = copy_string(ha, res.basename);
if (path_is_directory(fullpath)) {
if (res.basename.len > 0 && res.basename.text[res.basename.len - 1] == '/') {
res.basename.len--;
}
} else {
isize name_start = (res.basename.len > 0) ? res.basename.len + 1 : res.basename.len;
res.name = substring(fullpath, name_start, fullpath.len);
res.name = copy_string(ha, res.name);
}
}
output_path = res;
// Note(Dragos): This is a fix for empty filenames
// Turn the trailing folder into the file name
if (output_path.name.len == 0) {
+45 -42
View File
@@ -126,16 +126,6 @@ i32 bundle_android(String original_init_directory) {
defer (gb_string_free(cmd));
String current_directory = normalize_path(temporary_allocator(), get_working_directory(temporary_allocator()), NIX_SEPARATOR_STRING);
defer (set_working_directory(current_directory));
if (current_directory.len != 0) {
bool ok = set_working_directory(init_directory);
if (!ok) {
gb_printf_err("Error: Unable to currectly set the current working directory to '%.*s'\n", LIT(init_directory));
}
}
String output_filename = str_lit("test");
String output_apk = path_remove_extension(output_filename);
@@ -144,51 +134,36 @@ i32 bundle_android(String original_init_directory) {
TEMPORARY_ALLOCATOR_GUARD();
gb_string_clear(cmd);
String manifest = {};
if (build_context.android_manifest.len != 0) {
manifest = concatenate_strings(temporary_allocator(), current_directory, build_context.android_manifest);
} else {
manifest = concatenate_strings(temporary_allocator(), init_directory, str_lit("AndroidManifest.xml"));
}
String manifest = concatenate_strings(temporary_allocator(), init_directory, str_lit("AndroidManifest.xml"));
cmd = gb_string_append_length(cmd, android_sdk_build_tools.text, android_sdk_build_tools.len);
cmd = gb_string_appendc(cmd, "aapt");
cmd = gb_string_appendc(cmd, " package -f");
if (manifest.len != 0) {
cmd = gb_string_append_fmt(cmd, " -M \"%.*s\"", LIT(manifest));
}
cmd = gb_string_append_fmt(cmd, " -M \"%.*s\"", LIT(manifest));
cmd = gb_string_append_fmt(cmd, " -I \"%.*sandroid.jar\"", LIT(android_sdk_platforms));
cmd = gb_string_append_fmt(cmd, " -F \"%.*s.apk-build\"", LIT(output_apk));
String resources_dir = concatenate_strings(temporary_allocator(), init_directory, str_lit("res"));
if (gb_file_exists((const char *)resources_dir.text)) {
cmd = gb_string_append_fmt(cmd, " -S \"%.*s\"", LIT(resources_dir));
}
String assets_dir = concatenate_strings(temporary_allocator(), init_directory, str_lit("assets"));
if (gb_file_exists((const char *)assets_dir.text)) {
cmd = gb_string_append_fmt(cmd, " -A \"%.*s\"", LIT(assets_dir));
}
String lib_dir = concatenate_strings(temporary_allocator(), init_directory, str_lit("lib"));
if (gb_file_exists((const char *)lib_dir.text)) {
cmd = gb_string_append_fmt(cmd, " \"%.*s\"", LIT(lib_dir));
}
result = system_exec_command_line_app("android-aapt", cmd);
if (result) {
return result;
}
}
TIME_SECTION("Android jarsigner");
{
TEMPORARY_ALLOCATOR_GUARD();
gb_string_clear(cmd);
cmd = gb_string_append_length(cmd, build_context.ODIN_ANDROID_JAR_SIGNER.text, build_context.ODIN_ANDROID_JAR_SIGNER.len);
cmd = gb_string_append_fmt(cmd, " -storepass android");
if (build_context.android_keystore.len != 0) {
String keystore = concatenate_strings(temporary_allocator(), current_directory, build_context.android_keystore);
cmd = gb_string_append_fmt(cmd, " -keystore \"%.*s\"", LIT(keystore));
}
cmd = gb_string_append_fmt(cmd, " \"%.*s.apk-build\"", LIT(output_apk));
if (build_context.android_keystore_alias.len != 0) {
String keystore_alias = build_context.android_keystore_alias;
cmd = gb_string_append_fmt(cmd, " \"%.*s\"", LIT(keystore_alias));
}
result = system_exec_command_line_app("android-jarsigner", cmd);
if (result) {
return result;
}
}
TIME_SECTION("Android zipalign");
{
TEMPORARY_ALLOCATOR_GUARD();
@@ -205,5 +180,33 @@ i32 bundle_android(String original_init_directory) {
}
}
TIME_SECTION("Android apksigner");
{
TEMPORARY_ALLOCATOR_GUARD();
gb_string_clear(cmd);
cmd = gb_string_append_length(cmd, android_sdk_build_tools.text, android_sdk_build_tools.len);
cmd = gb_string_appendc(cmd, "apksigner");
cmd = gb_string_appendc(cmd, " sign");
String keystore = normalize_path(temporary_allocator(), build_context.android_keystore, NIX_SEPARATOR_STRING);
keystore = substring(keystore, 0, keystore.len - 1);
cmd = gb_string_append_fmt(cmd, " --ks \"%.*s\"", LIT(keystore));
if (build_context.android_keystore_alias.len != 0) {
cmd = gb_string_append_fmt(cmd, " --ks-key-alias \"%.*s\"", LIT(build_context.android_keystore_alias));
}
if (build_context.android_keystore_password.len != 0) {
cmd = gb_string_append_fmt(cmd, " --ks-pass pass:\"%.*s\"", LIT(build_context.android_keystore_password));
}
cmd = gb_string_append_fmt(cmd, " \"%.*s.apk\"", LIT(output_apk));
result = system_exec_command_line_app("android-apksigner", cmd);
if (result) {
return result;
}
}
return 0;
}
+366 -6
View File
@@ -223,9 +223,9 @@ gb_internal void add_objc_proc_type(CheckerContext *c, Ast *call, Type *return_t
data.kind = kind;
data.proc_type = alloc_type_proc(scope, params, param_types.count, results, results->Tuple.variables.count, false, ProcCC_CDecl);
mutex_lock(&c->info->objc_types_mutex);
mutex_lock(&c->info->objc_objc_msgSend_mutex);
map_set(&c->info->objc_msgSend_types, call, data);
mutex_unlock(&c->info->objc_types_mutex);
mutex_unlock(&c->info->objc_objc_msgSend_mutex);
try_to_add_package_dependency(c, "runtime", "objc_msgSend");
try_to_add_package_dependency(c, "runtime", "objc_msgSend_fpret");
@@ -387,6 +387,59 @@ gb_internal bool check_builtin_objc_procedure(CheckerContext *c, Operand *operan
try_to_add_package_dependency(c, "runtime", "objc_allocateClassPair");
return true;
} break;
case BuiltinProc_objc_ivar_get:
{
Type *self_type = nullptr;
Operand self = {};
check_expr_or_type(c, &self, ce->args[0]);
if (!is_operand_value(self) || !check_is_assignable_to(c, &self, t_objc_id)) {
gbString e = expr_to_string(self.expr);
gbString t = type_to_string(self.type);
error(self.expr, "'%.*s' expected a type or value derived from intrinsics.objc_object, got '%s' of type %s", LIT(builtin_name), e, t);
gb_string_free(t);
gb_string_free(e);
return false;
} else if (!is_type_pointer(self.type)) {
gbString e = expr_to_string(self.expr);
gbString t = type_to_string(self.type);
error(self.expr, "'%.*s' expected a pointer of a value derived from intrinsics.objc_object, got '%s' of type %s", LIT(builtin_name), e, t);
gb_string_free(t);
gb_string_free(e);
return false;
}
self_type = type_deref(self.type);
if (!(self_type->kind == Type_Named &&
self_type->Named.type_name != nullptr &&
self_type->Named.type_name->TypeName.objc_class_name != "")) {
gbString t = type_to_string(self_type);
error(self.expr, "'%.*s' expected a named type with the attribute @(objc_class=<string>) , got type %s", LIT(builtin_name), t);
gb_string_free(t);
return false;
}
Type *ivar_type = self_type->Named.type_name->TypeName.objc_ivar;
if (ivar_type == nullptr) {
gbString t = type_to_string(self_type);
error(self.expr, "'%.*s' requires that type %s have the attribute @(objc_ivar=<ivar_type_name>).", LIT(builtin_name), t);
gb_string_free(t);
return false;
}
if (type_hint != nullptr && type_hint->kind == Type_Pointer && type_hint->Pointer.elem == ivar_type) {
operand->type = type_hint;
} else {
operand->type = alloc_type_pointer(ivar_type);
}
operand->mode = Addressing_Value;
return true;
} break;
}
}
@@ -760,6 +813,36 @@ gb_internal bool check_builtin_simd_operation(CheckerContext *c, Operand *operan
return true;
}
case BuiltinProc_simd_indices:
{
Operand x = {};
check_expr_or_type(c, &x, ce->args[0], nullptr);
if (x.mode == Addressing_Invalid) return false;
if (x.mode != Addressing_Type) {
gbString s = expr_to_string(x.expr);
error(x.expr, "'%.*s' expected a simd vector type, got '%s'", LIT(builtin_name), s);
gb_string_free(s);
return false;
}
if (!is_type_simd_vector(x.type)) {
gbString s = type_to_string(x.type);
error(x.expr, "'%.*s' expected a simd vector type, got '%s'", LIT(builtin_name), s);
gb_string_free(s);
return false;
}
Type *elem = base_array_type(x.type);
if (!is_type_numeric(elem)) {
gbString s = type_to_string(x.type);
error(x.expr, "'%.*s' expected a simd vector type with a numeric element type, got '%s'", LIT(builtin_name), s);
gb_string_free(s);
}
operand->mode = Addressing_Value;
operand->type = x.type;
return true;
}
case BuiltinProc_simd_extract:
{
Operand x = {};
@@ -823,8 +906,12 @@ gb_internal bool check_builtin_simd_operation(CheckerContext *c, Operand *operan
}
break;
case BuiltinProc_simd_reduce_add_bisect:
case BuiltinProc_simd_reduce_mul_bisect:
case BuiltinProc_simd_reduce_add_ordered:
case BuiltinProc_simd_reduce_mul_ordered:
case BuiltinProc_simd_reduce_add_pairs:
case BuiltinProc_simd_reduce_mul_pairs:
case BuiltinProc_simd_reduce_min:
case BuiltinProc_simd_reduce_max:
{
@@ -2073,6 +2160,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
case BuiltinProc_atomic_type_is_lock_free:
case BuiltinProc_has_target_feature:
case BuiltinProc_procedure_of:
case BuiltinProc_simd_indices:
// NOTE(bill): The first arg may be a Type, this will be checked case by case
break;
@@ -2146,7 +2234,8 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
case BuiltinProc_objc_find_selector:
case BuiltinProc_objc_find_class:
case BuiltinProc_objc_register_selector:
case BuiltinProc_objc_register_class:
case BuiltinProc_objc_register_class:
case BuiltinProc_objc_ivar_get:
return check_builtin_objc_procedure(c, operand, call, id, type_hint);
case BuiltinProc___entry_point:
@@ -3168,6 +3257,194 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
break;
}
case BuiltinProc_compress_values: {
Operand *ops = gb_alloc_array(temporary_allocator(), Operand, ce->args.count);
isize value_count = 0;
for_array(i, ce->args) {
Ast *arg = ce->args[i];
Operand *op = &ops[i];
check_multi_expr(c, op, arg);
if (op->mode == Addressing_Invalid) {
return false;
}
if (op->type == nullptr || op->type == t_invalid) {
gbString s = expr_to_string(op->expr);
error(op->expr, "Invalid expression to '%.*s', got %s", LIT(builtin_name), s);
gb_string_free(s);
}
if (is_type_tuple(op->type)) {
value_count += op->type->Tuple.variables.count;
} else {
value_count += 1;
}
}
GB_ASSERT(value_count >= 1);
if (value_count == 1) {
*operand = ops[0];
break;
}
if (type_hint != nullptr) {
Type *th = base_type(type_hint);
if (th->kind == Type_Struct) {
if (value_count == th->Struct.fields.count) {
isize index = 0;
for_array(i, ce->args) {
Operand *op = &ops[i];
if (is_type_tuple(op->type)) {
for (Entity *v : op->type->Tuple.variables) {
Operand x = {};
x.mode = Addressing_Value;
x.type = v->type;
check_assignment(c, &x, th->Struct.fields[index++]->type, builtin_name);
if (x.mode == Addressing_Invalid) {
return false;
}
}
} else {
check_assignment(c, op, th->Struct.fields[index++]->type, builtin_name);
if (op->mode == Addressing_Invalid) {
return false;
}
}
}
operand->type = type_hint;
operand->mode = Addressing_Value;
break;
}
} else if (is_type_array_like(th)) {
if (cast(i64)value_count == get_array_type_count(th)) {
Type *elem = base_array_type(th);
for_array(i, ce->args) {
Operand *op = &ops[i];
if (is_type_tuple(op->type)) {
for (Entity *v : op->type->Tuple.variables) {
Operand x = {};
x.mode = Addressing_Value;
x.type = v->type;
check_assignment(c, &x, elem, builtin_name);
if (x.mode == Addressing_Invalid) {
return false;
}
}
} else {
check_assignment(c, op, elem, builtin_name);
if (op->mode == Addressing_Invalid) {
return false;
}
}
}
operand->type = type_hint;
operand->mode = Addressing_Value;
break;
}
}
}
bool all_types_the_same = true;
Type *last_type = nullptr;
for_array(i, ce->args) {
Operand *op = &ops[i];
if (is_type_tuple(op->type)) {
if (last_type == nullptr) {
op->type->Tuple.variables[0]->type;
}
for (Entity *v : op->type->Tuple.variables) {
if (!are_types_identical(last_type, v->type)) {
all_types_the_same = false;
break;
}
last_type = v->type;
}
} else {
if (last_type == nullptr) {
last_type = op->type;
} else {
if (!are_types_identical(last_type, op->type)) {
all_types_the_same = false;
break;
}
last_type = op->type;
}
}
}
if (all_types_the_same) {
Type *elem_type = default_type(last_type);
if (is_type_untyped(elem_type)) {
gbString s = expr_to_string(call);
error(call, "Invalid use of '%s' in '%.*s'", s, LIT(builtin_name));
gb_string_free(s);
return false;
}
operand->type = alloc_type_array(elem_type, value_count);
operand->mode = Addressing_Value;
} else {
Type *st = alloc_type_struct_complete();
st->Struct.fields = slice_make<Entity *>(permanent_allocator(), value_count);
st->Struct.tags = gb_alloc_array(permanent_allocator(), String, value_count);
st->Struct.offsets = gb_alloc_array(permanent_allocator(), i64, value_count);
Scope *scope = create_scope(c->info, nullptr);
Token token = {};
token.kind = Token_Ident;
token.pos = ast_token(call).pos;
isize index = 0;
for_array(i, ce->args) {
Operand *op = &ops[i];
if (is_type_tuple(op->type)) {
for (Entity *v : op->type->Tuple.variables) {
Type *t = default_type(v->type);
if (is_type_untyped(t)) {
gbString s = expr_to_string(op->expr);
error(op->expr, "Invalid use of '%s' in '%.*s'", s, LIT(builtin_name));
gb_string_free(s);
return false;
}
gbString s = gb_string_make_reserve(permanent_allocator(), 32);
s = gb_string_append_fmt(s, "v%lld", cast(long long)index);
token.string = make_string_c(s);
Entity *e = alloc_entity_field(scope, token, t, false, cast(i32)index, EntityState_Resolved);
st->Struct.fields[index++] = e;
}
} else {
Type *t = default_type(op->type);
if (is_type_untyped(t)) {
gbString s = expr_to_string(op->expr);
error(op->expr, "Invalid use of '%s' in '%.*s'", s, LIT(builtin_name));
gb_string_free(s);
return false;
}
gbString s = gb_string_make_reserve(permanent_allocator(), 32);
s = gb_string_append_fmt(s, "v%lld", cast(long long)index);
token.string = make_string_c(s);
Entity *e = alloc_entity_field(scope, token, t, false, cast(i32)index, EntityState_Resolved);
st->Struct.fields[index++] = e;
}
}
gb_unused(type_size_of(st));
operand->type = st;
operand->mode = Addressing_Value;
}
break;
}
case BuiltinProc_min: {
// min :: proc($T: typeid) -> ordered
// min :: proc(a: ..ordered) -> ordered
@@ -5564,6 +5841,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
case Type_EnumeratedArray: operand->type = bt->EnumeratedArray.elem; break;
case Type_Slice: operand->type = bt->Slice.elem; break;
case Type_DynamicArray: operand->type = bt->DynamicArray.elem; break;
case Type_SimdVector: operand->type = bt->SimdVector.elem; break;
}
}
operand->mode = Addressing_Type;
@@ -5613,6 +5891,87 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
}
operand->mode = Addressing_Type;
break;
case BuiltinProc_type_integer_to_unsigned:
if (operand->mode != Addressing_Type) {
error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name));
return false;
}
if (is_type_polymorphic(operand->type)) {
gbString t = type_to_string(operand->type);
error(operand->expr, "Expected a non-polymorphic type for '%.*s', got %s", LIT(builtin_name), t);
gb_string_free(t);
return false;
}
{
Type *bt = base_type(operand->type);
if (bt->kind != Type_Basic ||
(bt->Basic.flags & BasicFlag_Unsigned) != 0 ||
(bt->Basic.flags & BasicFlag_Integer) == 0) {
gbString t = type_to_string(operand->type);
error(operand->expr, "Expected a signed integer type for '%.*s', got %s", LIT(builtin_name), t);
gb_string_free(t);
return false;
}
if ((bt->Basic.flags & BasicFlag_Untyped) != 0) {
gbString t = type_to_string(operand->type);
error(operand->expr, "Expected a non-untyped integer type for '%.*s', got %s", LIT(builtin_name), t);
gb_string_free(t);
return false;
}
Type *u_type = &basic_types[bt->Basic.kind + 1];
operand->type = u_type;
}
break;
case BuiltinProc_type_integer_to_signed:
if (operand->mode != Addressing_Type) {
error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name));
return false;
}
if (is_type_polymorphic(operand->type)) {
gbString t = type_to_string(operand->type);
error(operand->expr, "Expected a non-polymorphic type for '%.*s', got %s", LIT(builtin_name), t);
gb_string_free(t);
return false;
}
{
Type *bt = base_type(operand->type);
if (bt->kind != Type_Basic ||
(bt->Basic.flags & BasicFlag_Unsigned) == 0 ||
(bt->Basic.flags & BasicFlag_Integer) == 0) {
gbString t = type_to_string(operand->type);
error(operand->expr, "Expected an unsigned integer type for '%.*s', got %s", LIT(builtin_name), t);
gb_string_free(t);
return false;
}
if ((bt->Basic.flags & BasicFlag_Untyped) != 0) {
gbString t = type_to_string(operand->type);
error(operand->expr, "Expected a non-untyped integer type for '%.*s', got %s", LIT(builtin_name), t);
gb_string_free(t);
return false;
}
if (bt->Basic.kind == Basic_uintptr) {
gbString t = type_to_string(operand->type);
error(operand->expr, "Type %s does not have a signed integer mapping for '%.*s'", t, LIT(builtin_name));
gb_string_free(t);
return false;
}
Type *u_type = &basic_types[bt->Basic.kind - 1];
operand->type = u_type;
}
break;
case BuiltinProc_type_merge:
{
operand->mode = Addressing_Type;
@@ -6014,12 +6373,13 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
// NOTE(jakubtomsu): forces calculation of variant_block_size
type_size_of(u);
i64 tag_offset = u->Union.variant_block_size;
GB_ASSERT(tag_offset > 0);
// NOTE(Jeroen): A tag offset of zero is perfectly fine if all members of the union are empty structs.
// What matters is that the tag size is > 0.
GB_ASSERT(u->Union.tag_size > 0);
operand->mode = Addressing_Constant;
operand->type = t_untyped_integer;
operand->value = exact_value_i64(tag_offset);
operand->value = exact_value_i64(u->Union.variant_block_size);
}
break;
+183 -37
View File
@@ -468,6 +468,10 @@ gb_internal void check_type_decl(CheckerContext *ctx, Entity *e, Ast *init_expr,
}
e->type = named;
if (!is_distinct) {
e->TypeName.is_type_alias = true;
}
check_type_path_push(ctx, e);
Type *bt = check_type_expr(ctx, te, named);
check_type_path_pop(ctx);
@@ -502,9 +506,9 @@ gb_internal void check_type_decl(CheckerContext *ctx, Entity *e, Ast *init_expr,
if (!is_distinct) {
e->type = bt;
named->Named.base = bt;
e->TypeName.is_type_alias = true;
}
e->TypeName.is_type_alias = !is_distinct;
if (decl->type_expr != nullptr) {
Type *t = check_type(ctx, decl->type_expr);
@@ -520,12 +524,90 @@ gb_internal void check_type_decl(CheckerContext *ctx, Entity *e, Ast *init_expr,
if (decl != nullptr) {
AttributeContext ac = {};
check_decl_attributes(ctx, decl->attributes, type_decl_attribute, &ac);
if (e->kind == Entity_TypeName && ac.objc_class != "") {
e->TypeName.objc_class_name = ac.objc_class;
if (ac.objc_is_implementation) {
e->TypeName.objc_is_implementation = ac.objc_is_implementation;
e->TypeName.objc_superclass = ac.objc_superclass;
e->TypeName.objc_ivar = ac.objc_ivar;
e->TypeName.objc_context_provider = ac.objc_context_provider;
mutex_lock(&ctx->info->objc_class_name_mutex);
bool class_exists = string_set_update(&ctx->info->obcj_class_name_set, ac.objc_class);
mutex_unlock(&ctx->info->objc_class_name_mutex);
if (class_exists) {
error(e->token, "@(objc_class) name '%.*s' has already been used elsewhere", LIT(ac.objc_class));
}
mpsc_enqueue(&ctx->info->objc_class_implementations, e);
GB_ASSERT(e->TypeName.objc_ivar == nullptr || e->TypeName.objc_ivar->kind == Type_Named);
// Enqueue the contex_provider proc to be checked after it is resolved
if (e->TypeName.objc_context_provider != nullptr) {
mpsc_enqueue(&ctx->checker->procs_with_objc_context_provider_to_check, e);
}
// TODO(harold): I think there's a Check elsewhere in the checker for checking cycles.
// See about moving this to the right location.
// Ensure superclass hierarchy are all Objective-C classes and does not cycle
// NOTE(harold): We check for superclass unconditionally (before checking if super is null)
// because this should be the case 99.99% of the time. Not subclassing something that
// is, or is the child of, NSObject means the objc runtime messaging will not properly work on this type.
TypeSet super_set{};
type_set_init(&super_set, 8);
defer (type_set_destroy(&super_set));
type_set_update(&super_set, e->type);
Type *super = ac.objc_superclass;
while (super != nullptr) {
if (type_set_update(&super_set, super)) {
error(e->token, "@(objc_superclass) Superclass hierarchy cycle encountered");
break;
}
check_single_global_entity(ctx->checker, super->Named.type_name, super->Named.type_name->decl_info);
if (super->kind != Type_Named) {
error(e->token, "@(objc_superclass) Referenced type must be a named struct");
break;
}
Type* named_type = base_named_type(super);
GB_ASSERT(named_type->kind == Type_Named);
if (!is_type_objc_object(named_type)) {
error(e->token, "@(objc_superclass) Superclass '%.*s' must be an Objective-C class", LIT(named_type->Named.name));
break;
}
if (named_type->Named.type_name->TypeName.objc_class_name == "") {
error(e->token, "@(objc_superclass) Superclass '%.*s' must have a valid @(objc_class) attribute", LIT(named_type->Named.name));
break;
}
super = named_type->Named.type_name->TypeName.objc_superclass;
}
} else {
if (ac.objc_superclass != nullptr) {
error(e->token, "@(objc_superclass) may only be applied when the @(obj_implement) attribute is also applied");
} else if (ac.objc_ivar != nullptr) {
error(e->token, "@(objc_ivar) may only be applied when the @(obj_implement) attribute is also applied");
} else if (ac.objc_context_provider != nullptr) {
error(e->token, "@(objc_context_provider) may only be applied when the @(obj_implement) attribute is also applied");
}
}
if (type_size_of(e->type) > 0) {
error(e->token, "@(objc_class) marked type must be of zero size");
}
} else if (ac.objc_is_implementation) {
error(e->token, "@(objc_implement) may only be applied when the @(objc_class) attribute is also applied");
}
}
@@ -918,7 +1000,7 @@ gb_internal String handle_link_name(CheckerContext *ctx, Token token, String lin
}
gb_internal void check_objc_methods(CheckerContext *ctx, Entity *e, AttributeContext const &ac) {
gb_internal void check_objc_methods(CheckerContext *ctx, Entity *e, AttributeContext &ac) {
if (!(ac.objc_name.len || ac.objc_is_class_method || ac.objc_type)) {
return;
}
@@ -930,48 +1012,107 @@ gb_internal void check_objc_methods(CheckerContext *ctx, Entity *e, AttributeCon
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);
GB_ASSERT(t->kind == Type_Named); // NOTE(harold): This is already checked for at the attribute resolution stage.
Entity *tn = t->Named.type_name;
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));
GB_ASSERT(tn->kind == Entity_TypeName);
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 (tn->scope != e->scope) {
error(e->token, "@(objc_name) attribute may only be applied to procedures and types within the same scope");
} else {
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});
}
// Enable implementation by default if the class is an implementer too and
// @objc_implement was not set to false explicitly in this proc.
bool implement = tn->TypeName.objc_is_implementation;
if (ac.objc_is_disabled_implement) {
implement = false;
}
if (implement) {
GB_ASSERT(e->kind == Entity_Procedure);
auto &proc = e->type->Proc;
Type *first_param = proc.param_count > 0 ? proc.params->Tuple.variables[0]->type : t_untyped_nil;
if (!tn->TypeName.objc_is_implementation) {
error(e->token, "@(objc_is_implement) attribute may only be applied to procedures whose class also have @(objc_is_implement) applied");
} else if (!ac.objc_is_class_method && !(first_param->kind == Type_Pointer && internal_check_is_assignable_to(t, first_param->Pointer.elem))) {
error(e->token, "Objective-C instance methods implementations require the first parameter to be a pointer to the class type set by @(objc_type)");
} else if (proc.calling_convention == ProcCC_Odin && !tn->TypeName.objc_context_provider) {
error(e->token, "Objective-C methods with Odin calling convention can only be used with classes that have @(objc_context_provider) set");
} else if (ac.objc_is_class_method && proc.calling_convention != ProcCC_CDecl) {
error(e->token, "Objective-C class methods (objc_is_class_method=true) that have @objc_is_implementation can only use \"c\" calling convention");
} else if (proc.result_count > 1) {
error(e->token, "Objective-C method implementations may return at most 1 value");
} else {
bool ok = true;
for (TypeNameObjCMetadataEntry const &entry : md->type_entries) {
if (entry.name == ac.objc_name) {
error(e->token, "Previous declaration of @(objc_name=\"%.*s\")", LIT(ac.objc_name));
ok = false;
break;
}
// Always export unconditionally
// NOTE(harold): This means check_objc_methods() MUST be called before
// e->Procedure.is_export is set in check_proc_decl()!
if (ac.is_export) {
error(e->token, "Explicit export not allowed when @(objc_implement) is set. It set exported implicitly");
}
if (ok) {
array_add(&md->type_entries, TypeNameObjCMetadataEntry{ac.objc_name, e});
if (ac.link_name != "") {
error(e->token, "Explicit linkage not allowed when @(objc_implement) is set. It set to \"strong\" implicitly");
}
ac.is_export = true;
ac.linkage = STR_LIT("strong");
auto method = ObjcMethodData{ ac, e };
method.ac.objc_selector = ac.objc_selector != "" ? ac.objc_selector : ac.objc_name;
CheckerInfo *info = ctx->info;
mutex_lock(&info->objc_method_mutex);
defer (mutex_unlock(&info->objc_method_mutex));
Array<ObjcMethodData>* method_list = map_get(&info->objc_method_implementations, t);
if (method_list) {
array_add(method_list, method);
} else {
auto list = array_make<ObjcMethodData>(permanent_allocator(), 1, 8);
list[0] = method;
map_set(&info->objc_method_implementations, t, list);
}
}
} else if (ac.objc_selector != "") {
error(e->token, "@(objc_selector) may only be applied to procedures that are Objective-C implementations.");
}
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});
}
}
}
@@ -1141,6 +1282,9 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
break;
}
// NOTE(harold): For Objective-C method implementations, this must happen after
// check_objc_methods() is called as it re-sets ac.is_export to true unconditionally.
// The same is true for the linkage, set below.
e->Procedure.entry_point_only = ac.entry_point_only;
e->Procedure.is_export = ac.is_export;
@@ -1225,6 +1369,7 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
e->Procedure.has_instrumentation = has_instrumentation;
e->Procedure.no_sanitize_address = ac.no_sanitize_address;
e->deprecated_message = ac.deprecated_message;
e->warning_message = ac.warning_message;
@@ -1240,6 +1385,7 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
}
}
// NOTE(harold): See export/linkage note above(where is_export is assigned) regarding Objective-C method implementations
bool is_foreign = e->Procedure.is_foreign;
bool is_export = e->Procedure.is_export;
+76 -24
View File
@@ -643,7 +643,7 @@ gb_internal bool find_or_generate_polymorphic_procedure(CheckerContext *old_c, E
gb_internal bool check_polymorphic_procedure_assignment(CheckerContext *c, Operand *operand, Type *type, Ast *poly_def_node, PolyProcData *poly_proc_data) {
if (operand->expr == nullptr) return false;
Entity *base_entity = entity_of_node(operand->expr);
Entity *base_entity = entity_from_expr(operand->expr);
if (base_entity == nullptr) return false;
return find_or_generate_polymorphic_procedure(c, base_entity, type, nullptr, poly_def_node, poly_proc_data);
}
@@ -995,14 +995,34 @@ gb_internal i64 assign_score_function(i64 distance, bool is_variadic=false) {
gb_internal bool check_is_assignable_to_with_score(CheckerContext *c, Operand *operand, Type *type, i64 *score_, bool is_variadic=false, bool allow_array_programming=true) {
i64 score = 0;
i64 distance = check_distance_between_types(c, operand, type, allow_array_programming);
bool ok = distance >= 0;
if (ok) {
score = assign_score_function(distance, is_variadic);
if (c == nullptr) {
GB_ASSERT(operand->mode == Addressing_Value);
GB_ASSERT(is_type_typed(operand->type));
}
if (score_) *score_ = score;
return ok;
if (operand->mode == Addressing_Invalid || type == t_invalid) {
if (score_) *score_ = 0;
return false;
}
// Handle polymorphic procedure used as default parameter
if (operand->mode == Addressing_Value && is_type_proc(type) && is_type_proc(operand->type)) {
Entity *e = entity_from_expr(operand->expr);
if (e != nullptr && e->kind == Entity_Procedure && is_type_polymorphic(e->type) && !is_type_polymorphic(type)) {
// Special case: Allow a polymorphic procedure to be used as default value for concrete proc type
// during the initial check. It will be properly instantiated when actually used.
if (score_) *score_ = assign_score_function(1);
return true;
}
}
i64 score = check_distance_between_types(c, operand, type, allow_array_programming);
if (score >= 0) {
if (score_) *score_ = assign_score_function(score, is_variadic);
return true;
}
if (score_) *score_ = 0;
return false;
}
@@ -1854,7 +1874,10 @@ gb_internal Entity *check_ident(CheckerContext *c, Operand *o, Ast *n, Type *nam
o->type = t_invalid;
}
if (o->type != nullptr && o->type->kind == Type_Named && o->type->Named.type_name->TypeName.is_type_alias) {
o->type = base_type(o->type);
Type *bt = base_type(o->type);
if (bt != nullptr) {
o->type = bt;
}
}
break;
@@ -2907,9 +2930,20 @@ gb_internal void check_comparison(CheckerContext *c, Ast *node, Operand *x, Oper
if (!defined) {
gbString xs = type_to_string(x->type, temporary_allocator());
gbString ys = type_to_string(y->type, temporary_allocator());
err_str = gb_string_make(temporary_allocator(),
gb_bprintf("operator '%.*s' not defined between the types '%s' and '%s'", LIT(token_strings[op]), xs, ys)
);
if (!is_type_comparable(x->type)) {
err_str = gb_string_make(temporary_allocator(),
gb_bprintf("Type '%s' is not simply comparable, so operator '%.*s' is not defined for it", xs, LIT(token_strings[op]))
);
} else if (!is_type_comparable(y->type)) {
err_str = gb_string_make(temporary_allocator(),
gb_bprintf("Type '%s' is not simply comparable, so operator '%.*s' is not defined for it", ys, LIT(token_strings[op]))
);
} else {
err_str = gb_string_make(temporary_allocator(),
gb_bprintf("Operator '%.*s' not defined between the types '%s' and '%s'", LIT(token_strings[op]), xs, ys)
);
}
} else {
Type *comparison_type = x->type;
if (x->type == err_type && is_operand_nil(*x)) {
@@ -2930,11 +2964,11 @@ gb_internal void check_comparison(CheckerContext *c, Ast *node, Operand *x, Oper
} else {
yt = type_to_string(y->type);
}
err_str = gb_string_make(temporary_allocator(), gb_bprintf("mismatched types '%s' and '%s'", xt, yt));
err_str = gb_string_make(temporary_allocator(), gb_bprintf("Mismatched types '%s' and '%s'", xt, yt));
}
if (err_str != nullptr) {
error(node, "Cannot compare expression, %s", err_str);
error(node, "Cannot compare expression. %s.", err_str);
x->type = t_untyped_bool;
} else {
if (x->mode == Addressing_Constant &&
@@ -5427,8 +5461,18 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod
}
}
if (operand->type && is_type_simd_vector(type_deref(operand->type))) {
String field_name = selector->Ident.token.string;
if (field_name.len == 1) {
error(op_expr, "Extracting an element from a #simd array using .%.*s syntax is disallowed, prefer `simd.extract`", LIT(field_name));
} else {
error(op_expr, "Extracting elements from a #simd array using .%.*s syntax is disallowed, prefer `swizzle`", LIT(field_name));
}
return nullptr;
}
if (entity == nullptr && selector->kind == Ast_Ident && operand->type != nullptr &&
(is_type_array(type_deref(operand->type)) || is_type_simd_vector(type_deref(operand->type)))) {
(is_type_array(type_deref(operand->type)))) {
String field_name = selector->Ident.token.string;
if (1 < field_name.len && field_name.len <= 4) {
u8 swizzles_xyzw[4] = {'x', 'y', 'z', 'w'};
@@ -5483,7 +5527,7 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod
Type *original_type = operand->type;
Type *array_type = base_type(type_deref(original_type));
GB_ASSERT(array_type->kind == Type_Array || array_type->kind == Type_SimdVector);
GB_ASSERT(array_type->kind == Type_Array);
i64 array_count = get_array_type_count(array_type);
@@ -5524,10 +5568,6 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod
break;
}
if (array_type->kind == Type_SimdVector) {
operand->mode = Addressing_Value;
}
Entity *swizzle_entity = alloc_entity_variable(nullptr, make_token_ident(field_name), operand->type, EntityState_Resolved);
add_type_and_value(c, operand->expr, operand->mode, operand->type, operand->value);
return swizzle_entity;
@@ -8142,7 +8182,7 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c
type = pt;
}
type = base_type(type);
if (type->kind == Type_Proc && type->Proc.optional_ok) {
if (type->kind == Type_Proc && type->Proc.optional_ok && type->Proc.result_count > 0) {
operand->mode = Addressing_OptionalOk;
operand->type = type->Proc.results->Tuple.variables[0]->type;
if (operand->expr != nullptr && operand->expr->kind == Ast_CallExpr) {
@@ -9013,7 +9053,19 @@ gb_internal ExprKind check_or_else_expr(CheckerContext *c, Operand *o, Ast *node
if (left_type != nullptr) {
if (!y_is_diverging) {
check_assignment(c, &y, left_type, name);
if (is_type_tuple(left_type)) {
if (!is_type_tuple(y.type)) {
error(y.expr, "Found a single value where a %td-valued expression was expected", left_type->Tuple.variables.count);
} else if (!are_types_identical(left_type, y.type)) {
gbString xt = type_to_string(left_type);
gbString yt = type_to_string(y.type);
error(y.expr, "Mismatched types, expected (%s), got (%s)", xt, yt);
gb_string_free(yt);
gb_string_free(xt);
}
} else {
check_assignment(c, &y, left_type, name);
}
}
} else {
check_or_else_expr_no_value_error(c, name, x, type_hint);
@@ -9389,7 +9441,7 @@ gb_internal bool is_expr_inferred_fixed_array(Ast *type_expr) {
}
gb_internal bool check_for_dynamic_literals(CheckerContext *c, Ast *node, AstCompoundLit *cl) {
if (cl->elems.count > 0 && (check_feature_flags(c, node) & OptInFeatureFlag_DynamicLiterals) == 0) {
if (cl->elems.count > 0 && (check_feature_flags(c, node) & OptInFeatureFlag_DynamicLiterals) == 0 && !build_context.dynamic_literals) {
ERROR_BLOCK();
error(node, "Compound literals of dynamic types are disabled by default");
error_line("\tSuggestion: If you want to enable them for this specific file, add '#+feature dynamic-literals' at the top of the file\n");
@@ -10972,7 +11024,7 @@ gb_internal ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast
return kind;
case_end;
case_ast_node(i, Implicit, node)
case_ast_node(i, Implicit, node);
switch (i->kind) {
case Token_context:
{
+43
View File
@@ -2108,10 +2108,12 @@ gb_internal void check_value_decl_stmt(CheckerContext *ctx, Ast *node, u32 mod_f
if (init_type == nullptr) {
init_type = t_invalid;
} else if (is_type_polymorphic(base_type(init_type))) {
/* DISABLED: This error seems too aggressive for instantiated generic types.
gbString str = type_to_string(init_type);
error(vd->type, "Invalid use of a polymorphic type '%s' in variable declaration", str);
gb_string_free(str);
init_type = t_invalid;
*/
}
if (init_type == t_invalid && entity_count == 1 && (mod_flags & (Stmt_BreakAllowed|Stmt_FallthroughAllowed))) {
Entity *e = entities[0];
@@ -2755,6 +2757,47 @@ gb_internal void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags)
if (ctx->decl) {
ctx->decl->defer_used += 1;
}
// NOTE(bill): Handling errors/warnings
Ast *stmt = ds->stmt;
Ast *original_stmt = stmt;
bool is_singular = true;
while (is_singular && stmt->kind == Ast_BlockStmt) {
Ast *inner_stmt = nullptr;
for (Ast *s : stmt->BlockStmt.stmts) {
if (s->kind == Ast_EmptyStmt) {
continue;
}
if (inner_stmt != nullptr) {
is_singular = false;
break;
}
inner_stmt = s;
}
if (inner_stmt != nullptr) {
stmt = inner_stmt;
}
}
if (!is_singular) {
stmt = original_stmt;
}
switch (stmt->kind) {
case_ast_node(as, AssignStmt, stmt);
if (as->op.kind != Token_Eq) {
break;
}
for (Ast *lhs : as->lhs) {
Entity *e = entity_of_node(lhs);
if (e && e->flags & EntityFlag_Result) {
error(lhs, "Assignments to named return values within 'defer' will not affect the value that is returned");
}
}
case_end;
}
}
case_end;
+45 -6
View File
@@ -1155,8 +1155,7 @@ gb_internal void check_bit_field_type(CheckerContext *ctx, Type *bit_field_type,
}
}
#if 0 // Reconsider at a later date
if (bit_sizes.count > 0 && is_type_integer(backing_type)) {
bool all_booleans = is_type_boolean(fields[0]->type);
bool all_ones = bit_sizes[0] == 1;
@@ -1182,7 +1181,7 @@ gb_internal void check_bit_field_type(CheckerContext *ctx, Type *bit_field_type,
}
}
}
#endif
bit_field_type->BitField.fields = slice_from_array(fields);
bit_field_type->BitField.bit_sizes = slice_from_array(bit_sizes);
@@ -1911,9 +1910,18 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para
case ParameterValue_Location:
case ParameterValue_Expression:
case ParameterValue_Value:
// Special case for polymorphic procedures as default values
if (param_value.ast_value != nullptr) {
Entity *e = entity_from_expr(param_value.ast_value);
if (e != nullptr && e->kind == Entity_Procedure && is_type_polymorphic(e->type)) {
// Allow polymorphic procedures as default parameter values
// The type will be correctly determined at call site
break;
}
}
gbString str = type_to_string(type);
error(params[i], "A default value for a parameter must not be a polymorphic constant type, got %s", str);
gb_string_free(str);
gb_string_free(str);
break;
}
}
@@ -2082,7 +2090,9 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para
if (type != t_invalid && !check_is_assignable_to(ctx, &op, type, allow_array_programming)) {
bool ok = true;
if (p->flags&FieldFlag_any_int) {
if ((!is_type_integer(op.type) && !is_type_enum(op.type)) || (!is_type_integer(type) && !is_type_enum(type))) {
if (op.type == nullptr) {
ok = false;
} else if ((!is_type_integer(op.type) && !is_type_enum(op.type)) || (!is_type_integer(type) && !is_type_enum(type))) {
ok = false;
} else if (!check_is_castable_to(ctx, &op, type)) {
ok = false;
@@ -2773,6 +2783,21 @@ gb_internal void add_map_key_type_dependencies(CheckerContext *ctx, Type *key) {
return;
}
if (key->kind == Type_Basic) {
if (key->Basic.flags & BasicFlag_Quaternion) {
add_package_dependency(ctx, "runtime", "default_hasher_f64");
add_package_dependency(ctx, "runtime", "default_hasher_quaternion256");
return;
} else if (key->Basic.flags & BasicFlag_Complex) {
add_package_dependency(ctx, "runtime", "default_hasher_f64");
add_package_dependency(ctx, "runtime", "default_hasher_complex128");
return;
} else if (key->Basic.flags & BasicFlag_Float) {
add_package_dependency(ctx, "runtime", "default_hasher_f64");
return;
}
}
if (key->kind == Type_Struct) {
add_package_dependency(ctx, "runtime", "default_hasher");
for_array(i, key->Struct.fields) {
@@ -3281,8 +3306,11 @@ gb_internal void check_array_type_internal(CheckerContext *ctx, Ast *e, Type **t
if (generic_type != nullptr) {
// Ignore
} else if (count < 1 || !is_power_of_two(count)) {
error(at->count, "Invalid length for #simd, expected a power of two length, got '%lld'", cast(long long)count);
*type = alloc_type_array(elem, count, generic_type);
if (ctx->disallow_polymorphic_return_types && count == 0) {
return;
}
error(at->count, "Invalid length for #simd, expected a power of two length, got '%lld'", cast(long long)count);
return;
}
@@ -3502,6 +3530,17 @@ gb_internal bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, T
elem = o.type;
}
if (!ctx->in_polymorphic_specialization && ctx->disallow_polymorphic_return_types) {
Type *t = base_type(elem);
if (t != nullptr &&
unparen_expr(pt->type)->kind == Ast_Ident &&
is_type_polymorphic_record_unspecialized(t)) {
gbString err_str = expr_to_string(e);
error(e, "Invalid use of a non-specialized polymorphic type '%s'", err_str);
gb_string_free(err_str);
}
}
if (pt->tag != nullptr) {
GB_ASSERT(pt->tag->kind == Ast_BasicDirective);
+155 -3
View File
@@ -728,12 +728,17 @@ gb_internal void check_scope_usage_internal(Checker *c, Scope *scope, u64 vet_fl
bool is_unused = false;
if (vet_unused && check_vet_unused(c, e, &ve_unused)) {
is_unused = true;
} else if (vet_unused_procedures &&
e->kind == Entity_Procedure) {
} else if (vet_unused_procedures && e->kind == Entity_Procedure) {
if (e->flags&EntityFlag_Used) {
is_unused = false;
} else if (e->flags & EntityFlag_Require) {
is_unused = false;
} else if (e->flags & EntityFlag_Init) {
is_unused = false;
} else if (e->flags & EntityFlag_Fini) {
is_unused = false;
} else if (e->Procedure.is_export) {
is_unused = false;
} else if (e->pkg && e->pkg->kind == Package_Init && e->token.string == "main") {
is_unused = false;
} else {
@@ -751,7 +756,7 @@ gb_internal void check_scope_usage_internal(Checker *c, Scope *scope, u64 vet_fl
array_add(&vetted_entities, ve_unused);
} else if (is_shadowed) {
array_add(&vetted_entities, ve_shadowed);
} else if (e->kind == Entity_Variable && (e->flags & (EntityFlag_Param|EntityFlag_Using|EntityFlag_Static)) == 0 && !e->Variable.is_global) {
} else if (e->kind == Entity_Variable && (e->flags & (EntityFlag_Param|EntityFlag_Using|EntityFlag_Static|EntityFlag_Field)) == 0 && !e->Variable.is_global) {
i64 sz = type_size_of(e->type);
// TODO(bill): When is a good size warn?
// Is >256 KiB good enough?
@@ -1351,10 +1356,12 @@ gb_internal void init_universal(void) {
t_objc_object = add_global_type_name(intrinsics_pkg->scope, str_lit("objc_object"), alloc_type_struct_complete());
t_objc_selector = add_global_type_name(intrinsics_pkg->scope, str_lit("objc_selector"), alloc_type_struct_complete());
t_objc_class = add_global_type_name(intrinsics_pkg->scope, str_lit("objc_class"), alloc_type_struct_complete());
t_objc_ivar = add_global_type_name(intrinsics_pkg->scope, str_lit("objc_ivar"), alloc_type_struct_complete());
t_objc_id = alloc_type_pointer(t_objc_object);
t_objc_SEL = alloc_type_pointer(t_objc_selector);
t_objc_Class = alloc_type_pointer(t_objc_class);
t_objc_Ivar = alloc_type_pointer(t_objc_ivar);
}
}
@@ -1387,6 +1394,10 @@ gb_internal void init_checker_info(CheckerInfo *i) {
array_init(&i->defineables, a);
map_init(&i->objc_msgSend_types);
mpsc_init(&i->objc_class_implementations, a);
string_set_init(&i->obcj_class_name_set, 0);
map_init(&i->objc_method_implementations);
string_map_init(&i->load_file_cache);
array_init(&i->all_procedures, heap_allocator());
@@ -1497,6 +1508,8 @@ gb_internal void init_checker(Checker *c) {
TIME_SECTION("init proc queues");
mpsc_init(&c->procs_with_deferred_to_check, a); //, 1<<10);
mpsc_init(&c->procs_with_objc_context_provider_to_check, a);
// NOTE(bill): 1 Mi elements should be enough on average
array_init(&c->procs_to_check, heap_allocator(), 0, 1<<20);
@@ -3662,6 +3675,33 @@ gb_internal DECL_ATTRIBUTE_PROC(proc_decl_attribute) {
}
}
return true;
} else if (name == "objc_implement") {
ExactValue ev = check_decl_attribute_value(c, value);
if (ev.kind == ExactValue_Bool) {
ac->objc_is_implementation = ev.value_bool;
if (!ac->objc_is_implementation) {
ac->objc_is_disabled_implement = true;
}
} else if (ev.kind == ExactValue_Invalid) {
ac->objc_is_implementation = true;
} else {
error(elem, "Expected a boolean value, or no value, for '%.*s'", LIT(name));
}
return true;
} else if (name == "objc_selector") {
ExactValue ev = check_decl_attribute_value(c, value);
if (ev.kind == ExactValue_String) {
if (string_is_valid_identifier(ev.value_string)) {
ac->objc_selector = ev.value_string;
} else {
error(elem, "Invalid identifier for '%.*s', got '%.*s'", LIT(name), LIT(ev.value_string));
}
} else {
error(elem, "Expected a string value for '%.*s'", LIT(name));
}
return true;
} else if (name == "require_target_feature") {
ExactValue ev = check_decl_attribute_value(c, value);
if (ev.kind == ExactValue_String) {
@@ -3711,6 +3751,12 @@ gb_internal DECL_ATTRIBUTE_PROC(proc_decl_attribute) {
}
ac->instrumentation_exit = true;
return true;
} else if (name == "no_sanitize_address") {
if (value != nullptr) {
error(value, "'%.*s' expects no parameter", LIT(name));
}
ac->no_sanitize_address = true;
return true;
}
return false;
}
@@ -3901,6 +3947,51 @@ gb_internal DECL_ATTRIBUTE_PROC(type_decl_attribute) {
ac->objc_class = ev.value_string;
}
return true;
} else if (name == "objc_implement") {
ExactValue ev = check_decl_attribute_value(c, value);
if (ev.kind == ExactValue_Bool) {
ac->objc_is_implementation = ev.value_bool;
} else if (ev.kind == ExactValue_Invalid) {
ac->objc_is_implementation = true;
} else {
error(elem, "Expected a boolean value, or no value, for '%.*s'", LIT(name));
}
return true;
} else if (name == "objc_superclass") {
Type *objc_superclass = check_type(c, value);
if (objc_superclass != nullptr) {
ac->objc_superclass = objc_superclass;
} else {
error(value, "'%.*s' expected a named type", LIT(name));
}
return true;
} else if (name == "objc_ivar") {
Type *objc_ivar = check_type(c, value);
if (objc_ivar != nullptr && objc_ivar->kind == Type_Named) {
ac->objc_ivar = objc_ivar;
} else {
error(value, "'%.*s' expected a named type", LIT(name));
}
return true;
} else if (name == "objc_context_provider") {
Operand o = {};
check_expr(c, &o, value);
Entity *e = entity_of_node(o.expr);
if (e != nullptr) {
if (ac->objc_context_provider != nullptr) {
error(elem, "Previous usage of a 'objc_context_provider' attribute");
}
if (e->kind != Entity_Procedure) {
error(elem, "'objc_context_provider' must refer to a procedure");
} else {
ac->objc_context_provider = e;
}
return true;
}
}
return false;
}
@@ -6234,6 +6325,12 @@ gb_internal void check_deferred_procedures(Checker *c) {
continue;
}
if (dst->flags & EntityFlag_Disabled) {
// Prevent procedures that have been disabled from acting as deferrals.
src->Procedure.deferred_procedure = {};
continue;
}
GB_ASSERT(is_type_proc(src->type));
GB_ASSERT(is_type_proc(dst->type));
Type *src_params = base_type(src->type)->Proc.params;
@@ -6389,6 +6486,44 @@ gb_internal void check_deferred_procedures(Checker *c) {
}
gb_internal void check_objc_context_provider_procedures(Checker *c) {
for (Entity *e = nullptr; mpsc_dequeue(&c->procs_with_objc_context_provider_to_check, &e); /**/) {
GB_ASSERT(e->kind == Entity_TypeName);
Entity *proc_entity = e->TypeName.objc_context_provider;
GB_ASSERT(proc_entity->kind == Entity_Procedure);
auto &proc = proc_entity->type->Proc;
Type *return_type = proc.result_count != 1 ? t_untyped_nil : base_named_type(proc.results->Tuple.variables[0]->type);
if (return_type != t_context) {
error(proc_entity->token, "The @(objc_context_provider) procedure must only return a context.");
}
const char *self_param_err = "The @(objc_context_provider) procedure must take as a parameter a single pointer to the @(objc_type) value.";
if (proc.param_count != 1) {
error(proc_entity->token, self_param_err);
}
Type *self_param = base_type(proc.params->Tuple.variables[0]->type);
if (self_param->kind != Type_Pointer) {
error(proc_entity->token, self_param_err);
}
Type *self_type = base_named_type(self_param->Pointer.elem);
if (!internal_check_is_assignable_to(self_type, e->type) &&
!(e->TypeName.objc_ivar && internal_check_is_assignable_to(self_type, e->TypeName.objc_ivar))) {
error(proc_entity->token, self_param_err);
}
if (proc.calling_convention != ProcCC_CDecl && proc.calling_convention != ProcCC_Contextless) {
error(e->token, self_param_err);
}
if (proc.is_polymorphic) {
error(e->token, self_param_err);
}
}
}
gb_internal void check_unique_package_names(Checker *c) {
ERROR_BLOCK();
@@ -6423,6 +6558,19 @@ gb_internal void check_unique_package_names(Checker *c) {
"\tThere is no relation between a package name and the directory that contains it, so they can be completely different\n"
"\tA package name is required for link name prefixing to have a consistent ABI\n");
error_line("%s found at previous location\n", token_pos_to_string(ast_token(prev).pos));
// NOTE(Jeroen): Check if the conflicting imports are the same case-folded directory
// See https://github.com/odin-lang/Odin/issues/5080
#if defined(GB_SYSTEM_WINDOWS)
String dir_a = pkg->files[0]->directory;
String dir_b = (*found)->files[0]->directory;
if (str_eq_ignore_case(dir_a, dir_b)) {
error_line("\tRemember that Windows case-folds paths, and so %.*s and %.*s are the same directory.\n", LIT(dir_a), LIT(dir_b));
// Could also perform a FS lookup to check which of the two is the actual directory and suggest it, but this should be enough.
}
#endif
end_error_block();
}
}
@@ -6536,6 +6684,7 @@ gb_internal void check_update_dependency_tree_for_procedures(Checker *c) {
}
}
gb_internal void check_parsed_files(Checker *c) {
TIME_SECTION("map full filepaths to scope");
add_type_info_type(&c->builtin_ctx, t_invalid);
@@ -6645,6 +6794,9 @@ gb_internal void check_parsed_files(Checker *c) {
TIME_SECTION("check deferred procedures");
check_deferred_procedures(c);
TIME_SECTION("check objc context provider procedures");
check_objc_context_provider_procedures(c);
TIME_SECTION("calculate global init order");
calculate_global_init_order(c);
+24 -3
View File
@@ -139,6 +139,7 @@ struct AttributeContext {
bool entry_point_only : 1;
bool instrumentation_enter : 1;
bool instrumentation_exit : 1;
bool no_sanitize_address : 1;
bool rodata : 1;
bool ignore_duplicates : 1;
u32 optimization_mode; // ProcedureOptimizationMode
@@ -148,8 +149,14 @@ struct AttributeContext {
String objc_class;
String objc_name;
bool objc_is_class_method;
String objc_selector;
Type * objc_type;
Type * objc_superclass;
Type * objc_ivar;
Entity *objc_context_provider;
bool objc_is_class_method;
bool objc_is_implementation; // This struct or proc provides a class/method implementation, not a binding to an existing type.
bool objc_is_disabled_implement; // This means the method explicitly set @objc_implement to false so it won't be inferred from the class' attribute.
String require_target_feature; // required by the target micro-architecture
String enable_target_feature; // will be enabled for the procedure only
@@ -365,6 +372,11 @@ struct ObjcMsgData {
Type *proc_type;
};
struct ObjcMethodData {
AttributeContext ac;
Entity *proc_entity;
};
enum LoadFileTier {
LoadFileTier_Invalid,
LoadFileTier_Exists,
@@ -476,9 +488,17 @@ struct CheckerInfo {
MPSCQueue<Ast *> intrinsics_entry_point_usage;
BlockingMutex objc_types_mutex;
BlockingMutex objc_objc_msgSend_mutex;
PtrMap<Ast *, ObjcMsgData> objc_msgSend_types;
BlockingMutex objc_class_name_mutex;
StringSet obcj_class_name_set;
MPSCQueue<Entity *> objc_class_implementations;
BlockingMutex objc_method_mutex;
PtrMap<Type *, Array<ObjcMethodData>> objc_method_implementations;
BlockingMutex load_file_mutex;
StringMap<LoadFileCache *> load_file_cache;
@@ -555,6 +575,7 @@ struct Checker {
CheckerContext builtin_ctx;
MPSCQueue<Entity *> procs_with_deferred_to_check;
MPSCQueue<Entity *> procs_with_objc_context_provider_to_check;
Array<ProcInfo *> procs_to_check;
BlockingMutex nested_proc_lits_mutex;
@@ -629,4 +650,4 @@ gb_internal void add_untyped_expressions(CheckerInfo *cinfo, UntypedExprInfoMap
gb_internal GenTypesData *ensure_polymorphic_record_entity_has_gen_types(CheckerContext *ctx, Type *original_type);
gb_internal void init_map_internal_types(Type *type);
gb_internal void init_map_internal_types(Type *type);
+23
View File
@@ -26,6 +26,7 @@ enum BuiltinProcId {
BuiltinProc_conj,
BuiltinProc_expand_values,
BuiltinProc_compress_values,
BuiltinProc_min,
BuiltinProc_max,
@@ -170,8 +171,12 @@ BuiltinProc__simd_begin,
BuiltinProc_simd_extract,
BuiltinProc_simd_replace,
BuiltinProc_simd_reduce_add_bisect,
BuiltinProc_simd_reduce_mul_bisect,
BuiltinProc_simd_reduce_add_ordered,
BuiltinProc_simd_reduce_mul_ordered,
BuiltinProc_simd_reduce_add_pairs,
BuiltinProc_simd_reduce_mul_pairs,
BuiltinProc_simd_reduce_min,
BuiltinProc_simd_reduce_max,
BuiltinProc_simd_reduce_and,
@@ -205,6 +210,9 @@ BuiltinProc__simd_begin,
BuiltinProc_simd_masked_expand_load,
BuiltinProc_simd_masked_compress_store,
BuiltinProc_simd_indices,
// Platform specific SIMD intrinsics
BuiltinProc_simd_x86__MM_SHUFFLE,
BuiltinProc__simd_end,
@@ -227,6 +235,9 @@ BuiltinProc__type_begin,
BuiltinProc_type_convert_variants_to_pointers,
BuiltinProc_type_merge,
BuiltinProc_type_integer_to_unsigned,
BuiltinProc_type_integer_to_signed,
BuiltinProc__type_simple_boolean_begin,
BuiltinProc_type_is_boolean,
BuiltinProc_type_is_integer,
@@ -331,6 +342,7 @@ BuiltinProc__type_end,
BuiltinProc_objc_find_class,
BuiltinProc_objc_register_selector,
BuiltinProc_objc_register_class,
BuiltinProc_objc_ivar_get,
BuiltinProc_constant_utf16_cstring,
@@ -368,6 +380,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT("conj"), 1, false, Expr_Expr, BuiltinProcPkg_builtin},
{STR_LIT("expand_values"), 1, false, Expr_Expr, BuiltinProcPkg_builtin},
{STR_LIT("compress_values"), 1, true, Expr_Expr, BuiltinProcPkg_builtin},
{STR_LIT("min"), 1, true, Expr_Expr, BuiltinProcPkg_builtin},
{STR_LIT("max"), 1, true, Expr_Expr, BuiltinProcPkg_builtin},
@@ -515,8 +528,12 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT("simd_extract"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_replace"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_reduce_add_bisect"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_reduce_mul_bisect"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_reduce_add_ordered"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_reduce_mul_ordered"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_reduce_add_pairs"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_reduce_mul_pairs"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_reduce_min"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_reduce_max"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_reduce_and"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
@@ -551,6 +568,8 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT("simd_masked_expand_load"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_masked_compress_store"), 3, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_indices"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_x86__MM_SHUFFLE"), 4, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
@@ -569,6 +588,9 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT("type_convert_variants_to_pointers"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_merge"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_integer_to_unsigned"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_integer_to_signed"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
{STR_LIT("type_is_boolean"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_is_integer"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
@@ -673,6 +695,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT("objc_find_class"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("objc_register_selector"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
{STR_LIT("objc_register_class"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
{STR_LIT("objc_ivar_get"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
{STR_LIT("constant_utf16_cstring"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+5
View File
@@ -235,6 +235,10 @@ struct Entity {
Type * type_parameter_specialization;
String ir_mangled_name;
bool is_type_alias;
bool objc_is_implementation;
Type* objc_superclass;
Type* objc_ivar;
Entity*objc_context_provider;
String objc_class_name;
TypeNameObjCMetadata *objc_metadata;
} TypeName;
@@ -258,6 +262,7 @@ struct Entity {
bool is_memcpy_like : 1;
bool uses_branch_location : 1;
bool is_anonymous : 1;
bool no_sanitize_address : 1;
} Procedure;
struct {
Array<Entity *> entities;
+4
View File
@@ -954,6 +954,10 @@ gb_internal bool compare_exact_values(TokenKind op, ExactValue x, ExactValue y)
case ExactValue_Float: {
f64 a = x.value_float;
f64 b = y.value_float;
if (isnan(a) || isnan(b)) {
return op == Token_NotEq;
}
switch (op) {
case Token_CmpEq: return cmp_f64(a, b) == 0;
case Token_NotEq: return cmp_f64(a, b) != 0;
+17 -9
View File
@@ -5838,18 +5838,25 @@ gb_inline isize gb_printf_err_va(char const *fmt, va_list va) {
gb_inline isize gb_fprintf_va(struct gbFile *f, char const *fmt, va_list va) {
char buf[4096];
isize len = gb_snprintf_va(buf, gb_size_of(buf), fmt, va);
va_list va_save;
va_copy(va_save, va);
isize len = gb_snprintf_va(buf, gb_size_of(buf), fmt, va_save);
va_end(va_save);
char *new_buf = NULL;
isize n = gb_size_of(buf);
while (len < 0) {
va_copy(va_save, va);
defer (va_end(va_save));
n <<= 1;
gb_free(gb_heap_allocator(), new_buf);
new_buf = gb_alloc_array(gb_heap_allocator(), char, n);;
len = gb_snprintf_va(new_buf, n, fmt, va);
len = gb_snprintf_va(new_buf, n, fmt, va_save);
}
gb_file_write(f, buf, len-1); // NOTE(bill): prevent extra whitespace
if (new_buf != NULL) {
gb_file_write(f, new_buf, len-1); // NOTE(bill): prevent extra whitespace
gb_free(gb_heap_allocator(), new_buf);
} else {
gb_file_write(f, buf, len-1); // NOTE(bill): prevent extra whitespace
}
return len;
}
@@ -5912,7 +5919,7 @@ gb_internal isize gb__print_string(char *text, isize max_len, gbprivFmtInfo *inf
len = info->precision < len ? info->precision : len;
}
res += gb_strlcpy(text, str, len);
res += gb_strlcpy(text, str, gb_min(len, remaining));
if (info->width > res) {
isize padding = info->width - len;
@@ -5930,7 +5937,7 @@ gb_internal isize gb__print_string(char *text, isize max_len, gbprivFmtInfo *inf
}
}
res += gb_strlcpy(text, str, len);
res += gb_strlcpy(text, str, gb_min(len, remaining));
}
@@ -6066,15 +6073,16 @@ gb_internal isize gb__print_f64(char *text, isize max_len, gbprivFmtInfo *info,
gb_no_inline isize gb_snprintf_va(char *text, isize max_len, char const *fmt, va_list va) {
char const *text_begin = text;
isize remaining = max_len, res;
isize remaining = max_len - 1, res;
while (*fmt) {
while (*fmt && remaining > 0) {
gbprivFmtInfo info = {0};
isize len = 0;
info.precision = -1;
while (*fmt && *fmt != '%' && remaining) {
while (remaining > 0 && *fmt && *fmt != '%') {
*text++ = *fmt++;
remaining--;
}
if (*fmt == '%') {
@@ -6240,7 +6248,7 @@ gb_no_inline isize gb_snprintf_va(char *text, isize max_len, char const *fmt, va
text += len;
if (len >= remaining) {
remaining = gb_min(remaining, 1);
remaining = 0;
} else {
remaining -= len;
}
+32 -5
View File
@@ -143,7 +143,7 @@ gb_internal i32 linker_stage(LinkerData *gen) {
LIT(target_arch_names[build_context.metrics.arch])
);
#endif
} else if (build_context.cross_compiling && build_context.different_os) {
} else if (build_context.cross_compiling && (build_context.different_os || selected_subtarget != Subtarget_Default)) {
switch (selected_subtarget) {
case Subtarget_Android:
is_cross_linking = true;
@@ -603,7 +603,7 @@ try_cross_linking:;
// object
// dynamic lib
// static libs, absolute full path relative to the file in which the lib was imported from
lib_str = gb_string_append_fmt(lib_str, " %.*s ", LIT(lib));
lib_str = gb_string_append_fmt(lib_str, " \"%.*s\" ", LIT(lib));
} else {
// dynamic or static system lib, just link regularly searching system library paths
lib_str = gb_string_append_fmt(lib_str, " -l%.*s ", LIT(lib));
@@ -643,9 +643,10 @@ try_cross_linking:;
android_glue_object = concatenate4_strings(temporary_allocator(), temp_dir, str_lit("android_native_app_glue-"), hash, str_lit(".o"));
android_glue_static_lib = concatenate4_strings(permanent_allocator(), temp_dir, str_lit("libandroid_native_app_glue-"), hash, str_lit(".a"));
gbString glue = gb_string_make(heap_allocator(), clang_path);
gbString glue = gb_string_make_length(heap_allocator(), ODIN_ANDROID_NDK_TOOLCHAIN.text, ODIN_ANDROID_NDK_TOOLCHAIN.len);
defer (gb_string_free(glue));
glue = gb_string_append_fmt(glue, "bin/clang");
glue = gb_string_append_fmt(glue, " --target=aarch64-linux-android%d ", ODIN_ANDROID_API_LEVEL);
glue = gb_string_appendc(glue, "-c \"");
glue = gb_string_append_length(glue, ODIN_ANDROID_NDK.text, ODIN_ANDROID_NDK.len);
@@ -655,6 +656,11 @@ try_cross_linking:;
glue = gb_string_append_length(glue, android_glue_object.text, android_glue_object.len);
glue = gb_string_appendc(glue, "\" ");
glue = gb_string_appendc(glue, "--sysroot \"");
glue = gb_string_append_length(glue, ODIN_ANDROID_NDK_TOOLCHAIN.text, ODIN_ANDROID_NDK_TOOLCHAIN.len);
glue = gb_string_appendc(glue, "sysroot");
glue = gb_string_appendc(glue, "\" ");
glue = gb_string_appendc(glue, "\"-I");
glue = gb_string_append_length(glue, ODIN_ANDROID_NDK_TOOLCHAIN.text, ODIN_ANDROID_NDK_TOOLCHAIN.len);
glue = gb_string_appendc(glue, "sysroot/usr/include/");
@@ -763,7 +769,17 @@ try_cross_linking:;
gbString platform_lib_str = gb_string_make(heap_allocator(), "");
defer (gb_string_free(platform_lib_str));
if (build_context.metrics.os == TargetOs_darwin) {
platform_lib_str = gb_string_appendc(platform_lib_str, "-Wl,-syslibroot /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk -L/usr/local/lib ");
// Get the MacOSX SDK path.
gbString darwin_sdk_path = gb_string_make(temporary_allocator(), "");
if (!system_exec_command_line_app_output("xcrun --sdk macosx --show-sdk-path", &darwin_sdk_path)) {
darwin_sdk_path = gb_string_set(darwin_sdk_path, "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk");
} else {
// Trim the trailing newline.
darwin_sdk_path = gb_string_trim_space(darwin_sdk_path);
}
platform_lib_str = gb_string_append_fmt(platform_lib_str, "--sysroot %s ", darwin_sdk_path);
platform_lib_str = gb_string_appendc(platform_lib_str, "-L/usr/local/lib ");
// Homebrew's default library path, checking if it exists to avoid linking warnings.
if (gb_file_exists("/opt/homebrew/lib")) {
@@ -785,6 +801,9 @@ try_cross_linking:;
// This points the linker to where the entry point is
link_settings = gb_string_appendc(link_settings, "-e _main ");
}
} else if (build_context.metrics.os == TargetOs_freebsd) {
// FreeBSD pkg installs third-party shared libraries in /usr/local/lib.
platform_lib_str = gb_string_appendc(platform_lib_str, "-Wl,-L/usr/local/lib ");
} else if (build_context.metrics.os == TargetOs_openbsd) {
// OpenBSD ports install shared libraries in /usr/local/lib. Also, we must explicitly link libpthread.
platform_lib_str = gb_string_appendc(platform_lib_str, "-lpthread -Wl,-L/usr/local/lib ");
@@ -803,6 +822,9 @@ try_cross_linking:;
platform_lib_str = gb_string_append_length(platform_lib_str, ODIN_ANDROID_NDK_TOOLCHAIN_LIB_LEVEL.text, ODIN_ANDROID_NDK_TOOLCHAIN_LIB_LEVEL.len);
platform_lib_str = gb_string_appendc(platform_lib_str, "\" ");
platform_lib_str = gb_string_appendc(platform_lib_str, "-landroid ");
platform_lib_str = gb_string_appendc(platform_lib_str, "-llog ");
platform_lib_str = gb_string_appendc(platform_lib_str, "\"--sysroot=");
platform_lib_str = gb_string_append_length(platform_lib_str, ODIN_ANDROID_NDK_TOOLCHAIN_SYSROOT.text, ODIN_ANDROID_NDK_TOOLCHAIN_SYSROOT.len);
platform_lib_str = gb_string_appendc(platform_lib_str, "\" ");
@@ -835,11 +857,16 @@ try_cross_linking:;
}
}
gbString link_command_line = gb_string_make(heap_allocator(), clang_path);
gbString link_command_line = gb_string_make(heap_allocator(), "");
defer (gb_string_free(link_command_line));
if (is_android) {
gbString ndk_bin_directory = gb_string_make_length(temporary_allocator(), ODIN_ANDROID_NDK_TOOLCHAIN.text, ODIN_ANDROID_NDK_TOOLCHAIN.len);
link_command_line = gb_string_appendc(link_command_line, ndk_bin_directory);
link_command_line = gb_string_appendc(link_command_line, "bin/clang");
link_command_line = gb_string_append_fmt(link_command_line, " --target=aarch64-linux-android%d ", ODIN_ANDROID_API_LEVEL);
} else {
link_command_line = gb_string_appendc(link_command_line, clang_path);
}
link_command_line = gb_string_appendc(link_command_line, " -Wno-unused-command-line-argument ");
link_command_line = gb_string_appendc(link_command_line, object_files);
+96 -37
View File
@@ -573,7 +573,9 @@ namespace lbAbiAmd64SysV {
gb_internal void classify_with(LLVMTypeRef t, Array<RegClass> *cls, i64 ix, i64 off);
gb_internal void fixup(LLVMTypeRef t, Array<RegClass> *cls);
gb_internal lbArgType amd64_type(LLVMContextRef c, LLVMTypeRef type, Amd64TypeAttributeKind attribute_kind, ProcCallingConvention calling_convention);
gb_internal lbArgType amd64_type(LLVMContextRef c, LLVMTypeRef type, Amd64TypeAttributeKind attribute_kind, ProcCallingConvention calling_convention,
bool is_arg,
i32 *int_regs, i32 *sse_regs);
gb_internal Array<RegClass> classify(LLVMTypeRef t);
gb_internal LLVMTypeRef llreg(LLVMContextRef c, Array<RegClass> const &reg_classes, LLVMTypeRef type);
@@ -583,7 +585,9 @@ namespace lbAbiAmd64SysV {
}
LB_ABI_MODIFY_RETURN_IF_TUPLE_MACRO();
return amd64_type(c, return_type, Amd64TypeAttribute_StructRect, ft->calling_convention);
return amd64_type(c, return_type, Amd64TypeAttribute_StructRect, ft->calling_convention,
false,
nullptr, nullptr);
}
gb_internal LB_ABI_INFO(abi_info) {
@@ -592,10 +596,16 @@ namespace lbAbiAmd64SysV {
ft->ctx = c;
ft->calling_convention = calling_convention;
i32 int_regs = 6; // rdi, rsi, rdx, rcx, r8, r9
i32 sse_regs = 8; // xmm0-xmm7
ft->args = array_make<lbArgType>(lb_function_type_args_allocator(), arg_count);
for (unsigned i = 0; i < arg_count; i++) {
ft->args[i] = amd64_type(c, arg_types[i], Amd64TypeAttribute_ByVal, calling_convention);
ft->args[i] = amd64_type(c, arg_types[i], Amd64TypeAttribute_ByVal, calling_convention,
true,
&int_regs, &sse_regs);
}
ft->ret = compute_return_type(ft, c, return_type, return_is_defined, return_is_tuple);
return ft;
@@ -654,17 +664,79 @@ namespace lbAbiAmd64SysV {
}
gb_internal lbArgType amd64_type(LLVMContextRef c, LLVMTypeRef type, Amd64TypeAttributeKind attribute_kind, ProcCallingConvention calling_convention) {
gb_internal bool is_aggregate(LLVMTypeRef type) {
LLVMTypeKind kind = LLVMGetTypeKind(type);
switch (kind) {
case LLVMStructTypeKind:
if (LLVMCountStructElementTypes(type) == 1) {
return is_aggregate(LLVMStructGetTypeAtIndex(type, 0));
}
return true;
case LLVMArrayTypeKind:
if (LLVMGetArrayLength(type) == 1) {
return is_aggregate(LLVMGetElementType(type));
}
return true;
}
return false;
};
gb_internal lbArgType amd64_type(LLVMContextRef c, LLVMTypeRef type, Amd64TypeAttributeKind attribute_kind, ProcCallingConvention calling_convention,
bool is_arg,
i32 *int_regs, i32 *sse_regs) {
auto cls = classify(type);
i32 needed_int = 0;
i32 needed_sse = 0;
for (auto c : cls) {
switch (c) {
case RegClass_Int:
needed_int += 1;
break;
case RegClass_SSEFs:
case RegClass_SSEFv:
case RegClass_SSEDs:
case RegClass_SSEDv:
case RegClass_SSEInt8:
case RegClass_SSEInt16:
case RegClass_SSEInt32:
case RegClass_SSEInt64:
case RegClass_SSEInt128:
case RegClass_SSEUp:
needed_sse += 1;
break;
}
}
bool ran_out_of_regs = false;
if (int_regs && sse_regs) {
*int_regs -= needed_int;
*sse_regs -= needed_sse;
bool int_ok = *int_regs >= 0;
bool sse_ok = *sse_regs >= 0;
*int_regs = gb_max(*int_regs, 0);
*sse_regs = gb_max(*sse_regs, 0);
if ((!int_ok || !sse_ok) && is_aggregate(type)) {
ran_out_of_regs = true;
}
}
if (is_register(type)) {
LLVMAttributeRef attribute = nullptr;
if (type == LLVMInt1TypeInContext(c)) {
attribute = lb_create_enum_attribute(c, "zeroext");
}
return lb_arg_type_direct(type, nullptr, nullptr, attribute);
}
auto cls = classify(type);
if (is_mem_cls(cls, attribute_kind)) {
} else if (ran_out_of_regs) {
if (is_arg) {
return lb_arg_type_indirect_byval(c, type);
} else {
LLVMAttributeRef attribute = lb_create_enum_attribute_with_type(c, "sret", type);
return lb_arg_type_indirect(type, attribute);
}
} else if (is_mem_cls(cls, attribute_kind)) {
LLVMAttributeRef attribute = nullptr;
if (attribute_kind == Amd64TypeAttribute_ByVal) {
if (is_calling_convention_odin(calling_convention)) {
@@ -905,7 +977,7 @@ namespace lbAbiAmd64SysV {
return types[0];
}
return LLVMStructTypeInContext(c, types.data, cast(unsigned)types.count, sz == 0);
return LLVMStructTypeInContext(c, types.data, cast(unsigned)types.count, false);
}
gb_internal void classify_with(LLVMTypeRef t, Array<RegClass> *cls, i64 ix, i64 off) {
@@ -1159,38 +1231,24 @@ namespace lbAbiArm64 {
}
} else {
i64 size = lb_sizeof(return_type);
if (size <= 16) {
LLVMTypeRef cast_type = nullptr;
if (size == 0) {
cast_type = LLVMStructTypeInContext(c, nullptr, 0, false);
} else if (size <= 8) {
cast_type = LLVMIntTypeInContext(c, cast(unsigned)(size*8));
} else {
unsigned count = cast(unsigned)((size+7)/8);
LLVMTypeRef llvm_i64 = LLVMIntTypeInContext(c, 64);
LLVMTypeRef *types = gb_alloc_array(temporary_allocator(), LLVMTypeRef, count);
i64 size_copy = size;
for (unsigned i = 0; i < count; i++) {
if (size_copy >= 8) {
types[i] = llvm_i64;
} else {
types[i] = LLVMIntTypeInContext(c, 8*cast(unsigned)size_copy);
}
size_copy -= 8;
}
GB_ASSERT(size_copy <= 0);
cast_type = LLVMStructTypeInContext(c, types, count, true);
}
return lb_arg_type_direct(return_type, cast_type, nullptr, nullptr);
} else {
if (size > 16) {
LB_ABI_MODIFY_RETURN_IF_TUPLE_MACRO();
LLVMAttributeRef attr = lb_create_enum_attribute_with_type(c, "sret", return_type);
return lb_arg_type_indirect(return_type, attr);
}
GB_ASSERT(size <= 16);
LLVMTypeRef cast_type = nullptr;
if (size == 0) {
cast_type = LLVMStructTypeInContext(c, nullptr, 0, false);
} else if (size <= 8) {
cast_type = LLVMIntTypeInContext(c, cast(unsigned)(size*8));
} else {
LLVMTypeRef llvm_i64 = LLVMIntTypeInContext(c, 64);
cast_type = llvm_array_type(llvm_i64, 2);
}
return lb_arg_type_direct(return_type, cast_type, nullptr, nullptr);
}
}
@@ -1818,7 +1876,8 @@ gb_internal LB_ABI_INFO(lb_get_abi_info) {
return_type, return_is_defined,
ALLOW_SPLIT_MULTI_RETURNS && return_is_tuple && is_calling_convention_odin(calling_convention),
calling_convention,
base_type(original_type));
base_type(original_type)
);
// NOTE(bill): this is handled here rather than when developing the type in `lb_type_internal_for_procedures_raw`
+819 -52
View File
@@ -563,6 +563,53 @@ gb_internal lbValue lb_hasher_proc_for_type(lbModule *m, Type *type) {
lbValue res = lb_emit_runtime_call(p, "default_hasher_string", args);
lb_add_callsite_force_inline(p, res);
LLVMBuildRet(p->builder, res.value);
} else if (is_type_float(type)) {
lbValue ptr = lb_emit_conv(p, data, pt);
lbValue v = lb_emit_load(p, ptr);
v = lb_emit_conv(p, v, t_f64);
auto args = array_make<lbValue>(temporary_allocator(), 2);
args[0] = v;
args[1] = seed;
lbValue res = lb_emit_runtime_call(p, "default_hasher_f64", args);
lb_add_callsite_force_inline(p, res);
LLVMBuildRet(p->builder, res.value);
} else if (is_type_complex(type)) {
lbValue ptr = lb_emit_conv(p, data, pt);
lbValue xp = lb_emit_struct_ep(p, ptr, 0);
lbValue yp = lb_emit_struct_ep(p, ptr, 1);
lbValue x = lb_emit_conv(p, lb_emit_load(p, xp), t_f64);
lbValue y = lb_emit_conv(p, lb_emit_load(p, yp), t_f64);
auto args = array_make<lbValue>(temporary_allocator(), 3);
args[0] = x;
args[1] = y;
args[2] = seed;
lbValue res = lb_emit_runtime_call(p, "default_hasher_complex128", args);
lb_add_callsite_force_inline(p, res);
LLVMBuildRet(p->builder, res.value);
} else if (is_type_quaternion(type)) {
lbValue ptr = lb_emit_conv(p, data, pt);
lbValue xp = lb_emit_struct_ep(p, ptr, 0);
lbValue yp = lb_emit_struct_ep(p, ptr, 1);
lbValue zp = lb_emit_struct_ep(p, ptr, 2);
lbValue wp = lb_emit_struct_ep(p, ptr, 3);
lbValue x = lb_emit_conv(p, lb_emit_load(p, xp), t_f64);
lbValue y = lb_emit_conv(p, lb_emit_load(p, yp), t_f64);
lbValue z = lb_emit_conv(p, lb_emit_load(p, zp), t_f64);
lbValue w = lb_emit_conv(p, lb_emit_load(p, wp), t_f64);
auto args = array_make<lbValue>(temporary_allocator(), 5);
args[0] = x;
args[1] = y;
args[2] = z;
args[3] = w;
args[4] = seed;
lbValue res = lb_emit_runtime_call(p, "default_hasher_quaternion256", args);
lb_add_callsite_force_inline(p, res);
LLVMBuildRet(p->builder, res.value);
} else {
GB_PANIC("Unhandled type for hasher: %s", type_to_string(type));
}
@@ -1126,6 +1173,344 @@ gb_internal lbProcedure *lb_create_objc_names(lbModule *main_module) {
return p;
}
String lb_get_objc_type_encoding(Type *t, isize pointer_depth = 0) {
// NOTE(harold): See https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html#//apple_ref/doc/uid/TP40008048-CH100
// NOTE(harold): Darwin targets are always 64-bit. Should we drop this and assume "q" always?
#define INT_SIZE_ENCODING (build_context.metrics.int_size == 4 ? "i" : "q")
switch (t->kind) {
case Type_Basic: {
switch (t->Basic.kind) {
case Basic_Invalid:
return str_lit("?");
case Basic_llvm_bool:
case Basic_bool:
case Basic_b8:
return str_lit("B");
case Basic_b16:
return str_lit("C");
case Basic_b32:
return str_lit("I");
case Basic_b64:
return str_lit("q");
case Basic_i8:
return str_lit("c");
case Basic_u8:
return str_lit("C");
case Basic_i16:
case Basic_i16le:
case Basic_i16be:
return str_lit("s");
case Basic_u16:
case Basic_u16le:
case Basic_u16be:
return str_lit("S");
case Basic_i32:
case Basic_i32le:
case Basic_i32be:
return str_lit("i");
case Basic_u32le:
case Basic_u32:
case Basic_u32be:
return str_lit("I");
case Basic_i64:
case Basic_i64le:
case Basic_i64be:
return str_lit("q");
case Basic_u64:
case Basic_u64le:
case Basic_u64be:
return str_lit("Q");
case Basic_i128:
case Basic_i128le:
case Basic_i128be:
return str_lit("t");
case Basic_u128:
case Basic_u128le:
case Basic_u128be:
return str_lit("T");
case Basic_rune:
return str_lit("I");
case Basic_f16:
case Basic_f16le:
case Basic_f16be:
return str_lit("s"); // @harold: Closest we've got?
case Basic_f32:
case Basic_f32le:
case Basic_f32be:
return str_lit("f");
case Basic_f64:
case Basic_f64le:
case Basic_f64be:
return str_lit("d");
case Basic_complex32: return str_lit("{complex32=ss}"); // No f16 encoding, so fallback to i16, as above in Basic_f16*
case Basic_complex64: return str_lit("{complex64=ff}");
case Basic_complex128: return str_lit("{complex128=dd}");
case Basic_quaternion64: return str_lit("{quaternion64=ssss}");
case Basic_quaternion128: return str_lit("{quaternion128=ffff}");
case Basic_quaternion256: return str_lit("{quaternion256=dddd}");
case Basic_int:
return str_lit(INT_SIZE_ENCODING);
case Basic_uint:
return build_context.metrics.int_size == 4 ? str_lit("I") : str_lit("Q");
case Basic_uintptr:
case Basic_rawptr:
return str_lit("^v");
case Basic_string:
return build_context.metrics.int_size == 4 ? str_lit("{string=*i}") : str_lit("{string=*q}");
case Basic_cstring: return str_lit("*");
case Basic_any: return str_lit("{any=^v^v}"); // rawptr + ^Type_Info
case Basic_typeid:
GB_ASSERT(t->Basic.size == 8);
return str_lit("q");
// Untyped types
case Basic_UntypedBool:
case Basic_UntypedInteger:
case Basic_UntypedFloat:
case Basic_UntypedComplex:
case Basic_UntypedQuaternion:
case Basic_UntypedString:
case Basic_UntypedRune:
case Basic_UntypedNil:
case Basic_UntypedUninit:
GB_PANIC("Untyped types cannot be @encoded()");
return str_lit("?");
}
break;
}
case Type_Named:
case Type_Struct:
case Type_Union: {
Type* base = t;
if (base->kind == Type_Named) {
base = base_type(base);
if(base->kind != Type_Struct && base->kind != Type_Union) {
return lb_get_objc_type_encoding(base, pointer_depth);
}
}
const bool is_union = base->kind == Type_Union;
if (!is_union) {
// Check for objc_SEL
if (internal_check_is_assignable_to(base, t_objc_SEL)) {
return str_lit(":");
}
// Check for objc_Class
if (internal_check_is_assignable_to(base, t_objc_SEL)) {
return str_lit("#");
}
// Treat struct as an Objective-C Class?
if (has_type_got_objc_class_attribute(base) && pointer_depth == 0) {
return str_lit("#");
}
}
if (is_type_objc_object(base)) {
return str_lit("@");
}
gbString s = gb_string_make_reserve(temporary_allocator(), 16);
s = gb_string_append_length(s, is_union ? "(" :"{", 1);
if (t->kind == Type_Named) {
s = gb_string_append_length(s, t->Named.name.text, t->Named.name.len);
}
// Write fields
if (pointer_depth < 2) {
s = gb_string_append_length(s, "=", 1);
if (!is_union) {
for( auto& f : base->Struct.fields ) {
String field_type = lb_get_objc_type_encoding(f->type, pointer_depth);
s = gb_string_append_length(s, field_type.text, field_type.len);
}
} else {
for( auto& v : base->Union.variants ) {
String variant_type = lb_get_objc_type_encoding(v, pointer_depth);
s = gb_string_append_length(s, variant_type.text, variant_type.len);
}
}
}
s = gb_string_append_length(s, is_union ? ")" :"}", 1);
return make_string_c(s);
}
case Type_Generic:
GB_PANIC("Generic types cannot be @encoded()");
return str_lit("?");
case Type_Pointer: {
String pointee = lb_get_objc_type_encoding(t->Pointer.elem, pointer_depth +1);
// Special case for Objective-C Objects
if (pointer_depth == 0 && pointee == "@") {
return pointee;
}
return concatenate_strings(temporary_allocator(), str_lit("^"), pointee);
}
case Type_MultiPointer:
return concatenate_strings(temporary_allocator(), str_lit("^"), lb_get_objc_type_encoding(t->Pointer.elem, pointer_depth +1));
case Type_Array: {
String type_str = lb_get_objc_type_encoding(t->Array.elem, pointer_depth);
gbString s = gb_string_make_reserve(temporary_allocator(), type_str.len + 8);
s = gb_string_append_fmt(s, "[%lld%.*s]", t->Array.count, LIT(type_str));
return make_string_c(s);
}
case Type_EnumeratedArray: {
String type_str = lb_get_objc_type_encoding(t->EnumeratedArray.elem, pointer_depth);
gbString s = gb_string_make_reserve(temporary_allocator(), type_str.len + 8);
s = gb_string_append_fmt(s, "[%lld%.*s]", t->EnumeratedArray.count, LIT(type_str));
return make_string_c(s);
}
case Type_Slice: {
String type_str = lb_get_objc_type_encoding(t->Slice.elem, pointer_depth);
gbString s = gb_string_make_reserve(temporary_allocator(), type_str.len + 8);
s = gb_string_append_fmt(s, "{slice=^%.*s%s}", LIT(type_str), INT_SIZE_ENCODING);
return make_string_c(s);
}
case Type_DynamicArray: {
String type_str = lb_get_objc_type_encoding(t->DynamicArray.elem, pointer_depth);
gbString s = gb_string_make_reserve(temporary_allocator(), type_str.len + 8);
s = gb_string_append_fmt(s, "{dynamic=^%.*s%s%sAllocator={?^v}}", LIT(type_str), INT_SIZE_ENCODING, INT_SIZE_ENCODING);
return make_string_c(s);
}
case Type_Map:
return str_lit("{^v^v{Allocator=?^v}}");
case Type_Enum:
return lb_get_objc_type_encoding(t->Enum.base_type, pointer_depth);
case Type_Tuple:
// NOTE(harold): Is this type allowed here?
return str_lit("?");
case Type_Proc:
return str_lit("?");
case Type_BitSet:
return lb_get_objc_type_encoding(t->BitSet.underlying, pointer_depth);
case Type_SimdVector: {
String type_str = lb_get_objc_type_encoding(t->SimdVector.elem, pointer_depth);
gbString s = gb_string_make_reserve(temporary_allocator(), type_str.len + 5);
gb_string_append_fmt(s, "[%lld%.*s]", t->SimdVector.count, LIT(type_str));
return make_string_c(s);
}
case Type_Matrix: {
String type_str = lb_get_objc_type_encoding(t->Matrix.elem, pointer_depth);
gbString s = gb_string_make_reserve(temporary_allocator(), type_str.len + 5);
i64 element_count = t->Matrix.column_count * t->Matrix.row_count;
gb_string_append_fmt(s, "[%lld%.*s]", element_count, LIT(type_str));
return make_string_c(s);
}
case Type_BitField:
return lb_get_objc_type_encoding(t->BitField.backing_type, pointer_depth);
case Type_SoaPointer: {
gbString s = gb_string_make_reserve(temporary_allocator(), 8);
s = gb_string_append_fmt(s, "{=^v%s}", INT_SIZE_ENCODING);
return make_string_c(s);
}
} // End switch t->kind
#undef INT_SIZE_ENCODING
GB_PANIC("Unreachable");
return str_lit("");
}
struct lbObjCGlobalClass {
lbObjCGlobal g;
lbValue class_value; // Local registered class value
};
gb_internal void lb_register_objc_thing(
StringSet &handled,
lbModule *m,
Array<lbValue> &args,
Array<lbObjCGlobalClass> &class_impls,
StringMap<lbObjCGlobalClass> &class_map,
lbProcedure *p,
lbObjCGlobal const &g,
char const *call
) {
if (string_set_update(&handled, g.name)) {
return;
}
lbAddr addr = {};
lbValue *found = string_map_get(&m->members, g.global_name);
if (found) {
addr = lb_addr(*found);
} else {
lbValue v = {};
LLVMTypeRef t = lb_type(m, g.type);
v.value = LLVMAddGlobal(m->mod, t, g.global_name);
v.type = alloc_type_pointer(g.type);
addr = lb_addr(v);
LLVMSetInitializer(v.value, LLVMConstNull(t));
}
lbValue class_ptr = {};
lbValue class_name = lb_const_value(m, t_cstring, exact_value_string(g.name));
// If this class requires an implementation, save it for registration below.
if (g.class_impl_type != nullptr) {
// Make sure the superclass has been initialized before us
lbValue superclass_value = lb_const_nil(m, t_objc_Class);
auto &tn = g.class_impl_type->Named.type_name->TypeName;
Type *superclass = tn.objc_superclass;
if (superclass != nullptr) {
auto& superclass_global = string_map_must_get(&class_map, superclass->Named.type_name->TypeName.objc_class_name);
lb_register_objc_thing(handled, m, args, class_impls, class_map, p, superclass_global.g, call);
GB_ASSERT(superclass_global.class_value.value);
superclass_value = superclass_global.class_value;
}
args.count = 3;
args[0] = superclass_value;
args[1] = class_name;
args[2] = lb_const_int(m, t_uint, 0);
class_ptr = lb_emit_runtime_call(p, "objc_allocateClassPair", args);
array_add(&class_impls, lbObjCGlobalClass{g, class_ptr});
}
else {
args.count = 1;
args[0] = class_name;
class_ptr = lb_emit_runtime_call(p, call, args);
}
lb_addr_store(p, addr, class_ptr);
lbObjCGlobalClass* class_global = string_map_get(&class_map, g.name);
if (class_global != nullptr) {
class_global->class_value = class_ptr;
}
}
gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) {
if (p == nullptr) {
return;
@@ -1139,38 +1524,329 @@ gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) {
string_set_init(&handled);
defer (string_set_destroy(&handled));
auto args = array_make<lbValue>(temporary_allocator(), 1);
auto args = array_make<lbValue>(temporary_allocator(), 3, 8);
auto class_impls = array_make<lbObjCGlobalClass>(temporary_allocator(), 0, 16);
// Register all class implementations unconditionally, even if not statically referenced
for (Entity *e = {}; mpsc_dequeue(&gen->info->objc_class_implementations, &e); /**/) {
GB_ASSERT(e->kind == Entity_TypeName && e->TypeName.objc_is_implementation);
lb_handle_objc_find_or_register_class(p, e->TypeName.objc_class_name, e->type);
}
// Ensure classes that have been implicitly referenced through
// the objc_superclass attribute have a global variable available for them.
TypeSet class_set{};
type_set_init(&class_set, gen->objc_classes.count+16);
defer (type_set_destroy(&class_set));
auto referenced_classes = array_make<lbObjCGlobal>(temporary_allocator());
for (lbObjCGlobal g = {}; mpsc_dequeue(&gen->objc_classes, &g); /**/) {
array_add(&referenced_classes, g);
Type *cls = g.class_impl_type;
while (cls) {
if (type_set_update(&class_set, cls)) {
break;
}
GB_ASSERT(cls->kind == Type_Named);
cls = cls->Named.type_name->TypeName.objc_superclass;
}
}
for (auto pair : class_set) {
auto& tn = pair.type->Named.type_name->TypeName;
Type *class_impl = !tn.objc_is_implementation ? nullptr : pair.type;
lb_handle_objc_find_or_register_class(p, tn.objc_class_name, class_impl);
}
for (lbObjCGlobal g = {}; mpsc_dequeue(&gen->objc_classes, &g); /**/) {
array_add( &referenced_classes, g );
}
// Add all class globals to a map so that we can look them up dynamically
// in order to resolve out-of-order because classes that are being implemented
// require their superclasses to be registered before them.
StringMap<lbObjCGlobalClass> global_class_map{};
string_map_init(&global_class_map, (usize)gen->objc_classes.count);
defer (string_map_destroy(&global_class_map));
for (lbObjCGlobal g :referenced_classes) {
string_map_set(&global_class_map, g.name, lbObjCGlobalClass{g});
}
LLVMSetLinkage(p->value, LLVMInternalLinkage);
lb_begin_procedure_body(p);
auto register_thing = [&handled, &m, &args](lbProcedure *p, lbObjCGlobal const &g, char const *call) {
if (!string_set_update(&handled, g.name)) {
lbAddr addr = {};
lbValue *found = string_map_get(&m->members, g.global_name);
if (found) {
addr = lb_addr(*found);
} else {
lbValue v = {};
LLVMTypeRef t = lb_type(m, g.type);
v.value = LLVMAddGlobal(m->mod, t, g.global_name);
v.type = alloc_type_pointer(g.type);
addr = lb_addr(v);
LLVMSetInitializer(v.value, LLVMConstNull(t));
}
args[0] = lb_const_value(m, t_cstring, exact_value_string(g.name));
lbValue ptr = lb_emit_runtime_call(p, call, args);
lb_addr_store(p, addr, ptr);
}
};
for (lbObjCGlobal g = {}; mpsc_dequeue(&gen->objc_classes, &g); /**/) {
register_thing(p, g, "objc_lookUpClass");
// Register class globals, gathering classes that must be implemented
for (auto& kv : global_class_map) {
lb_register_objc_thing(handled, m, args, class_impls, global_class_map, p, kv.value.g, "objc_lookUpClass");
}
// Prefetch selectors for implemented methods so that they can also be registered.
for (const auto& cd : class_impls) {
auto& g = cd.g;
Type *class_type = g.class_impl_type;
Array<ObjcMethodData>* methods = map_get(&m->info->objc_method_implementations, class_type);
if (!methods) {
continue;
}
for (const ObjcMethodData& md : *methods) {
lb_handle_objc_find_or_register_selector(p, md.ac.objc_selector);
}
}
// Now we can register all referenced selectors
for (lbObjCGlobal g = {}; mpsc_dequeue(&gen->objc_selectors, &g); /**/) {
register_thing(p, g, "sel_registerName");
lb_register_objc_thing(handled, m, args, class_impls, global_class_map, p, g, "sel_registerName");
}
// Emit method wrapper implementations and registration
auto wrapper_args = array_make<Type *>(temporary_allocator(), 2, 8);
auto get_context_args = array_make<lbValue>(temporary_allocator(), 1);
PtrMap<Type *, lbObjCGlobal> ivar_map{};
map_init(&ivar_map, gen->objc_ivars.count);
for (lbObjCGlobal g = {}; mpsc_dequeue(&gen->objc_ivars, &g); /**/) {
map_set(&ivar_map, g.class_impl_type, g);
}
for (const auto &cd : class_impls) {
auto &g = cd.g;
Type *class_type = g.class_impl_type;
Type *class_ptr_type = alloc_type_pointer(class_type);
lbValue class_value = cd.class_value;
Type *ivar_type = class_type->Named.type_name->TypeName.objc_ivar;
Entity *context_provider = class_type->Named.type_name->TypeName.objc_context_provider;
Type *contex_provider_self_ptr_type = nullptr;
Type *contex_provider_self_named_type = nullptr;
bool is_context_provider_ivar = false;
lbValue context_provider_proc_value{};
if (context_provider) {
context_provider_proc_value = lb_find_procedure_value_from_entity(m, context_provider);
contex_provider_self_ptr_type = base_type(context_provider->type->Proc.params->Tuple.variables[0]->type);
GB_ASSERT(contex_provider_self_ptr_type->kind == Type_Pointer);
contex_provider_self_named_type = base_named_type(type_deref(contex_provider_self_ptr_type));
is_context_provider_ivar = ivar_type != nullptr && internal_check_is_assignable_to(contex_provider_self_named_type, ivar_type);
}
Array<ObjcMethodData> *methods = map_get(&m->info->objc_method_implementations, class_type);
if (!methods) {
continue;
}
for (const ObjcMethodData &md : *methods) {
GB_ASSERT( md.proc_entity->kind == Entity_Procedure);
Type *method_type = md.proc_entity->type;
String proc_name = make_string_c("__$objc_method::");
proc_name = concatenate_strings(temporary_allocator(), proc_name, g.name);
proc_name = concatenate_strings(temporary_allocator(), proc_name, str_lit("::"));
proc_name = concatenate_strings( permanent_allocator(), proc_name, md.ac.objc_name);
wrapper_args.count = 2;
wrapper_args[0] = md.ac.objc_is_class_method ? t_objc_Class : class_ptr_type;
wrapper_args[1] = t_objc_SEL;
isize method_param_count = method_type->Proc.param_count;
isize method_param_offset = 0;
if (!md.ac.objc_is_class_method) {
GB_ASSERT(method_param_count >= 1);
method_param_count -= 1;
method_param_offset = 1;
}
for (isize i = 0; i < method_param_count; i++) {
array_add(&wrapper_args, method_type->Proc.params->Tuple.variables[method_param_offset+i]->type);
}
Type *wrapper_args_tuple = alloc_type_tuple_from_field_types(wrapper_args.data, wrapper_args.count, false, true);
Type *wrapper_results_tuple = nullptr;
if (method_type->Proc.result_count > 0) {
GB_ASSERT(method_type->Proc.result_count == 1);
wrapper_results_tuple = alloc_type_tuple_from_field_types(&method_type->Proc.results->Tuple.variables[0]->type, 1, false, true);
}
Type *wrapper_proc_type = alloc_type_proc(nullptr, wrapper_args_tuple, wrapper_args_tuple->Tuple.variables.count,
wrapper_results_tuple, method_type->Proc.result_count, false, ProcCC_CDecl);
lbProcedure *wrapper_proc = lb_create_dummy_procedure(m, proc_name, wrapper_proc_type);
lb_add_attribute_to_proc(wrapper_proc->module, wrapper_proc->value, "nounwind");
// Emit the wrapper
LLVMSetLinkage(wrapper_proc->value, LLVMExternalLinkage);
lb_begin_procedure_body(wrapper_proc);
{
if (method_type->Proc.calling_convention == ProcCC_Odin) {
GB_ASSERT(context_provider);
// Emit the get odin context call
get_context_args[0] = lbValue {
wrapper_proc->raw_input_parameters[0],
contex_provider_self_ptr_type,
};
if (is_context_provider_ivar) {
// The context provider takes the ivar's type.
// Emit an objc_ivar_get call and use that pointer for 'self' instead.
lbValue real_self {
wrapper_proc->raw_input_parameters[0],
class_ptr_type
};
get_context_args[0] = lb_handle_objc_ivar_for_objc_object_pointer(wrapper_proc, real_self);
}
lbValue context = lb_emit_call(wrapper_proc, context_provider_proc_value, get_context_args);
lbAddr context_addr = lb_addr(lb_address_from_load_or_generate_local(wrapper_proc, context));
lb_push_context_onto_stack(wrapper_proc, context_addr);
}
auto method_call_args = array_make<lbValue>(temporary_allocator(), method_param_count + method_param_offset);
if (!md.ac.objc_is_class_method) {
method_call_args[0] = lbValue {
wrapper_proc->raw_input_parameters[0],
class_ptr_type,
};
}
for (isize i = 0; i < method_param_count; i++) {
method_call_args[i+method_param_offset] = lbValue {
wrapper_proc->raw_input_parameters[i+2],
method_type->Proc.params->Tuple.variables[i+method_param_offset]->type,
};
}
lbValue method_proc_value = lb_find_procedure_value_from_entity(m, md.proc_entity);
// Call real procedure for method from here, passing the parameters expected, if any.
lbValue return_value = lb_emit_call(wrapper_proc, method_proc_value, method_call_args);
if (wrapper_results_tuple != nullptr) {
auto &result_var = method_type->Proc.results->Tuple.variables[0];
return_value = lb_emit_conv(wrapper_proc, return_value, result_var->type);
lb_build_return_stmt_internal(wrapper_proc, return_value, result_var->token.pos);
}
}
lb_end_procedure_body(wrapper_proc);
// Add the method to the class
String method_encoding = str_lit("v");
// TODO (harold): Checker must ensure that objc_methods have a single return value or none!
GB_ASSERT(method_type->Proc.result_count <= 1);
if (method_type->Proc.result_count != 0) {
method_encoding = lb_get_objc_type_encoding(method_type->Proc.results->Tuple.variables[0]->type);
}
if (!md.ac.objc_is_class_method) {
method_encoding = concatenate_strings(temporary_allocator(), method_encoding, str_lit("@:"));
} else {
method_encoding = concatenate_strings(temporary_allocator(), method_encoding, str_lit("#:"));
}
for (isize i = method_param_offset; i < method_param_count; i++) {
Type *param_type = method_type->Proc.params->Tuple.variables[i]->type;
String param_encoding = lb_get_objc_type_encoding(param_type);
method_encoding = concatenate_strings(temporary_allocator(), method_encoding, param_encoding);
}
// Emit method registration
lbAddr* sel_address = string_map_get(&m->objc_selectors, md.ac.objc_selector);
GB_ASSERT(sel_address);
lbValue selector_value = lb_addr_load(p, *sel_address);
args.count = 4;
args[0] = class_value; // Class
args[1] = selector_value; // SEL
args[2] = lbValue { wrapper_proc->value, wrapper_proc->type };
args[3] = lb_const_value(m, t_cstring, exact_value_string(method_encoding));
// TODO(harold): Emit check BOOL result and panic if false.
lb_emit_runtime_call(p, "class_addMethod", args);
} // End methods
// Add ivar if we have one
if (ivar_type != nullptr) {
// Register a single ivar for this class
Type *ivar_base = ivar_type->Named.base;
// @note(harold): The alignment is supposed to be passed as log2(alignment): https://developer.apple.com/documentation/objectivec/class_addivar(_:_:_:_:_:)?language=objc
const i64 size = type_size_of(ivar_base);
const i64 alignment = (i64)floor_log2((u64)type_align_of(ivar_base));
// NOTE(harold): I've opted to not emit the type encoding for ivars in order to keep the data private.
// If there is desire in the future to emit the type encoding for introspection through the Obj-C runtime,
// then perhaps an option can be added for it then.
// Should we pass the actual type encoding? Might not be ideal for obfuscation.
String ivar_name = str_lit("__$ivar");
String ivar_types = str_lit("{= }"); //lb_get_objc_type_encoding(ivar_type);
args.count = 5;
args[0] = class_value;
args[1] = lb_const_value(m, t_cstring, exact_value_string(ivar_name));
args[2] = lb_const_value(m, t_uint, exact_value_u64((u64)size));
args[3] = lb_const_value(m, t_u8, exact_value_u64((u64)alignment));
args[4] = lb_const_value(m, t_cstring, exact_value_string(ivar_types));
lb_emit_runtime_call(p, "class_addIvar", args);
}
// Complete the class registration
args.count = 1;
args[0] = class_value;
lb_emit_runtime_call(p, "objc_registerClassPair", args);
}
// Register ivar offsets for any `objc_ivar_get` expressions emitted.
for (auto const& kv : ivar_map) {
lbObjCGlobal const& g = kv.value;
lbAddr ivar_addr = {};
lbValue *found = string_map_get(&m->members, g.global_name);
if (found) {
ivar_addr = lb_addr(*found);
GB_ASSERT(ivar_addr.addr.type == t_int_ptr);
} else {
// Defined in an external package, define it now in the main package
LLVMTypeRef t = lb_type(m, t_int);
lbValue global{};
global.value = LLVMAddGlobal(m->mod, t, g.global_name);
global.type = t_int_ptr;
LLVMSetInitializer(global.value, LLVMConstInt(t, 0, true));
ivar_addr = lb_addr(global);
}
String class_name = g.class_impl_type->Named.type_name->TypeName.objc_class_name;
lbValue class_value = string_map_must_get(&global_class_map, class_name).class_value;
args.count = 2;
args[0] = class_value;
args[1] = lb_const_value(m, t_cstring, exact_value_string(str_lit("__$ivar")));
lbValue ivar = lb_emit_runtime_call(p, "class_getInstanceVariable", args);
args.count = 1;
args[0] = ivar;
lbValue ivar_offset = lb_emit_runtime_call(p, "ivar_getOffset", args);
lbValue ivar_offset_int = lb_emit_conv(p, ivar_offset, t_int);
lb_addr_store(p, ivar_addr, ivar_offset_int);
}
lb_end_procedure_body(p);
@@ -1231,6 +1907,10 @@ gb_internal lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProc
lb_add_attribute_to_proc(p->module, p->value, "optnone");
lb_add_attribute_to_proc(p->module, p->value, "noinline");
// Make sure shared libraries call their own runtime startup on Linux.
LLVMSetVisibility(p->value, LLVMHiddenVisibility);
LLVMSetLinkage(p->value, LLVMWeakAnyLinkage);
lb_begin_procedure_body(p);
lb_setup_type_info_data(main_module);
@@ -1297,14 +1977,14 @@ gb_internal lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProc
gbString var_name = gb_string_make(permanent_allocator(), "__$global_any::");
gbString e_str = string_canonical_entity_name(temporary_allocator(), e);
var_name = gb_string_append_length(var_name, e_str, gb_strlen(e_str));
lbAddr g = lb_add_global_generated_with_name(main_module, var_type, var.init, make_string_c(var_name));
lbAddr g = lb_add_global_generated_with_name(main_module, var_type, {}, make_string_c(var_name));
lb_addr_store(p, g, var.init);
lbValue gp = lb_addr_get_ptr(p, g);
lbValue data = lb_emit_struct_ep(p, var.var, 0);
lbValue ti = lb_emit_struct_ep(p, var.var, 1);
lb_emit_store(p, data, lb_emit_conv(p, gp, t_rawptr));
lb_emit_store(p, ti, lb_type_info(p, var_type));
lb_emit_store(p, ti, lb_typeid(p->module, var_type));
} else {
LLVMTypeRef vt = llvm_addr_type(p->module, var.var);
lbValue src0 = lb_emit_conv(p, var.init, t);
@@ -1340,6 +2020,10 @@ gb_internal lbProcedure *lb_create_cleanup_runtime(lbModule *main_module) { // C
lb_add_attribute_to_proc(p->module, p->value, "optnone");
lb_add_attribute_to_proc(p->module, p->value, "noinline");
// Make sure shared libraries call their own runtime cleanup on Linux.
LLVMSetVisibility(p->value, LLVMHiddenVisibility);
LLVMSetLinkage(p->value, LLVMWeakAnyLinkage);
lb_begin_procedure_body(p);
CheckerInfo *info = main_module->gen->info;
@@ -1414,7 +2098,7 @@ gb_internal void lb_create_global_procedures_and_types(lbGenerator *gen, Checker
break;
case Entity_Constant:
if (build_context.ODIN_DEBUG) {
add_debug_info_for_global_constant_from_entity(gen, e);
lb_add_debug_info_for_global_constant_from_entity(gen, e);
}
break;
}
@@ -2056,8 +2740,8 @@ gb_internal lbProcedure *lb_create_main_procedure(lbModule *m, lbProcedure *star
if (testing_proc->pkg != nullptr) {
pkg_name = testing_proc->pkg->name;
}
lbValue v_pkg = lb_find_or_add_entity_string(m, pkg_name);
lbValue v_name = lb_find_or_add_entity_string(m, name);
lbValue v_pkg = lb_find_or_add_entity_string(m, pkg_name, false);
lbValue v_name = lb_find_or_add_entity_string(m, name, false);
lbValue v_proc = lb_find_procedure_value_from_entity(m, testing_proc);
indices[1] = LLVMConstInt(lb_type(m, t_int), testing_proc_index++, false);
@@ -2138,7 +2822,6 @@ gb_internal void lb_generate_procedure(lbModule *m, lbProcedure *p) {
p->is_done = true;
m->curr_procedure = nullptr;
}
lb_end_procedure(p);
// Add Flags
if (p->entity && p->entity->kind == Entity_Procedure && p->entity->Procedure.is_memcpy_like) {
@@ -2447,6 +3130,7 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
LLVMSetInitializer(g, LLVMConstNull(lb_type(m, t)));
LLVMSetLinkage(g, LLVMInternalLinkage);
lb_make_global_private_const(g);
lb_set_odin_rtti_section(g);
return lb_addr({g, alloc_type_pointer(t)});
};
@@ -2518,24 +3202,9 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
lbValue g = {};
g.value = LLVMAddGlobal(m->mod, lb_type(m, e->type), alloc_cstring(permanent_allocator(), name));
g.type = alloc_type_pointer(e->type);
if (e->Variable.thread_local_model != "") {
LLVMSetThreadLocal(g.value, true);
String m = e->Variable.thread_local_model;
LLVMThreadLocalMode mode = LLVMGeneralDynamicTLSModel;
if (m == "default") {
mode = LLVMGeneralDynamicTLSModel;
} else if (m == "localdynamic") {
mode = LLVMLocalDynamicTLSModel;
} else if (m == "initialexec") {
mode = LLVMInitialExecTLSModel;
} else if (m == "localexec") {
mode = LLVMLocalExecTLSModel;
} else {
GB_PANIC("Unhandled thread local mode %.*s", LIT(m));
}
LLVMSetThreadLocalMode(g.value, mode);
}
lb_apply_thread_local_model(g.value, e->Variable.thread_local_model);
if (is_foreign) {
LLVMSetLinkage(g.value, LLVMExternalLinkage);
LLVMSetDLLStorageClass(g.value, LLVMDLLImportStorageClass);
@@ -2551,6 +3220,7 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
LLVMSetLinkage(g.value, USE_SEPARATE_MODULES ? LLVMWeakAnyLinkage : LLVMInternalLinkage);
}
lb_set_linkage_from_entity_flags(m, g.value, e->flags);
LLVMSetAlignment(g.value, cast(u32)type_align_of(e->type));
if (e->Variable.link_section.len > 0) {
LLVMSetSection(g.value, alloc_cstring(permanent_allocator(), e->Variable.link_section));
@@ -2565,12 +3235,16 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
if (!is_type_any(e->type) && !is_type_union(e->type)) {
if (tav.mode != Addressing_Invalid) {
if (tav.value.kind != ExactValue_Invalid) {
bool is_rodata = e->kind == Entity_Variable && e->Variable.is_rodata;
auto cc = LB_CONST_CONTEXT_DEFAULT;
cc.is_rodata = e->kind == Entity_Variable && e->Variable.is_rodata;
cc.allow_local = false;
cc.link_section = e->Variable.link_section;
ExactValue v = tav.value;
lbValue init = lb_const_value(m, tav.type, v, false, is_rodata);
lbValue init = lb_const_value(m, tav.type, v, cc);
LLVMSetInitializer(g.value, init.value);
var.is_initialized = true;
if (is_rodata) {
if (cc.is_rodata) {
LLVMSetGlobalConstant(g.value, true);
}
}
@@ -2585,6 +3259,11 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
} else if (e->kind == Entity_Variable && e->Variable.is_rodata) {
LLVMSetGlobalConstant(g.value, true);
}
if (e->flags & EntityFlag_Require) {
lb_append_to_compiler_used(m, g.value);
}
array_add(&global_variables, var);
lb_add_entity(m, e, g);
@@ -2621,6 +3300,28 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
}
}
if (build_context.ODIN_DEBUG) {
// Custom `.raddbg` section for its debugger
if (build_context.metrics.os == TargetOs_windows) {
lbModule *m = default_module;
LLVMModuleRef mod = m->mod;
LLVMContextRef ctx = m->ctx;
{
LLVMTypeRef type = LLVMArrayType(LLVMInt8TypeInContext(ctx), 1);
LLVMValueRef global = LLVMAddGlobal(mod, type, "raddbg_is_attached_byte_marker");
LLVMSetInitializer(global, LLVMConstNull(type));
LLVMSetSection(global, ".raddbg");
}
if (gen->info->entry_point) {
String mangled_name = lb_get_entity_name(m, gen->info->entry_point);
char const *str = alloc_cstring(temporary_allocator(), mangled_name);
lb_add_raddbg_string(m, "entry_point: \"", str, "\"");
}
}
}
TIME_SECTION("LLVM Runtime Objective-C Names Creation");
gen->objc_names = lb_create_objc_names(default_module);
@@ -2634,7 +3335,7 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
if (build_context.ODIN_DEBUG) {
for (auto const &entry : builtin_pkg->scope->elements) {
Entity *e = entry.value;
add_debug_info_for_global_constant_from_entity(gen, e);
lb_add_debug_info_for_global_constant_from_entity(gen, e);
}
}
@@ -2664,6 +3365,72 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
if (build_context.ODIN_DEBUG) {
TIME_SECTION("LLVM Debug Info Complete Types and Finalize");
lb_debug_info_complete_types_and_finalize(gen);
// Custom `.raddbg` section for its debugger
if (build_context.metrics.os == TargetOs_windows) {
lbModule *m = default_module;
LLVMModuleRef mod = m->mod;
LLVMContextRef ctx = m->ctx;
lb_add_raddbg_string(m, "type_view: {type: \"[]?\", expr: \"array(data, len)\"}");
lb_add_raddbg_string(m, "type_view: {type: \"string\", expr: \"array(data, len)\"}");
// column major matrices
lb_add_raddbg_string(m, "type_view: {type: \"matrix[1, ?]?\", expr: \"table($.data, $[0])\"}");
lb_add_raddbg_string(m, "type_view: {type: \"matrix[2, ?]?\", expr: \"table($.data, $[0], $[1])\"}");
lb_add_raddbg_string(m, "type_view: {type: \"matrix[3, ?]?\", expr: \"table($.data, $[0], $[1], $[2])\"}");
lb_add_raddbg_string(m, "type_view: {type: \"matrix[4, ?]?\", expr: \"table($.data, $[0], $[1], $[2], $[3])\"}");
lb_add_raddbg_string(m, "type_view: {type: \"matrix[5, ?]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4])\"}");
lb_add_raddbg_string(m, "type_view: {type: \"matrix[6, ?]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5])\"}");
lb_add_raddbg_string(m, "type_view: {type: \"matrix[7, ?]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6])\"}");
lb_add_raddbg_string(m, "type_view: {type: \"matrix[8, ?]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7])\"}");
lb_add_raddbg_string(m, "type_view: {type: \"matrix[9, ?]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8])\"}");
lb_add_raddbg_string(m, "type_view: {type: \"matrix[10, ?]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9])\"}");
lb_add_raddbg_string(m, "type_view: {type: \"matrix[11, ?]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10])\"}");
lb_add_raddbg_string(m, "type_view: {type: \"matrix[12, ?]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10], $[11])\"}");
lb_add_raddbg_string(m, "type_view: {type: \"matrix[13, ?]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10], $[11], $[12])\"}");
lb_add_raddbg_string(m, "type_view: {type: \"matrix[14, ?]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10], $[11], $[12], $[13])\"}");
lb_add_raddbg_string(m, "type_view: {type: \"matrix[15, ?]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10], $[11], $[12], $[13], $[14])\"}");
lb_add_raddbg_string(m, "type_view: {type: \"matrix[16, ?]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10], $[11], $[12], $[13], $[14], $[15])\"}");
// row major matrices
lb_add_raddbg_string(m, "type_view: {type: \"#row_major matrix[?, 1]?\", expr: \"table($.data, $[0])\"}");
lb_add_raddbg_string(m, "type_view: {type: \"#row_major matrix[?, 2]?\", expr: \"table($.data, $[0], $[1])\"}");
lb_add_raddbg_string(m, "type_view: {type: \"#row_major matrix[?, 3]?\", expr: \"table($.data, $[0], $[1], $[2])\"}");
lb_add_raddbg_string(m, "type_view: {type: \"#row_major matrix[?, 4]?\", expr: \"table($.data, $[0], $[1], $[2], $[3])\"}");
lb_add_raddbg_string(m, "type_view: {type: \"#row_major matrix[?, 5]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4])\"}");
lb_add_raddbg_string(m, "type_view: {type: \"#row_major matrix[?, 6]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5])\"}");
lb_add_raddbg_string(m, "type_view: {type: \"#row_major matrix[?, 7]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6])\"}");
lb_add_raddbg_string(m, "type_view: {type: \"#row_major matrix[?, 8]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7])\"}");
lb_add_raddbg_string(m, "type_view: {type: \"#row_major matrix[?, 9]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8])\"}");
lb_add_raddbg_string(m, "type_view: {type: \"#row_major matrix[?, 10]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9])\"}");
lb_add_raddbg_string(m, "type_view: {type: \"#row_major matrix[?, 11]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10])\"}");
lb_add_raddbg_string(m, "type_view: {type: \"#row_major matrix[?, 12]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10], $[11])\"}");
lb_add_raddbg_string(m, "type_view: {type: \"#row_major matrix[?, 13]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10], $[11], $[12])\"}");
lb_add_raddbg_string(m, "type_view: {type: \"#row_major matrix[?, 14]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10], $[11], $[12], $[13])\"}");
lb_add_raddbg_string(m, "type_view: {type: \"#row_major matrix[?, 15]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10], $[11], $[12], $[13], $[14])\"}");
lb_add_raddbg_string(m, "type_view: {type: \"#row_major matrix[?, 16]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10], $[11], $[12], $[13], $[14], $[15])\"}");
TEMPORARY_ALLOCATOR_GUARD();
u32 global_name_index = 0;
for (String str = {}; mpsc_dequeue(&gen->raddebug_section_strings, &str); /**/) {
LLVMValueRef data = LLVMConstStringInContext(ctx, cast(char const *)str.text, cast(unsigned)str.len, false);
LLVMTypeRef type = LLVMTypeOf(data);
gbString global_name = gb_string_make(temporary_allocator(), "raddbg_data__");
global_name = gb_string_append_fmt(global_name, "%u", global_name_index);
global_name_index += 1;
LLVMValueRef global = LLVMAddGlobal(mod, type, global_name);
LLVMSetInitializer(global, data);
LLVMSetAlignment(global, 1);
LLVMSetSection(global, ".raddbg");
}
}
}
if (do_threading) {
+18 -3
View File
@@ -196,6 +196,7 @@ struct lbModule {
StringMap<lbAddr> objc_classes;
StringMap<lbAddr> objc_selectors;
StringMap<lbAddr> objc_ivars;
PtrMap<u64/*type hash*/, lbAddr> map_cell_info_map; // address of runtime.Map_Info
PtrMap<u64/*type hash*/, lbAddr> map_info_map; // address of runtime.Map_Cell_Info
@@ -219,6 +220,7 @@ struct lbObjCGlobal {
gbString global_name;
String name;
Type * type;
Type * class_impl_type; // This is set when the class has the objc_implement attribute set to true.
};
struct lbGenerator : LinkerData {
@@ -240,6 +242,8 @@ struct lbGenerator : LinkerData {
MPSCQueue<lbEntityCorrection> entities_to_correct_linkage;
MPSCQueue<lbObjCGlobal> objc_selectors;
MPSCQueue<lbObjCGlobal> objc_classes;
MPSCQueue<lbObjCGlobal> objc_ivars;
MPSCQueue<String> raddebug_section_strings;
};
@@ -383,6 +387,8 @@ struct lbProcedure {
PtrMap<Ast *, lbValue> selector_values;
PtrMap<Ast *, lbAddr> selector_addr;
PtrMap<LLVMValueRef, lbTupleFix> tuple_fix_map;
Array<lbValue> asan_stack_locals;
};
@@ -407,7 +413,6 @@ gb_internal LLVMAttributeRef lb_create_enum_attribute_with_type(LLVMContextRef c
gb_internal void lb_add_proc_attribute_at_index(lbProcedure *p, isize index, char const *name, u64 value);
gb_internal void lb_add_proc_attribute_at_index(lbProcedure *p, isize index, char const *name);
gb_internal lbProcedure *lb_create_procedure(lbModule *module, Entity *entity, bool ignore_body=false);
gb_internal void lb_end_procedure(lbProcedure *p);
gb_internal LLVMTypeRef lb_type(lbModule *m, Type *type);
@@ -415,9 +420,19 @@ gb_internal LLVMTypeRef llvm_get_element_type(LLVMTypeRef type);
gb_internal lbBlock *lb_create_block(lbProcedure *p, char const *name, bool append=false);
struct lbConstContext {
bool allow_local;
bool is_rodata;
String link_section;
};
static lbConstContext const LB_CONST_CONTEXT_DEFAULT = {true, false, {}};
static lbConstContext const LB_CONST_CONTEXT_DEFAULT_ALLOW_LOCAL = {true, false, {}};
static lbConstContext const LB_CONST_CONTEXT_DEFAULT_NO_LOCAL = {false, false, {}};
gb_internal lbValue lb_const_nil(lbModule *m, Type *type);
gb_internal lbValue lb_const_undef(lbModule *m, Type *type);
gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_local=true, bool is_rodata=false);
gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lbConstContext cc = LB_CONST_CONTEXT_DEFAULT);
gb_internal lbValue lb_const_bool(lbModule *m, Type *type, bool value);
gb_internal lbValue lb_const_int(lbModule *m, Type *type, u64 value);
@@ -514,7 +529,7 @@ gb_internal void lb_fill_slice(lbProcedure *p, lbAddr const &slice, lbValue base
gb_internal lbValue lb_type_info(lbProcedure *p, Type *type);
gb_internal lbValue lb_find_or_add_entity_string(lbModule *m, String const &str);
gb_internal lbValue lb_find_or_add_entity_string(lbModule *m, String const &str, bool custom_link_section);
gb_internal lbValue lb_generate_anonymous_proc_lit(lbModule *m, String const &prefix_name, Ast *expr, lbProcedure *parent = nullptr);
gb_internal bool lb_is_const(lbValue value);
+56 -41
View File
@@ -301,10 +301,10 @@ gb_internal lbValue lb_const_source_code_location_const(lbModule *m, String cons
}
LLVMValueRef fields[4] = {};
fields[0]/*file*/ = lb_find_or_add_entity_string(m, file).value;
fields[0]/*file*/ = lb_find_or_add_entity_string(m, file, false).value;
fields[1]/*line*/ = lb_const_int(m, t_i32, line).value;
fields[2]/*column*/ = lb_const_int(m, t_i32, column).value;
fields[3]/*procedure*/ = lb_find_or_add_entity_string(m, procedure).value;
fields[3]/*procedure*/ = lb_find_or_add_entity_string(m, procedure, false).value;
lbValue res = {};
res.value = llvm_const_named_struct(m, t_source_code_location, fields, gb_count_of(fields));
@@ -391,12 +391,12 @@ gb_internal lbValue lb_emit_source_code_location_as_global(lbProcedure *p, Ast *
gb_internal LLVMValueRef lb_build_constant_array_values(lbModule *m, Type *type, Type *elem_type, isize count, LLVMValueRef *values, bool allow_local, bool is_rodata) {
if (allow_local) {
is_rodata = false;
gb_internal LLVMValueRef lb_build_constant_array_values(lbModule *m, Type *type, Type *elem_type, isize count, LLVMValueRef *values, lbConstContext cc) {
if (cc.allow_local) {
cc.is_rodata = false;
}
bool is_local = allow_local && m->curr_procedure != nullptr;
bool is_local = cc.allow_local && m->curr_procedure != nullptr;
bool is_const = true;
if (is_local) {
for (isize i = 0; i < count; i++) {
@@ -500,9 +500,9 @@ gb_internal bool lb_is_nested_possibly_constant(Type *ft, Selection const &sel,
return lb_is_elem_const(elem, ft);
}
gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_local, bool is_rodata) {
if (allow_local) {
is_rodata = false;
gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lbConstContext cc) {
if (cc.allow_local) {
cc.is_rodata = false;
}
LLVMContextRef ctx = m->ctx;
@@ -533,7 +533,10 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
Entity *e = entity_from_expr(expr);
res = lb_find_procedure_value_from_entity(m, e);
}
GB_ASSERT(res.value != nullptr);
if (res.value == nullptr) {
// This is an unspecialized polymorphic procedure, return nil or dummy value
return lb_const_nil(m, original_type);
}
GB_ASSERT(LLVMGetValueKind(res.value) == LLVMFunctionValueKind);
if (LLVMGetIntrinsicID(res.value) == 0) {
@@ -543,7 +546,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
return res;
}
bool is_local = allow_local && m->curr_procedure != nullptr;
bool is_local = cc.allow_local && m->curr_procedure != nullptr;
// GB_ASSERT_MSG(is_type_typed(type), "%s", type_to_string(type));
@@ -562,7 +565,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
count = gb_max(cast(isize)cl->max_count, count);
Type *elem = base_type(type)->Slice.elem;
Type *t = alloc_type_array(elem, count);
lbValue backing_array = lb_const_value(m, t, value, allow_local, is_rodata);
lbValue backing_array = lb_const_value(m, t, value, cc);
LLVMValueRef array_data = nullptr;
@@ -599,7 +602,10 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
array_data = LLVMAddGlobal(m->mod, lb_type(m, t), str);
LLVMSetInitializer(array_data, backing_array.value);
if (is_rodata) {
if (cc.link_section.len > 0) {
LLVMSetSection(array_data, alloc_cstring(permanent_allocator(), cc.link_section));
}
if (cc.is_rodata) {
LLVMSetGlobalConstant(array_data, true);
}
@@ -650,7 +656,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
}
// NOTE(bill, 2021-10-07): Allow for array programming value constants
Type *core_elem = core_array_type(type);
return lb_const_value(m, core_elem, value, allow_local, is_rodata);
return lb_const_value(m, core_elem, value, cc);
} else if (is_type_u8_array(type) && value.kind == ExactValue_String) {
GB_ASSERT(type->Array.count == value.value_string.len);
LLVMValueRef data = LLVMConstStringInContext(ctx,
@@ -668,7 +674,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
Type *elem = type->Array.elem;
lbValue single_elem = lb_const_value(m, elem, value, allow_local, is_rodata);
lbValue single_elem = lb_const_value(m, elem, value, cc);
LLVMValueRef *elems = gb_alloc_array(permanent_allocator(), LLVMValueRef, cast(isize)count);
for (i64 i = 0; i < count; i++) {
@@ -686,7 +692,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
Type *elem = type->Matrix.elem;
lbValue single_elem = lb_const_value(m, elem, value, allow_local, is_rodata);
lbValue single_elem = lb_const_value(m, elem, value, cc);
single_elem.value = llvm_const_cast(single_elem.value, lb_type(m, elem));
i64 total_elem_count = matrix_type_total_internal_elems(type);
@@ -708,7 +714,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
i64 count = type->SimdVector.count;
Type *elem = type->SimdVector.elem;
lbValue single_elem = lb_const_value(m, elem, value, allow_local, is_rodata);
lbValue single_elem = lb_const_value(m, elem, value, cc);
single_elem.value = llvm_const_cast(single_elem.value, lb_type(m, elem));
LLVMValueRef *elems = gb_alloc_array(permanent_allocator(), LLVMValueRef, count);
@@ -729,9 +735,16 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
return res;
case ExactValue_String:
{
LLVMValueRef ptr = lb_find_or_add_entity_string_ptr(m, value.value_string);
bool custom_link_section = cc.link_section.len > 0;
LLVMValueRef ptr = lb_find_or_add_entity_string_ptr(m, value.value_string, custom_link_section);
lbValue res = {};
res.type = default_type(original_type);
if (custom_link_section) {
LLVMSetSection(ptr, alloc_cstring(permanent_allocator(), cc.link_section));
}
if (is_type_cstring(res.type)) {
res.value = ptr;
} else {
@@ -837,7 +850,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
case ExactValue_Compound:
if (is_type_slice(type)) {
return lb_const_value(m, type, value, allow_local, is_rodata);
return lb_const_value(m, type, value, cc);
} else if (is_type_array(type)) {
ast_node(cl, CompoundLit, value.value_compound);
Type *elem_type = type->Array.elem;
@@ -871,7 +884,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
}
if (lo == i) {
TypeAndValue tav = fv->value->tav;
LLVMValueRef val = lb_const_value(m, elem_type, tav.value, allow_local, is_rodata).value;
LLVMValueRef val = lb_const_value(m, elem_type, tav.value, cc).value;
for (i64 k = lo; k < hi; k++) {
values[value_index++] = val;
}
@@ -886,7 +899,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
i64 index = exact_value_to_i64(index_tav.value);
if (index == i) {
TypeAndValue tav = fv->value->tav;
LLVMValueRef val = lb_const_value(m, elem_type, tav.value, allow_local, is_rodata).value;
LLVMValueRef val = lb_const_value(m, elem_type, tav.value, cc).value;
values[value_index++] = val;
found = true;
break;
@@ -899,7 +912,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
}
}
res.value = lb_build_constant_array_values(m, type, elem_type, cast(isize)type->Array.count, values, allow_local, is_rodata);
res.value = lb_build_constant_array_values(m, type, elem_type, cast(isize)type->Array.count, values, cc);
return res;
} else {
GB_ASSERT_MSG(elem_count == type->Array.count, "%td != %td", elem_count, type->Array.count);
@@ -909,13 +922,13 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
for (isize i = 0; i < elem_count; i++) {
TypeAndValue tav = cl->elems[i]->tav;
GB_ASSERT(tav.mode != Addressing_Invalid);
values[i] = lb_const_value(m, elem_type, tav.value, allow_local, is_rodata).value;
values[i] = lb_const_value(m, elem_type, tav.value, cc).value;
}
for (isize i = elem_count; i < type->Array.count; i++) {
values[i] = LLVMConstNull(lb_type(m, elem_type));
}
res.value = lb_build_constant_array_values(m, type, elem_type, cast(isize)type->Array.count, values, allow_local, is_rodata);
res.value = lb_build_constant_array_values(m, type, elem_type, cast(isize)type->Array.count, values, cc);
return res;
}
} else if (is_type_enumerated_array(type)) {
@@ -955,7 +968,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
}
if (lo == i) {
TypeAndValue tav = fv->value->tav;
LLVMValueRef val = lb_const_value(m, elem_type, tav.value, allow_local, is_rodata).value;
LLVMValueRef val = lb_const_value(m, elem_type, tav.value, cc).value;
for (i64 k = lo; k < hi; k++) {
values[value_index++] = val;
}
@@ -970,7 +983,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
i64 index = exact_value_to_i64(index_tav.value);
if (index == i) {
TypeAndValue tav = fv->value->tav;
LLVMValueRef val = lb_const_value(m, elem_type, tav.value, allow_local, is_rodata).value;
LLVMValueRef val = lb_const_value(m, elem_type, tav.value, cc).value;
values[value_index++] = val;
found = true;
break;
@@ -983,7 +996,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
}
}
res.value = lb_build_constant_array_values(m, type, elem_type, cast(isize)type->EnumeratedArray.count, values, allow_local, is_rodata);
res.value = lb_build_constant_array_values(m, type, elem_type, cast(isize)type->EnumeratedArray.count, values, cc);
return res;
} else {
GB_ASSERT_MSG(elem_count == type->EnumeratedArray.count, "%td != %td", elem_count, type->EnumeratedArray.count);
@@ -993,13 +1006,13 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
for (isize i = 0; i < elem_count; i++) {
TypeAndValue tav = cl->elems[i]->tav;
GB_ASSERT(tav.mode != Addressing_Invalid);
values[i] = lb_const_value(m, elem_type, tav.value, allow_local, is_rodata).value;
values[i] = lb_const_value(m, elem_type, tav.value, cc).value;
}
for (isize i = elem_count; i < type->EnumeratedArray.count; i++) {
values[i] = LLVMConstNull(lb_type(m, elem_type));
}
res.value = lb_build_constant_array_values(m, type, elem_type, cast(isize)type->EnumeratedArray.count, values, allow_local, is_rodata);
res.value = lb_build_constant_array_values(m, type, elem_type, cast(isize)type->EnumeratedArray.count, values, cc);
return res;
}
} else if (is_type_simd_vector(type)) {
@@ -1038,7 +1051,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
}
if (lo == i) {
TypeAndValue tav = fv->value->tav;
LLVMValueRef val = lb_const_value(m, elem_type, tav.value, allow_local, is_rodata).value;
LLVMValueRef val = lb_const_value(m, elem_type, tav.value, cc).value;
for (i64 k = lo; k < hi; k++) {
values[value_index++] = val;
}
@@ -1053,7 +1066,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
i64 index = exact_value_to_i64(index_tav.value);
if (index == i) {
TypeAndValue tav = fv->value->tav;
LLVMValueRef val = lb_const_value(m, elem_type, tav.value, allow_local, is_rodata).value;
LLVMValueRef val = lb_const_value(m, elem_type, tav.value, cc).value;
values[value_index++] = val;
found = true;
break;
@@ -1072,7 +1085,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
for (isize i = 0; i < elem_count; i++) {
TypeAndValue tav = cl->elems[i]->tav;
GB_ASSERT(tav.mode != Addressing_Invalid);
values[i] = lb_const_value(m, elem_type, tav.value, allow_local, is_rodata).value;
values[i] = lb_const_value(m, elem_type, tav.value, cc).value;
}
LLVMTypeRef et = lb_type(m, elem_type);
@@ -1121,11 +1134,13 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
i32 index = field_remapping[f->Variable.field_index];
if (elem_type_can_be_constant(f->type)) {
if (sel.index.count == 1) {
values[index] = lb_const_value(m, f->type, tav.value, allow_local, is_rodata).value;
values[index] = lb_const_value(m, f->type, tav.value, cc).value;
visited[index] = true;
} else {
if (!visited[index]) {
values[index] = lb_const_value(m, f->type, {}, /*allow_local*/false, is_rodata).value;
auto new_cc = cc;
new_cc.allow_local = false;
values[index] = lb_const_value(m, f->type, {}, new_cc).value;
visited[index] = true;
}
@@ -1165,7 +1180,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
}
}
if (is_constant) {
LLVMValueRef elem_value = lb_const_value(m, tav.type, tav.value, allow_local, is_rodata).value;
LLVMValueRef elem_value = lb_const_value(m, tav.type, tav.value, cc).value;
if (LLVMIsConstant(elem_value) && LLVMIsConstant(values[index])) {
values[index] = llvm_const_insert_value(m, values[index], elem_value, idx_list, idx_list_len);
} else if (is_local) {
@@ -1219,7 +1234,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
i32 index = field_remapping[f->Variable.field_index];
if (elem_type_can_be_constant(f->type)) {
values[index] = lb_const_value(m, f->type, val, allow_local, is_rodata).value;
values[index] = lb_const_value(m, f->type, val, cc).value;
visited[index] = true;
}
}
@@ -1353,7 +1368,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
TypeAndValue tav = fv->value->tav;
LLVMValueRef val = lb_const_value(m, elem_type, tav.value, allow_local, is_rodata).value;
LLVMValueRef val = lb_const_value(m, elem_type, tav.value, cc).value;
for (i64 k = lo; k < hi; k++) {
i64 offset = matrix_row_major_index_to_offset(type, k);
GB_ASSERT(values[offset] == nullptr);
@@ -1365,7 +1380,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
i64 index = exact_value_to_i64(index_tav.value);
GB_ASSERT(index < max_count);
TypeAndValue tav = fv->value->tav;
LLVMValueRef val = lb_const_value(m, elem_type, tav.value, allow_local, is_rodata).value;
LLVMValueRef val = lb_const_value(m, elem_type, tav.value, cc).value;
i64 offset = matrix_row_major_index_to_offset(type, index);
GB_ASSERT(values[offset] == nullptr);
values[offset] = val;
@@ -1378,7 +1393,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
}
}
res.value = lb_build_constant_array_values(m, type, elem_type, cast(isize)total_count, values, allow_local, is_rodata);
res.value = lb_build_constant_array_values(m, type, elem_type, cast(isize)total_count, values, cc);
return res;
} else {
GB_ASSERT_MSG(elem_count == max_count, "%td != %td", elem_count, max_count);
@@ -1389,7 +1404,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
GB_ASSERT(tav.mode != Addressing_Invalid);
i64 offset = 0;
offset = matrix_row_major_index_to_offset(type, i);
values[offset] = lb_const_value(m, elem_type, tav.value, allow_local, is_rodata).value;
values[offset] = lb_const_value(m, elem_type, tav.value, cc).value;
}
for (isize i = 0; i < total_count; i++) {
if (values[i] == nullptr) {
@@ -1397,7 +1412,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
}
}
res.value = lb_build_constant_array_values(m, type, elem_type, cast(isize)total_count, values, allow_local, is_rodata);
res.value = lb_build_constant_array_values(m, type, elem_type, cast(isize)total_count, values, cc);
return res;
}
} else {
+162 -27
View File
@@ -18,6 +18,25 @@ gb_internal void lb_set_llvm_metadata(lbModule *m, void *key, LLVMMetadataRef va
}
}
gb_internal void lb_add_raddbg_string(lbModule *m, String const &str) {
mpsc_enqueue(&m->gen->raddebug_section_strings, copy_string(permanent_allocator(), str));
}
gb_internal void lb_add_raddbg_string(lbModule *m, char const *cstr) {
mpsc_enqueue(&m->gen->raddebug_section_strings, copy_string(permanent_allocator(), make_string_c(cstr)));
}
gb_internal void lb_add_raddbg_string(lbModule *m, char const *a, char const *b) {
String str = concatenate_strings(permanent_allocator(), make_string_c(a), make_string_c(b));
mpsc_enqueue(&m->gen->raddebug_section_strings, str);
}
gb_internal void lb_add_raddbg_string(lbModule *m, char const *a, char const *b, char const *c) {
String str = concatenate3_strings(permanent_allocator(), make_string_c(a), make_string_c(b), make_string_c(c));
mpsc_enqueue(&m->gen->raddebug_section_strings, str);
}
gb_internal LLVMMetadataRef lb_get_current_debug_scope(lbProcedure *p) {
GB_ASSERT_MSG(p->debug_info != nullptr, "missing debug information for %.*s", LIT(p->name));
@@ -564,22 +583,21 @@ gb_internal LLVMMetadataRef lb_debug_bitfield(lbModule *m, Type *type, String na
u64 size_in_bits = 8*type_size_of(bt);
u32 align_in_bits = 8*cast(u32)type_align_of(bt);
unsigned element_count = cast(unsigned)bt->BitField.fields.count;
LLVMMetadataRef *elements = gb_alloc_array(permanent_allocator(), LLVMMetadataRef, element_count);
unsigned element_count = cast(unsigned)bt->BitField.fields.count;
LLVMMetadataRef *elements = gb_alloc_array(permanent_allocator(), LLVMMetadataRef, element_count);
u64 offset_in_bits = 0;
for (unsigned i = 0; i < element_count; i++) {
Entity *f = bt->BitField.fields[i];
u8 bit_size = bt->BitField.bit_sizes[i];
GB_ASSERT(f->kind == Entity_Variable);
String name = f->token.string;
elements[i] = LLVMDIBuilderCreateBitFieldMemberType(m->debug_builder, scope, cast(char const *)name.text, name.len, file, line,
bit_size, offset_in_bits, 0,
LLVMDIFlagZero, lb_debug_type(m, f->type)
);
offset_in_bits += bit_size;
}
u64 offset_in_bits = 0;
for (unsigned i = 0; i < element_count; i++) {
Entity *f = bt->BitField.fields[i];
u8 bit_size = bt->BitField.bit_sizes[i];
GB_ASSERT(f->kind == Entity_Variable);
String name = f->token.string;
elements[i] = LLVMDIBuilderCreateBitFieldMemberType(m->debug_builder, scope, cast(char const *)name.text, name.len, file, line,
bit_size, offset_in_bits, 0,
LLVMDIFlagZero, lb_debug_type(m, f->type)
);
offset_in_bits += bit_size;
}
LLVMMetadataRef final_decl = LLVMDIBuilderCreateStructType(
m->debug_builder, scope,
@@ -924,6 +942,7 @@ gb_internal LLVMMetadataRef lb_debug_type_internal(lbModule *m, Type *type) {
}
case Type_Matrix: {
#if 0
LLVMMetadataRef subscripts[1] = {};
subscripts[0] = LLVMDIBuilderGetOrCreateSubrange(m->debug_builder,
0ll,
@@ -935,6 +954,66 @@ gb_internal LLVMMetadataRef lb_debug_type_internal(lbModule *m, Type *type) {
8*cast(unsigned)type_align_of(type),
lb_debug_type(m, type->Matrix.elem),
subscripts, gb_count_of(subscripts));
#else
LLVMMetadataRef subscripts[2] = {};
subscripts[0] = LLVMDIBuilderGetOrCreateSubrange(m->debug_builder, 0ll, type->Matrix.row_count);
subscripts[1] = LLVMDIBuilderGetOrCreateSubrange(m->debug_builder, 0ll, type->Matrix.column_count);
LLVMMetadataRef scope = nullptr;
LLVMMetadataRef array_type = nullptr;
uint64_t size_in_bits = 8*cast(uint64_t)(type_size_of(type));
unsigned align_in_bits = 8*cast(unsigned)(type_align_of(type));
if (type->Matrix.is_row_major) {
LLVMMetadataRef base = LLVMDIBuilderCreateArrayType(m->debug_builder,
8*cast(uint64_t)(type_size_of(type->Matrix.elem) * type->Matrix.column_count),
8*cast(unsigned)type_align_of(type->Matrix.elem),
lb_debug_type(m, type->Matrix.elem),
subscripts+1, 1);
array_type = LLVMDIBuilderCreateArrayType(m->debug_builder,
size_in_bits,
align_in_bits,
base,
subscripts+0, 1);
} else {
LLVMMetadataRef base = LLVMDIBuilderCreateArrayType(m->debug_builder,
8*cast(uint64_t)(type_size_of(type->Matrix.elem) * type->Matrix.row_count),
8*cast(unsigned)type_align_of(type->Matrix.elem),
lb_debug_type(m, type->Matrix.elem),
subscripts+0, 1);
array_type = LLVMDIBuilderCreateArrayType(m->debug_builder,
size_in_bits,
align_in_bits,
base,
subscripts+1, 1);
}
LLVMMetadataRef elements[1] = {};
elements[0] = LLVMDIBuilderCreateMemberType(m->debug_builder, scope,
"data", 4,
nullptr, 0,
size_in_bits, align_in_bits, 0, LLVMDIFlagZero,
array_type
);
gbString name = temp_canonical_string(type);
LLVMMetadataRef final_decl = LLVMDIBuilderCreateStructType(
m->debug_builder, scope,
name, gb_string_length(name),
nullptr, 0,
size_in_bits, align_in_bits,
LLVMDIFlagZero,
nullptr,
elements, 1,
0,
nullptr,
"", 0
);
return final_decl;
#endif
}
}
@@ -1186,7 +1265,7 @@ gb_internal void lb_add_debug_context_variable(lbProcedure *p, lbAddr const &ctx
}
gb_internal String debug_info_mangle_constant_name(Entity *e, gbAllocator const &allocator, bool *did_allocate_) {
gb_internal String lb_debug_info_mangle_constant_name(Entity *e, gbAllocator const &allocator, bool *did_allocate_) {
String name = e->token.string;
if (e->pkg && e->pkg->name.len > 0) {
gbString s = string_canonical_entity_name(allocator, e);
@@ -1196,7 +1275,7 @@ gb_internal String debug_info_mangle_constant_name(Entity *e, gbAllocator const
return name;
}
gb_internal void add_debug_info_global_variable_expr(lbModule *m, String const &name, LLVMMetadataRef dtype, LLVMMetadataRef expr) {
gb_internal void lb_add_debug_info_global_variable_expr(lbModule *m, String const &name, LLVMMetadataRef dtype, LLVMMetadataRef expr) {
LLVMMetadataRef scope = nullptr;
LLVMMetadataRef file = nullptr;
unsigned line = 0;
@@ -1212,20 +1291,20 @@ gb_internal void add_debug_info_global_variable_expr(lbModule *m, String const &
expr, decl, 8/*AlignInBits*/);
}
gb_internal void add_debug_info_for_global_constant_internal_i64(lbModule *m, Entity *e, LLVMMetadataRef dtype, i64 v) {
gb_internal void lb_add_debug_info_for_global_constant_internal_i64(lbModule *m, Entity *e, LLVMMetadataRef dtype, i64 v) {
LLVMMetadataRef expr = LLVMDIBuilderCreateConstantValueExpression(m->debug_builder, v);
TEMPORARY_ALLOCATOR_GUARD();
String name = debug_info_mangle_constant_name(e, temporary_allocator(), nullptr);
String name = lb_debug_info_mangle_constant_name(e, temporary_allocator(), nullptr);
add_debug_info_global_variable_expr(m, name, dtype, expr);
lb_add_debug_info_global_variable_expr(m, name, dtype, expr);
if ((e->pkg && e->pkg->kind == Package_Init) ||
(e->scope && (e->scope->flags & ScopeFlag_Global))) {
add_debug_info_global_variable_expr(m, e->token.string, dtype, expr);
lb_add_debug_info_global_variable_expr(m, e->token.string, dtype, expr);
}
}
gb_internal void add_debug_info_for_global_constant_from_entity(lbGenerator *gen, Entity *e) {
gb_internal void lb_add_debug_info_for_global_constant_from_entity(lbGenerator *gen, Entity *e) {
if (e == nullptr || e->kind != Entity_Constant) {
return;
}
@@ -1256,14 +1335,14 @@ gb_internal void add_debug_info_for_global_constant_from_entity(lbGenerator *gen
dtype = lb_debug_type(m, e->type);
}
add_debug_info_for_global_constant_internal_i64(m, e, dtype, v);
lb_add_debug_info_for_global_constant_internal_i64(m, e, dtype, v);
}
} else if (is_type_rune(e->type)) {
ExactValue const &value = e->Constant.value;
if (value.kind == ExactValue_Integer) {
LLVMMetadataRef dtype = lb_debug_type(m, t_rune);
i64 v = exact_value_to_i64(value);
add_debug_info_for_global_constant_internal_i64(m, e, dtype, v);
lb_add_debug_info_for_global_constant_internal_i64(m, e, dtype, v);
}
} else if (is_type_boolean(e->type)) {
ExactValue const &value = e->Constant.value;
@@ -1271,7 +1350,7 @@ gb_internal void add_debug_info_for_global_constant_from_entity(lbGenerator *gen
LLVMMetadataRef dtype = lb_debug_type(m, default_type(e->type));
i64 v = cast(i64)value.value_bool;
add_debug_info_for_global_constant_internal_i64(m, e, dtype, v);
lb_add_debug_info_for_global_constant_internal_i64(m, e, dtype, v);
}
} else if (is_type_enum(e->type)) {
ExactValue const &value = e->Constant.value;
@@ -1284,14 +1363,70 @@ gb_internal void add_debug_info_for_global_constant_from_entity(lbGenerator *gen
v = cast(i64)exact_value_to_u64(value);
}
add_debug_info_for_global_constant_internal_i64(m, e, dtype, v);
lb_add_debug_info_for_global_constant_internal_i64(m, e, dtype, v);
}
} else if (is_type_pointer(e->type)) {
ExactValue const &value = e->Constant.value;
if (value.kind == ExactValue_Integer) {
LLVMMetadataRef dtype = lb_debug_type(m, default_type(e->type));
i64 v = cast(i64)exact_value_to_u64(value);
add_debug_info_for_global_constant_internal_i64(m, e, dtype, v);
lb_add_debug_info_for_global_constant_internal_i64(m, e, dtype, v);
}
}
}
gb_internal void lb_add_debug_label(lbProcedure *p, Ast *label, lbBlock *target) {
// NOTE(tf2spi): LLVM-C DILabel API used only existed for major versions 20+
#if LLVM_VERSION_MAJOR >= 20
if (p == nullptr || p->debug_info == nullptr) {
return;
}
if (target == nullptr || label == nullptr || label->kind != Ast_Label) {
return;
}
Token label_token = label->Label.token;
if (is_blank_ident(label_token.string)) {
return;
}
lbModule *m = p->module;
if (m == nullptr) {
return;
}
AstFile *file = label->file();
LLVMMetadataRef llvm_file = lb_get_llvm_metadata(m, file);
if (llvm_file == nullptr) {
debugf("llvm file not found for label\n");
return;
}
LLVMMetadataRef llvm_scope = p->debug_info;
if(llvm_scope == nullptr) {
debugf("llvm scope not found for label\n");
return;
}
LLVMMetadataRef llvm_debug_loc = lb_debug_location_from_token_pos(p, label_token.pos);
LLVMBasicBlockRef llvm_block = target->block;
if (llvm_block == nullptr || llvm_debug_loc == nullptr) {
return;
}
LLVMMetadataRef llvm_label = LLVMDIBuilderCreateLabel(
m->debug_builder,
llvm_scope,
(const char *)label_token.string.text,
(size_t)label_token.string.len,
llvm_file,
label_token.pos.line,
// NOTE(tf2spi): Defaults to false in LLVM API, but I'd rather not take chances
// Always preserve the label no matter what when debugging
true
);
GB_ASSERT(llvm_label != nullptr);
(void)LLVMDIBuilderInsertLabelAtEnd(
m->debug_builder,
llvm_label,
llvm_debug_loc,
llvm_block
);
#endif
}
+42 -16
View File
@@ -222,6 +222,7 @@ gb_internal lbValue lb_emit_unary_arith(lbProcedure *p, TokenKind op, lbValue x,
return lb_emit_byte_swap(p, res, type);
}
Type* bt = base_type(type);
lbValue res = {};
switch (op) {
@@ -233,6 +234,8 @@ gb_internal lbValue lb_emit_unary_arith(lbProcedure *p, TokenKind op, lbValue x,
case Token_Sub: // Number negation
if (is_type_integer(x.type)) {
res.value = LLVMBuildNeg(p->builder, x.value, "");
} else if (bt->kind == Type_Enum && is_type_integer(bt->Enum.base_type)) {
res.value = LLVMBuildNeg(p->builder, x.value, "");
} else if (is_type_float(x.type)) {
res.value = LLVMBuildFNeg(p->builder, x.value, "");
} else if (is_type_complex(x.type)) {
@@ -2352,7 +2355,7 @@ gb_internal lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
Type *elem = base_array_type(dst);
lbValue e = lb_emit_conv(p, value, elem);
lbAddr v = lb_add_local_generated(p, t, false);
lbValue zero = lb_const_value(p->module, elem, exact_value_i64(0), true);
lbValue zero = lb_const_value(p->module, elem, exact_value_i64(0), LB_CONST_CONTEXT_DEFAULT_ALLOW_LOCAL);
for (i64 j = 0; j < dst->Matrix.column_count; j++) {
for (i64 i = 0; i < dst->Matrix.row_count; i++) {
lbValue ptr = lb_emit_matrix_epi(p, v.addr, i, j);
@@ -2389,7 +2392,7 @@ gb_internal lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
lb_emit_store(p, d, s);
} else if (i == j) {
lbValue d = lb_emit_matrix_epi(p, v.addr, i, j);
lbValue s = lb_const_value(p->module, dst->Matrix.elem, exact_value_i64(1), true);
lbValue s = lb_const_value(p->module, dst->Matrix.elem, exact_value_i64(1), LB_CONST_CONTEXT_DEFAULT_ALLOW_LOCAL);
lb_emit_store(p, d, s);
}
}
@@ -2514,7 +2517,7 @@ gb_internal lbValue lb_emit_c_vararg(lbProcedure *p, lbValue arg, Type *type) {
}
gb_internal lbValue lb_compare_records(lbProcedure *p, TokenKind op_kind, lbValue left, lbValue right, Type *type) {
GB_ASSERT((is_type_struct(type) || is_type_union(type)) && is_type_comparable(type));
GB_ASSERT((is_type_struct(type) || is_type_soa_pointer(type) || is_type_union(type)) && is_type_comparable(type));
lbValue left_ptr = lb_address_from_load_or_generate_local(p, left);
lbValue right_ptr = lb_address_from_load_or_generate_local(p, right);
lbValue res = {};
@@ -2899,6 +2902,7 @@ gb_internal lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left
is_type_proc(a) ||
is_type_enum(a)) {
LLVMIntPredicate pred = {};
if (is_type_unsigned(left.type)) {
switch (op_kind) {
case Token_Gt: pred = LLVMIntUGT; break;
@@ -2941,7 +2945,7 @@ gb_internal lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left
case Token_GtEq: pred = LLVMRealOGE; break;
case Token_Lt: pred = LLVMRealOLT; break;
case Token_LtEq: pred = LLVMRealOLE; break;
case Token_NotEq: pred = LLVMRealONE; break;
case Token_NotEq: pred = LLVMRealUNE; break;
}
if (is_type_different_to_arch_endianness(left.type)) {
@@ -2969,7 +2973,7 @@ gb_internal lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left
LLVMRealPredicate pred = {};
switch (op_kind) {
case Token_CmpEq: pred = LLVMRealOEQ; break;
case Token_NotEq: pred = LLVMRealONE; break;
case Token_NotEq: pred = LLVMRealUNE; break;
}
mask = LLVMBuildFCmp(p->builder, pred, left.value, right.value, "");
} else {
@@ -3022,6 +3026,9 @@ gb_internal lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left
return res;
} else if (is_type_soa_pointer(a)) {
// NOTE(Jeroen): Compare data pointer and index tag as if it were a simple struct.
return lb_compare_records(p, op_kind, left, right, a);
} else {
GB_PANIC("Unhandled comparison kind %s (%s) %.*s %s (%s)", type_to_string(left.type), type_to_string(base_type(left.type)), LIT(token_strings[op_kind]), type_to_string(right.type), type_to_string(base_type(right.type)));
}
@@ -3143,6 +3150,18 @@ gb_internal lbValue lb_emit_comp_against_nil(lbProcedure *p, TokenKind op_kind,
}
}
break;
case Type_SoaPointer:
{
// NOTE(bill): An SoaPointer is essentially just a pointer for nil comparison
lbValue ptr = lb_emit_struct_ev(p, x, 0); // Extract the base pointer component (field 0)
if (op_kind == Token_CmpEq) {
res.value = LLVMBuildIsNull(p->builder, ptr.value, "");
} else if (op_kind == Token_NotEq) {
res.value = LLVMBuildIsNotNull(p->builder, ptr.value, "");
}
return res;
}
case Type_Union:
{
@@ -3493,8 +3512,7 @@ gb_internal lbValue lb_build_expr_internal(lbProcedure *p, Ast *expr) {
if (tv.value.kind != ExactValue_Invalid) {
// NOTE(bill): Short on constant values
bool allow_local = true;
return lb_const_value(p->module, type, tv.value, allow_local);
return lb_const_value(p->module, type, tv.value, LB_CONST_CONTEXT_DEFAULT_ALLOW_LOCAL);
} else if (tv.mode == Addressing_Type) {
// NOTE(bill, 2023-01-16): is this correct? I hope so at least
return lb_typeid(m, tv.type);
@@ -4826,7 +4844,7 @@ gb_internal lbAddr lb_build_addr_compound_lit(lbProcedure *p, Ast *expr) {
if (cl->elems.count == 0) {
break;
}
GB_ASSERT(expr->file()->feature_flags & OptInFeatureFlag_DynamicLiterals);
GB_ASSERT(expr->file()->feature_flags & OptInFeatureFlag_DynamicLiterals || build_context.dynamic_literals);
lbValue err = lb_dynamic_map_reserve(p, v.addr, 2*cl->elems.count, pos);
gb_unused(err);
@@ -5136,8 +5154,6 @@ gb_internal lbAddr lb_build_addr_internal(lbProcedure *p, Ast *expr) {
return lb_build_addr(p, unparen_expr(se->selector));
}
Type *type = base_type(tav.type);
if (tav.mode == Addressing_Type) { // Addressing_Type
Selection sel = lookup_field(tav.type, selector, true);
if (sel.pseudo_field) {
@@ -5172,18 +5188,29 @@ gb_internal lbAddr lb_build_addr_internal(lbProcedure *p, Ast *expr) {
return lb_addr_swizzle(a, type, swizzle_count, swizzle_indices);
}
Selection sel = lookup_field(type, selector, false);
Selection sel = lookup_field(tav.type, selector, false);
GB_ASSERT(sel.entity != nullptr);
if (sel.pseudo_field) {
GB_ASSERT(sel.entity->kind == Entity_Procedure || sel.entity->kind == Entity_ProcGroup);
if (sel.pseudo_field && (sel.entity->kind == Entity_Procedure || sel.entity->kind == Entity_ProcGroup)) {
Entity *e = entity_of_node(sel_node);
GB_ASSERT(e->kind == Entity_Procedure);
return lb_addr(lb_find_value_from_entity(p->module, e));
}
if (sel.is_bit_field) {
lbAddr addr = lb_build_addr(p, se->expr);
lbAddr addr = lb_build_addr(p, se->expr);
// NOTE(harold): Only allow ivar pseudo field access on indirect selectors.
// It is incoherent otherwise as Objective-C objects are zero-sized.
Type *deref_type = type_deref(tav.type);
if (tav.type->kind == Type_Pointer && deref_type->kind == Type_Named && deref_type->Named.type_name->TypeName.objc_ivar) {
// NOTE(harold): We need to load the ivar from the current address and
// replace addr with the loaded ivar addr to apply the selector load properly.
addr = lb_addr(lb_emit_load(p, addr.addr));
lbValue ivar_ptr = lb_handle_objc_ivar_for_objc_object_pointer(p, addr.addr);
addr = lb_addr(ivar_ptr);
}
if (sel.is_bit_field) {
Selection sub_sel = sel;
sub_sel.index.count -= 1;
@@ -5209,7 +5236,6 @@ gb_internal lbAddr lb_build_addr_internal(lbProcedure *p, Ast *expr) {
}
{
lbAddr addr = lb_build_addr(p, se->expr);
if (addr.kind == lbAddr_Map) {
lbValue v = lb_addr_load(p, addr);
lbValue a = lb_address_from_load_or_generate_local(p, v);
+123 -33
View File
@@ -101,6 +101,7 @@ gb_internal void lb_init_module(lbModule *m, Checker *c) {
string_map_init(&m->objc_classes);
string_map_init(&m->objc_selectors);
string_map_init(&m->objc_ivars);
map_init(&m->map_info_map, 0);
map_init(&m->map_cell_info_map, 0);
@@ -173,7 +174,9 @@ gb_internal bool lb_init_generator(lbGenerator *gen, Checker *c) {
mpsc_init(&gen->entities_to_correct_linkage, heap_allocator());
mpsc_init(&gen->objc_selectors, heap_allocator());
mpsc_init(&gen->objc_classes, heap_allocator());
mpsc_init(&gen->objc_ivars, heap_allocator());
mpsc_init(&gen->raddebug_section_strings, heap_allocator());
return true;
}
@@ -356,7 +359,7 @@ gb_internal LLVMValueRef llvm_const_insert_value(lbModule *m, LLVMValueRef agg,
gb_internal LLVMValueRef llvm_cstring(lbModule *m, String const &str) {
lbValue v = lb_find_or_add_entity_string(m, str);
lbValue v = lb_find_or_add_entity_string(m, str, false);
unsigned indices[1] = {0};
return llvm_const_extract_value(m, v.value, indices, gb_count_of(indices));
}
@@ -568,7 +571,7 @@ gb_internal void lb_set_file_line_col(lbProcedure *p, Array<lbValue> arr, TokenP
col = obfuscate_i32(col);
}
arr[0] = lb_find_or_add_entity_string(p->module, file);
arr[0] = lb_find_or_add_entity_string(p->module, file, false);
arr[1] = lb_const_int(p->module, t_i32, line);
arr[2] = lb_const_int(p->module, t_i32, col);
}
@@ -885,8 +888,8 @@ gb_internal void lb_addr_store(lbProcedure *p, lbAddr addr, lbValue value) {
Type *t = base_type(type_deref(addr.addr.type));
GB_ASSERT(t->kind == Type_Struct && t->Struct.soa_kind != StructSoa_None);
lbValue len = lb_soa_struct_len(p, addr.addr);
if (addr.soa.index_expr != nullptr) {
lb_emit_bounds_check(p, ast_token(addr.soa.index_expr), index, len);
if (addr.soa.index_expr != nullptr && (!lb_is_const(addr.soa.index) || t->Struct.soa_kind != StructSoa_Fixed)) {
lb_emit_bounds_check(p, ast_token(addr.soa.index_expr), addr.soa.index, len);
}
}
@@ -2212,6 +2215,14 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
case Type_BitField:
return lb_type_internal(m, type->BitField.backing_type);
case Type_Generic:
if (type->Generic.specialized) {
return lb_type_internal(m, type->Generic.specialized);
} else {
// For unspecialized generics, use a pointer type as a placeholder
return LLVMPointerType(LLVMInt8TypeInContext(m->ctx), 0);
}
}
GB_PANIC("Invalid type %s", type_to_string(type));
@@ -2377,6 +2388,29 @@ gb_internal void lb_add_attribute_to_proc_with_string(lbModule *m, LLVMValueRef
}
gb_internal bool lb_apply_thread_local_model(LLVMValueRef value, String model) {
if (model != "") {
LLVMSetThreadLocal(value, true);
LLVMThreadLocalMode mode = LLVMGeneralDynamicTLSModel;
if (model == "default") {
mode = LLVMGeneralDynamicTLSModel;
} else if (model == "localdynamic") {
mode = LLVMLocalDynamicTLSModel;
} else if (model == "initialexec") {
mode = LLVMInitialExecTLSModel;
} else if (model == "localexec") {
mode = LLVMLocalExecTLSModel;
} else {
GB_PANIC("Unhandled thread local mode %.*s", LIT(model));
}
LLVMSetThreadLocalMode(value, mode);
return true;
}
return false;
}
gb_internal void lb_add_edge(lbBlock *from, lbBlock *to) {
LLVMValueRef instr = LLVMGetLastInstruction(from->block);
@@ -2515,10 +2549,13 @@ general_end:;
}
}
src_size = align_formula(src_size, src_align);
dst_size = align_formula(dst_size, dst_align);
// NOTE(laytan): even though this logic seems sound, the Address Sanitizer does not
// want you to load/store the space of a value that is there for alignment.
#if 0
i64 aligned_src_size = align_formula(src_size, src_align);
i64 aligned_dst_size = align_formula(dst_size, dst_align);
if (LLVMIsALoadInst(val) && (src_size >= dst_size && src_align >= dst_align)) {
if (LLVMIsALoadInst(val) && (aligned_src_size >= aligned_dst_size && src_align >= dst_align)) {
LLVMValueRef val_ptr = LLVMGetOperand(val, 0);
val_ptr = LLVMBuildPointerCast(p->builder, val_ptr, LLVMPointerType(dst_type, 0), "");
LLVMValueRef loaded_val = OdinLLVMBuildLoad(p, dst_type, val_ptr);
@@ -2526,8 +2563,57 @@ general_end:;
// LLVMSetAlignment(loaded_val, gb_min(src_align, dst_align));
return loaded_val;
}
#endif
if (src_size > dst_size) {
GB_ASSERT(p->decl_block != p->curr_block);
// NOTE(laytan): src is bigger than dst, need to memcpy the part of src we want.
LLVMValueRef val_ptr;
if (LLVMIsALoadInst(val)) {
val_ptr = LLVMGetOperand(val, 0);
} else if (LLVMIsAAllocaInst(val)) {
val_ptr = LLVMBuildPointerCast(p->builder, val, LLVMPointerType(src_type, 0), "");
} else {
// NOTE(laytan): we need a pointer to memcpy from.
LLVMValueRef val_copy = llvm_alloca(p, src_type, src_align);
val_ptr = LLVMBuildPointerCast(p->builder, val_copy, LLVMPointerType(src_type, 0), "");
LLVMBuildStore(p->builder, val, val_ptr);
}
i64 max_align = gb_max(lb_alignof(src_type), lb_alignof(dst_type));
max_align = gb_max(max_align, 16);
LLVMValueRef ptr = llvm_alloca(p, dst_type, max_align);
LLVMValueRef nptr = LLVMBuildPointerCast(p->builder, ptr, LLVMPointerType(dst_type, 0), "");
LLVMTypeRef types[3] = {
lb_type(p->module, t_rawptr),
lb_type(p->module, t_rawptr),
lb_type(p->module, t_int)
};
LLVMValueRef args[4] = {
nptr,
val_ptr,
LLVMConstInt(LLVMIntTypeInContext(p->module->ctx, 8*cast(unsigned)build_context.int_size), dst_size, 0),
LLVMConstInt(LLVMInt1TypeInContext(p->module->ctx), 0, 0),
};
lb_call_intrinsic(
p,
"llvm.memcpy.inline",
args,
gb_count_of(args),
types,
gb_count_of(types)
);
return OdinLLVMBuildLoad(p, dst_type, ptr);
} else {
GB_ASSERT(p->decl_block != p->curr_block);
GB_ASSERT(dst_size >= src_size);
i64 max_align = gb_max(lb_alignof(src_type), lb_alignof(dst_type));
max_align = gb_max(max_align, 16);
@@ -2543,9 +2629,14 @@ general_end:;
gb_internal LLVMValueRef lb_find_or_add_entity_string_ptr(lbModule *m, String const &str) {
StringHashKey key = string_hash_string(str);
LLVMValueRef *found = string_map_get(&m->const_strings, key);
gb_internal LLVMValueRef lb_find_or_add_entity_string_ptr(lbModule *m, String const &str, bool custom_link_section) {
StringHashKey key = {};
LLVMValueRef *found = nullptr;
if (!custom_link_section) {
key = string_hash_string(str);
found = string_map_get(&m->const_strings, key);
}
if (found != nullptr) {
return *found;
} else {
@@ -2568,15 +2659,17 @@ gb_internal LLVMValueRef lb_find_or_add_entity_string_ptr(lbModule *m, String co
LLVMSetAlignment(global_data, 1);
LLVMValueRef ptr = LLVMConstInBoundsGEP2(type, global_data, indices, 2);
string_map_set(&m->const_strings, key, ptr);
if (!custom_link_section) {
string_map_set(&m->const_strings, key, ptr);
}
return ptr;
}
}
gb_internal lbValue lb_find_or_add_entity_string(lbModule *m, String const &str) {
gb_internal lbValue lb_find_or_add_entity_string(lbModule *m, String const &str, bool custom_link_section) {
LLVMValueRef ptr = nullptr;
if (str.len != 0) {
ptr = lb_find_or_add_entity_string_ptr(m, str);
ptr = lb_find_or_add_entity_string_ptr(m, str, custom_link_section);
} else {
ptr = LLVMConstNull(lb_type(m, t_u8_ptr));
}
@@ -2721,6 +2814,14 @@ gb_internal lbValue lb_find_procedure_value_from_entity(lbModule *m, Entity *e)
ignore_body = other_module != m;
lbProcedure *missing_proc = lb_create_procedure(m, e, ignore_body);
if (missing_proc == nullptr) {
// This is an unspecialized polymorphic procedure, which should not be codegen'd
lbValue dummy = {};
dummy.value = nullptr;
dummy.type = nullptr;
return dummy;
}
if (ignore_body) {
mutex_lock(&gen->anonymous_proc_lits_mutex);
defer (mutex_unlock(&gen->anonymous_proc_lits_mutex));
@@ -2913,25 +3014,7 @@ gb_internal lbValue lb_find_value_from_entity(lbModule *m, Entity *e) {
lb_set_entity_from_other_modules_linkage_correctly(other_module, e, name);
if (e->Variable.thread_local_model != "") {
LLVMSetThreadLocal(g.value, true);
String m = e->Variable.thread_local_model;
LLVMThreadLocalMode mode = LLVMGeneralDynamicTLSModel;
if (m == "default") {
mode = LLVMGeneralDynamicTLSModel;
} else if (m == "localdynamic") {
mode = LLVMLocalDynamicTLSModel;
} else if (m == "initialexec") {
mode = LLVMInitialExecTLSModel;
} else if (m == "localexec") {
mode = LLVMLocalExecTLSModel;
} else {
GB_PANIC("Unhandled thread local mode %.*s", LIT(m));
}
LLVMSetThreadLocalMode(g.value, mode);
}
lb_apply_thread_local_model(g.value, e->Variable.thread_local_model);
return g;
}
@@ -3063,6 +3146,13 @@ gb_internal lbAddr lb_add_local(lbProcedure *p, Type *type, Entity *e, bool zero
if (e != nullptr) {
lb_add_entity(p->module, e, val);
lb_add_debug_local_variable(p, ptr, type, e->token);
// NOTE(lucas): In LLVM 20 and below we do not have the option to have asan cleanup poisoned stack
// locals ourselves. So we need to manually track and unpoison these locals on proc return.
// LLVM 21 adds the 'use-after-scope' asan option which does this for us.
if (build_context.sanitizer_flags & SanitizerFlag_Address && !p->entity->Procedure.no_sanitize_address) {
array_add(&p->asan_stack_locals, val);
}
}
if (zero_init) {
+2 -2
View File
@@ -516,7 +516,7 @@ gb_internal void llvm_delete_function(LLVMValueRef func) {
LLVMDeleteFunction(func);
}
gb_internal void lb_append_to_compiler_used(lbModule *m, LLVMValueRef func) {
gb_internal void lb_append_to_compiler_used(lbModule *m, LLVMValueRef value) {
LLVMValueRef global = LLVMGetNamedGlobal(m->mod, "llvm.compiler.used");
LLVMValueRef *constants;
@@ -544,7 +544,7 @@ gb_internal void lb_append_to_compiler_used(lbModule *m, LLVMValueRef func) {
LLVMTypeRef Int8PtrTy = LLVMPointerType(LLVMInt8TypeInContext(m->ctx), 0);
LLVMTypeRef ATy = llvm_array_type(Int8PtrTy, operands);
constants[operands - 1] = LLVMConstBitCast(func, Int8PtrTy);
constants[operands - 1] = LLVMConstBitCast(value, Int8PtrTy);
LLVMValueRef initializer = LLVMConstArray(Int8PtrTy, constants, operands);
global = LLVMAddGlobal(m->mod, ATy, "llvm.compiler.used");
+205 -23
View File
@@ -67,6 +67,14 @@ gb_internal void lb_mem_copy_non_overlapping(lbProcedure *p, lbValue dst, lbValu
gb_internal lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool ignore_body) {
GB_ASSERT(entity != nullptr);
GB_ASSERT(entity->kind == Entity_Procedure);
// Skip codegen for unspecialized polymorphic procedures
if (is_type_polymorphic(entity->type) && !entity->Procedure.is_foreign) {
Type *bt = base_type(entity->type);
if (bt->kind == Type_Proc && bt->Proc.is_polymorphic && !bt->Proc.is_poly_specialized) {
// Do not generate code for unspecialized polymorphic procedures
return nullptr;
}
}
if (!entity->Procedure.is_foreign) {
if ((entity->flags & EntityFlag_ProcBodyChecked) == 0) {
GB_PANIC("%.*s :: %s (was parapoly: %d %d)", LIT(entity->token.string), type_to_string(entity->type), is_type_polymorphic(entity->type, true), is_type_polymorphic(entity->type, false));
@@ -115,12 +123,13 @@ gb_internal lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool i
p->is_entry_point = false;
gbAllocator a = heap_allocator();
p->children.allocator = a;
p->defer_stmts.allocator = a;
p->blocks.allocator = a;
p->branch_blocks.allocator = a;
p->context_stack.allocator = a;
p->scope_stack.allocator = a;
p->children.allocator = a;
p->defer_stmts.allocator = a;
p->blocks.allocator = a;
p->branch_blocks.allocator = a;
p->context_stack.allocator = a;
p->scope_stack.allocator = a;
p->asan_stack_locals.allocator = a;
// map_init(&p->selector_values, 0);
// map_init(&p->selector_addr, 0);
// map_init(&p->tuple_fix_map, 0);
@@ -333,7 +342,7 @@ gb_internal lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool i
}
if (p->body && entity->pkg && ((entity->pkg->kind == Package_Normal) || (entity->pkg->kind == Package_Init))) {
if (build_context.sanitizer_flags & SanitizerFlag_Address) {
if (build_context.sanitizer_flags & SanitizerFlag_Address && !entity->Procedure.no_sanitize_address) {
lb_add_attribute_to_proc(m, p->value, "sanitize_address");
}
if (build_context.sanitizer_flags & SanitizerFlag_Memory) {
@@ -385,11 +394,12 @@ gb_internal lbProcedure *lb_create_dummy_procedure(lbModule *m, String link_name
p->is_entry_point = false;
gbAllocator a = permanent_allocator();
p->children.allocator = a;
p->defer_stmts.allocator = a;
p->blocks.allocator = a;
p->branch_blocks.allocator = a;
p->context_stack.allocator = a;
p->children.allocator = a;
p->defer_stmts.allocator = a;
p->blocks.allocator = a;
p->branch_blocks.allocator = a;
p->context_stack.allocator = a;
p->asan_stack_locals.allocator = a;
map_init(&p->tuple_fix_map, 0);
@@ -781,8 +791,7 @@ gb_internal void lb_end_procedure_body(lbProcedure *p) {
p->curr_block = nullptr;
p->state_flags = 0;
}
gb_internal void lb_end_procedure(lbProcedure *p) {
LLVMDisposeBuilder(p->builder);
}
@@ -815,6 +824,10 @@ gb_internal void lb_build_nested_proc(lbProcedure *p, AstProcLit *pd, Entity *e)
e->Procedure.link_name = name;
lbProcedure *nested_proc = lb_create_procedure(p->module, e);
if (nested_proc == nullptr) {
// This is an unspecialized polymorphic procedure, skip codegen
return;
}
e->code_gen_procedure = nested_proc;
lbValue value = {};
@@ -1293,6 +1306,23 @@ gb_internal lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAn
lbValue res = {};
res.type = tv.type;
switch (builtin_id) {
case BuiltinProc_simd_indices: {
Type *type = base_type(res.type);
GB_ASSERT(type->kind == Type_SimdVector);
Type *elem = type->SimdVector.elem;
i64 count = type->SimdVector.count;
LLVMValueRef *scalars = gb_alloc_array(temporary_allocator(), LLVMValueRef, count);
for (i64 i = 0; i < count; i++) {
scalars[i] = lb_const_value(m, elem, exact_value_i64(i)).value;
}
res.value = LLVMConstVector(scalars, cast(unsigned)count);
return res;
}
}
lbValue arg0 = {}; if (ce->args.count > 0) arg0 = lb_build_expr(p, ce->args[0]);
lbValue arg1 = {}; if (ce->args.count > 1) arg1 = lb_build_expr(p, ce->args[1]);
lbValue arg2 = {}; if (ce->args.count > 2) arg2 = lb_build_expr(p, ce->args[2]);
@@ -1442,7 +1472,7 @@ gb_internal lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAn
LLVMRealPredicate pred = cast(LLVMRealPredicate)0;
switch (builtin_id) {
case BuiltinProc_simd_lanes_eq: pred = LLVMRealOEQ; break;
case BuiltinProc_simd_lanes_ne: pred = LLVMRealONE; break;
case BuiltinProc_simd_lanes_ne: pred = LLVMRealUNE; break;
case BuiltinProc_simd_lanes_lt: pred = LLVMRealOLT; break;
case BuiltinProc_simd_lanes_le: pred = LLVMRealOLE; break;
case BuiltinProc_simd_lanes_gt: pred = LLVMRealOGT; break;
@@ -1478,6 +1508,38 @@ gb_internal lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAn
res.value = LLVMBuildInsertElement(p->builder, arg0.value, arg2.value, arg1.value, "");
return res;
case BuiltinProc_simd_reduce_add_bisect:
case BuiltinProc_simd_reduce_mul_bisect:
{
GB_ASSERT(arg0.type->kind == Type_SimdVector);
i64 num_elems = arg0.type->SimdVector.count;
LLVMValueRef *indices = gb_alloc_array(temporary_allocator(), LLVMValueRef, num_elems);
for (i64 i = 0; i < num_elems; i++) {
indices[i] = lb_const_int(m, t_uint, cast(u64)i).value;
}
switch (builtin_id) {
case BuiltinProc_simd_reduce_add_bisect: op_code = is_float ? LLVMFAdd : LLVMAdd; break;
case BuiltinProc_simd_reduce_mul_bisect: op_code = is_float ? LLVMFMul : LLVMMul; break;
}
LLVMValueRef remaining = arg0.value;
i64 num_remaining = num_elems;
while (num_remaining > 1) {
num_remaining /= 2;
LLVMValueRef left_indices = LLVMConstVector(&indices[0], cast(unsigned)num_remaining);
LLVMValueRef left_value = LLVMBuildShuffleVector(p->builder, remaining, remaining, left_indices, "");
LLVMValueRef right_indices = LLVMConstVector(&indices[num_remaining], cast(unsigned)num_remaining);
LLVMValueRef right_value = LLVMBuildShuffleVector(p->builder, remaining, remaining, right_indices, "");
remaining = LLVMBuildBinOp(p->builder, op_code, left_value, right_value, "");
}
res.value = LLVMBuildExtractElement(p->builder, remaining, indices[0], "");
return res;
}
case BuiltinProc_simd_reduce_add_ordered:
case BuiltinProc_simd_reduce_mul_ordered:
{
@@ -1510,6 +1572,40 @@ gb_internal lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAn
res.value = lb_call_intrinsic(p, name, args, cast(unsigned)args_count, types, gb_count_of(types));
return res;
}
case BuiltinProc_simd_reduce_add_pairs:
case BuiltinProc_simd_reduce_mul_pairs:
{
GB_ASSERT(arg0.type->kind == Type_SimdVector);
i64 num_elems = arg0.type->SimdVector.count;
LLVMValueRef *indices = gb_alloc_array(temporary_allocator(), LLVMValueRef, num_elems);
for (i64 i = 0; i < num_elems/2; i++) {
indices[i] = lb_const_int(m, t_uint, cast(u64)(2*i)).value;
indices[i+num_elems/2] = lb_const_int(m, t_uint, cast(u64)(2*i+1)).value;
}
switch (builtin_id) {
case BuiltinProc_simd_reduce_add_pairs: op_code = is_float ? LLVMFAdd : LLVMAdd; break;
case BuiltinProc_simd_reduce_mul_pairs: op_code = is_float ? LLVMFMul : LLVMMul; break;
}
LLVMValueRef remaining = arg0.value;
i64 num_remaining = num_elems;
while (num_remaining > 1) {
num_remaining /= 2;
LLVMValueRef left_indices = LLVMConstVector(&indices[0], cast(unsigned)num_remaining);
LLVMValueRef left_value = LLVMBuildShuffleVector(p->builder, remaining, remaining, left_indices, "");
LLVMValueRef right_indices = LLVMConstVector(&indices[num_elems/2], cast(unsigned)num_remaining);
LLVMValueRef right_value = LLVMBuildShuffleVector(p->builder, remaining, remaining, right_indices, "");
remaining = LLVMBuildBinOp(p->builder, op_code, left_value, right_value, "");
}
res.value = LLVMBuildExtractElement(p->builder, remaining, indices[0], "");
return res;
}
case BuiltinProc_simd_reduce_min:
case BuiltinProc_simd_reduce_max:
case BuiltinProc_simd_reduce_and:
@@ -2150,6 +2246,68 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
return lb_emit_load(p, tuple);
}
case BuiltinProc_compress_values: {
isize value_count = 0;
for (Ast *arg : ce->args) {
Type *t = arg->tav.type;
if (is_type_tuple(t)) {
value_count += t->Tuple.variables.count;
} else {
value_count += 1;
}
}
if (value_count == 1) {
lbValue x = lb_build_expr(p, ce->args[0]);
x = lb_emit_conv(p, x, tv.type);
return x;
}
Type *dt = base_type(tv.type);
lbAddr addr = lb_add_local_generated(p, tv.type, true);
if (is_type_struct(dt) || is_type_tuple(dt)) {
i32 index = 0;
for (Ast *arg : ce->args) {
lbValue x = lb_build_expr(p, arg);
if (is_type_tuple(x.type)) {
for (isize i = 0; i < x.type->Tuple.variables.count; i++) {
lbValue y = lb_emit_tuple_ev(p, x, cast(i32)i);
lbValue ptr = lb_emit_struct_ep(p, addr.addr, index++);
y = lb_emit_conv(p, y, type_deref(ptr.type));
lb_emit_store(p, ptr, y);
}
} else {
lbValue ptr = lb_emit_struct_ep(p, addr.addr, index++);
x = lb_emit_conv(p, x, type_deref(ptr.type));
lb_emit_store(p, ptr, x);
}
}
GB_ASSERT(index == value_count);
} else if (is_type_array_like(dt)) {
i32 index = 0;
for (Ast *arg : ce->args) {
lbValue x = lb_build_expr(p, arg);
if (is_type_tuple(x.type)) {
for (isize i = 0; i < x.type->Tuple.variables.count; i++) {
lbValue y = lb_emit_tuple_ev(p, x, cast(i32)i);
lbValue ptr = lb_emit_array_epi(p, addr.addr, index++);
y = lb_emit_conv(p, y, type_deref(ptr.type));
lb_emit_store(p, ptr, y);
}
} else {
lbValue ptr = lb_emit_array_epi(p, addr.addr, index++);
x = lb_emit_conv(p, x, type_deref(ptr.type));
lb_emit_store(p, ptr, x);
}
}
GB_ASSERT(index == value_count);
} else {
GB_PANIC("TODO(bill): compress_values -> %s", type_to_string(tv.type));
}
return lb_addr_load(p, addr);
}
case BuiltinProc_min: {
Type *t = type_of_expr(expr);
if (ce->args.count == 2) {
@@ -3226,11 +3384,28 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
{
GB_ASSERT(arg_count <= 7);
char asm_string[] = "svc #0; cset x8, cc";
gbString constraints = gb_string_make(heap_allocator(), "={x0},={x8}");
for (unsigned i = 0; i < arg_count; i++) {
constraints = gb_string_appendc(constraints, ",{");
static char const *regs[] = {
char const *asm_string;
char const **regs;
gbString constraints;
if (build_context.metrics.os == TargetOs_netbsd) {
asm_string = "svc #0; cset x17, cc";
constraints = gb_string_make(heap_allocator(), "={x0},={x17}");
static char const *_regs[] = {
"x17",
"x0",
"x1",
"x2",
"x3",
"x4",
"x5",
};
regs = _regs;
} else {
// FreeBSD (tested), OpenBSD (untested).
asm_string = "svc #0; cset x8, cc";
constraints = gb_string_make(heap_allocator(), "={x0},={x8}");
static char const *_regs[] = {
"x8",
"x0",
"x1",
@@ -3239,13 +3414,19 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
"x4",
"x5",
};
regs = _regs;
// FreeBSD clobbered x1 on a call to sysctl.
constraints = gb_string_appendc(constraints, ",~{x1}");
}
for (unsigned i = 0; i < arg_count; i++) {
constraints = gb_string_appendc(constraints, ",{");
constraints = gb_string_appendc(constraints, regs[i]);
constraints = gb_string_appendc(constraints, "}");
}
// FreeBSD clobbered x1 on a call to sysctl.
constraints = gb_string_appendc(constraints, ",~{x1},~{cc},~{memory}");
constraints = gb_string_appendc(constraints, ",~{cc},~{memory}");
inline_asm = llvm_get_inline_asm(func_type, make_string_c(asm_string), make_string_c(constraints));
}
break;
@@ -3267,6 +3448,7 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
case BuiltinProc_objc_find_class: return lb_handle_objc_find_class(p, expr);
case BuiltinProc_objc_register_selector: return lb_handle_objc_register_selector(p, expr);
case BuiltinProc_objc_register_class: return lb_handle_objc_register_class(p, expr);
case BuiltinProc_objc_ivar_get: return lb_handle_objc_ivar_get(p, expr);
case BuiltinProc_constant_utf16_cstring:
+111 -27
View File
@@ -136,7 +136,6 @@ gb_internal lbBranchBlocks lb_lookup_branch_blocks(lbProcedure *p, Ast *ident) {
return empty;
}
gb_internal lbTargetList *lb_push_target_list(lbProcedure *p, Ast *label, lbBlock *break_, lbBlock *continue_, lbBlock *fallthrough_) {
lbTargetList *tl = gb_alloc_item(permanent_allocator(), lbTargetList);
tl->prev = p->target_list;
@@ -688,6 +687,18 @@ gb_internal void lb_build_range_interval(lbProcedure *p, AstBinaryExpr *node,
lbBlock *body = lb_create_block(p, "for.interval.body");
lbBlock *done = lb_create_block(p, "for.interval.done");
// TODO(tf2spi): This is inlined in more than several places.
// Putting this in a function might be preferred.
// LLVMSetCurrentDebugLocation2 has side effects,
// so I didn't want to hide that before it got reviewed.
if (rs->label != nullptr && p->debug_info != nullptr) {
lbBlock *label = lb_create_block(p, "for.interval.label");
lb_emit_jump(p, label);
lb_start_block(p, label);
LLVMSetCurrentDebugLocation2(p->builder, lb_debug_location_from_ast(p, rs->label));
lb_add_debug_label(p, rs->label, label);
}
lb_emit_jump(p, loop);
lb_start_block(p, loop);
@@ -893,6 +904,14 @@ gb_internal void lb_build_range_stmt_struct_soa(lbProcedure *p, AstRangeStmt *rs
lbAddr index = lb_add_local_generated(p, t_int, false);
if (rs->label != nullptr && p->debug_info != nullptr) {
lbBlock *label = lb_create_block(p, "for.soa.label");
lb_emit_jump(p, label);
lb_start_block(p, label);
LLVMSetCurrentDebugLocation2(p->builder, lb_debug_location_from_ast(p, rs->label));
lb_add_debug_label(p, rs->label, label);
}
if (!is_reverse) {
/*
for x, i in array {
@@ -970,7 +989,6 @@ gb_internal void lb_build_range_stmt_struct_soa(lbProcedure *p, AstRangeStmt *rs
lb_store_range_stmt_val(p, val1, lb_addr_load(p, index));
}
lb_push_target_list(p, rs->label, done, loop, nullptr);
lb_build_stmt(p, rs->body);
@@ -1029,6 +1047,15 @@ gb_internal void lb_build_range_stmt(lbProcedure *p, AstRangeStmt *rs, Scope *sc
lbBlock *done = nullptr;
bool is_map = false;
if (rs->label != nullptr && p->debug_info != nullptr) {
lbBlock *label = lb_create_block(p, "for.range.label");
lb_emit_jump(p, label);
lb_start_block(p, label);
LLVMSetCurrentDebugLocation2(p->builder, lb_debug_location_from_ast(p, rs->label));
lb_add_debug_label(p, rs->label, label);
}
if (tav.mode == Addressing_Type) {
lb_build_range_enum(p, type_deref(tav.type), val0_type, &val, &key, &loop, &done);
} else {
@@ -1530,6 +1557,14 @@ gb_internal bool lb_switch_stmt_can_be_trivial_jump_table(AstSwitchStmt *ss, boo
gb_internal void lb_build_switch_stmt(lbProcedure *p, AstSwitchStmt *ss, Scope *scope) {
lb_open_scope(p, scope);
if (ss->label != nullptr && p->debug_info != nullptr) {
lbBlock *label = lb_create_block(p, "switch.label");
lb_emit_jump(p, label);
lb_start_block(p, label);
LLVMSetCurrentDebugLocation2(p->builder, lb_debug_location_from_ast(p, ss->label));
lb_add_debug_label(p, ss->label, label);
}
if (ss->init != nullptr) {
lb_build_stmt(p, ss->init);
}
@@ -1736,6 +1771,7 @@ gb_internal lbAddr lb_store_range_stmt_val(lbProcedure *p, Ast *stmt_val, lbValu
gb_internal void lb_type_case_body(lbProcedure *p, Ast *label, Ast *clause, lbBlock *body, lbBlock *done) {
ast_node(cc, CaseClause, clause);
// NOTE(tf2spi): Debug info for label not generated here on purpose
lb_push_target_list(p, label, done, nullptr, nullptr);
lb_build_stmt_list(p, cc->stmts);
lb_close_scope(p, lbDeferExit_Default, body, clause);
@@ -1963,8 +1999,7 @@ gb_internal void lb_build_static_variables(lbProcedure *p, AstValueDecl *vd) {
GB_ASSERT(ast_value->tav.mode == Addressing_Constant ||
ast_value->tav.mode == Addressing_Invalid);
bool allow_local = false;
value = lb_const_value(p->module, ast_value->tav.type, ast_value->tav.value, allow_local);
value = lb_const_value(p->module, ast_value->tav.type, ast_value->tav.value, LB_CONST_CONTEXT_DEFAULT_NO_LOCAL);
}
Ast *ident = vd->names[i];
@@ -1985,34 +2020,45 @@ gb_internal void lb_build_static_variables(lbProcedure *p, AstValueDecl *vd) {
char *c_name = alloc_cstring(permanent_allocator(), mangled_name);
LLVMValueRef global = LLVMAddGlobal(p->module->mod, lb_type(p->module, e->type), c_name);
LLVMSetAlignment(global, cast(u32)type_align_of(e->type));
LLVMSetInitializer(global, LLVMConstNull(lb_type(p->module, e->type)));
if (value.value != nullptr) {
LLVMSetInitializer(global, value.value);
}
if (e->Variable.is_rodata) {
LLVMSetGlobalConstant(global, true);
}
if (e->Variable.thread_local_model != "") {
LLVMSetThreadLocal(global, true);
String m = e->Variable.thread_local_model;
LLVMThreadLocalMode mode = LLVMGeneralDynamicTLSModel;
if (m == "default") {
mode = LLVMGeneralDynamicTLSModel;
} else if (m == "localdynamic") {
mode = LLVMLocalDynamicTLSModel;
} else if (m == "initialexec") {
mode = LLVMInitialExecTLSModel;
} else if (m == "localexec") {
mode = LLVMLocalExecTLSModel;
} else {
GB_PANIC("Unhandled thread local mode %.*s", LIT(m));
}
LLVMSetThreadLocalMode(global, mode);
} else {
if (!lb_apply_thread_local_model(global, e->Variable.thread_local_model)) {
LLVMSetLinkage(global, LLVMInternalLinkage);
}
if (value.value != nullptr) {
if (is_type_any(e->type)) {
Type *var_type = default_type(value.type);
gbString var_name = gb_string_make(temporary_allocator(), "__$static_any::");
var_name = gb_string_append_length(var_name, mangled_name.text, mangled_name.len);
lbAddr var_global = lb_add_global_generated_with_name(p->module, var_type, value, make_string_c(var_name), nullptr);
LLVMValueRef var_global_ref = var_global.addr.value;
if (e->Variable.is_rodata) {
LLVMSetGlobalConstant(var_global_ref, true);
}
if (!lb_apply_thread_local_model(var_global_ref, e->Variable.thread_local_model)) {
LLVMSetLinkage(var_global_ref, LLVMInternalLinkage);
}
LLVMValueRef vals[2] = {
lb_emit_conv(p, var_global.addr, t_rawptr).value,
lb_typeid(p->module, var_type).value,
};
LLVMValueRef init = llvm_const_named_struct(p->module, e->type, vals, gb_count_of(vals));
LLVMSetInitializer(global, init);
} else {
LLVMSetInitializer(global, value.value);
}
}
lbValue global_val = {global, alloc_type_pointer(e->type)};
lb_add_entity(p->module, e, global_val);
@@ -2307,6 +2353,14 @@ gb_internal void lb_build_if_stmt(lbProcedure *p, Ast *node) {
else_ = lb_create_block(p, "if.else");
}
if (is->label != nullptr) {
if (p->debug_info != nullptr) {
lbBlock *label = lb_create_block(p, "if.label");
lb_emit_jump(p, label);
lb_start_block(p, label);
LLVMSetCurrentDebugLocation2(p->builder, lb_debug_location_from_ast(p, is->label));
lb_add_debug_label(p, is->label, label);
}
lbTargetList *tl = lb_push_target_list(p, is->label, done, nullptr, nullptr);
tl->is_block = true;
}
@@ -2399,12 +2453,19 @@ gb_internal void lb_build_for_stmt(lbProcedure *p, Ast *node) {
lb_push_target_list(p, fs->label, done, post, nullptr);
if (fs->label != nullptr && p->debug_info != nullptr) {
lbBlock *label = lb_create_block(p, "for.label");
lb_emit_jump(p, label);
lb_start_block(p, label);
LLVMSetCurrentDebugLocation2(p->builder, lb_debug_location_from_ast(p, fs->label));
lb_add_debug_label(p, fs->label, label);
}
if (fs->init != nullptr) {
#if 1
lbBlock *init = lb_create_block(p, "for.init");
lb_emit_jump(p, init);
lb_start_block(p, init);
#endif
lb_build_stmt(p, fs->init);
}
@@ -2420,7 +2481,6 @@ gb_internal void lb_build_for_stmt(lbProcedure *p, Ast *node) {
lb_start_block(p, body);
}
lb_build_stmt(p, fs->body);
lb_pop_target_list(p);
@@ -2758,9 +2818,21 @@ gb_internal void lb_build_stmt(lbProcedure *p, Ast *node) {
case_ast_node(bs, BlockStmt, node);
lbBlock *body = nullptr;
lbBlock *done = nullptr;
if (bs->label != nullptr) {
if (p->debug_info != nullptr) {
lbBlock *label = lb_create_block(p, "block.label");
lb_emit_jump(p, label);
lb_start_block(p, label);
LLVMSetCurrentDebugLocation2(p->builder, lb_debug_location_from_ast(p, bs->label));
lb_add_debug_label(p, bs->label, label);
}
body = lb_create_block(p, "block.body");
done = lb_create_block(p, "block.done");
lb_emit_jump(p, body);
lb_start_block(p, body);
lbTargetList *tl = lb_push_target_list(p, bs->label, done, nullptr, nullptr);
tl->is_block = true;
}
@@ -2981,6 +3053,18 @@ gb_internal void lb_emit_defer_stmts(lbProcedure *p, lbDeferExitKind kind, lbBlo
}
defer (p->branch_location_pos = prev_token_pos);
// TODO(lucas): In LLVM 21 use the 'use-after-scope' asan option which does this for us.
if (kind == lbDeferExit_Return) {
for_array(i, p->asan_stack_locals) {
lbValue local = p->asan_stack_locals[i];
auto args = array_make<lbValue>(temporary_allocator(), 2);
args[0] = lb_emit_conv(p, local, t_rawptr);
args[1] = lb_const_int(p->module, t_int, type_size_of(local.type->Pointer.elem));
lb_emit_runtime_call(p, "__asan_unpoison_memory_region", args);
}
}
isize count = p->defer_stmts.count;
isize i = count;
while (i --> 0) {
+10
View File
@@ -1,4 +1,10 @@
gb_internal void lb_set_odin_rtti_section(LLVMValueRef value) {
if (build_context.metrics.os != TargetOs_darwin) {
LLVMSetSection(value, ".odinti");
}
}
gb_internal isize lb_type_info_index(CheckerInfo *info, TypeInfoPair pair, bool err_on_not_found=true) {
isize index = type_info_index(info, pair, err_on_not_found);
if (index >= 0) {
@@ -221,6 +227,7 @@ gb_internal void lb_setup_type_info_data_giant_array(lbModule *m, i64 global_typ
gb_snprintf(name, 63, "__$ti-%lld", cast(long long)index);
LLVMValueRef g = LLVMAddGlobal(m->mod, type, name);
lb_make_global_private_const(g);
lb_set_odin_rtti_section(g);
return g;
};
@@ -716,6 +723,8 @@ gb_internal void lb_setup_type_info_data_giant_array(lbModule *m, i64 global_typ
LLVMSetInitializer(value_array.value, value_init);
LLVMSetGlobalConstant(name_array.value, true);
LLVMSetGlobalConstant(value_array.value, true);
lb_set_odin_rtti_section(name_array.value);
lb_set_odin_rtti_section(value_array.value);
lbValue v_count = lb_const_int(m, t_int, fields.count);
@@ -1056,6 +1065,7 @@ gb_internal void lb_setup_type_info_data_giant_array(lbModule *m, i64 global_typ
LLVMValueRef giant_array = lb_global_type_info_data_ptr(m).value;
LLVMSetInitializer(giant_array, giant_const);
lb_make_global_private_const(giant_array);
lb_set_odin_rtti_section(giant_array);
}
+69 -8
View File
@@ -2125,7 +2125,7 @@ gb_internal lbAddr lb_handle_objc_find_or_register_selector(lbProcedure *p, Stri
return addr;
}
gb_internal lbAddr lb_handle_objc_find_or_register_class(lbProcedure *p, String const &name) {
gb_internal lbAddr lb_handle_objc_find_or_register_class(lbProcedure *p, String const &name, Type *class_impl_type) {
lbModule *m = p->module;
lbAddr *found = string_map_get(&m->objc_classes, name);
if (found) {
@@ -2148,13 +2148,75 @@ gb_internal lbAddr lb_handle_objc_find_or_register_class(lbProcedure *p, String
} else {
LLVMSetLinkage(g.value, LLVMExternalLinkage);
}
mpsc_enqueue(&m->gen->objc_classes, lbObjCGlobal{m, global_name, name, t_objc_Class});
mpsc_enqueue(&m->gen->objc_classes, lbObjCGlobal{m, global_name, name, t_objc_Class, class_impl_type});
lbAddr addr = lb_addr(g);
string_map_set(&m->objc_classes, name, addr);
return addr;
}
gb_internal lbAddr lb_handle_objc_find_or_register_ivar(lbModule *m, Type *self_type) {
String name = self_type->Named.type_name->TypeName.objc_class_name;
GB_ASSERT(name != "");
lbAddr *found = string_map_get(&m->objc_ivars, name);
if (found) {
return *found;
}
lbModule *default_module = &m->gen->default_module;
gbString global_name = gb_string_make(permanent_allocator(), "__$objc_ivar::");
global_name = gb_string_append_length(global_name, name.text, name.len);
// Create a global variable to store offset of the ivar in an instance of an object
LLVMTypeRef t = lb_type(m, t_int);
lbValue g = {};
g.value = LLVMAddGlobal(m->mod, t, global_name);
g.type = t_int_ptr;
if (default_module == m) {
LLVMSetInitializer(g.value, LLVMConstInt(t, 0, true));
lb_add_member(m, make_string_c(global_name), g);
} else {
LLVMSetLinkage(g.value, LLVMExternalLinkage);
}
mpsc_enqueue(&m->gen->objc_ivars, lbObjCGlobal{m, global_name, name, t_int, self_type});
lbAddr addr = lb_addr(g);
string_map_set(&m->objc_ivars, name, addr);
return addr;
}
gb_internal lbValue lb_handle_objc_ivar_for_objc_object_pointer(lbProcedure *p, lbValue self) {
GB_ASSERT(self.type->kind == Type_Pointer && self.type->Pointer.elem->kind == Type_Named);
Type *self_type = self.type->Pointer.elem;
lbValue self_uptr = lb_emit_conv(p, self, t_uintptr);
lbValue ivar_offset = lb_addr_load(p, lb_handle_objc_find_or_register_ivar(p->module, self_type));
lbValue ivar_offset_uptr = lb_emit_conv(p, ivar_offset, t_uintptr);
lbValue ivar_uptr = lb_emit_arith(p, Token_Add, self_uptr, ivar_offset_uptr, t_uintptr);
Type *ivar_type = self_type->Named.type_name->TypeName.objc_ivar;
return lb_emit_conv(p, ivar_uptr, alloc_type_pointer(ivar_type));
}
gb_internal lbValue lb_handle_objc_ivar_get(lbProcedure *p, Ast *expr) {
ast_node(ce, CallExpr, expr);
GB_ASSERT(ce->args[0]->tav.type->kind == Type_Pointer);
lbValue self = lb_build_expr(p, ce->args[0]);
return lb_handle_objc_ivar_for_objc_object_pointer(p, self);
}
gb_internal lbValue lb_handle_objc_find_selector(lbProcedure *p, Ast *expr) {
ast_node(ce, CallExpr, expr);
@@ -2188,7 +2250,7 @@ gb_internal lbValue lb_handle_objc_find_class(lbProcedure *p, Ast *expr) {
auto tav = ce->args[0]->tav;
GB_ASSERT(tav.value.kind == ExactValue_String);
String name = tav.value.value_string;
return lb_addr_load(p, lb_handle_objc_find_or_register_class(p, name));
return lb_addr_load(p, lb_handle_objc_find_or_register_class(p, name, nullptr));
}
gb_internal lbValue lb_handle_objc_register_class(lbProcedure *p, Ast *expr) {
@@ -2198,7 +2260,7 @@ gb_internal lbValue lb_handle_objc_register_class(lbProcedure *p, Ast *expr) {
auto tav = ce->args[0]->tav;
GB_ASSERT(tav.value.kind == ExactValue_String);
String name = tav.value.value_string;
lbAddr dst = lb_handle_objc_find_or_register_class(p, name);
lbAddr dst = lb_handle_objc_find_or_register_class(p, name, nullptr);
auto args = array_make<lbValue>(permanent_allocator(), 3);
args[0] = lb_const_nil(m, t_objc_Class);
@@ -2220,7 +2282,9 @@ gb_internal lbValue lb_handle_objc_id(lbProcedure *p, Ast *expr) {
GB_ASSERT(e->kind == Entity_TypeName);
String name = e->TypeName.objc_class_name;
return lb_addr_load(p, lb_handle_objc_find_or_register_class(p, name));
Type *class_impl_type = e->TypeName.objc_is_implementation ? type : nullptr;
return lb_addr_load(p, lb_handle_objc_find_or_register_class(p, name, class_impl_type));
}
return lb_build_expr(p, expr);
@@ -2266,9 +2330,6 @@ gb_internal lbValue lb_handle_objc_send(lbProcedure *p, Ast *expr) {
return lb_emit_call(p, the_proc, args);
}
gb_internal LLVMAtomicOrdering llvm_atomic_ordering_from_odin(ExactValue const &value) {
GB_ASSERT(value.kind == ExactValue_Integer);
i64 v = exact_value_to_i64(value);
+176 -78
View File
@@ -277,10 +277,11 @@ gb_internal void usage(String argv0, String argv1 = {}) {
print_usage_line(1, "build Compiles directory of .odin files, as an executable.");
print_usage_line(1, " One must contain the program's entry point, all must be in the same package.");
print_usage_line(1, "run Same as 'build', but also then runs the newly compiled executable.");
print_usage_line(1, "check Parses, and type checks a directory of .odin files.");
print_usage_line(1, "bundle Bundles a directory in a specific layout for that platform.");
print_usage_line(1, "check Parses and type checks a directory of .odin files.");
print_usage_line(1, "strip-semicolon Parses, type checks, and removes unneeded semicolons from the entire program.");
print_usage_line(1, "test Builds and runs procedures with the attribute @(test) in the initial package.");
print_usage_line(1, "doc Generates documentation on a directory of .odin files.");
print_usage_line(1, "doc Generates documentation from a directory of .odin files.");
print_usage_line(1, "version Prints version.");
print_usage_line(1, "report Prints information useful to reporting a bug.");
print_usage_line(1, "root Prints the root path where Odin looks for the builtin collections.");
@@ -318,6 +319,7 @@ enum BuildFlagKind {
BuildFlag_NoBoundsCheck,
BuildFlag_NoTypeAssert,
BuildFlag_NoDynamicLiterals,
BuildFlag_DynamicLiterals,
BuildFlag_NoCRT,
BuildFlag_NoRPath,
BuildFlag_NoEntryPoint,
@@ -411,7 +413,7 @@ enum BuildFlagKind {
BuildFlag_AndroidKeystore,
BuildFlag_AndroidKeystoreAlias,
BuildFlag_AndroidManifest,
BuildFlag_AndroidKeystorePassword,
BuildFlag_COUNT,
};
@@ -537,6 +539,7 @@ gb_internal bool parse_build_flags(Array<String> args) {
add_flag(&build_flags, BuildFlag_NoTypeAssert, str_lit("no-type-assert"), BuildFlagParam_None, Command__does_check);
add_flag(&build_flags, BuildFlag_NoThreadLocal, str_lit("no-thread-local"), BuildFlagParam_None, Command__does_check);
add_flag(&build_flags, BuildFlag_NoDynamicLiterals, str_lit("no-dynamic-literals"), BuildFlagParam_None, Command__does_check);
add_flag(&build_flags, BuildFlag_DynamicLiterals, str_lit("dynamic-literals"), BuildFlagParam_None, Command__does_check);
add_flag(&build_flags, BuildFlag_NoCRT, str_lit("no-crt"), BuildFlagParam_None, Command__does_build);
add_flag(&build_flags, BuildFlag_NoRPath, str_lit("no-rpath"), BuildFlagParam_None, Command__does_build);
add_flag(&build_flags, BuildFlag_NoEntryPoint, str_lit("no-entry-point"), BuildFlagParam_None, Command__does_check &~ Command_test);
@@ -631,7 +634,7 @@ gb_internal bool parse_build_flags(Array<String> args) {
add_flag(&build_flags, BuildFlag_AndroidKeystore, str_lit("android-keystore"), BuildFlagParam_String, Command_bundle_android);
add_flag(&build_flags, BuildFlag_AndroidKeystoreAlias, str_lit("android-keystore-alias"), BuildFlagParam_String, Command_bundle_android);
add_flag(&build_flags, BuildFlag_AndroidManifest, str_lit("android-manifest"), BuildFlagParam_String, Command_bundle_android);
add_flag(&build_flags, BuildFlag_AndroidKeystorePassword, str_lit("android-keystore-password"), BuildFlagParam_String, Command_bundle_android);
Array<String> flag_args = {};
@@ -1206,6 +1209,9 @@ gb_internal bool parse_build_flags(Array<String> args) {
case BuildFlag_NoDynamicLiterals:
gb_printf_err("Warning: Use of -no-dynamic-literals is now redundant\n");
break;
case BuildFlag_DynamicLiterals:
build_context.dynamic_literals = true;
break;
case BuildFlag_NoCRT:
build_context.no_crt = true;
break;
@@ -1664,9 +1670,9 @@ gb_internal bool parse_build_flags(Array<String> args) {
build_context.android_keystore_alias = value.value_string;
break;
case BuildFlag_AndroidManifest:
case BuildFlag_AndroidKeystorePassword:
GB_ASSERT(value.kind == ExactValue_String);
build_context.android_manifest = value.value_string;
build_context.android_keystore_password = value.value_string;
break;
}
}
@@ -2200,7 +2206,7 @@ gb_internal void remove_temp_files(lbGenerator *gen) {
return;
}
TIME_SECTION("remove keep temp files");
TIME_SECTION("remove temp files");
for (String const &path : gen->output_temp_paths) {
gb_file_remove(cast(char const *)path.text);
@@ -2220,20 +2226,30 @@ gb_internal void remove_temp_files(lbGenerator *gen) {
}
gb_internal void print_show_help(String const arg0, String command, String optional_flag = {}) {
gb_internal int print_show_help(String const arg0, String command, String optional_flag = {}) {
bool help_resolved = false;
bool printed_usage_header = false;
bool printed_flags_header = false;
if (command == "help" && optional_flag.len != 0 && optional_flag[0] != '-') {
command = optional_flag;
optional_flag = {};
}
print_usage_line(0, "%.*s is a tool for managing Odin source code.", LIT(arg0));
print_usage_line(0, "Usage:");
print_usage_line(1, "%.*s %.*s [arguments]", LIT(arg0), LIT(command));
print_usage_line(0, "");
defer (print_usage_line(0, ""));
auto const print_usage_header_once = [&help_resolved, &printed_usage_header, arg0, command]() {
if (printed_usage_header) {
return;
}
print_usage_line(0, "%.*s is a tool for managing Odin source code.", LIT(arg0));
print_usage_line(0, "Usage:");
print_usage_line(1, "%.*s %.*s [arguments]", LIT(arg0), LIT(command));
print_usage_line(0, "");
help_resolved = true;
printed_usage_header = true;
};
if (command == "build") {
print_usage_header_once();
print_usage_line(1, "build Compiles directory of .odin files as an executable.");
print_usage_line(2, "One must contain the program's entry point, all must be in the same package.");
print_usage_line(2, "Use `-file` to build a single file instead.");
@@ -2242,6 +2258,7 @@ gb_internal void print_show_help(String const arg0, String command, String optio
print_usage_line(3, "odin build <dir> Builds package in <dir>.");
print_usage_line(3, "odin build filename.odin -file Builds single-file package, must contain entry point.");
} else if (command == "run") {
print_usage_header_once();
print_usage_line(1, "run Same as 'build', but also then runs the newly compiled executable.");
print_usage_line(2, "Append an empty flag and then the args, '-- <args>', to specify args for the output.");
print_usage_line(2, "Examples:");
@@ -2249,28 +2266,40 @@ gb_internal void print_show_help(String const arg0, String command, String optio
print_usage_line(3, "odin run <dir> Builds and runs package in <dir>.");
print_usage_line(3, "odin run filename.odin -file Builds and runs single-file package, must contain entry point.");
} else if (command == "check") {
print_usage_header_once();
print_usage_line(1, "check Parses and type checks directory of .odin files.");
print_usage_line(2, "Examples:");
print_usage_line(3, "odin check . Type checks package in current directory.");
print_usage_line(3, "odin check <dir> Type checks package in <dir>.");
print_usage_line(3, "odin check filename.odin -file Type checks single-file package, must contain entry point.");
} else if (command == "test") {
print_usage_header_once();
print_usage_line(1, "test Builds and runs procedures with the attribute @(test) in the initial package.");
} else if (command == "doc") {
print_usage_header_once();
print_usage_line(1, "doc Generates documentation from a directory of .odin files.");
print_usage_line(2, "Examples:");
print_usage_line(3, "odin doc . Generates documentation on package in current directory.");
print_usage_line(3, "odin doc <dir> Generates documentation on package in <dir>.");
print_usage_line(3, "odin doc filename.odin -file Generates documentation on single-file package.");
} else if (command == "version") {
print_usage_header_once();
print_usage_line(1, "version Prints version.");
} else if (command == "strip-semicolon") {
print_usage_header_once();
print_usage_line(1, "strip-semicolon");
print_usage_line(2, "Parses and type checks .odin file(s) and then removes unneeded semicolons from the entire project.");
} else if (command == "bundle") {
print_usage_header_once();
print_usage_line(1, "bundle <platform> Bundles a directory in a specific layout for that platform");
print_usage_line(2, "Supported platforms:");
print_usage_line(3, "android");
} else if (command == "report") {
print_usage_header_once();
print_usage_line(1, "report Prints information useful to reporting a bug.");
} else if (command == "root") {
print_usage_header_once();
print_usage_line(1, "root Prints the root path where Odin looks for the builtin collections.");
}
bool doc = command == "doc";
@@ -2292,13 +2321,10 @@ gb_internal void print_show_help(String const arg0, String command, String optio
check = true;
}
print_usage_line(0, "");
print_usage_line(1, "Flags");
print_usage_line(0, "");
auto const print_flag = [&optional_flag](char const *flag) -> bool {
auto const print_flag = [&optional_flag, &help_resolved, &printed_flags_header, print_usage_header_once](char const *flag) -> bool {
if (optional_flag.len != 0) {
String f = make_string_c(flag);
isize i = string_index_byte(f, ':');
@@ -2309,6 +2335,14 @@ gb_internal void print_show_help(String const arg0, String command, String optio
return false;
}
}
print_usage_header_once();
if (!printed_flags_header) {
print_usage_line(0, "");
print_usage_line(1, "Flags");
print_usage_line(0, "");
printed_flags_header = true;
}
help_resolved = true;
print_usage_line(0, "");
print_usage_line(1, flag);
return true;
@@ -2330,20 +2364,20 @@ gb_internal void print_show_help(String const arg0, String command, String optio
if (print_flag("-build-mode:<mode>")) {
print_usage_line(2, "Sets the build mode.");
print_usage_line(2, "Available options:");
print_usage_line(3, "-build-mode:exe Builds as an executable.");
print_usage_line(3, "-build-mode:test Builds as an executable that executes tests.");
print_usage_line(3, "-build-mode:dll Builds as a dynamically linked library.");
print_usage_line(3, "-build-mode:shared Builds as a dynamically linked library.");
print_usage_line(3, "-build-mode:dynamic Builds as a dynamically linked library.");
print_usage_line(3, "-build-mode:lib Builds as a statically linked library.");
print_usage_line(3, "-build-mode:static Builds as a statically linked library.");
print_usage_line(3, "-build-mode:obj Builds as an object file.");
print_usage_line(3, "-build-mode:object Builds as an object file.");
print_usage_line(3, "-build-mode:assembly Builds as an assembly file.");
print_usage_line(3, "-build-mode:assembler Builds as an assembly file.");
print_usage_line(3, "-build-mode:asm Builds as an assembly file.");
print_usage_line(3, "-build-mode:llvm-ir Builds as an LLVM IR file.");
print_usage_line(3, "-build-mode:llvm Builds as an LLVM IR file.");
print_usage_line(3, "-build-mode:exe Builds as an executable.");
print_usage_line(3, "-build-mode:test Builds as an executable that executes tests.");
print_usage_line(3, "-build-mode:dll Builds as a dynamically linked library.");
print_usage_line(3, "-build-mode:shared Builds as a dynamically linked library.");
print_usage_line(3, "-build-mode:dynamic Builds as a dynamically linked library.");
print_usage_line(3, "-build-mode:lib Builds as a statically linked library.");
print_usage_line(3, "-build-mode:static Builds as a statically linked library.");
print_usage_line(3, "-build-mode:obj Builds as an object file.");
print_usage_line(3, "-build-mode:object Builds as an object file.");
print_usage_line(3, "-build-mode:assembly Builds as an assembly file.");
print_usage_line(3, "-build-mode:assembler Builds as an assembly file.");
print_usage_line(3, "-build-mode:asm Builds as an assembly file.");
print_usage_line(3, "-build-mode:llvm-ir Builds as an LLVM IR file.");
print_usage_line(3, "-build-mode:llvm Builds as an LLVM IR file.");
}
}
@@ -2352,16 +2386,16 @@ gb_internal void print_show_help(String const arg0, String command, String optio
print_usage_line(2, "Defines a library collection used for imports.");
print_usage_line(2, "Example: -collection:shared=dir/to/shared");
print_usage_line(2, "Usage in Code:");
print_usage_line(3, "import \"shared:foo\"");
print_usage_line(3, "import \"shared:foo\"");
}
if (print_flag("-custom-attribute:<string>")) {
print_usage_line(2, "Add a custom attribute which will be ignored if it is unknown.");
print_usage_line(2, "This can be used with metaprogramming tools.");
print_usage_line(2, "Examples:");
print_usage_line(3, "-custom-attribute:my_tag");
print_usage_line(3, "-custom-attribute:my_tag,the_other_thing");
print_usage_line(3, "-custom-attribute:my_tag -custom-attribute:the_other_thing");
print_usage_line(3, "-custom-attribute:my_tag");
print_usage_line(3, "-custom-attribute:my_tag,the_other_thing");
print_usage_line(3, "-custom-attribute:my_tag -custom-attribute:the_other_thing");
}
}
@@ -2384,7 +2418,7 @@ gb_internal void print_show_help(String const arg0, String command, String optio
print_usage_line(2, "Defines a scalar boolean, integer or string as global constant.");
print_usage_line(2, "Example: -define:SPAM=123");
print_usage_line(2, "Usage in code:");
print_usage_line(3, "#config(SPAM, default_value)");
print_usage_line(3, "#config(SPAM, default_value)");
}
}
@@ -2419,9 +2453,9 @@ gb_internal void print_show_help(String const arg0, String command, String optio
if (check) {
if (print_flag("-error-pos-style:<string>")) {
print_usage_line(2, "Available options:");
print_usage_line(3, "-error-pos-style:unix file/path:45:3:");
print_usage_line(3, "-error-pos-style:odin file/path(45:3)");
print_usage_line(3, "-error-pos-style:default (Defaults to 'odin'.)");
print_usage_line(3, "-error-pos-style:unix file/path:45:3:");
print_usage_line(3, "-error-pos-style:odin file/path(45:3)");
print_usage_line(3, "-error-pos-style:default (Defaults to 'odin'.)");
}
if (print_flag("-export-defineables:<filename>")) {
@@ -2432,8 +2466,8 @@ gb_internal void print_show_help(String const arg0, String command, String optio
if (print_flag("-export-dependencies:<format>")) {
print_usage_line(2, "Exports dependencies to one of a few formats. Requires `-export-dependencies-file`.");
print_usage_line(2, "Available options:");
print_usage_line(3, "-export-dependencies:make Exports in Makefile format");
print_usage_line(3, "-export-dependencies:json Exports in JSON format");
print_usage_line(3, "-export-dependencies:make Exports in Makefile format");
print_usage_line(3, "-export-dependencies:json Exports in JSON format");
}
if (print_flag("-export-dependencies-file:<filename>")) {
@@ -2444,8 +2478,8 @@ gb_internal void print_show_help(String const arg0, String command, String optio
if (print_flag("-export-timings:<format>")) {
print_usage_line(2, "Exports timings to one of a few formats. Requires `-show-timings` or `-show-more-timings`.");
print_usage_line(2, "Available options:");
print_usage_line(3, "-export-timings:json Exports compile time stats to JSON.");
print_usage_line(3, "-export-timings:csv Exports compile time stats to CSV.");
print_usage_line(3, "-export-timings:json Exports compile time stats to JSON.");
print_usage_line(3, "-export-timings:csv Exports compile time stats to CSV.");
}
if (print_flag("-export-timings-file:<filename>")) {
@@ -2535,9 +2569,9 @@ gb_internal void print_show_help(String const arg0, String command, String optio
if (print_flag("-microarch:<string>")) {
print_usage_line(2, "Specifies the specific micro-architecture for the build in a string.");
print_usage_line(2, "Examples:");
print_usage_line(3, "-microarch:sandybridge");
print_usage_line(3, "-microarch:native");
print_usage_line(3, "-microarch:\"?\" for a list");
print_usage_line(3, "-microarch:sandybridge");
print_usage_line(3, "-microarch:native");
print_usage_line(3, "-microarch:\"?\" for a list");
}
}
@@ -2552,7 +2586,7 @@ gb_internal void print_show_help(String const arg0, String command, String optio
if (print_flag("-minimum-os-version:<string>")) {
print_usage_line(2, "Sets the minimum OS version targeted by the application.");
print_usage_line(2, "Default: -minimum-os-version:11.0.0");
print_usage_line(2, "Only used when target is Darwin, if given, linking mismatched versions will emit a warning.");
print_usage_line(2, "Only used when target is Darwin or subtarget is Android, if given, linking mismatched versions will emit a warning.");
}
}
@@ -2594,10 +2628,10 @@ gb_internal void print_show_help(String const arg0, String command, String optio
if (print_flag("-o:<string>")) {
print_usage_line(2, "Sets the optimization mode for compilation.");
print_usage_line(2, "Available options:");
print_usage_line(3, "-o:none");
print_usage_line(3, "-o:minimal");
print_usage_line(3, "-o:size");
print_usage_line(3, "-o:speed");
print_usage_line(3, "-o:none");
print_usage_line(3, "-o:minimal");
print_usage_line(3, "-o:size");
print_usage_line(3, "-o:speed");
if (LB_USE_NEW_PASS_SYSTEM) {
print_usage_line(3, "-o:aggressive (use this with caution)");
}
@@ -2648,10 +2682,10 @@ gb_internal void print_show_help(String const arg0, String command, String optio
if (print_flag("-reloc-mode:<string>")) {
print_usage_line(2, "Specifies the reloc mode.");
print_usage_line(2, "Available options:");
print_usage_line(3, "-reloc-mode:default");
print_usage_line(3, "-reloc-mode:static");
print_usage_line(3, "-reloc-mode:pic");
print_usage_line(3, "-reloc-mode:dynamic-no-pic");
print_usage_line(3, "-reloc-mode:default");
print_usage_line(3, "-reloc-mode:static");
print_usage_line(3, "-reloc-mode:pic");
print_usage_line(3, "-reloc-mode:dynamic-no-pic");
}
#if defined(GB_SYSTEM_WINDOWS)
@@ -2666,9 +2700,9 @@ gb_internal void print_show_help(String const arg0, String command, String optio
if (print_flag("-sanitize:<string>")) {
print_usage_line(2, "Enables sanitization analysis.");
print_usage_line(2, "Available options:");
print_usage_line(3, "-sanitize:address");
print_usage_line(3, "-sanitize:memory");
print_usage_line(3, "-sanitize:thread");
print_usage_line(3, "-sanitize:address");
print_usage_line(3, "-sanitize:memory");
print_usage_line(3, "-sanitize:thread");
print_usage_line(2, "NOTE: This flag can be used multiple times.");
}
}
@@ -2729,17 +2763,32 @@ gb_internal void print_show_help(String const arg0, String command, String optio
print_usage_line(2, "[Windows only]");
print_usage_line(2, "Defines the subsystem for the application.");
print_usage_line(2, "Available options:");
print_usage_line(3, "-subsystem:console");
print_usage_line(3, "-subsystem:windows");
print_usage_line(3, "-subsystem:console");
print_usage_line(3, "-subsystem:windows");
}
#endif
}
if (build) {
if (print_flag("-subtarget:<subtarget>")) {
print_usage_line(2, "[Darwin and Linux only]");
print_usage_line(2, "Available subtargets:");
String prefix = str_lit("-subtarget:");
for (u32 i = 1; i < Subtarget_COUNT; i++) {
String name = subtarget_strings[i];
String help_string = concatenate_strings(temporary_allocator(), prefix, name);
print_usage_line(3, (const char *)help_string.text);
}
}
}
if (run_or_build) {
if (print_flag("-target-features:<string>")) {
print_usage_line(2, "Specifies CPU features to enable on top of the enabled features implied by -microarch.");
print_usage_line(2, "Examples:");
print_usage_line(3, "-target-features:atomics");
print_usage_line(3, "-target-features:\"sse2,aes\"");
print_usage_line(3, "-target-features:\"?\" for a list");
print_usage_line(3, "-target-features:atomics");
print_usage_line(3, "-target-features:\"sse2,aes\"");
print_usage_line(3, "-target-features:\"?\" for a list");
}
}
@@ -2776,11 +2825,11 @@ gb_internal void print_show_help(String const arg0, String command, String optio
if (print_flag("-vet")) {
print_usage_line(2, "Does extra checks on the code.");
print_usage_line(2, "Extra checks include:");
print_usage_line(3, "-vet-unused");
print_usage_line(3, "-vet-unused-variables");
print_usage_line(3, "-vet-unused-imports");
print_usage_line(3, "-vet-shadowing");
print_usage_line(3, "-vet-using-stmt");
print_usage_line(3, "-vet-unused");
print_usage_line(3, "-vet-unused-variables");
print_usage_line(3, "-vet-unused-imports");
print_usage_line(3, "-vet-shadowing");
print_usage_line(3, "-vet-using-stmt");
}
if (print_flag("-vet-cast")) {
@@ -2847,6 +2896,40 @@ gb_internal void print_show_help(String const arg0, String command, String optio
print_usage_line(2, "Treats warning messages as error messages.");
}
}
if (bundle) {
print_usage_line(0, "");
print_usage_line(1, "Android-specific flags");
print_usage_line(0, "");
if (print_flag("-android-keystore:<string>")) {
print_usage_line(2, "Specifies the keystore file to use to sign the apk.");
}
if (print_flag("-android-keystore-alias:<string>")) {
print_usage_line(2, "Specifies the key alias to use when signing the apk");
print_usage_line(2, "Can be omitted if the keystore only contains one key");
}
if (print_flag("-android-keystore-password:<string>")) {
print_usage_line(2, "Sets the password to use to unlock the keystore");
print_usage_line(2, "If this is omitted, the terminal will prompt you to provide it.");
}
}
if (!help_resolved) {
usage(arg0);
print_usage_line(0, "");
if (command == "help") {
print_usage_line(0, "'%.*s' is not a recognized flag.", LIT(optional_flag));
} else {
print_usage_line(0, "'%.*s' is not a recognized command.", LIT(command));
}
return 1;
}
print_usage_line(0, "");
return 0;
}
gb_internal void print_show_unused(Checker *c) {
@@ -3219,6 +3302,16 @@ int main(int arg_count, char const **arg_ptr) {
String run_args_string = {};
isize last_non_run_arg = args.count;
for_array(i, args) {
if (args[i] == "--") {
break;
}
if (args[i] == "-help" || args[i] == "--help") {
build_context.show_help = true;
return print_show_help(args[0], command);
}
}
bool run_output = false;
if (command == "run" || command == "test") {
if (args.count < 3) {
@@ -3312,6 +3405,10 @@ int main(int arg_count, char const **arg_ptr) {
return 1;
#endif
} else if (command == "version") {
if (args.count != 2) {
usage(args[0]);
return 1;
}
build_context.command_kind = Command_version;
gb_printf("%.*s version %.*s", LIT(args[0]), LIT(ODIN_VERSION));
@@ -3326,6 +3423,10 @@ int main(int arg_count, char const **arg_ptr) {
gb_printf("\n");
return 0;
} else if (command == "report") {
if (args.count != 2) {
usage(args[0]);
return 1;
}
build_context.command_kind = Command_bug_report;
print_bug_report_help();
return 0;
@@ -3334,8 +3435,7 @@ int main(int arg_count, char const **arg_ptr) {
usage(args[0]);
return 1;
} else {
print_show_help(args[0], args[1], args[2]);
return 0;
return print_show_help(args[0], args[1], args[2]);
}
} else if (command == "bundle") {
if (args.count < 4) {
@@ -3351,6 +3451,10 @@ int main(int arg_count, char const **arg_ptr) {
}
init_filename = args[3];
} else if (command == "root") {
if (args.count != 2) {
usage(args[0]);
return 1;
}
gb_printf("%.*s", LIT(odin_root_dir()));
return 0;
} else if (command == "clear-cache") {
@@ -3366,11 +3470,6 @@ int main(int arg_count, char const **arg_ptr) {
init_filename = copy_string(permanent_allocator(), init_filename);
if (init_filename == "-help" ||
init_filename == "--help") {
build_context.show_help = true;
}
if (init_filename.len > 0 && !build_context.show_help) {
// The command must be build, run, test, check, or another that takes a directory or filename.
if (!path_is_directory(init_filename)) {
@@ -3421,8 +3520,7 @@ int main(int arg_count, char const **arg_ptr) {
}
if (build_context.show_help) {
print_show_help(args[0], command);
return 0;
return print_show_help(args[0], command);
}
if (command == "bundle") {
+38 -3
View File
@@ -1,3 +1,5 @@
gb_internal bool is_in_doc_writer(void);
gb_internal GB_COMPARE_PROC(type_info_pair_cmp) {
TypeInfoPair *x = cast(TypeInfoPair *)a;
TypeInfoPair *y = cast(TypeInfoPair *)b;
@@ -284,6 +286,23 @@ gb_internal void write_canonical_params(TypeWriter *w, Type *params) {
} else {
write_type_to_canonical_string(w, v->type);
}
if (is_in_doc_writer()) {
// NOTE(bill): This just exists to make sure the entities default values exist when
// writing to the odin doc format
Ast *expr = v->Variable.init_expr;
if (expr == nullptr) {
expr = v->Variable.param_value.original_ast_expr;
}
if (expr != nullptr) {
type_writer_appendc(w, "=");
gbString s = write_expr_to_string( // Minor leak
gb_string_make(temporary_allocator(), ""),
expr,
build_context.cmd_doc_flags & CmdDocFlag_Short
);
type_writer_append(w, s, gb_string_length(s));
}
}
break;
case Entity_TypeName:
type_writer_appendc(w, CANONICAL_PARAM_TYPEID);
@@ -520,7 +539,6 @@ write_base_name:
return;
}
gb_internal bool is_in_doc_writer(void);
// NOTE(bill): This exists so that we deterministically hash a type by serializing it to a canonical string
gb_internal void write_type_to_canonical_string(TypeWriter *w, Type *type) {
@@ -631,6 +649,10 @@ gb_internal void write_type_to_canonical_string(TypeWriter *w, Type *type) {
case Type_Union:
type_writer_appendc(w, "union");
if (is_in_doc_writer() && type->Union.polymorphic_params) {
write_canonical_params(w, type->Union.polymorphic_params);
}
switch (type->Union.kind) {
case UnionType_no_nil: type_writer_appendc(w, "#no_nil"); break;
case UnionType_shared_nil: type_writer_appendc(w, "#shared_nil"); break;
@@ -658,6 +680,11 @@ gb_internal void write_type_to_canonical_string(TypeWriter *w, Type *type) {
}
type_writer_appendc(w, "struct");
if (is_in_doc_writer() && type->Struct.polymorphic_params) {
write_canonical_params(w, type->Struct.polymorphic_params);
}
if (type->Struct.is_packed) type_writer_appendc(w, "#packed");
if (type->Struct.is_raw_union) type_writer_appendc(w, "#raw_union");
if (type->Struct.is_no_copy) type_writer_appendc(w, "#no_copy");
@@ -724,9 +751,17 @@ gb_internal void write_type_to_canonical_string(TypeWriter *w, Type *type) {
if (is_in_doc_writer()) {
type_writer_appendc(w, "$");
type_writer_append(w, type->Generic.name.text, type->Generic.name.len);
type_writer_append_fmt(w, "%lld", cast(long long)type->Generic.id);
type_writer_append_fmt(w, "-%lld", cast(long long)type->Generic.id);
if (type->Generic.specialized) {
type_writer_appendc(w, "/");
write_type_to_canonical_string(w, type->Generic.specialized);
}
} else if (type->Generic.specialized) {
// If we have a specialized type, use that instead of panicking
write_type_to_canonical_string(w, type->Generic.specialized);
} else {
GB_PANIC("Type_Generic should never be hit");
// For unspecialized generics, use a generic placeholder string
type_writer_appendc(w, "rawptr");
}
return;
+8 -4
View File
@@ -6195,7 +6195,7 @@ gb_internal String build_tag_get_token(String s, String *out) {
isize width = utf8_decode(&s[n], s.len-n, &rune);
if (n == 0 && rune == '!') {
} else if (!rune_is_letter(rune) && !rune_is_digit(rune)) {
} else if (!rune_is_letter(rune) && !rune_is_digit(rune) && rune != ':') {
isize k = gb_max(gb_max(n, width), 1);
*out = substring(s, k, s.len);
return substring(s, 0, k);
@@ -6247,7 +6247,9 @@ gb_internal bool parse_build_tag(Token token_for_pos, String s) {
continue;
}
TargetOsKind os = get_target_os_from_string(p);
Subtarget subtarget = Subtarget_Default;
TargetOsKind os = get_target_os_from_string(p, &subtarget);
TargetArchKind arch = get_target_arch_from_string(p);
num_tokens += 1;
@@ -6261,11 +6263,13 @@ gb_internal bool parse_build_tag(Token token_for_pos, String s) {
if (os != TargetOs_Invalid) {
this_kind_os_seen = true;
bool same_subtarget = (subtarget == Subtarget_Default) || (subtarget == selected_subtarget);
GB_ASSERT(arch == TargetArch_Invalid);
if (is_notted) {
this_kind_correct = this_kind_correct && (os != build_context.metrics.os);
this_kind_correct = this_kind_correct && (os != build_context.metrics.os || !same_subtarget);
} else {
this_kind_correct = this_kind_correct && (os == build_context.metrics.os);
this_kind_correct = this_kind_correct && (os == build_context.metrics.os && same_subtarget);
}
} else if (arch != TargetArch_Invalid) {
this_kind_arch_seen = true;
+1 -1
View File
@@ -81,7 +81,7 @@ String get_working_directory(gbAllocator allocator) {
auto buf = array_make<char>(temporary_allocator());
size_t size = PATH_MAX;
char const *cwd;
char const *cwd = nullptr;
for (; cwd == nullptr; size *= 2) {
array_resize(&buf, size);
+3 -3
View File
@@ -15,7 +15,7 @@ static void *const MAP_TOMBSTONE = (void *)~(uintptr)0;
template <typename K, typename V>
struct PtrMapEntry {
static_assert(sizeof(K) == sizeof(void *), "Key size must be pointer size");
K key;
V value;
};
@@ -374,7 +374,7 @@ struct PtrMapIterator {
}
bool operator==(PtrMapIterator<K, V> const &other) const noexcept {
return this->map == other->map && this->index == other->index;
return this->map == other.map && this->index == other.index;
}
operator PtrMapEntry<K, V> *() const {
@@ -858,4 +858,4 @@ gb_internal OrderedInsertPtrMapEntry<K, V> *end(OrderedInsertPtrMap<K, V> &m) {
template <typename K, typename V>
gb_internal OrderedInsertPtrMapEntry<K, V> const *end(OrderedInsertPtrMap<K, V> const &m) {
return m.entries + m.count;
}
}
+77
View File
@@ -336,6 +336,83 @@ gb_internal Array<String> split_lines_from_array(Array<u8> const &array, gbAlloc
return lines;
}
enum : u32 { PRIME_RABIN_KARP = 16777619u };
gb_internal u32 hash_str_rabin_karp(String const &s, u32 *pow_) {
u32 hash = 0;
u32 pow = 1;
for (isize i = 0; i < s.len; i++) {
hash = hash*PRIME_RABIN_KARP + cast(u32)s.text[i];
}
u32 sq = PRIME_RABIN_KARP;
for (isize i = s.len; i > 0; i >>= 1) {
if ((i & 1) != 0) {
pow *= sq;
}
sq *= sq;
}
if (pow_) *pow_ = pow;
return hash;
}
gb_internal isize string_index(String const &s, String const &substr) {
isize n = substr.len;
if (n == 0) {
return 0;
} else if (n == 1) {
return string_index_byte(s, substr[0]);
} else if (n == s.len) {
if (s == substr) {
return 0;
}
return -1;
} else if (n > s.len) {
return -1;
}
u32 pow = 1;
u32 hash = hash_str_rabin_karp(s, &pow);
u32 h = 0;
for (isize i = 0; i < n; i++) {
h = h*PRIME_RABIN_KARP + cast(u32)s.text[i];
}
if (h == hash && substring(s, 0, n) == substr) {
return 0;
}
for (isize i = n; i < s.len; /**/) {
h *= PRIME_RABIN_KARP;
h += cast(u32)s.text[i];
h -= pow * u32(s.text[i-n]);
i += 1;
if (h == hash && substring(s, i-n, i) == substr) {
return i - n;
}
}
return -1;
}
struct StringPartition {
String head;
String match;
String tail;
};
gb_internal StringPartition string_partition(String const &str, String const &sep) {
StringPartition res = {};
isize i = string_index(str, sep);
if (i < 0) {
res.head = str;
return res;
}
res.head = substring(str, 0, i);
res.match = substring(str, i, i+sep.len);
res.tail = substring(str, i+sep.len, str.len);
return res;
}
gb_internal bool string_contains_char(String const &s, u8 c) {
isize i;
for (i = 0; i < s.len; i++) {
+106 -36
View File
@@ -111,7 +111,7 @@ enum BasicFlag {
BasicFlag_Ordered = BasicFlag_Integer | BasicFlag_Float | BasicFlag_String | BasicFlag_Pointer | BasicFlag_Rune,
BasicFlag_OrderedNumeric = BasicFlag_Integer | BasicFlag_Float | BasicFlag_Rune,
BasicFlag_ConstantType = BasicFlag_Boolean | BasicFlag_Numeric | BasicFlag_String | BasicFlag_Pointer | BasicFlag_Rune,
BasicFlag_SimpleCompare = BasicFlag_Boolean | BasicFlag_Numeric | BasicFlag_Pointer | BasicFlag_Rune,
BasicFlag_SimpleCompare = BasicFlag_Boolean | BasicFlag_Integer | BasicFlag_Pointer | BasicFlag_Rune,
};
struct BasicType {
@@ -729,10 +729,12 @@ gb_global Type *t_map_set_proc = nullptr;
gb_global Type *t_objc_object = nullptr;
gb_global Type *t_objc_selector = nullptr;
gb_global Type *t_objc_class = nullptr;
gb_global Type *t_objc_ivar = nullptr;
gb_global Type *t_objc_id = nullptr;
gb_global Type *t_objc_SEL = nullptr;
gb_global Type *t_objc_Class = nullptr;
gb_global Type *t_objc_Ivar = nullptr;
enum OdinAtomicMemoryOrder : i32 {
OdinAtomicMemoryOrder_relaxed = 0, // unordered
@@ -872,6 +874,29 @@ gb_internal Type *base_type(Type *t) {
return t;
}
gb_internal Type *base_named_type(Type *t) {
if (t->kind != Type_Named) {
return t_invalid;
}
Type *prev_named = t;
t = t->Named.base;
for (;;) {
if (t == nullptr) {
break;
}
if (t->kind != Type_Named) {
break;
}
if (t == t->Named.base) {
return t_invalid;
}
prev_named = t;
t = t->Named.base;
}
return prev_named;
}
gb_internal Type *base_enum_type(Type *t) {
Type *bt = base_type(t);
if (bt != nullptr &&
@@ -2932,6 +2957,10 @@ gb_internal Type *default_type(Type *type) {
case Basic_UntypedString: return t_string;
case Basic_UntypedRune: return t_rune;
}
} else if (type->kind == Type_Generic) {
if (type->Generic.specialized) {
return default_type(type->Generic.specialized);
}
}
return type;
}
@@ -3327,6 +3356,15 @@ gb_internal Selection lookup_field_with_selection(Type *type_, String field_name
}
}
}
Type *objc_ivar_type = e->TypeName.objc_ivar;
if (objc_ivar_type != nullptr) {
sel = lookup_field_with_selection(objc_ivar_type, field_name, false, sel, allow_blank_ident);
if (sel.entity != nullptr) {
sel.pseudo_field = true;
return sel;
}
}
}
if (is_type_polymorphic(type)) {
@@ -4108,10 +4146,10 @@ gb_internal i64 type_size_of_internal(Type *t, TypePath *path) {
}
i64 max = 0;
i64 field_size = 0;
for_array(i, t->Union.variants) {
Type *variant_type = t->Union.variants[i];
i64 size = type_size_of_internal(variant_type, path);
if (max < size) {
max = size;
@@ -4130,7 +4168,7 @@ gb_internal i64 type_size_of_internal(Type *t, TypePath *path) {
size = align_formula(max, tag_size);
// NOTE(bill): Calculate the padding between the common fields and the tag
t->Union.tag_size = cast(i16)tag_size;
t->Union.variant_block_size = size - field_size;
t->Union.variant_block_size = size;
size += tag_size;
}
@@ -4542,7 +4580,7 @@ gb_internal Type *alloc_type_proc_from_types(Type **param_types, unsigned param_
// return type;
// }
gb_internal gbString write_type_to_string(gbString str, Type *type, bool shorthand=false) {
gb_internal gbString write_type_to_string(gbString str, Type *type, bool shorthand=false, bool allow_polymorphic=false) {
if (type == nullptr) {
return gb_string_appendc(str, "<no type>");
}
@@ -4567,24 +4605,24 @@ gb_internal gbString write_type_to_string(gbString str, Type *type, bool shortha
str = gb_string_append_length(str, name.text, name.len);
if (type->Generic.specialized != nullptr) {
str = gb_string_append_rune(str, '/');
str = write_type_to_string(str, type->Generic.specialized);
str = write_type_to_string(str, type->Generic.specialized, shorthand, allow_polymorphic);
}
}
break;
case Type_Pointer:
str = gb_string_append_rune(str, '^');
str = write_type_to_string(str, type->Pointer.elem);
str = write_type_to_string(str, type->Pointer.elem, shorthand, allow_polymorphic);
break;
case Type_SoaPointer:
str = gb_string_appendc(str, "#soa ^");
str = write_type_to_string(str, type->SoaPointer.elem);
str = write_type_to_string(str, type->SoaPointer.elem, shorthand, allow_polymorphic);
break;
case Type_MultiPointer:
str = gb_string_appendc(str, "[^]");
str = write_type_to_string(str, type->Pointer.elem);
str = write_type_to_string(str, type->Pointer.elem, shorthand, allow_polymorphic);
break;
case Type_EnumeratedArray:
@@ -4592,31 +4630,31 @@ gb_internal gbString write_type_to_string(gbString str, Type *type, bool shortha
str = gb_string_appendc(str, "#sparse");
}
str = gb_string_append_rune(str, '[');
str = write_type_to_string(str, type->EnumeratedArray.index);
str = write_type_to_string(str, type->EnumeratedArray.index, shorthand, allow_polymorphic);
str = gb_string_append_rune(str, ']');
str = write_type_to_string(str, type->EnumeratedArray.elem);
str = write_type_to_string(str, type->EnumeratedArray.elem, shorthand, allow_polymorphic);
break;
case Type_Array:
str = gb_string_appendc(str, gb_bprintf("[%lld]", cast(long long)type->Array.count));
str = write_type_to_string(str, type->Array.elem);
str = write_type_to_string(str, type->Array.elem, shorthand, allow_polymorphic);
break;
case Type_Slice:
str = gb_string_appendc(str, "[]");
str = write_type_to_string(str, type->Array.elem);
str = write_type_to_string(str, type->Array.elem, shorthand, allow_polymorphic);
break;
case Type_DynamicArray:
str = gb_string_appendc(str, "[dynamic]");
str = write_type_to_string(str, type->DynamicArray.elem);
str = write_type_to_string(str, type->DynamicArray.elem, shorthand, allow_polymorphic);
break;
case Type_Enum:
str = gb_string_appendc(str, "enum");
if (type->Enum.base_type != nullptr) {
str = gb_string_appendc(str, " ");
str = write_type_to_string(str, type->Enum.base_type);
str = write_type_to_string(str, type->Enum.base_type, shorthand, allow_polymorphic);
}
str = gb_string_appendc(str, " {");
for_array(i, type->Enum.fields) {
@@ -4633,6 +4671,13 @@ gb_internal gbString write_type_to_string(gbString str, Type *type, bool shortha
case Type_Union:
str = gb_string_appendc(str, "union");
if (allow_polymorphic && type->Struct.polymorphic_params) {
str = gb_string_appendc(str, "(");
str = write_type_to_string(str, type->Struct.polymorphic_params, shorthand, allow_polymorphic);
str = gb_string_appendc(str, ")");
}
switch (type->Union.kind) {
case UnionType_no_nil: str = gb_string_appendc(str, " #no_nil"); break;
case UnionType_shared_nil: str = gb_string_appendc(str, " #shared_nil"); break;
@@ -4642,7 +4687,7 @@ gb_internal gbString write_type_to_string(gbString str, Type *type, bool shortha
for_array(i, type->Union.variants) {
Type *t = type->Union.variants[i];
if (i > 0) str = gb_string_appendc(str, ", ");
str = write_type_to_string(str, t);
str = write_type_to_string(str, t, shorthand, allow_polymorphic);
}
str = gb_string_append_rune(str, '}');
break;
@@ -4655,17 +4700,24 @@ gb_internal gbString write_type_to_string(gbString str, Type *type, bool shortha
case StructSoa_Dynamic: str = gb_string_appendc(str, "#soa[dynamic]"); break;
default: GB_PANIC("Unknown StructSoaKind"); break;
}
str = write_type_to_string(str, type->Struct.soa_elem);
str = write_type_to_string(str, type->Struct.soa_elem, shorthand, allow_polymorphic);
break;
}
str = gb_string_appendc(str, "struct");
if (allow_polymorphic && type->Struct.polymorphic_params) {
str = gb_string_appendc(str, "(");
str = write_type_to_string(str, type->Struct.polymorphic_params, shorthand, allow_polymorphic);
str = gb_string_appendc(str, ")");
}
if (type->Struct.is_packed) str = gb_string_appendc(str, " #packed");
if (type->Struct.is_raw_union) str = gb_string_appendc(str, " #raw_union");
if (type->Struct.is_no_copy) str = gb_string_appendc(str, " #no_copy");
if (type->Struct.custom_align != 0) str = gb_string_append_fmt(str, " #align %d", cast(int)type->Struct.custom_align);
str = gb_string_appendc(str, " {");
str = gb_string_appendc(str, " {");
if (shorthand && type->Struct.fields.count > 16) {
str = gb_string_append_fmt(str, "%lld fields...", cast(long long)type->Struct.fields.count);
@@ -4678,7 +4730,7 @@ gb_internal gbString write_type_to_string(gbString str, Type *type, bool shortha
}
str = gb_string_append_length(str, f->token.string.text, f->token.string.len);
str = gb_string_appendc(str, ": ");
str = write_type_to_string(str, f->type);
str = write_type_to_string(str, f->type, shorthand, allow_polymorphic);
}
}
str = gb_string_append_rune(str, '}');
@@ -4686,9 +4738,9 @@ gb_internal gbString write_type_to_string(gbString str, Type *type, bool shortha
case Type_Map: {
str = gb_string_appendc(str, "map[");
str = write_type_to_string(str, type->Map.key);
str = write_type_to_string(str, type->Map.key, shorthand, allow_polymorphic);
str = gb_string_append_rune(str, ']');
str = write_type_to_string(str, type->Map.value);
str = write_type_to_string(str, type->Map.value, shorthand, allow_polymorphic);
} break;
case Type_Named:
@@ -4718,9 +4770,11 @@ gb_internal gbString write_type_to_string(gbString str, Type *type, bool shortha
str = gb_string_append_length(str, name.text, name.len);
if (!is_type_untyped(var->type)) {
str = gb_string_appendc(str, ": ");
str = write_type_to_string(str, var->type);
str = gb_string_appendc(str, " = ");
str = write_exact_value_to_string(str, var->Constant.value);
str = write_type_to_string(str, var->type, shorthand, allow_polymorphic);
if (var->Constant.value.kind) {
str = gb_string_appendc(str, " = ");
str = write_exact_value_to_string(str, var->Constant.value);
}
} else {
str = gb_string_appendc(str, " := ");
str = write_exact_value_to_string(str, var->Constant.value);
@@ -4736,20 +4790,31 @@ gb_internal gbString write_type_to_string(gbString str, Type *type, bool shortha
Type *slice = base_type(var->type);
str = gb_string_appendc(str, "..");
GB_ASSERT(var->type->kind == Type_Slice);
str = write_type_to_string(str, slice->Slice.elem);
str = write_type_to_string(str, slice->Slice.elem, shorthand, allow_polymorphic);
} else {
str = write_type_to_string(str, var->type);
str = write_type_to_string(str, var->type, shorthand, allow_polymorphic);
}
} else {
GB_ASSERT(var->kind == Entity_TypeName);
if (var->type->kind == Type_Generic) {
str = gb_string_appendc(str, "typeid/");
str = write_type_to_string(str, var->type);
if (var->token.string.len != 0) {
String name = var->token.string;
str = gb_string_appendc(str, "$");
str = gb_string_append_length(str, name.text, name.len);
str = gb_string_appendc(str, ": typeid");
if (var->type->Generic.specialized) {
str = gb_string_appendc(str, "/");
str = write_type_to_string(str, var->type->Generic.specialized, shorthand, allow_polymorphic);
}
} else {
str = gb_string_appendc(str, "typeid/");
str = write_type_to_string(str, var->type, shorthand, allow_polymorphic);
}
} else {
str = gb_string_appendc(str, "$");
str = gb_string_append_length(str, name.text, name.len);
str = gb_string_appendc(str, "=");
str = write_type_to_string(str, var->type);
str = write_type_to_string(str, var->type, shorthand, allow_polymorphic);
}
}
}
@@ -4795,7 +4860,7 @@ gb_internal gbString write_type_to_string(gbString str, Type *type, bool shortha
}
str = gb_string_appendc(str, "(");
if (type->Proc.params) {
str = write_type_to_string(str, type->Proc.params);
str = write_type_to_string(str, type->Proc.params, shorthand, allow_polymorphic);
}
str = gb_string_appendc(str, ")");
if (type->Proc.results) {
@@ -4803,7 +4868,7 @@ gb_internal gbString write_type_to_string(gbString str, Type *type, bool shortha
if (type->Proc.results->Tuple.variables.count > 1) {
str = gb_string_appendc(str, "(");
}
str = write_type_to_string(str, type->Proc.results);
str = write_type_to_string(str, type->Proc.results, shorthand, allow_polymorphic);
if (type->Proc.results->Tuple.variables.count > 1) {
str = gb_string_appendc(str, ")");
}
@@ -4815,7 +4880,7 @@ gb_internal gbString write_type_to_string(gbString str, Type *type, bool shortha
if (type->BitSet.elem == nullptr) {
str = gb_string_appendc(str, "<unresolved>");
} else if (is_type_enum(type->BitSet.elem)) {
str = write_type_to_string(str, type->BitSet.elem);
str = write_type_to_string(str, type->BitSet.elem, shorthand, allow_polymorphic);
} else {
str = gb_string_append_fmt(str, "%lld", type->BitSet.lower);
str = gb_string_append_fmt(str, "..=");
@@ -4823,14 +4888,14 @@ gb_internal gbString write_type_to_string(gbString str, Type *type, bool shortha
}
if (type->BitSet.underlying != nullptr) {
str = gb_string_appendc(str, "; ");
str = write_type_to_string(str, type->BitSet.underlying);
str = write_type_to_string(str, type->BitSet.underlying, shorthand, allow_polymorphic);
}
str = gb_string_appendc(str, "]");
break;
case Type_SimdVector:
str = gb_string_append_fmt(str, "#simd[%d]", cast(int)type->SimdVector.count);
str = write_type_to_string(str, type->SimdVector.elem);
str = write_type_to_string(str, type->SimdVector.elem, shorthand, allow_polymorphic);
break;
case Type_Matrix:
@@ -4838,12 +4903,12 @@ gb_internal gbString write_type_to_string(gbString str, Type *type, bool shortha
str = gb_string_appendc(str, "#row_major ");
}
str = gb_string_appendc(str, gb_bprintf("matrix[%d, %d]", cast(int)type->Matrix.row_count, cast(int)type->Matrix.column_count));
str = write_type_to_string(str, type->Matrix.elem);
str = write_type_to_string(str, type->Matrix.elem, shorthand, allow_polymorphic);
break;
case Type_BitField:
str = gb_string_appendc(str, "bit_field ");
str = write_type_to_string(str, type->BitField.backing_type);
str = write_type_to_string(str, type->BitField.backing_type, shorthand, allow_polymorphic);
str = gb_string_appendc(str, " {");
for (isize i = 0; i < type->BitField.fields.count; i++) {
Entity *f = type->BitField.fields[i];
@@ -4852,7 +4917,7 @@ gb_internal gbString write_type_to_string(gbString str, Type *type, bool shortha
}
str = gb_string_append_length(str, f->token.string.text, f->token.string.len);
str = gb_string_appendc(str, ": ");
str = write_type_to_string(str, f->type);
str = write_type_to_string(str, f->type, shorthand, allow_polymorphic);
str = gb_string_append_fmt(str, " | %u", type->BitField.bit_sizes[i]);
}
str = gb_string_appendc(str, " }");
@@ -4870,6 +4935,11 @@ gb_internal gbString type_to_string(Type *type, bool shorthand) {
return write_type_to_string(gb_string_make(heap_allocator(), ""), type, shorthand);
}
gb_internal gbString type_to_string_polymorphic(Type *type) {
return write_type_to_string(gb_string_make(heap_allocator(), ""), type, false, true);
}
gb_internal gbString type_to_string_shorthand(Type *type) {
return type_to_string(type, true);
}