mirror of
https://github.com/Ed94/Odin.git
synced 2026-08-30 21:10:08 +00:00
Merge tag 'dev-2025-08'
# Conflicts: # .gitignore
This commit is contained in:
+72
-25
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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;
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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));
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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
@@ -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, " {");
|
||||
|
||||
Reference in New Issue
Block a user