aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorColin Davidson <colrdavidson@gmail.com>2025-07-28 14:24:46 -0700
committerColin Davidson <colrdavidson@gmail.com>2025-07-28 14:24:46 -0700
commitb88f9194d0d25bd5121f45eb3696b0e1725dfd41 (patch)
tree8f15e33fa41dd191f786b4ad414ab96062d96cbf /src
parent2dae1d8a4134226887a6e6a0aad0318e46e40cde (diff)
parentbe3006dbf26fbe6b51bb489f346793823968aedf (diff)
Merge remote-tracking branch 'live/master' into macharena
Diffstat (limited to 'src')
-rw-r--r--src/build_settings.cpp90
-rw-r--r--src/check_builtin.cpp111
-rw-r--r--src/check_decl.cpp196
-rw-r--r--src/check_expr.cpp111
-rw-r--r--src/check_stmt.cpp4
-rw-r--r--src/checker.cpp7
-rw-r--r--src/checker_builtin_procs.hpp6
-rw-r--r--src/gb/gb.h2
-rw-r--r--src/linker.cpp64
-rw-r--r--src/llvm_backend.cpp42
-rw-r--r--src/llvm_backend.hpp2
-rw-r--r--src/llvm_backend_general.cpp4
-rw-r--r--src/llvm_backend_proc.cpp271
-rw-r--r--src/parser.cpp72
14 files changed, 768 insertions, 214 deletions
diff --git a/src/build_settings.cpp b/src/build_settings.cpp
index ebe57bf1e..e1b9c4156 100644
--- a/src/build_settings.cpp
+++ b/src/build_settings.cpp
@@ -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) {
@@ -1892,10 +1919,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) {
diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp
index 9f9787b61..58fa44ec9 100644
--- a/src/check_builtin.cpp
+++ b/src/check_builtin.cpp
@@ -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
@@ -1150,6 +1159,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:
@@ -2324,7 +2385,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;
}
@@ -6919,6 +6984,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 = {};
diff --git a/src/check_decl.cpp b/src/check_decl.cpp
index 3d0d95556..0bacf891b 100644
--- a/src/check_decl.cpp
+++ b/src/check_decl.cpp
@@ -1001,120 +1001,128 @@ 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);
-
- auto &proc = e->type->Proc;
- Type *first_param = proc.param_count > 0 ? proc.params->Tuple.variables[0]->type : t_untyped_nil;
-
- if (!tn->TypeName.objc_is_implementation) {
- error(e->token, "@(objc_is_implement) attribute may only be applied to procedures whose class also have @(objc_is_implement) applied");
- } else if (!ac.objc_is_class_method && !(first_param->kind == Type_Pointer && internal_check_is_assignable_to(t, first_param->Pointer.elem))) {
- error(e->token, "Objective-C instance methods implementations require the first parameter to be a pointer to the class type set by @(objc_type)");
- } else if (proc.calling_convention == ProcCC_Odin && !tn->TypeName.objc_context_provider) {
- error(e->token, "Objective-C methods with Odin calling convention can only be used with classes that have @(objc_context_provider) set");
- } else if (ac.objc_is_class_method && proc.calling_convention != ProcCC_CDecl) {
- error(e->token, "Objective-C class methods (objc_is_class_method=true) that have @objc_is_implementation can only use \"c\" calling convention");
- } else if (proc.result_count > 1) {
- error(e->token, "Objective-C method implementations may return at most 1 value");
- } else {
- // 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");
- }
+ 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;
+ }
+
+ if (implement) {
+ GB_ASSERT(e->kind == Entity_Procedure);
+
+ auto &proc = e->type->Proc;
+ Type *first_param = proc.param_count > 0 ? proc.params->Tuple.variables[0]->type : t_untyped_nil;
+
+ if (!tn->TypeName.objc_is_implementation) {
+ error(e->token, "@(objc_is_implement) attribute may only be applied to procedures whose class also have @(objc_is_implement) applied");
+ } else if (!ac.objc_is_class_method && !(first_param->kind == Type_Pointer && internal_check_is_assignable_to(t, first_param->Pointer.elem))) {
+ error(e->token, "Objective-C instance methods implementations require the first parameter to be a pointer to the class type set by @(objc_type)");
+ } else if (proc.calling_convention == ProcCC_Odin && !tn->TypeName.objc_context_provider) {
+ error(e->token, "Objective-C methods with Odin calling convention can only be used with classes that have @(objc_context_provider) set");
+ } else if (ac.objc_is_class_method && proc.calling_convention != ProcCC_CDecl) {
+ error(e->token, "Objective-C class methods (objc_is_class_method=true) that have @objc_is_implementation can only use \"c\" calling convention");
+ } else if (proc.result_count > 1) {
+ error(e->token, "Objective-C method implementations may return at most 1 value");
+ } else {
+ // 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");
+ }
- ac.is_export = true;
- ac.linkage = STR_LIT("strong");
+ 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;
+ 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));
+ 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;
+ 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);
- }
+ 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.");
}
+ } 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));
+ 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 (!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->type_entries, TypeNameObjCMetadataEntry{ac.objc_name, e});
+ }
+ 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});
+ }
}
}
}
diff --git a/src/check_expr.cpp b/src/check_expr.cpp
index aa9c8837d..dd6a89e5b 100644
--- a/src/check_expr.cpp
+++ b/src/check_expr.cpp
@@ -6245,20 +6245,43 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A
for (isize i = 0; i < pt->param_count; i++) {
if (!visited[i]) {
Entity *e = pt->params->Tuple.variables[i];
+ 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) {
@@ -10312,51 +10335,47 @@ 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");
- continue;
- }
+ 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);
+ check_expr_with_type_hint(c, o, elem, et);
- if (is_constant) {
- is_constant = o->mode == Addressing_Constant;
- }
+ 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;
+ 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_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;
}
}
}
diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp
index 07801b477..7187d95b3 100644
--- a/src/check_stmt.cpp
+++ b/src/check_stmt.cpp
@@ -2778,6 +2778,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;
diff --git a/src/checker.cpp b/src/checker.cpp
index 4ebabe004..1821cbd4d 100644
--- a/src/checker.cpp
+++ b/src/checker.cpp
@@ -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));
diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp
index 90652cb0b..91cef481e 100644
--- a/src/checker_builtin_procs.hpp
+++ b/src/checker_builtin_procs.hpp
@@ -191,6 +191,7 @@ BuiltinProc__simd_begin,
BuiltinProc_simd_shuffle,
BuiltinProc_simd_select,
+ BuiltinProc_simd_runtime_swizzle,
BuiltinProc_simd_ceil,
BuiltinProc_simd_floor,
@@ -325,6 +326,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,
@@ -550,6 +553,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},
@@ -678,6 +682,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},
diff --git a/src/gb/gb.h b/src/gb/gb.h
index 6ce8626c0..ffc40b8ca 100644
--- a/src/gb/gb.h
+++ b/src/gb/gb.h
@@ -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++;
diff --git a/src/linker.cpp b/src/linker.cpp
index bf2ba6fe0..41333a3c9 100644
--- a/src/linker.cpp
+++ b/src/linker.cpp
@@ -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) {
diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp
index 3cf77256b..13a1d8cf3 100644
--- a/src/llvm_backend.cpp
+++ b/src/llvm_backend.cpp
@@ -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));
diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp
index fd6f50dcd..fef6e754d 100644
--- a/src/llvm_backend.hpp
+++ b/src/llvm_backend.hpp
@@ -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;
};
diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp
index aaa9ffd4d..64ea58578 100644
--- a/src/llvm_backend_general.cpp
+++ b/src/llvm_backend_general.cpp
@@ -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;
}
diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp
index 9f6a1d653..ffa434278 100644
--- a/src/llvm_backend_proc.cpp
+++ b/src/llvm_backend_proc.cpp
@@ -1721,6 +1721,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:
@@ -1962,7 +2231,7 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
LLVMValueRef values[2] = {};
values[0] = lb_const_string(m, file_name).value;
- values[1] = lb_const_string(m, file->data).value;
+ values[1] = lb_const_value(m, t_u8_slice, exact_value_string(file->data)).value;
LLVMValueRef element = llvm_const_named_struct(m, t_load_directory_file, values, gb_count_of(values));
elements[i] = element;
}
diff --git a/src/parser.cpp b/src/parser.cpp
index 942e83f29..1ccc3feaa 100644
--- a/src/parser.cpp
+++ b/src/parser.cpp
@@ -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;
- }
- }
+ if (f->curr_token.kind != Token_Comment) {
+ return;
+ }
+ CommentGroup *comment = nullptr;
+ isize end_line = 0;
- 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;
+ 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;
}
+ }
- GB_ASSERT(f->curr_token.kind != Token_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) {
@@ -5794,7 +5801,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)) {
@@ -5819,7 +5826,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;
}
@@ -6217,9 +6224,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;
@@ -6230,10 +6238,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) {
@@ -6333,6 +6360,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;
}
}