Merge tag 'dev-2025-08'

# Conflicts:
#	.gitignore
This commit is contained in:
ed
2025-08-09 09:50:50 -04:00
144 changed files with 5490 additions and 1995 deletions
+72 -25
View File
@@ -171,15 +171,18 @@ struct TargetMetrics {
enum Subtarget : u32 {
Subtarget_Default,
Subtarget_iOS,
Subtarget_iPhone,
Subtarget_iPhoneSimulator,
Subtarget_Android,
Subtarget_COUNT,
Subtarget_Invalid, // NOTE(harold): Must appear after _COUNT as this is not a real subtarget
};
gb_global String subtarget_strings[Subtarget_COUNT] = {
str_lit(""),
str_lit("ios"),
str_lit("iphone"),
str_lit("iphonesimulator"),
str_lit("android"),
};
@@ -306,6 +309,7 @@ enum VetFlags : u64 {
VetFlag_Cast = 1u<<8,
VetFlag_Tabs = 1u<<9,
VetFlag_UnusedProcedures = 1u<<10,
VetFlag_ExplicitAllocators = 1u<<11,
VetFlag_Unused = VetFlag_UnusedVariables|VetFlag_UnusedImports,
@@ -339,6 +343,8 @@ u64 get_vet_flag_from_name(String const &name) {
return VetFlag_Tabs;
} else if (name == "unused-procedures") {
return VetFlag_UnusedProcedures;
} else if (name == "explicit-allocators") {
return VetFlag_ExplicitAllocators;
}
return VetFlag_NONE;
}
@@ -857,7 +863,7 @@ gb_global NamedTargetMetrics *selected_target_metrics;
gb_global Subtarget selected_subtarget;
gb_internal TargetOsKind get_target_os_from_string(String str, Subtarget *subtarget_ = nullptr) {
gb_internal TargetOsKind get_target_os_from_string(String str, Subtarget *subtarget_ = nullptr, String *subtarget_str = nullptr) {
String os_name = str;
String subtarget = {};
auto part = string_partition(str, str_lit(":"));
@@ -874,18 +880,26 @@ gb_internal TargetOsKind get_target_os_from_string(String str, Subtarget *subtar
break;
}
}
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;
if (subtarget_str) *subtarget_str = subtarget;
if (subtarget_) {
if (subtarget.len != 0) {
*subtarget_ = Subtarget_Invalid;
if (str_eq_ignore_case(subtarget, "generic") || str_eq_ignore_case(subtarget, "default")) {
*subtarget_ = Subtarget_Default;
} else {
for (isize i = 1; i < Subtarget_COUNT; i++) {
if (str_eq_ignore_case(subtarget_strings[i], subtarget)) {
*subtarget_ = cast(Subtarget)i;
break;
}
}
}
} else {
*subtarget_ = Subtarget_Default;
}
}
@@ -1824,16 +1838,29 @@ gb_internal void init_build_context(TargetMetrics *cross_target, Subtarget subta
}
}
if (metrics->os == TargetOs_darwin && subtarget == Subtarget_iOS) {
switch (metrics->arch) {
case TargetArch_arm64:
bc->metrics.target_triplet = str_lit("arm64-apple-ios");
break;
case TargetArch_amd64:
bc->metrics.target_triplet = str_lit("x86_64-apple-ios");
break;
default:
GB_PANIC("Unknown architecture for darwin");
if (metrics->os == TargetOs_darwin) {
switch (subtarget) {
case Subtarget_iPhone:
switch (metrics->arch) {
case TargetArch_arm64:
bc->metrics.target_triplet = str_lit("arm64-apple-ios");
break;
default:
GB_PANIC("Unknown architecture for -subtarget:iphone");
}
break;
case Subtarget_iPhoneSimulator:
switch (metrics->arch) {
case TargetArch_arm64:
bc->metrics.target_triplet = str_lit("arm64-apple-ios-simulator");
break;
case TargetArch_amd64:
bc->metrics.target_triplet = str_lit("x86_64-apple-ios-simulator");
break;
default:
GB_PANIC("Unknown architecture for -subtarget:iphonesimulator");
}
break;
}
} else if (metrics->os == TargetOs_linux && subtarget == Subtarget_Android) {
switch (metrics->arch) {
@@ -1860,6 +1887,13 @@ gb_internal void init_build_context(TargetMetrics *cross_target, Subtarget subta
str_lit("-target "), bc->metrics.target_triplet, str_lit(" "));
} else if (is_arch_wasm()) {
gbString link_flags = gb_string_make(heap_allocator(), " ");
// NOTE(laytan): Put the stack first in the memory,
// causing a stack overflow to error immediately instead of corrupting globals.
link_flags = gb_string_appendc(link_flags, "--stack-first ");
// NOTE(laytan): default stack size is 64KiB, up to a more reasonable 1MiB.
link_flags = gb_string_appendc(link_flags, "-z stack-size=1048576 ");
// link_flags = gb_string_appendc(link_flags, "--export-all ");
// link_flags = gb_string_appendc(link_flags, "--export-table ");
// if (bc->metrics.arch == TargetArch_wasm64) {
@@ -1892,10 +1926,23 @@ gb_internal void init_build_context(TargetMetrics *cross_target, Subtarget subta
// does not annoy the user with version warnings.
if (metrics->os == TargetOs_darwin) {
if (!bc->minimum_os_version_string_given) {
bc->minimum_os_version_string = str_lit("11.0.0");
if (subtarget == Subtarget_Default) {
bc->minimum_os_version_string = str_lit("11.0.0");
} else if (subtarget == Subtarget_iPhone || subtarget == Subtarget_iPhoneSimulator) {
// NOTE(harold): We default to 17.4 on iOS because that's when os_sync_wait_on_address was added and
// we'd like to avoid any potential App Store issues by using the private ulock_* there.
bc->minimum_os_version_string = str_lit("17.4");
}
}
if (subtarget == Subtarget_Default) {
if (subtarget == Subtarget_iPhoneSimulator) {
// For the iPhoneSimulator subtarget, the version must be between 'ios' and '-simulator'.
String suffix = str_lit("-simulator");
GB_ASSERT(string_ends_with(bc->metrics.target_triplet, suffix));
String prefix = substring(bc->metrics.target_triplet, 0, bc->metrics.target_triplet.len - suffix.len);
bc->metrics.target_triplet = concatenate3_strings(permanent_allocator(), prefix, bc->minimum_os_version_string, suffix);
} else {
bc->metrics.target_triplet = concatenate_strings(permanent_allocator(), bc->metrics.target_triplet, bc->minimum_os_version_string);
}
} else if (selected_subtarget == Subtarget_Android) {
+121 -1
View File
@@ -1,5 +1,14 @@
typedef bool (BuiltinTypeIsProc)(Type *t);
gb_internal int enum_constant_entity_cmp(void const* a, void const* b) {
Entity const *ea = *(cast(Entity const **)a);
Entity const *eb = *(cast(Entity const **)b);
GB_ASSERT(ea->kind == Entity_Constant && eb->kind == Entity_Constant);
GB_ASSERT(ea->Constant.value.kind == ExactValue_Integer && eb->Constant.value.kind == ExactValue_Integer);
return big_int_cmp(&ea->Constant.value.value_integer, &eb->Constant.value.value_integer);
}
gb_global BuiltinTypeIsProc *builtin_type_is_procs[BuiltinProc__type_simple_boolean_end - BuiltinProc__type_simple_boolean_begin] = {
nullptr, // BuiltinProc__type_simple_boolean_begin
@@ -23,6 +32,7 @@ gb_global BuiltinTypeIsProc *builtin_type_is_procs[BuiltinProc__type_simple_bool
is_type_sliceable,
is_type_comparable,
is_type_simple_compare,
is_type_nearly_simple_compare,
is_type_dereferenceable,
is_type_valid_for_keys,
is_type_valid_for_matrix_elems,
@@ -1150,6 +1160,58 @@ gb_internal bool check_builtin_simd_operation(CheckerContext *c, Operand *operan
return true;
}
case BuiltinProc_simd_runtime_swizzle:
{
if (ce->args.count != 2) {
error(call, "'%.*s' expected 2 arguments, got %td", LIT(builtin_name), ce->args.count);
return false;
}
Operand src = {};
Operand indices = {};
check_expr(c, &src, ce->args[0]); if (src.mode == Addressing_Invalid) return false;
check_expr_with_type_hint(c, &indices, ce->args[1], src.type); if (indices.mode == Addressing_Invalid) return false;
if (!is_type_simd_vector(src.type)) {
error(src.expr, "'%.*s' expected first argument to be a simd vector", LIT(builtin_name));
return false;
}
if (!is_type_simd_vector(indices.type)) {
error(indices.expr, "'%.*s' expected second argument (indices) to be a simd vector", LIT(builtin_name));
return false;
}
Type *src_elem = base_array_type(src.type);
Type *indices_elem = base_array_type(indices.type);
if (!is_type_integer(src_elem)) {
gbString src_str = type_to_string(src.type);
error(src.expr, "'%.*s' expected first argument to be a simd vector of integers, got '%s'", LIT(builtin_name), src_str);
gb_string_free(src_str);
return false;
}
if (!is_type_integer(indices_elem)) {
gbString indices_str = type_to_string(indices.type);
error(indices.expr, "'%.*s' expected indices to be a simd vector of integers, got '%s'", LIT(builtin_name), indices_str);
gb_string_free(indices_str);
return false;
}
if (!are_types_identical(src.type, indices.type)) {
gbString src_str = type_to_string(src.type);
gbString indices_str = type_to_string(indices.type);
error(indices.expr, "'%.*s' expected both arguments to have the same type, got '%s' vs '%s'", LIT(builtin_name), src_str, indices_str);
gb_string_free(indices_str);
gb_string_free(src_str);
return false;
}
operand->mode = Addressing_Value;
operand->type = src.type;
return true;
}
case BuiltinProc_simd_ceil:
case BuiltinProc_simd_floor:
case BuiltinProc_simd_trunc:
@@ -2325,7 +2387,11 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
if (mode == Addressing_Invalid) {
gbString t = type_to_string(operand->type);
error(call, "'%.*s' is not supported for '%s'", LIT(builtin_name), t);
if (is_type_bit_set(op_type) && id == BuiltinProc_len) {
error(call, "'%.*s' is not supported for '%s', did you mean 'card'?", LIT(builtin_name), t);
} else {
error(call, "'%.*s' is not supported for '%s'", LIT(builtin_name), t);
}
return false;
}
@@ -4649,6 +4715,15 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
}
break;
case BuiltinProc_read_cycle_counter_frequency:
if (build_context.metrics.arch != TargetArch_arm64) {
error(call, "'%.*s' is only allowed on arm64 targets", LIT(builtin_name));
return false;
}
operand->mode = Addressing_Value;
operand->type = t_i64;
break;
case BuiltinProc_read_cycle_counter:
operand->mode = Addressing_Value;
operand->type = t_i64;
@@ -6072,6 +6147,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
case BuiltinProc_type_is_sliceable:
case BuiltinProc_type_is_comparable:
case BuiltinProc_type_is_simple_compare:
case BuiltinProc_type_is_nearly_simple_compare:
case BuiltinProc_type_is_dereferenceable:
case BuiltinProc_type_is_valid_map_key:
case BuiltinProc_type_is_valid_matrix_elements:
@@ -6920,6 +6996,50 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
break;
}
case BuiltinProc_type_enum_is_contiguous:
{
Operand op = {};
Type *bt = check_type(c, ce->args[0]);
Type *type = base_type(bt);
if (type == nullptr || type == t_invalid) {
error(ce->args[0], "Expected a type for '%.*s'", LIT(builtin_name));
return false;
}
if (!is_type_enum(type)) {
gbString t = type_to_string(type);
error(ce->args[0], "Expected an enum type for '%.*s', got %s", LIT(builtin_name), t);
gb_string_free(t);
return false;
}
auto enum_constants = array_make<Entity *>(temporary_allocator(), type->Enum.fields.count);
array_copy(&enum_constants, type->Enum.fields, 0);
array_sort(enum_constants, enum_constant_entity_cmp);
BigInt minus_one = big_int_make_i64(-1);
defer (big_int_dealloc(&minus_one));
BigInt diff = {};
bool contiguous = true;
operand->mode = Addressing_Constant;
operand->type = t_untyped_bool;
for (isize i = 0; i < enum_constants.count - 1; i++) {
BigInt curr = enum_constants[i]->Constant.value.value_integer;
BigInt next = enum_constants[i + 1]->Constant.value.value_integer;
big_int_sub(&diff, &curr, &next);
defer (big_int_dealloc(&diff));
if (!big_int_is_zero(&diff) && big_int_cmp(&diff, &minus_one) != 0) {
contiguous = false;
break;
}
}
operand->value = exact_value_bool(contiguous);
break;
}
case BuiltinProc_type_equal_proc:
{
Operand op = {};
+107 -106
View File
@@ -145,13 +145,6 @@ gb_internal void check_init_variables(CheckerContext *ctx, Entity **lhs, isize l
if (d != nullptr) {
d->init_expr = o->expr;
}
if (o->type && is_type_no_copy(o->type)) {
ERROR_BLOCK();
if (check_no_copy_assignment(*o, str_lit("initialization"))) {
error_line("\tInitialization of a #no_copy type must be either implicitly zero, a constant literal, or a return value from a call expression");
}
}
}
if (rhs_count > 0 && lhs_count != rhs_count) {
error(lhs[0]->token, "Assignment count mismatch '%td' = '%td'", lhs_count, rhs_count);
@@ -1001,119 +994,127 @@ gb_internal String handle_link_name(CheckerContext *ctx, Token token, String lin
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)) {
if (!ac.objc_type) {
return;
}
if (ac.objc_name.len == 0 && ac.objc_is_class_method) {
error(e->token, "@(objc_name) is required with @(objc_is_class_method)");
} else if (ac.objc_type == nullptr) {
error(e->token, "@(objc_name) requires that @(objc_type) to be set");
} else if (ac.objc_name.len == 0 && ac.objc_type) {
error(e->token, "@(objc_name) is required with @(objc_type)");
} else {
Type *t = ac.objc_type;
GB_ASSERT(t->kind == Type_Named); // NOTE(harold): This is already checked for at the attribute resolution stage.
Entity *tn = t->Named.type_name;
Type *t = ac.objc_type;
GB_ASSERT(t->kind == Type_Named); // NOTE(harold): This is already checked for at the attribute resolution stage.
GB_ASSERT(tn->kind == Entity_TypeName);
// Attempt to infer th objc_name automatically if the proc name contains
// the type name objc_type's name, followed by an underscore, as a prefix.
if (ac.objc_name.len == 0) {
String proc_name = e->token.string;
String type_name = t->Named.name;
if (tn->scope != e->scope) {
error(e->token, "@(objc_name) attribute may only be applied to procedures and types within the same scope");
if (proc_name.len > type_name.len + 1 &&
proc_name[type_name.len] == '_' &&
str_eq(type_name, substring(proc_name, 0, type_name.len))
) {
ac.objc_name = substring(proc_name, type_name.len+1, proc_name.len);
} else {
error(e->token, "@(objc_name) requires that @(objc_type) be set or inferred "
"by prefixing the proc name with the type and underscore: MyObjcType_myProcName :: proc().");
}
}
// 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;
}
Entity *tn = t->Named.type_name;
GB_ASSERT(tn->kind == Entity_TypeName);
if (implement) {
GB_ASSERT(e->kind == Entity_Procedure);
if (tn->scope != e->scope) {
error(e->token, "@(objc_name) attribute may only be applied to procedures and types within the same scope");
} else {
// Enable implementation by default if the class is an implementer too and
// @objc_implement was not set to false explicitly in this proc.
bool implement = tn->TypeName.objc_is_implementation;
if (ac.objc_is_disabled_implement) {
implement = false;
}
auto &proc = e->type->Proc;
Type *first_param = proc.param_count > 0 ? proc.params->Tuple.variables[0]->type : t_untyped_nil;
if (implement) {
GB_ASSERT(e->kind == Entity_Procedure);
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 {
// 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 (ac.link_name != "") {
error(e->token, "Explicit linkage not allowed when @(objc_implement) is set. It set to \"strong\" implicitly");
}
auto &proc = e->type->Proc;
Type *first_param = proc.param_count > 0 ? proc.params->Tuple.variables[0]->type : t_untyped_nil;
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});
}
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});
}
}
}
+71 -75
View File
@@ -5763,22 +5763,6 @@ gb_internal bool check_identifier_exists(Scope *s, Ast *node, bool nested = fals
return false;
}
gb_internal bool check_no_copy_assignment(Operand const &o, String const &context) {
if (o.type && is_type_no_copy(o.type)) {
Ast *expr = unparen_expr(o.expr);
if (expr && o.mode != Addressing_Constant && o.mode != Addressing_Type) {
if (expr->kind == Ast_CallExpr) {
// Okay
} else {
error(o.expr, "Invalid use of #no_copy value in %.*s", LIT(context));
return true;
}
}
}
return false;
}
gb_internal bool check_assignment_arguments(CheckerContext *ctx, Array<Operand> const &lhs, Array<Operand> *operands, Slice<Ast *> const &rhs) {
bool optional_ok = false;
isize tuple_index = 0;
@@ -5849,7 +5833,6 @@ gb_internal bool check_assignment_arguments(CheckerContext *ctx, Array<Operand>
for (Entity *e : tuple->variables) {
o.type = e->type;
array_add(operands, o);
check_no_copy_assignment(o, str_lit("assignment"));
}
tuple_index += tuple->variables.count;
@@ -6236,29 +6219,46 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A
}
for (Operand const &o : ordered_operands) {
if (o.mode != Addressing_Invalid) {
check_no_copy_assignment(o, str_lit("procedure call expression"));
}
}
for (isize i = 0; i < pt->param_count; i++) {
if (!visited[i]) {
Entity *e = pt->params->Tuple.variables[i];
bool context_allocator_error = false;
if (e->kind == Entity_Variable) {
if (e->Variable.param_value.kind != ParameterValue_Invalid) {
ordered_operands[i].mode = Addressing_Value;
ordered_operands[i].type = e->type;
ordered_operands[i].expr = e->Variable.param_value.original_ast_expr;
if (ast_file_vet_explicit_allocators(c->file)) {
// NOTE(lucas): check if we are trying to default to context.allocator or context.temp_allocator
if (e->Variable.param_value.original_ast_expr->kind == Ast_SelectorExpr) {
auto& expr = e->Variable.param_value.original_ast_expr->SelectorExpr.expr;
auto& selector = e->Variable.param_value.original_ast_expr->SelectorExpr.selector;
if (expr->kind == Ast_Implicit &&
expr->Implicit.string == STR_LIT("context") &&
selector->kind == Ast_Ident &&
(selector->Ident.token.string == STR_LIT("allocator") ||
selector->Ident.token.string == STR_LIT("temp_allocator"))) {
context_allocator_error = true;
}
}
}
dummy_argument_count += 1;
score += assign_score_function(1);
continue;
if (!context_allocator_error) {
ordered_operands[i].mode = Addressing_Value;
ordered_operands[i].type = e->type;
ordered_operands[i].expr = e->Variable.param_value.original_ast_expr;
dummy_argument_count += 1;
score += assign_score_function(1);
continue;
}
}
}
if (show_error) {
if (e->kind == Entity_TypeName) {
if (context_allocator_error) {
gbString str = type_to_string(e->type);
error(call, "Parameter '%.*s' of type '%s' must be explicitly provided in procedure call",
LIT(e->token.string), str);
gb_string_free(str);
} else if (e->kind == Entity_TypeName) {
error(call, "Type parameter '%.*s' is missing in procedure call",
LIT(e->token.string));
} else if (e->kind == Entity_Constant && e->Constant.value.kind != ExactValue_Invalid) {
@@ -6533,7 +6533,7 @@ gb_internal bool evaluate_where_clauses(CheckerContext *ctx, Ast *call_expr, Sco
Entity *e = entry.value;
switch (e->kind) {
case Entity_TypeName: {
if (print_count == 0) error_line("\n\tWith the following definitions:\n");
// if (print_count == 0) error_line("\n\tWith the following definitions:\n");
gbString str = type_to_string(e->type);
error_line("\t\t%.*s :: %s;\n", LIT(e->token.string), str);
@@ -10312,52 +10312,48 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
is_constant = false;
}
if (cl->elems[0]->kind == Ast_FieldValue) {
error(cl->elems[0], "'field = value' in a bit_set a literal is not allowed");
is_constant = false;
} else {
for (Ast *elem : cl->elems) {
if (elem->kind == Ast_FieldValue) {
error(elem, "'field = value' in a bit_set a literal is not allowed");
for (Ast *elem : cl->elems) {
if (elem->kind == Ast_FieldValue) {
error(elem, "'field = value' in a bit_set literal is not allowed");
is_constant = false;
continue;
}
check_expr_with_type_hint(c, o, elem, et);
if (is_constant) {
is_constant = o->mode == Addressing_Constant;
}
if (elem->kind == Ast_BinaryExpr) {
switch (elem->BinaryExpr.op.kind) {
case Token_Or:
{
gbString x = expr_to_string(elem->BinaryExpr.left);
gbString y = expr_to_string(elem->BinaryExpr.right);
gbString e = expr_to_string(elem);
error(elem, "Was the following intended? '%s, %s'; if not, surround the expression with parentheses '(%s)'", x, y, e);
gb_string_free(e);
gb_string_free(y);
gb_string_free(x);
}
break;
}
}
check_assignment(c, o, t->BitSet.elem, str_lit("bit_set literal"));
if (o->mode == Addressing_Constant) {
i64 lower = t->BitSet.lower;
i64 upper = t->BitSet.upper;
i64 v = exact_value_to_i64(o->value);
if (lower <= v && v <= upper) {
// okay
} else {
gbString s = expr_to_string(o->expr);
error(elem, "Bit field value out of bounds, %s (%lld) not in the range %lld .. %lld", s, v, lower, upper);
gb_string_free(s);
continue;
}
check_expr_with_type_hint(c, o, elem, et);
if (is_constant) {
is_constant = o->mode == Addressing_Constant;
}
if (elem->kind == Ast_BinaryExpr) {
switch (elem->BinaryExpr.op.kind) {
case Token_Or:
{
gbString x = expr_to_string(elem->BinaryExpr.left);
gbString y = expr_to_string(elem->BinaryExpr.right);
gbString e = expr_to_string(elem);
error(elem, "Was the following intended? '%s, %s'; if not, surround the expression with parentheses '(%s)'", x, y, e);
gb_string_free(e);
gb_string_free(y);
gb_string_free(x);
}
break;
}
}
check_assignment(c, o, t->BitSet.elem, str_lit("bit_set literal"));
if (o->mode == Addressing_Constant) {
i64 lower = t->BitSet.lower;
i64 upper = t->BitSet.upper;
i64 v = exact_value_to_i64(o->value);
if (lower <= v && v <= upper) {
// okay
} else {
gbString s = expr_to_string(o->expr);
error(elem, "Bit field value out of bounds, %s (%lld) not in the range %lld .. %lld", s, v, lower, upper);
gb_string_free(s);
continue;
}
}
}
}
break;
+4 -2
View File
@@ -430,8 +430,6 @@ gb_internal Type *check_assignment_variable(CheckerContext *ctx, Operand *lhs, O
Ast *node = unparen_expr(lhs->expr);
check_no_copy_assignment(*rhs, context_name);
// NOTE(bill): Ignore assignments to '_'
if (is_blank_ident(node)) {
check_assignment(ctx, rhs, nullptr, str_lit("assignment to '_' identifier"));
@@ -2778,6 +2776,10 @@ gb_internal void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags)
Ast *stmt = ds->stmt;
Ast *original_stmt = stmt;
if (stmt->kind == Ast_BlockStmt && stmt->BlockStmt.stmts.count == 0) {
break; // empty defer statement
}
bool is_singular = true;
while (is_singular && stmt->kind == Ast_BlockStmt) {
Ast *inner_stmt = nullptr;
-1
View File
@@ -646,7 +646,6 @@ gb_internal void check_struct_type(CheckerContext *ctx, Type *struct_type, Ast *
struct_type->Struct.node = node;
struct_type->Struct.scope = ctx->scope;
struct_type->Struct.is_packed = st->is_packed;
struct_type->Struct.is_no_copy = st->is_no_copy;
struct_type->Struct.polymorphic_params = check_record_polymorphic_params(
ctx, st->polymorphic_params,
&struct_type->Struct.is_polymorphic,
+10 -5
View File
@@ -1171,9 +1171,10 @@ gb_internal void init_universal(void) {
{
GlobalEnumValue values[Subtarget_COUNT] = {
{"Default", Subtarget_Default},
{"iOS", Subtarget_iOS},
{"Android", Subtarget_Android},
{"Default", Subtarget_Default},
{"iPhone", Subtarget_iPhone},
{"iPhoneSimulator", Subtarget_iPhoneSimulator},
{"Android", Subtarget_Android},
};
auto fields = add_global_enum_type(str_lit("Odin_Platform_Subtarget_Type"), values, gb_count_of(values));
@@ -6801,7 +6802,11 @@ gb_internal void check_parsed_files(Checker *c) {
for_array(i, c->info.definitions) {
Entity *e = c->info.definitions[i];
if (e->kind == Entity_TypeName && e->type != nullptr && is_type_typed(e->type)) {
(void)type_align_of(e->type);
if (e->TypeName.is_type_alias) {
// Ignore for the time being
} else {
(void)type_align_of(e->type);
}
} else if (e->kind == Entity_Procedure) {
DeclInfo *decl = e->decl_info;
ast_node(pl, ProcLit, decl->proc_lit);
@@ -6951,7 +6956,7 @@ gb_internal void check_parsed_files(Checker *c) {
if (entry.key != tt.hash) {
continue;
}
auto const &other = type_info_types[entry.value];
auto const &other = c->info.type_info_types_hash_map[entry.value];
if (are_types_identical_unique_tuples(tt.type, other.type)) {
continue;
}
+11
View File
@@ -61,6 +61,7 @@ enum BuiltinProcId {
BuiltinProc_trap,
BuiltinProc_debug_trap,
BuiltinProc_read_cycle_counter,
BuiltinProc_read_cycle_counter_frequency,
BuiltinProc_count_ones,
BuiltinProc_count_zeros,
@@ -191,6 +192,7 @@ BuiltinProc__simd_begin,
BuiltinProc_simd_shuffle,
BuiltinProc_simd_select,
BuiltinProc_simd_runtime_swizzle,
BuiltinProc_simd_ceil,
BuiltinProc_simd_floor,
@@ -224,6 +226,7 @@ BuiltinProc__simd_end,
BuiltinProc_x86_cpuid,
BuiltinProc_x86_xgetbv,
// Constant type tests
BuiltinProc__type_begin,
@@ -261,6 +264,7 @@ BuiltinProc__type_simple_boolean_begin,
BuiltinProc_type_is_sliceable,
BuiltinProc_type_is_comparable,
BuiltinProc_type_is_simple_compare, // easily compared using memcmp
BuiltinProc_type_is_nearly_simple_compare, // easily compared using memcmp (including floats)
BuiltinProc_type_is_dereferenceable,
BuiltinProc_type_is_valid_map_key,
BuiltinProc_type_is_valid_matrix_elements,
@@ -325,6 +329,8 @@ BuiltinProc__type_simple_boolean_end,
BuiltinProc_type_bit_set_backing_type,
BuiltinProc_type_enum_is_contiguous,
BuiltinProc_type_equal_proc,
BuiltinProc_type_hasher_proc,
BuiltinProc_type_map_info,
@@ -418,6 +424,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT("trap"), 0, false, Expr_Expr, BuiltinProcPkg_intrinsics, /*diverging*/true},
{STR_LIT("debug_trap"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics, /*diverging*/false},
{STR_LIT("read_cycle_counter"), 0, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("read_cycle_counter_frequency"), 0, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("count_ones"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("count_zeros"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
@@ -550,6 +557,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT("simd_shuffle"), 2, true, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_select"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_runtime_swizzle"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_ceil") , 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_floor"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
@@ -614,6 +622,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT("type_is_sliceable"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_is_comparable"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_is_simple_compare"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_is_nearly_simple_compare"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_is_dereferenceable"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_is_valid_map_key"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_is_valid_matrix_elements"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
@@ -678,6 +687,8 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT("type_bit_set_backing_type"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_enum_is_contiguous"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics },
{STR_LIT("type_equal_proc"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_hasher_proc"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_map_info"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+1 -1
View File
@@ -4869,8 +4869,8 @@ u64 gb_murmur64_seed(void const *data_, isize len, u64 seed) {
u64 h = seed ^ (len * m);
u64 const *data = cast(u64 const *)data_;
u8 const *data2 = cast(u8 const *)data_;
u64 const* end = data + (len / 8);
u8 const *data2 = cast(u8 const *)end;
while (data != end) {
u64 k = *data++;
+58 -6
View File
@@ -431,8 +431,10 @@ try_cross_linking:;
// Link using `clang`, unless overridden by `ODIN_CLANG_PATH` environment variable.
const char* clang_path = gb_get_env("ODIN_CLANG_PATH", permanent_allocator());
bool has_odin_clang_path_env = true;
if (clang_path == NULL) {
clang_path = "clang";
has_odin_clang_path_env = false;
}
// NOTE(vassvik): needs to add the root to the library search paths, so that the full filenames of the library
@@ -591,7 +593,7 @@ try_cross_linking:;
// Do not add libc again, this is added later already, and omitted with
// the `-no-crt` flag, not skipping here would cause duplicate library
// warnings when linking on darwin and might link libc silently even with `-no-crt`.
if (lib == str_lit("System.framework") || lib == str_lit("c")) {
if (lib == str_lit("System.framework") || lib == str_lit("System") || lib == str_lit("c")) {
continue;
}
@@ -772,10 +774,58 @@ 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) {
// Get the MacOSX SDK path.
// Get the 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");
char const* darwin_platform_name = "MacOSX";
char const* darwin_xcrun_sdk_name = "macosx";
char const* darwin_min_version_id = "macosx";
const char* original_clang_path = clang_path;
// NOTE(harold): We set the clang_path to run through xcrun because otherwise it complaints about the the sysroot
// being set to 'MacOSX' even though we've set the sysroot to the correct SDK (-Wincompatible-sysroot).
// This is because it is likely not using the SDK's toolchain Apple Clang but another one installed in the system.
switch (selected_subtarget) {
case Subtarget_iPhone:
darwin_platform_name = "iPhoneOS";
darwin_xcrun_sdk_name = "iphoneos";
darwin_min_version_id = "ios";
if (!has_odin_clang_path_env) {
clang_path = "xcrun --sdk iphoneos clang";
}
break;
case Subtarget_iPhoneSimulator:
darwin_platform_name = "iPhoneSimulator";
darwin_xcrun_sdk_name = "iphonesimulator";
darwin_min_version_id = "ios-simulator";
if (!has_odin_clang_path_env) {
clang_path = "xcrun --sdk iphonesimulator clang";
}
break;
}
gbString darwin_find_sdk_cmd = gb_string_make(temporary_allocator(), "");
darwin_find_sdk_cmd = gb_string_append_fmt(darwin_find_sdk_cmd, "xcrun --sdk %s --show-sdk-path", darwin_xcrun_sdk_name);
if (!system_exec_command_line_app_output(darwin_find_sdk_cmd, &darwin_sdk_path)) {
// Fallback to default clang, since `xcrun --sdk` did not work.
clang_path = original_clang_path;
// Best-effort fallback to known locations
gbString darwin_sdk_path = gb_string_make(temporary_allocator(), "");
darwin_sdk_path = gb_string_append_fmt(darwin_sdk_path, "/Library/Developer/CommandLineTools/SDKs/%s.sdk", darwin_platform_name);
if (!path_is_directory(make_string_c(darwin_sdk_path))) {
gb_string_clear(darwin_sdk_path);
darwin_sdk_path = gb_string_append_fmt(darwin_sdk_path, "/Applications/Xcode.app/Contents/Developer/Platforms/%s.platform/Developer/SDKs/%s.sdk", darwin_platform_name);
if (!path_is_directory(make_string_c(darwin_sdk_path))) {
gb_printf_err("Failed to find %s SDK\n", darwin_platform_name);
return -1;
}
}
} else {
// Trim the trailing newline.
darwin_sdk_path = gb_string_trim_space(darwin_sdk_path);
@@ -796,8 +846,10 @@ try_cross_linking:;
// Only specify this flag if the user has given a minimum version to target.
// This will cause warnings to show up for mismatched libraries.
if (build_context.minimum_os_version_string_given) {
link_settings = gb_string_append_fmt(link_settings, "-mmacosx-version-min=%.*s ", LIT(build_context.minimum_os_version_string));
// NOTE(harold): For device subtargets we have to explicitly set the default version to
// avoid the same warning since we configure our own minimum version when compiling for devices.
if (build_context.minimum_os_version_string_given || selected_subtarget != Subtarget_Default) {
link_settings = gb_string_append_fmt(link_settings, "-m%s-version-min=%.*s ", darwin_min_version_id, LIT(build_context.minimum_os_version_string));
}
if (build_context.build_mode != BuildMode_DynamicLibrary) {
+36 -10
View File
@@ -527,6 +527,8 @@ namespace lbAbiAmd64SysV {
enum RegClass {
RegClass_NoClass,
RegClass_Int,
RegClass_SSEHs,
RegClass_SSEHv,
RegClass_SSEFs,
RegClass_SSEFv,
RegClass_SSEDs,
@@ -545,6 +547,8 @@ namespace lbAbiAmd64SysV {
gb_internal bool is_sse(RegClass reg_class) {
switch (reg_class) {
case RegClass_SSEHs:
case RegClass_SSEHv:
case RegClass_SSEFs:
case RegClass_SSEFv:
case RegClass_SSEDs:
@@ -693,6 +697,8 @@ namespace lbAbiAmd64SysV {
case RegClass_Int:
needed_int += 1;
break;
case RegClass_SSEHs:
case RegClass_SSEHv:
case RegClass_SSEFs:
case RegClass_SSEFv:
case RegClass_SSEDs:
@@ -804,6 +810,8 @@ namespace lbAbiAmd64SysV {
to_write = RegClass_Memory;
} else if (newv == RegClass_SSEUp) {
switch (oldv) {
case RegClass_SSEHv:
case RegClass_SSEHs:
case RegClass_SSEFv:
case RegClass_SSEFs:
case RegClass_SSEDv:
@@ -850,14 +858,18 @@ namespace lbAbiAmd64SysV {
} else if (oldv == RegClass_SSEUp) {
oldv = RegClass_SSEDv;
} else if (is_sse(oldv)) {
i++;
while (i != e && oldv == RegClass_SSEUp) {
i++;
for (i++; i < e; i++) {
RegClass v = (*cls)[cast(isize)i];
if (v != RegClass_SSEUp) {
break;
}
}
} else if (oldv == RegClass_X87) {
i++;
while (i != e && oldv == RegClass_X87Up) {
i++;
for (i++; i < e; i++) {
RegClass v = (*cls)[cast(isize)i];
if (v != RegClass_X87Up) {
break;
}
}
} else {
i++;
@@ -914,6 +926,7 @@ namespace lbAbiAmd64SysV {
sz -= rs;
break;
}
case RegClass_SSEHv:
case RegClass_SSEFv:
case RegClass_SSEDv:
case RegClass_SSEInt8:
@@ -924,6 +937,10 @@ namespace lbAbiAmd64SysV {
unsigned elems_per_word = 0;
LLVMTypeRef elem_type = nullptr;
switch (reg_class) {
case RegClass_SSEHv:
elems_per_word = 4;
elem_type = LLVMHalfTypeInContext(c);
break;
case RegClass_SSEFv:
elems_per_word = 2;
elem_type = LLVMFloatTypeInContext(c);
@@ -958,6 +975,10 @@ namespace lbAbiAmd64SysV {
continue;
}
break;
case RegClass_SSEHs:
array_add(&types, LLVMHalfTypeInContext(c));
sz -= 2;
break;
case RegClass_SSEFs:
array_add(&types, LLVMFloatTypeInContext(c));
sz -= 4;
@@ -1004,9 +1025,11 @@ namespace lbAbiAmd64SysV {
break;
}
case LLVMPointerTypeKind:
case LLVMHalfTypeKind:
unify(cls, ix + off/8, RegClass_Int);
break;
case LLVMHalfTypeKind:
unify(cls, ix + off/8, (off%8 != 0) ? RegClass_SSEHv : RegClass_SSEHs);
break;
case LLVMFloatTypeKind:
unify(cls, ix + off/8, (off%8 == 4) ? RegClass_SSEFv : RegClass_SSEFs);
break;
@@ -1046,10 +1069,9 @@ namespace lbAbiAmd64SysV {
i64 elem_sz = lb_sizeof(elem);
LLVMTypeKind elem_kind = LLVMGetTypeKind(elem);
RegClass reg = RegClass_NoClass;
unsigned elem_width = LLVMGetIntTypeWidth(elem);
switch (elem_kind) {
case LLVMIntegerTypeKind:
case LLVMHalfTypeKind:
case LLVMIntegerTypeKind: {
unsigned elem_width = LLVMGetIntTypeWidth(elem);
switch (elem_width) {
case 8: reg = RegClass_SSEInt8; break;
case 16: reg = RegClass_SSEInt16; break;
@@ -1065,6 +1087,10 @@ namespace lbAbiAmd64SysV {
GB_PANIC("Unhandled integer width for vector type %u", elem_width);
}
break;
};
case LLVMHalfTypeKind:
reg = RegClass_SSEHv;
break;
case LLVMFloatTypeKind:
reg = RegClass_SSEFv;
break;
+30 -12
View File
@@ -1300,18 +1300,8 @@ String lb_get_objc_type_encoding(Type *t, isize pointer_depth = 0) {
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) {
if (has_type_got_objc_class_attribute(t) && pointer_depth == 0) {
return str_lit("#");
}
}
@@ -1354,6 +1344,17 @@ String lb_get_objc_type_encoding(Type *t, isize pointer_depth = 0) {
return str_lit("?");
case Type_Pointer: {
// NOTE: These types are pointers, so we must check here for special cases
// Check for objc_SEL
if (internal_check_is_assignable_to(t, t_objc_SEL)) {
return str_lit(":");
}
// Check for objc_Class
if (internal_check_is_assignable_to(t, t_objc_Class)) {
return str_lit("#");
}
String pointee = lb_get_objc_type_encoding(t->Pointer.elem, pointer_depth +1);
// Special case for Objective-C Objects
if (pointer_depth == 0 && pointee == "@") {
@@ -1645,6 +1646,21 @@ gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) {
continue;
}
// Check if it has any class methods ahead of time so that we know to grab the meta_class
lbValue meta_class_value = {};
for (const ObjcMethodData &md : *methods) {
if (!md.ac.objc_is_class_method) {
continue;
}
// Get the meta_class
args.count = 1;
args[0] = class_value;
meta_class_value = lb_emit_runtime_call(p, "object_getClass", args);
break;
}
for (const ObjcMethodData &md : *methods) {
GB_ASSERT( md.proc_entity->kind == Entity_Procedure);
Type *method_type = md.proc_entity->type;
@@ -1770,8 +1786,10 @@ gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) {
GB_ASSERT(sel_address);
lbValue selector_value = lb_addr_load(p, *sel_address);
lbValue target_class = !md.ac.objc_is_class_method ? class_value : meta_class_value;
args.count = 4;
args[0] = class_value; // Class
args[0] = target_class; // Class
args[1] = selector_value; // SEL
args[2] = lbValue { wrapper_proc->value, wrapper_proc->type };
args[3] = lb_const_value(m, t_cstring, exact_value_string(method_encoding));
+1 -1
View File
@@ -242,7 +242,7 @@ struct lbGenerator : LinkerData {
MPSCQueue<lbEntityCorrection> entities_to_correct_linkage;
MPSCQueue<lbObjCGlobal> objc_selectors;
MPSCQueue<lbObjCGlobal> objc_classes;
MPSCQueue<lbObjCGlobal> objc_ivars;
MPSCQueue<lbObjCGlobal> objc_ivars;
MPSCQueue<String> raddebug_section_strings;
};
+10 -7
View File
@@ -174,9 +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->objc_ivars, heap_allocator());
mpsc_init(&gen->raddebug_section_strings, heap_allocator());
return true;
}
@@ -2584,16 +2584,19 @@ general_end:;
if (src_size > dst_size) {
GB_ASSERT(p->decl_block != p->curr_block);
// NOTE(laytan): src is bigger than dst, need to memcpy the part of src we want.
LLVMTypeRef llvm_src_type = LLVMPointerType(src_type, 0);
LLVMTypeRef llvm_dst_type = LLVMPointerType(dst_type, 0);
LLVMValueRef val_ptr;
if (LLVMIsALoadInst(val)) {
val_ptr = LLVMGetOperand(val, 0);
} else if (LLVMIsAAllocaInst(val)) {
val_ptr = LLVMBuildPointerCast(p->builder, val, LLVMPointerType(src_type, 0), "");
val_ptr = LLVMBuildPointerCast(p->builder, val, llvm_src_type, "");
} else {
// NOTE(laytan): we need a pointer to memcpy from.
LLVMValueRef val_copy = llvm_alloca(p, src_type, src_align);
val_ptr = LLVMBuildPointerCast(p->builder, val_copy, LLVMPointerType(src_type, 0), "");
val_ptr = LLVMBuildPointerCast(p->builder, val_copy, llvm_src_type, "");
LLVMBuildStore(p->builder, val, val_ptr);
}
@@ -2601,11 +2604,11 @@ general_end:;
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), "");
LLVMValueRef nptr = LLVMBuildPointerCast(p->builder, ptr, llvm_dst_type, "");
LLVMTypeRef types[3] = {
lb_type(p->module, t_rawptr),
lb_type(p->module, t_rawptr),
llvm_dst_type,
llvm_src_type,
lb_type(p->module, t_int)
};
+287 -1
View File
@@ -1112,6 +1112,7 @@ gb_internal lbValue lb_emit_call(lbProcedure *p, lbValue value, Array<lbValue> c
lbValue result = {};
isize ignored_args = 0;
auto processed_args = array_make<lbValue>(permanent_allocator(), 0, args.count);
{
@@ -1134,6 +1135,7 @@ gb_internal lbValue lb_emit_call(lbProcedure *p, lbValue value, Array<lbValue> c
lbArgType *arg = &ft->args[param_index];
if (arg->kind == lbArg_Ignore) {
param_index += 1;
ignored_args += 1;
continue;
}
@@ -1242,7 +1244,7 @@ gb_internal lbValue lb_emit_call(lbProcedure *p, lbValue value, Array<lbValue> c
auto tuple_fix_values = slice_make<lbValue>(permanent_allocator(), ret_count);
auto tuple_geps = slice_make<lbValue>(permanent_allocator(), ret_count);
isize offset = ft->original_arg_count;
isize offset = ft->original_arg_count - ignored_args;
for (isize j = 0; j < ret_count-1; j++) {
lbValue ret_arg_ptr = processed_args[offset + j];
lbValue ret_arg = lb_emit_load(p, ret_arg_ptr);
@@ -1760,6 +1762,275 @@ gb_internal lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAn
return res;
}
case BuiltinProc_simd_runtime_swizzle:
{
LLVMValueRef src = arg0.value;
LLVMValueRef indices = lb_build_expr(p, ce->args[1]).value;
Type *vt = arg0.type;
GB_ASSERT(vt->kind == Type_SimdVector);
i64 count = vt->SimdVector.count;
Type *elem_type = vt->SimdVector.elem;
i64 elem_size = type_size_of(elem_type);
// Determine strategy based on element size and target architecture
char const *intrinsic_name = nullptr;
bool use_hardware_runtime_swizzle = false;
// 8-bit elements: Use dedicated table lookup instructions
if (elem_size == 1) {
use_hardware_runtime_swizzle = true;
if (build_context.metrics.arch == TargetArch_amd64 || build_context.metrics.arch == TargetArch_i386) {
// x86/x86-64: Use pshufb intrinsics
switch (count) {
case 16:
intrinsic_name = "llvm.x86.ssse3.pshuf.b.128";
break;
case 32:
intrinsic_name = "llvm.x86.avx2.pshuf.b";
break;
case 64:
intrinsic_name = "llvm.x86.avx512.pshuf.b.512";
break;
default:
use_hardware_runtime_swizzle = false;
break;
}
} else if (build_context.metrics.arch == TargetArch_arm64) {
// ARM64: Use NEON tbl intrinsics with automatic table splitting
switch (count) {
case 16:
intrinsic_name = "llvm.aarch64.neon.tbl1";
break;
case 32:
intrinsic_name = "llvm.aarch64.neon.tbl2";
break;
case 48:
intrinsic_name = "llvm.aarch64.neon.tbl3";
break;
case 64:
intrinsic_name = "llvm.aarch64.neon.tbl4";
break;
default:
use_hardware_runtime_swizzle = false;
break;
}
} else if (build_context.metrics.arch == TargetArch_arm32) {
// ARM32: Use NEON vtbl intrinsics with automatic table splitting
switch (count) {
case 8:
intrinsic_name = "llvm.arm.neon.vtbl1";
break;
case 16:
intrinsic_name = "llvm.arm.neon.vtbl2";
break;
case 24:
intrinsic_name = "llvm.arm.neon.vtbl3";
break;
case 32:
intrinsic_name = "llvm.arm.neon.vtbl4";
break;
default:
use_hardware_runtime_swizzle = false;
break;
}
} else if (build_context.metrics.arch == TargetArch_wasm32 || build_context.metrics.arch == TargetArch_wasm64p32) {
// WebAssembly: Use swizzle (only supports 16-byte vectors)
if (count == 16) {
intrinsic_name = "llvm.wasm.swizzle";
} else {
use_hardware_runtime_swizzle = false;
}
} else {
use_hardware_runtime_swizzle = false;
}
}
if (use_hardware_runtime_swizzle && intrinsic_name != nullptr) {
// Use dedicated hardware swizzle instruction
// Check if required target features are enabled
bool features_enabled = true;
if (build_context.metrics.arch == TargetArch_amd64 || build_context.metrics.arch == TargetArch_i386) {
// x86/x86-64 feature checking
if (count == 16) {
// SSE/SSSE3 for 128-bit vectors
if (!check_target_feature_is_enabled(str_lit("ssse3"), nullptr)) {
features_enabled = false;
}
} else if (count == 32) {
// AVX2 requires ssse3 + avx2 features
if (!check_target_feature_is_enabled(str_lit("ssse3"), nullptr) ||
!check_target_feature_is_enabled(str_lit("avx2"), nullptr)) {
features_enabled = false;
}
} else if (count == 64) {
// AVX512 requires ssse3 + avx2 + avx512f + avx512bw features
if (!check_target_feature_is_enabled(str_lit("ssse3"), nullptr) ||
!check_target_feature_is_enabled(str_lit("avx2"), nullptr) ||
!check_target_feature_is_enabled(str_lit("avx512f"), nullptr) ||
!check_target_feature_is_enabled(str_lit("avx512bw"), nullptr)) {
features_enabled = false;
}
}
} else if (build_context.metrics.arch == TargetArch_arm64 || build_context.metrics.arch == TargetArch_arm32) {
// ARM/ARM64 feature checking - NEON is required for all table/swizzle ops
if (!check_target_feature_is_enabled(str_lit("neon"), nullptr)) {
features_enabled = false;
}
}
if (features_enabled) {
// Add target features to function attributes for LLVM instruction selection
if (build_context.metrics.arch == TargetArch_amd64 || build_context.metrics.arch == TargetArch_i386) {
// x86/x86-64 function attributes
if (count == 16) {
// SSE/SSSE3 for 128-bit vectors
lb_add_attribute_to_proc_with_string(p->module, p->value, str_lit("target-features"), str_lit("+ssse3"));
lb_add_attribute_to_proc_with_string(p->module, p->value, str_lit("min-legal-vector-width"), str_lit("128"));
} else if (count == 32) {
lb_add_attribute_to_proc_with_string(p->module, p->value, str_lit("target-features"), str_lit("+avx,+avx2,+ssse3"));
lb_add_attribute_to_proc_with_string(p->module, p->value, str_lit("min-legal-vector-width"), str_lit("256"));
} else if (count == 64) {
lb_add_attribute_to_proc_with_string(p->module, p->value, str_lit("target-features"), str_lit("+avx,+avx2,+avx512f,+avx512bw,+ssse3"));
lb_add_attribute_to_proc_with_string(p->module, p->value, str_lit("min-legal-vector-width"), str_lit("512"));
}
} else if (build_context.metrics.arch == TargetArch_arm64) {
// ARM64 function attributes - enable NEON for swizzle instructions
lb_add_attribute_to_proc_with_string(p->module, p->value, str_lit("target-features"), str_lit("+neon"));
// Set appropriate vector width for multi-swizzle operations
if (count >= 32) {
lb_add_attribute_to_proc_with_string(p->module, p->value, str_lit("min-legal-vector-width"), str_lit("256"));
}
} else if (build_context.metrics.arch == TargetArch_arm32) {
// ARM32 function attributes - enable NEON for swizzle instructions
lb_add_attribute_to_proc_with_string(p->module, p->value, str_lit("target-features"), str_lit("+neon"));
}
// Handle ARM's multi-swizzle intrinsics by splitting the src vector
if (build_context.metrics.arch == TargetArch_arm64 && count > 16) {
// ARM64 TBL2/TBL3/TBL4: Split src into multiple 16-byte vectors
int num_tables = cast(int)(count / 16);
GB_ASSERT_MSG(count % 16 == 0, "ARM64 src size must be multiple of 16 bytes, got %lld bytes", count);
GB_ASSERT_MSG(num_tables <= 4, "ARM64 NEON supports maximum 4 tables (tbl4), got %d tables for %lld-byte vector", num_tables, count);
LLVMValueRef src_parts[4]; // Max 4 tables for tbl4
for (int i = 0; i < num_tables; i++) {
// Extract 16-byte slice from the larger src
LLVMValueRef indices_for_extract[16];
for (int j = 0; j < 16; j++) {
indices_for_extract[j] = LLVMConstInt(LLVMInt32TypeInContext(p->module->ctx), i * 16 + j, false);
}
LLVMValueRef extract_mask = LLVMConstVector(indices_for_extract, 16);
src_parts[i] = LLVMBuildShuffleVector(p->builder, src, LLVMGetUndef(LLVMTypeOf(src)), extract_mask, "");
}
// Call appropriate ARM64 tbl intrinsic
if (count == 32) {
LLVMValueRef args[3] = { src_parts[0], src_parts[1], indices };
res.value = lb_call_intrinsic(p, intrinsic_name, args, 3, nullptr, 0);
} else if (count == 48) {
LLVMValueRef args[4] = { src_parts[0], src_parts[1], src_parts[2], indices };
res.value = lb_call_intrinsic(p, intrinsic_name, args, 4, nullptr, 0);
} else if (count == 64) {
LLVMValueRef args[5] = { src_parts[0], src_parts[1], src_parts[2], src_parts[3], indices };
res.value = lb_call_intrinsic(p, intrinsic_name, args, 5, nullptr, 0);
}
} else if (build_context.metrics.arch == TargetArch_arm32 && count > 8) {
// ARM32 VTBL2/VTBL3/VTBL4: Split src into multiple 8-byte vectors
int num_tables = cast(int)count / 8;
GB_ASSERT_MSG(count % 8 == 0, "ARM32 src size must be multiple of 8 bytes, got %lld bytes", count);
GB_ASSERT_MSG(num_tables <= 4, "ARM32 NEON supports maximum 4 tables (vtbl4), got %d tables for %lld-byte vector", num_tables, count);
LLVMValueRef src_parts[4]; // Max 4 tables for vtbl4
for (int i = 0; i < num_tables; i++) {
// Extract 8-byte slice from the larger src
LLVMValueRef indices_for_extract[8];
for (int j = 0; j < 8; j++) {
indices_for_extract[j] = LLVMConstInt(LLVMInt32TypeInContext(p->module->ctx), i * 8 + j, false);
}
LLVMValueRef extract_mask = LLVMConstVector(indices_for_extract, 8);
src_parts[i] = LLVMBuildShuffleVector(p->builder, src, LLVMGetUndef(LLVMTypeOf(src)), extract_mask, "");
}
// Call appropriate ARM32 vtbl intrinsic
if (count == 16) {
LLVMValueRef args[3] = { src_parts[0], src_parts[1], indices };
res.value = lb_call_intrinsic(p, intrinsic_name, args, 3, nullptr, 0);
} else if (count == 24) {
LLVMValueRef args[4] = { src_parts[0], src_parts[1], src_parts[2], indices };
res.value = lb_call_intrinsic(p, intrinsic_name, args, 4, nullptr, 0);
} else if (count == 32) {
LLVMValueRef args[5] = { src_parts[0], src_parts[1], src_parts[2], src_parts[3], indices };
res.value = lb_call_intrinsic(p, intrinsic_name, args, 5, nullptr, 0);
}
} else {
// Single runtime swizzle case (x86, WebAssembly, ARM single-table)
LLVMValueRef args[2] = { src, indices };
res.value = lb_call_intrinsic(p, intrinsic_name, args, gb_count_of(args), nullptr, 0);
}
return res;
} else {
// Features not enabled, fall back to emulation
use_hardware_runtime_swizzle = false;
}
}
// Fallback: Emulate with extracts and inserts for all element sizes
GB_ASSERT(count > 0 && count <= 64); // Sanity check
LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, count);
LLVMTypeRef i32_type = LLVMInt32TypeInContext(p->module->ctx);
LLVMTypeRef elem_llvm_type = lb_type(p->module, elem_type);
// Calculate mask based on element size and vector count
i64 max_index = count - 1;
LLVMValueRef index_mask;
if (elem_size == 1) {
// 8-bit: mask to src size (like pshufb behavior)
index_mask = LLVMConstInt(elem_llvm_type, max_index, false);
} else if (elem_size == 2) {
// 16-bit: mask to src size
index_mask = LLVMConstInt(elem_llvm_type, max_index, false);
} else if (elem_size == 4) {
// 32-bit: mask to src size
index_mask = LLVMConstInt(elem_llvm_type, max_index, false);
} else {
// 64-bit: mask to src size
index_mask = LLVMConstInt(elem_llvm_type, max_index, false);
}
for (i64 i = 0; i < count; i++) {
LLVMValueRef idx_i = LLVMConstInt(i32_type, cast(unsigned)i, false);
LLVMValueRef index_elem = LLVMBuildExtractElement(p->builder, indices, idx_i, "");
// Mask index to valid range
LLVMValueRef masked_index = LLVMBuildAnd(p->builder, index_elem, index_mask, "");
// Convert to i32 for extractelement
LLVMValueRef index_i32;
if (LLVMGetIntTypeWidth(LLVMTypeOf(masked_index)) < 32) {
index_i32 = LLVMBuildZExt(p->builder, masked_index, i32_type, "");
} else if (LLVMGetIntTypeWidth(LLVMTypeOf(masked_index)) > 32) {
index_i32 = LLVMBuildTrunc(p->builder, masked_index, i32_type, "");
} else {
index_i32 = masked_index;
}
values[i] = LLVMBuildExtractElement(p->builder, src, index_i32, "");
}
// Build result vector
res.value = LLVMGetUndef(LLVMTypeOf(src));
for (i64 i = 0; i < count; i++) {
LLVMValueRef idx_i = LLVMConstInt(i32_type, cast(unsigned)i, false);
res.value = LLVMBuildInsertElement(p->builder, res.value, values[i], idx_i, "");
}
return res;
}
case BuiltinProc_simd_ceil:
case BuiltinProc_simd_floor:
case BuiltinProc_simd_trunc:
@@ -2579,6 +2850,21 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
}
return res;
}
case BuiltinProc_read_cycle_counter_frequency:
{
lbValue res = {};
res.type = tv.type;
if (build_context.metrics.arch == TargetArch_arm64) {
LLVMTypeRef func_type = LLVMFunctionType(LLVMInt64TypeInContext(p->module->ctx), nullptr, 0, false);
bool has_side_effects = false;
LLVMValueRef the_asm = llvm_get_inline_asm(func_type, str_lit("mrs $0, cntfrq_el0"), str_lit("=r"), has_side_effects);
GB_ASSERT(the_asm != nullptr);
res.value = LLVMBuildCall2(p->builder, func_type, the_asm, nullptr, 0, "");
}
return res;
}
case BuiltinProc_count_trailing_zeros:
return lb_emit_count_trailing_zeros(p, lb_build_expr(p, ce->args[0]), tv.type);
+1 -1
View File
@@ -797,7 +797,7 @@ gb_internal void lb_setup_type_info_data_giant_array(lbModule *m, i64 global_typ
u8 flags = 0;
if (t->Struct.is_packed) flags |= 1<<0;
if (t->Struct.is_raw_union) flags |= 1<<1;
if (t->Struct.is_no_copy) flags |= 1<<2;
//
if (t->Struct.custom_align) flags |= 1<<3;
vals[6] = lb_const_int(m, t_u8, flags).value;
-1
View File
@@ -687,7 +687,6 @@ gb_internal void write_type_to_canonical_string(TypeWriter *w, Type *type) {
if (type->Struct.is_packed) type_writer_appendc(w, "#packed");
if (type->Struct.is_raw_union) type_writer_appendc(w, "#raw_union");
if (type->Struct.is_no_copy) type_writer_appendc(w, "#no_copy");
if (type->Struct.custom_min_field_align != 0) type_writer_append_fmt(w, "#min_field_align(%lld)", cast(long long)type->Struct.custom_min_field_align);
if (type->Struct.custom_max_field_align != 0) type_writer_append_fmt(w, "#max_field_align(%lld)", cast(long long)type->Struct.custom_max_field_align);
if (type->Struct.custom_align != 0) type_writer_append_fmt(w, "#align(%lld)", cast(long long)type->Struct.custom_align);
+53 -25
View File
@@ -33,6 +33,10 @@ gb_internal bool ast_file_vet_deprecated(AstFile *f) {
return (ast_file_vet_flags(f) & VetFlag_Deprecated) != 0;
}
gb_internal bool ast_file_vet_explicit_allocators(AstFile *f) {
return (ast_file_vet_flags(f) & VetFlag_ExplicitAllocators) != 0;
}
gb_internal bool file_allow_newline(AstFile *f) {
bool is_strict = build_context.strict_style || ast_file_vet_style(f);
return !is_strict;
@@ -1436,27 +1440,30 @@ gb_internal CommentGroup *consume_comment_group(AstFile *f, isize n, isize *end_
}
gb_internal void consume_comment_groups(AstFile *f, Token prev) {
if (f->curr_token.kind == Token_Comment) {
CommentGroup *comment = nullptr;
isize end_line = 0;
if (f->curr_token.pos.line == prev.pos.line) {
comment = consume_comment_group(f, 0, &end_line);
if (f->curr_token.pos.line != end_line || f->curr_token.kind == Token_EOF) {
f->line_comment = comment;
}
}
end_line = -1;
while (f->curr_token.kind == Token_Comment) {
comment = consume_comment_group(f, 1, &end_line);
}
if (end_line+1 == f->curr_token.pos.line || end_line < 0) {
f->lead_comment = comment;
}
GB_ASSERT(f->curr_token.kind != Token_Comment);
if (f->curr_token.kind != Token_Comment) {
return;
}
CommentGroup *comment = nullptr;
isize end_line = 0;
if (f->curr_token.pos.line == prev.pos.line) {
comment = consume_comment_group(f, 0, &end_line);
if (f->curr_token.pos.line != end_line ||
f->curr_token.pos.line == prev.pos.line+1 ||
f->curr_token.kind == Token_EOF) {
f->line_comment = comment;
}
}
end_line = -1;
while (f->curr_token.kind == Token_Comment) {
comment = consume_comment_group(f, 1, &end_line);
}
if (end_line+1 == f->curr_token.pos.line || end_line < 0) {
f->lead_comment = comment;
}
GB_ASSERT(f->curr_token.kind != Token_Comment);
}
gb_internal gb_inline bool ignore_newlines(AstFile *f) {
@@ -5832,7 +5839,7 @@ gb_internal AstPackage *try_add_import_path(Parser *p, String path, String const
for (FileInfo fi : list) {
String name = fi.name;
String ext = path_extension(name);
if (ext == FILE_EXT && !path_is_directory(name)) {
if (ext == FILE_EXT && !fi.is_dir) {
files_with_ext += 1;
}
if (ext == FILE_EXT && !is_excluded_target_filename(name)) {
@@ -5857,7 +5864,7 @@ gb_internal AstPackage *try_add_import_path(Parser *p, String path, String const
for (FileInfo fi : list) {
String name = fi.name;
String ext = path_extension(name);
if (ext == FILE_EXT && !path_is_directory(name)) {
if (ext == FILE_EXT && !fi.is_dir) {
if (is_excluded_target_filename(name)) {
continue;
}
@@ -6255,9 +6262,10 @@ gb_internal bool parse_build_tag(Token token_for_pos, String s) {
continue;
}
Subtarget subtarget = Subtarget_Default;
Subtarget subtarget = Subtarget_Invalid;
String subtarget_str = {};
TargetOsKind os = get_target_os_from_string(p, &subtarget);
TargetOsKind os = get_target_os_from_string(p, &subtarget, &subtarget_str);
TargetArchKind arch = get_target_arch_from_string(p);
num_tokens += 1;
@@ -6268,10 +6276,29 @@ gb_internal bool parse_build_tag(Token token_for_pos, String s) {
break;
}
bool is_ios_subtarget = false;
if (subtarget == Subtarget_Invalid) {
// Special case for pseudo subtarget
if (!str_eq_ignore_case(subtarget_str, "ios")) {
syntax_error(token_for_pos, "Invalid subtarget '%.*s'.", LIT(subtarget_str));
break;
}
is_ios_subtarget = true;
}
if (os != TargetOs_Invalid) {
this_kind_os_seen = true;
bool same_subtarget = (subtarget == Subtarget_Default) || (subtarget == selected_subtarget);
// NOTE: Only testing for 'default' and not 'generic' because the 'generic' nomenclature implies any subtarget.
bool is_explicit_default_subtarget = str_eq_ignore_case(subtarget_str, "default");
bool same_subtarget = (subtarget == Subtarget_Default && !is_explicit_default_subtarget) || (subtarget == selected_subtarget);
// Special case for iPhone or iPhoneSimulator
if (is_ios_subtarget && (selected_subtarget == Subtarget_iPhone || selected_subtarget == Subtarget_iPhoneSimulator)) {
same_subtarget = true;
}
GB_ASSERT(arch == TargetArch_Invalid);
if (is_notted) {
@@ -6371,6 +6398,7 @@ gb_internal u64 parse_vet_tag(Token token_for_pos, String s) {
error_line("\textra\n");
error_line("\tcast\n");
error_line("\ttabs\n");
error_line("\texplicit-allocators\n");
return build_context.vet_flags;
}
}
+69 -16
View File
@@ -152,11 +152,11 @@ struct TypeStruct {
bool is_polymorphic;
bool are_offsets_set : 1;
bool are_offsets_being_processed : 1;
bool is_packed : 1;
bool is_raw_union : 1;
bool is_no_copy : 1;
bool is_poly_specialized : 1;
std::atomic<bool> are_offsets_being_processed;
};
struct TypeUnion {
@@ -255,7 +255,7 @@ struct TypeProc {
Slice<Entity *> variables; /* Entity_Variable */ \
i64 * offsets; \
BlockingMutex mutex; /* for settings offsets */ \
bool are_offsets_being_processed; \
std::atomic<bool> are_offsets_being_processed; \
bool are_offsets_set; \
bool is_packed; \
}) \
@@ -1780,10 +1780,6 @@ gb_internal bool is_type_raw_union(Type *t) {
t = base_type(t);
return (t->kind == Type_Struct && t->Struct.is_raw_union);
}
gb_internal bool is_type_no_copy(Type *t) {
t = base_type(t);
return (t->kind == Type_Struct && t->Struct.is_no_copy);
}
gb_internal bool is_type_enum(Type *t) {
t = base_type(t);
return (t->kind == Type_Enum);
@@ -2564,6 +2560,62 @@ gb_internal bool is_type_simple_compare(Type *t) {
return false;
}
// NOTE(bill): type can be easily compared using memcmp or contains a float
gb_internal bool is_type_nearly_simple_compare(Type *t) {
t = core_type(t);
switch (t->kind) {
case Type_Array:
return is_type_nearly_simple_compare(t->Array.elem);
case Type_EnumeratedArray:
return is_type_nearly_simple_compare(t->EnumeratedArray.elem);
case Type_Basic:
if (t->Basic.flags & (BasicFlag_SimpleCompare|BasicFlag_Numeric)) {
return true;
}
if (t->Basic.kind == Basic_typeid) {
return true;
}
return false;
case Type_Pointer:
case Type_MultiPointer:
case Type_SoaPointer:
case Type_Proc:
case Type_BitSet:
return true;
case Type_Matrix:
return is_type_nearly_simple_compare(t->Matrix.elem);
case Type_Struct:
for_array(i, t->Struct.fields) {
Entity *f = t->Struct.fields[i];
if (!is_type_nearly_simple_compare(f->type)) {
return false;
}
}
return true;
case Type_Union:
for_array(i, t->Union.variants) {
Type *v = t->Union.variants[i];
if (!is_type_nearly_simple_compare(v)) {
return false;
}
}
// make it dumb on purpose
return t->Union.variants.count == 1;
case Type_SimdVector:
return is_type_nearly_simple_compare(t->SimdVector.elem);
}
return false;
}
gb_internal bool is_type_load_safe(Type *type) {
GB_ASSERT(type != nullptr);
type = core_type(core_array_type(type));
@@ -2859,7 +2911,6 @@ gb_internal bool are_types_identical_internal(Type *x, Type *y, bool check_tuple
case Type_Struct:
if (x->Struct.is_raw_union == y->Struct.is_raw_union &&
x->Struct.is_no_copy == y->Struct.is_no_copy &&
x->Struct.fields.count == y->Struct.fields.count &&
x->Struct.is_packed == y->Struct.is_packed &&
x->Struct.soa_kind == y->Struct.soa_kind &&
@@ -4049,18 +4100,18 @@ gb_internal bool type_set_offsets(Type *t) {
if (t->kind == Type_Struct) {
MUTEX_GUARD(&t->Struct.offset_mutex);
if (!t->Struct.are_offsets_set) {
t->Struct.are_offsets_being_processed = true;
t->Struct.are_offsets_being_processed.store(true);
t->Struct.offsets = type_set_offsets_of(t->Struct.fields, t->Struct.is_packed, t->Struct.is_raw_union, t->Struct.custom_min_field_align, t->Struct.custom_max_field_align);
t->Struct.are_offsets_being_processed = false;
t->Struct.are_offsets_being_processed.store(false);
t->Struct.are_offsets_set = true;
return true;
}
} else if (is_type_tuple(t)) {
MUTEX_GUARD(&t->Tuple.mutex);
if (!t->Tuple.are_offsets_set) {
t->Tuple.are_offsets_being_processed = true;
t->Tuple.are_offsets_being_processed.store(true);
t->Tuple.offsets = type_set_offsets_of(t->Tuple.variables, t->Tuple.is_packed, false, 1, 0);
t->Tuple.are_offsets_being_processed = false;
t->Tuple.are_offsets_being_processed.store(false);
t->Tuple.are_offsets_set = true;
return true;
}
@@ -4243,9 +4294,12 @@ gb_internal i64 type_size_of_internal(Type *t, TypePath *path) {
if (path->failure) {
return FAILURE_SIZE;
}
if (t->Struct.are_offsets_being_processed && t->Struct.offsets == nullptr) {
type_path_print_illegal_cycle(path, path->path.count-1);
return FAILURE_SIZE;
{
MUTEX_GUARD(&t->Struct.offset_mutex);
if (t->Struct.are_offsets_being_processed.load() && t->Struct.offsets == nullptr) {
type_path_print_illegal_cycle(path, path->path.count-1);
return FAILURE_SIZE;
}
}
type_set_offsets(t);
GB_ASSERT(t->Struct.fields.count == 0 || t->Struct.offsets != nullptr);
@@ -4832,7 +4886,6 @@ gb_internal gbString write_type_to_string(gbString str, Type *type, bool shortha
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, " {");