Merge branch 'master' into bill/raddebugger-custom-section

This commit is contained in:
gingerBill
2025-05-22 16:04:42 +01:00
committed by GitHub
146 changed files with 11910 additions and 1692 deletions
+7 -1
View File
@@ -667,8 +667,14 @@ gb_internal void print_bug_report_help() {
gb_printf("-nightly");
#endif
String version = {};
#ifdef GIT_SHA
gb_printf(":%s", GIT_SHA);
version.text = cast(u8 *)GIT_SHA;
version.len = gb_strlen(GIT_SHA);
if (version != "") {
gb_printf(":%.*s", LIT(version));
}
#endif
gb_printf("\n");
+27 -9
View File
@@ -459,6 +459,7 @@ struct BuildContext {
bool ignore_unknown_attributes;
bool no_bounds_check;
bool no_type_assert;
bool dynamic_literals; // Opt-in to `#+feature dynamic-literals` project-wide.
bool no_output_files;
bool no_crt;
bool no_rpath;
@@ -1915,12 +1916,6 @@ gb_internal void init_build_context(TargetMetrics *cross_target, Subtarget subta
}
// TODO: Static map calls are bugged on `amd64sysv` abi.
if (bc->metrics.os != TargetOs_windows && bc->metrics.arch == TargetArch_amd64) {
// ENFORCE DYNAMIC MAP CALLS
bc->dynamic_map_calls = true;
}
bc->ODIN_VALGRIND_SUPPORT = false;
if (build_context.metrics.os != TargetOs_windows) {
switch (bc->metrics.arch) {
@@ -2214,11 +2209,34 @@ gb_internal bool init_build_paths(String init_filename) {
while (output_name.len > 0 && (output_name[output_name.len-1] == '/' || output_name[output_name.len-1] == '\\')) {
output_name.len -= 1;
}
// Only trim the extension if it's an Odin source file.
// This lets people build folders with extensions or files beginning with dots.
if (path_extension(output_name) == ".odin" && !path_is_directory(output_name)) {
output_name = remove_extension_from_path(output_name);
}
output_name = remove_directory_from_path(output_name);
output_name = remove_extension_from_path(output_name);
output_name = copy_string(ha, string_trim_whitespace(output_name));
output_path = path_from_string(ha, output_name);
// This is `path_from_string` without the extension trimming.
Path res = {};
if (output_name.len > 0) {
String fullpath = path_to_full_path(ha, output_name);
defer (gb_free(ha, fullpath.text));
res.basename = directory_from_path(fullpath);
res.basename = copy_string(ha, res.basename);
if (path_is_directory(fullpath)) {
if (res.basename.len > 0 && res.basename.text[res.basename.len - 1] == '/') {
res.basename.len--;
}
} else {
isize name_start = (res.basename.len > 0) ? res.basename.len + 1 : res.basename.len;
res.name = substring(fullpath, name_start, fullpath.len);
res.name = copy_string(ha, res.name);
}
}
output_path = res;
// Note(Dragos): This is a fix for empty filenames
// Turn the trailing folder into the file name
if (output_path.name.len == 0) {
+326 -3
View File
@@ -223,9 +223,9 @@ gb_internal void add_objc_proc_type(CheckerContext *c, Ast *call, Type *return_t
data.kind = kind;
data.proc_type = alloc_type_proc(scope, params, param_types.count, results, results->Tuple.variables.count, false, ProcCC_CDecl);
mutex_lock(&c->info->objc_types_mutex);
mutex_lock(&c->info->objc_objc_msgSend_mutex);
map_set(&c->info->objc_msgSend_types, call, data);
mutex_unlock(&c->info->objc_types_mutex);
mutex_unlock(&c->info->objc_objc_msgSend_mutex);
try_to_add_package_dependency(c, "runtime", "objc_msgSend");
try_to_add_package_dependency(c, "runtime", "objc_msgSend_fpret");
@@ -387,6 +387,59 @@ gb_internal bool check_builtin_objc_procedure(CheckerContext *c, Operand *operan
try_to_add_package_dependency(c, "runtime", "objc_allocateClassPair");
return true;
} break;
case BuiltinProc_objc_ivar_get:
{
Type *self_type = nullptr;
Operand self = {};
check_expr_or_type(c, &self, ce->args[0]);
if (!is_operand_value(self) || !check_is_assignable_to(c, &self, t_objc_id)) {
gbString e = expr_to_string(self.expr);
gbString t = type_to_string(self.type);
error(self.expr, "'%.*s' expected a type or value derived from intrinsics.objc_object, got '%s' of type %s", LIT(builtin_name), e, t);
gb_string_free(t);
gb_string_free(e);
return false;
} else if (!is_type_pointer(self.type)) {
gbString e = expr_to_string(self.expr);
gbString t = type_to_string(self.type);
error(self.expr, "'%.*s' expected a pointer of a value derived from intrinsics.objc_object, got '%s' of type %s", LIT(builtin_name), e, t);
gb_string_free(t);
gb_string_free(e);
return false;
}
self_type = type_deref(self.type);
if (!(self_type->kind == Type_Named &&
self_type->Named.type_name != nullptr &&
self_type->Named.type_name->TypeName.objc_class_name != "")) {
gbString t = type_to_string(self_type);
error(self.expr, "'%.*s' expected a named type with the attribute @(objc_class=<string>) , got type %s", LIT(builtin_name), t);
gb_string_free(t);
return false;
}
Type *ivar_type = self_type->Named.type_name->TypeName.objc_ivar;
if (ivar_type == nullptr) {
gbString t = type_to_string(self_type);
error(self.expr, "'%.*s' requires that type %s have the attribute @(objc_ivar=<ivar_type_name>).", LIT(builtin_name), t);
gb_string_free(t);
return false;
}
if (type_hint != nullptr && type_hint->kind == Type_Pointer && type_hint->Pointer.elem == ivar_type) {
operand->type = type_hint;
} else {
operand->type = alloc_type_pointer(ivar_type);
}
operand->mode = Addressing_Value;
return true;
} break;
}
}
@@ -2167,7 +2220,8 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
case BuiltinProc_objc_find_selector:
case BuiltinProc_objc_find_class:
case BuiltinProc_objc_register_selector:
case BuiltinProc_objc_register_class:
case BuiltinProc_objc_register_class:
case BuiltinProc_objc_ivar_get:
return check_builtin_objc_procedure(c, operand, call, id, type_hint);
case BuiltinProc___entry_point:
@@ -3189,6 +3243,194 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
break;
}
case BuiltinProc_compress_values: {
Operand *ops = gb_alloc_array(temporary_allocator(), Operand, ce->args.count);
isize value_count = 0;
for_array(i, ce->args) {
Ast *arg = ce->args[i];
Operand *op = &ops[i];
check_multi_expr(c, op, arg);
if (op->mode == Addressing_Invalid) {
return false;
}
if (op->type == nullptr || op->type == t_invalid) {
gbString s = expr_to_string(op->expr);
error(op->expr, "Invalid expression to '%.*s', got %s", LIT(builtin_name), s);
gb_string_free(s);
}
if (is_type_tuple(op->type)) {
value_count += op->type->Tuple.variables.count;
} else {
value_count += 1;
}
}
GB_ASSERT(value_count >= 1);
if (value_count == 1) {
*operand = ops[0];
break;
}
if (type_hint != nullptr) {
Type *th = base_type(type_hint);
if (th->kind == Type_Struct) {
if (value_count == th->Struct.fields.count) {
isize index = 0;
for_array(i, ce->args) {
Operand *op = &ops[i];
if (is_type_tuple(op->type)) {
for (Entity *v : op->type->Tuple.variables) {
Operand x = {};
x.mode = Addressing_Value;
x.type = v->type;
check_assignment(c, &x, th->Struct.fields[index++]->type, builtin_name);
if (x.mode == Addressing_Invalid) {
return false;
}
}
} else {
check_assignment(c, op, th->Struct.fields[index++]->type, builtin_name);
if (op->mode == Addressing_Invalid) {
return false;
}
}
}
operand->type = type_hint;
operand->mode = Addressing_Value;
break;
}
} else if (is_type_array_like(th)) {
if (cast(i64)value_count == get_array_type_count(th)) {
Type *elem = base_array_type(th);
for_array(i, ce->args) {
Operand *op = &ops[i];
if (is_type_tuple(op->type)) {
for (Entity *v : op->type->Tuple.variables) {
Operand x = {};
x.mode = Addressing_Value;
x.type = v->type;
check_assignment(c, &x, elem, builtin_name);
if (x.mode == Addressing_Invalid) {
return false;
}
}
} else {
check_assignment(c, op, elem, builtin_name);
if (op->mode == Addressing_Invalid) {
return false;
}
}
}
operand->type = type_hint;
operand->mode = Addressing_Value;
break;
}
}
}
bool all_types_the_same = true;
Type *last_type = nullptr;
for_array(i, ce->args) {
Operand *op = &ops[i];
if (is_type_tuple(op->type)) {
if (last_type == nullptr) {
op->type->Tuple.variables[0]->type;
}
for (Entity *v : op->type->Tuple.variables) {
if (!are_types_identical(last_type, v->type)) {
all_types_the_same = false;
break;
}
last_type = v->type;
}
} else {
if (last_type == nullptr) {
last_type = op->type;
} else {
if (!are_types_identical(last_type, op->type)) {
all_types_the_same = false;
break;
}
last_type = op->type;
}
}
}
if (all_types_the_same) {
Type *elem_type = default_type(last_type);
if (is_type_untyped(elem_type)) {
gbString s = expr_to_string(call);
error(call, "Invalid use of '%s' in '%.*s'", s, LIT(builtin_name));
gb_string_free(s);
return false;
}
operand->type = alloc_type_array(elem_type, value_count);
operand->mode = Addressing_Value;
} else {
Type *st = alloc_type_struct_complete();
st->Struct.fields = slice_make<Entity *>(permanent_allocator(), value_count);
st->Struct.tags = gb_alloc_array(permanent_allocator(), String, value_count);
st->Struct.offsets = gb_alloc_array(permanent_allocator(), i64, value_count);
Scope *scope = create_scope(c->info, nullptr);
Token token = {};
token.kind = Token_Ident;
token.pos = ast_token(call).pos;
isize index = 0;
for_array(i, ce->args) {
Operand *op = &ops[i];
if (is_type_tuple(op->type)) {
for (Entity *v : op->type->Tuple.variables) {
Type *t = default_type(v->type);
if (is_type_untyped(t)) {
gbString s = expr_to_string(op->expr);
error(op->expr, "Invalid use of '%s' in '%.*s'", s, LIT(builtin_name));
gb_string_free(s);
return false;
}
gbString s = gb_string_make_reserve(permanent_allocator(), 32);
s = gb_string_append_fmt(s, "v%lld", cast(long long)index);
token.string = make_string_c(s);
Entity *e = alloc_entity_field(scope, token, t, false, cast(i32)index, EntityState_Resolved);
st->Struct.fields[index++] = e;
}
} else {
Type *t = default_type(op->type);
if (is_type_untyped(t)) {
gbString s = expr_to_string(op->expr);
error(op->expr, "Invalid use of '%s' in '%.*s'", s, LIT(builtin_name));
gb_string_free(s);
return false;
}
gbString s = gb_string_make_reserve(permanent_allocator(), 32);
s = gb_string_append_fmt(s, "v%lld", cast(long long)index);
token.string = make_string_c(s);
Entity *e = alloc_entity_field(scope, token, t, false, cast(i32)index, EntityState_Resolved);
st->Struct.fields[index++] = e;
}
}
gb_unused(type_size_of(st));
operand->type = st;
operand->mode = Addressing_Value;
}
break;
}
case BuiltinProc_min: {
// min :: proc($T: typeid) -> ordered
// min :: proc(a: ..ordered) -> ordered
@@ -5635,6 +5877,87 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
}
operand->mode = Addressing_Type;
break;
case BuiltinProc_type_integer_to_unsigned:
if (operand->mode != Addressing_Type) {
error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name));
return false;
}
if (is_type_polymorphic(operand->type)) {
gbString t = type_to_string(operand->type);
error(operand->expr, "Expected a non-polymorphic type for '%.*s', got %s", LIT(builtin_name), t);
gb_string_free(t);
return false;
}
{
Type *bt = base_type(operand->type);
if (bt->kind != Type_Basic ||
(bt->Basic.flags & BasicFlag_Unsigned) != 0 ||
(bt->Basic.flags & BasicFlag_Integer) == 0) {
gbString t = type_to_string(operand->type);
error(operand->expr, "Expected a signed integer type for '%.*s', got %s", LIT(builtin_name), t);
gb_string_free(t);
return false;
}
if ((bt->Basic.flags & BasicFlag_Untyped) != 0) {
gbString t = type_to_string(operand->type);
error(operand->expr, "Expected a non-untyped integer type for '%.*s', got %s", LIT(builtin_name), t);
gb_string_free(t);
return false;
}
Type *u_type = &basic_types[bt->Basic.kind + 1];
operand->type = u_type;
}
break;
case BuiltinProc_type_integer_to_signed:
if (operand->mode != Addressing_Type) {
error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name));
return false;
}
if (is_type_polymorphic(operand->type)) {
gbString t = type_to_string(operand->type);
error(operand->expr, "Expected a non-polymorphic type for '%.*s', got %s", LIT(builtin_name), t);
gb_string_free(t);
return false;
}
{
Type *bt = base_type(operand->type);
if (bt->kind != Type_Basic ||
(bt->Basic.flags & BasicFlag_Unsigned) == 0 ||
(bt->Basic.flags & BasicFlag_Integer) == 0) {
gbString t = type_to_string(operand->type);
error(operand->expr, "Expected an unsigned integer type for '%.*s', got %s", LIT(builtin_name), t);
gb_string_free(t);
return false;
}
if ((bt->Basic.flags & BasicFlag_Untyped) != 0) {
gbString t = type_to_string(operand->type);
error(operand->expr, "Expected a non-untyped integer type for '%.*s', got %s", LIT(builtin_name), t);
gb_string_free(t);
return false;
}
if (bt->Basic.kind == Basic_uintptr) {
gbString t = type_to_string(operand->type);
error(operand->expr, "Type %s does not have a signed integer mapping for '%.*s'", t, LIT(builtin_name));
gb_string_free(t);
return false;
}
Type *u_type = &basic_types[bt->Basic.kind - 1];
operand->type = u_type;
}
break;
case BuiltinProc_type_merge:
{
operand->mode = Addressing_Type;
+177 -36
View File
@@ -524,12 +524,90 @@ gb_internal void check_type_decl(CheckerContext *ctx, Entity *e, Ast *init_expr,
if (decl != nullptr) {
AttributeContext ac = {};
check_decl_attributes(ctx, decl->attributes, type_decl_attribute, &ac);
if (e->kind == Entity_TypeName && ac.objc_class != "") {
e->TypeName.objc_class_name = ac.objc_class;
if (ac.objc_is_implementation) {
e->TypeName.objc_is_implementation = ac.objc_is_implementation;
e->TypeName.objc_superclass = ac.objc_superclass;
e->TypeName.objc_ivar = ac.objc_ivar;
e->TypeName.objc_context_provider = ac.objc_context_provider;
mutex_lock(&ctx->info->objc_class_name_mutex);
bool class_exists = string_set_update(&ctx->info->obcj_class_name_set, ac.objc_class);
mutex_unlock(&ctx->info->objc_class_name_mutex);
if (class_exists) {
error(e->token, "@(objc_class) name '%.*s' has already been used elsewhere", LIT(ac.objc_class));
}
mpsc_enqueue(&ctx->info->objc_class_implementations, e);
GB_ASSERT(e->TypeName.objc_ivar == nullptr || e->TypeName.objc_ivar->kind == Type_Named);
// Enqueue the contex_provider proc to be checked after it is resolved
if (e->TypeName.objc_context_provider != nullptr) {
mpsc_enqueue(&ctx->checker->procs_with_objc_context_provider_to_check, e);
}
// TODO(harold): I think there's a Check elsewhere in the checker for checking cycles.
// See about moving this to the right location.
// Ensure superclass hierarchy are all Objective-C classes and does not cycle
// NOTE(harold): We check for superclass unconditionally (before checking if super is null)
// because this should be the case 99.99% of the time. Not subclassing something that
// is, or is the child of, NSObject means the objc runtime messaging will not properly work on this type.
TypeSet super_set{};
type_set_init(&super_set, 8);
defer (type_set_destroy(&super_set));
type_set_update(&super_set, e->type);
Type *super = ac.objc_superclass;
while (super != nullptr) {
if (type_set_update(&super_set, super)) {
error(e->token, "@(objc_superclass) Superclass hierarchy cycle encountered");
break;
}
check_single_global_entity(ctx->checker, super->Named.type_name, super->Named.type_name->decl_info);
if (super->kind != Type_Named) {
error(e->token, "@(objc_superclass) Referenced type must be a named struct");
break;
}
Type* named_type = base_named_type(super);
GB_ASSERT(named_type->kind == Type_Named);
if (!is_type_objc_object(named_type)) {
error(e->token, "@(objc_superclass) Superclass '%.*s' must be an Objective-C class", LIT(named_type->Named.name));
break;
}
if (named_type->Named.type_name->TypeName.objc_class_name == "") {
error(e->token, "@(objc_superclass) Superclass '%.*s' must have a valid @(objc_class) attribute", LIT(named_type->Named.name));
break;
}
super = named_type->Named.type_name->TypeName.objc_superclass;
}
} else {
if (ac.objc_superclass != nullptr) {
error(e->token, "@(objc_superclass) may only be applied when the @(obj_implement) attribute is also applied");
} else if (ac.objc_ivar != nullptr) {
error(e->token, "@(objc_ivar) may only be applied when the @(obj_implement) attribute is also applied");
} else if (ac.objc_context_provider != nullptr) {
error(e->token, "@(objc_context_provider) may only be applied when the @(obj_implement) attribute is also applied");
}
}
if (type_size_of(e->type) > 0) {
error(e->token, "@(objc_class) marked type must be of zero size");
}
} else if (ac.objc_is_implementation) {
error(e->token, "@(objc_implement) may only be applied when the @(objc_class) attribute is also applied");
}
}
@@ -922,7 +1000,7 @@ gb_internal String handle_link_name(CheckerContext *ctx, Token token, String lin
}
gb_internal void check_objc_methods(CheckerContext *ctx, Entity *e, AttributeContext const &ac) {
gb_internal void check_objc_methods(CheckerContext *ctx, Entity *e, AttributeContext &ac) {
if (!(ac.objc_name.len || ac.objc_is_class_method || ac.objc_type)) {
return;
}
@@ -934,48 +1012,107 @@ gb_internal void check_objc_methods(CheckerContext *ctx, Entity *e, AttributeCon
error(e->token, "@(objc_name) is required with @(objc_type)");
} else {
Type *t = ac.objc_type;
if (t->kind == Type_Named) {
Entity *tn = t->Named.type_name;
GB_ASSERT(tn->kind == Entity_TypeName);
GB_ASSERT(t->kind == Type_Named); // NOTE(harold): This is already checked for at the attribute resolution stage.
Entity *tn = t->Named.type_name;
if (tn->scope != e->scope) {
error(e->token, "@(objc_name) attribute may only be applied to procedures and types within the same scope");
} else {
mutex_lock(&global_type_name_objc_metadata_mutex);
defer (mutex_unlock(&global_type_name_objc_metadata_mutex));
GB_ASSERT(tn->kind == Entity_TypeName);
if (!tn->TypeName.objc_metadata) {
tn->TypeName.objc_metadata = create_type_name_obj_c_metadata();
}
auto *md = tn->TypeName.objc_metadata;
mutex_lock(md->mutex);
defer (mutex_unlock(md->mutex));
if (tn->scope != e->scope) {
error(e->token, "@(objc_name) attribute may only be applied to procedures and types within the same scope");
} else {
if (!ac.objc_is_class_method) {
bool ok = true;
for (TypeNameObjCMetadataEntry const &entry : md->value_entries) {
if (entry.name == ac.objc_name) {
error(e->token, "Previous declaration of @(objc_name=\"%.*s\")", LIT(ac.objc_name));
ok = false;
break;
}
}
if (ok) {
array_add(&md->value_entries, TypeNameObjCMetadataEntry{ac.objc_name, e});
}
// Enable implementation by default if the class is an implementer too and
// @objc_implement was not set to false explicitly in this proc.
bool implement = tn->TypeName.objc_is_implementation;
if (ac.objc_is_disabled_implement) {
implement = false;
}
if (implement) {
GB_ASSERT(e->kind == Entity_Procedure);
auto &proc = e->type->Proc;
Type *first_param = proc.param_count > 0 ? proc.params->Tuple.variables[0]->type : t_untyped_nil;
if (!tn->TypeName.objc_is_implementation) {
error(e->token, "@(objc_is_implement) attribute may only be applied to procedures whose class also have @(objc_is_implement) applied");
} else if (!ac.objc_is_class_method && !(first_param->kind == Type_Pointer && internal_check_is_assignable_to(t, first_param->Pointer.elem))) {
error(e->token, "Objective-C instance methods implementations require the first parameter to be a pointer to the class type set by @(objc_type)");
} else if (proc.calling_convention == ProcCC_Odin && !tn->TypeName.objc_context_provider) {
error(e->token, "Objective-C methods with Odin calling convention can only be used with classes that have @(objc_context_provider) set");
} else if (ac.objc_is_class_method && proc.calling_convention != ProcCC_CDecl) {
error(e->token, "Objective-C class methods (objc_is_class_method=true) that have @objc_is_implementation can only use \"c\" calling convention");
} else if (proc.result_count > 1) {
error(e->token, "Objective-C method implementations may return at most 1 value");
} else {
bool ok = true;
for (TypeNameObjCMetadataEntry const &entry : md->type_entries) {
if (entry.name == ac.objc_name) {
error(e->token, "Previous declaration of @(objc_name=\"%.*s\")", LIT(ac.objc_name));
ok = false;
break;
}
// Always export unconditionally
// NOTE(harold): This means check_objc_methods() MUST be called before
// e->Procedure.is_export is set in check_proc_decl()!
if (ac.is_export) {
error(e->token, "Explicit export not allowed when @(objc_implement) is set. It set exported implicitly");
}
if (ok) {
array_add(&md->type_entries, TypeNameObjCMetadataEntry{ac.objc_name, e});
if (ac.link_name != "") {
error(e->token, "Explicit linkage not allowed when @(objc_implement) is set. It set to \"strong\" implicitly");
}
ac.is_export = true;
ac.linkage = STR_LIT("strong");
auto method = ObjcMethodData{ ac, e };
method.ac.objc_selector = ac.objc_selector != "" ? ac.objc_selector : ac.objc_name;
CheckerInfo *info = ctx->info;
mutex_lock(&info->objc_method_mutex);
defer (mutex_unlock(&info->objc_method_mutex));
Array<ObjcMethodData>* method_list = map_get(&info->objc_method_implementations, t);
if (method_list) {
array_add(method_list, method);
} else {
auto list = array_make<ObjcMethodData>(permanent_allocator(), 1, 8);
list[0] = method;
map_set(&info->objc_method_implementations, t, list);
}
}
} else if (ac.objc_selector != "") {
error(e->token, "@(objc_selector) may only be applied to procedures that are Objective-C implementations.");
}
mutex_lock(&global_type_name_objc_metadata_mutex);
defer (mutex_unlock(&global_type_name_objc_metadata_mutex));
if (!tn->TypeName.objc_metadata) {
tn->TypeName.objc_metadata = create_type_name_obj_c_metadata();
}
auto *md = tn->TypeName.objc_metadata;
mutex_lock(md->mutex);
defer (mutex_unlock(md->mutex));
if (!ac.objc_is_class_method) {
bool ok = true;
for (TypeNameObjCMetadataEntry const &entry : md->value_entries) {
if (entry.name == ac.objc_name) {
error(e->token, "Previous declaration of @(objc_name=\"%.*s\")", LIT(ac.objc_name));
ok = false;
break;
}
}
if (ok) {
array_add(&md->value_entries, TypeNameObjCMetadataEntry{ac.objc_name, e});
}
} else {
bool ok = true;
for (TypeNameObjCMetadataEntry const &entry : md->type_entries) {
if (entry.name == ac.objc_name) {
error(e->token, "Previous declaration of @(objc_name=\"%.*s\")", LIT(ac.objc_name));
ok = false;
break;
}
}
if (ok) {
array_add(&md->type_entries, TypeNameObjCMetadataEntry{ac.objc_name, e});
}
}
}
@@ -1145,6 +1282,9 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
break;
}
// NOTE(harold): For Objective-C method implementations, this must happen after
// check_objc_methods() is called as it re-sets ac.is_export to true unconditionally.
// The same is true for the linkage, set below.
e->Procedure.entry_point_only = ac.entry_point_only;
e->Procedure.is_export = ac.is_export;
@@ -1245,6 +1385,7 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
}
}
// NOTE(harold): See export/linkage note above(where is_export is assigned) regarding Objective-C method implementations
bool is_foreign = e->Procedure.is_foreign;
bool is_export = e->Procedure.is_export;
+42 -16
View File
@@ -643,7 +643,7 @@ gb_internal bool find_or_generate_polymorphic_procedure(CheckerContext *old_c, E
gb_internal bool check_polymorphic_procedure_assignment(CheckerContext *c, Operand *operand, Type *type, Ast *poly_def_node, PolyProcData *poly_proc_data) {
if (operand->expr == nullptr) return false;
Entity *base_entity = entity_of_node(operand->expr);
Entity *base_entity = entity_from_expr(operand->expr);
if (base_entity == nullptr) return false;
return find_or_generate_polymorphic_procedure(c, base_entity, type, nullptr, poly_def_node, poly_proc_data);
}
@@ -995,14 +995,34 @@ gb_internal i64 assign_score_function(i64 distance, bool is_variadic=false) {
gb_internal bool check_is_assignable_to_with_score(CheckerContext *c, Operand *operand, Type *type, i64 *score_, bool is_variadic=false, bool allow_array_programming=true) {
i64 score = 0;
i64 distance = check_distance_between_types(c, operand, type, allow_array_programming);
bool ok = distance >= 0;
if (ok) {
score = assign_score_function(distance, is_variadic);
if (c == nullptr) {
GB_ASSERT(operand->mode == Addressing_Value);
GB_ASSERT(is_type_typed(operand->type));
}
if (score_) *score_ = score;
return ok;
if (operand->mode == Addressing_Invalid || type == t_invalid) {
if (score_) *score_ = 0;
return false;
}
// Handle polymorphic procedure used as default parameter
if (operand->mode == Addressing_Value && is_type_proc(type) && is_type_proc(operand->type)) {
Entity *e = entity_from_expr(operand->expr);
if (e != nullptr && e->kind == Entity_Procedure && is_type_polymorphic(e->type) && !is_type_polymorphic(type)) {
// Special case: Allow a polymorphic procedure to be used as default value for concrete proc type
// during the initial check. It will be properly instantiated when actually used.
if (score_) *score_ = assign_score_function(1);
return true;
}
}
i64 score = check_distance_between_types(c, operand, type, allow_array_programming);
if (score >= 0) {
if (score_) *score_ = assign_score_function(score, is_variadic);
return true;
}
if (score_) *score_ = 0;
return false;
}
@@ -5441,8 +5461,18 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod
}
}
if (operand->type && is_type_simd_vector(type_deref(operand->type))) {
String field_name = selector->Ident.token.string;
if (field_name.len == 1) {
error(op_expr, "Extracting an element from a #simd array using .%.*s syntax is disallowed, prefer `simd.extract`", LIT(field_name));
} else {
error(op_expr, "Extracting elements from a #simd array using .%.*s syntax is disallowed, prefer `swizzle`", LIT(field_name));
}
return nullptr;
}
if (entity == nullptr && selector->kind == Ast_Ident && operand->type != nullptr &&
(is_type_array(type_deref(operand->type)) || is_type_simd_vector(type_deref(operand->type)))) {
(is_type_array(type_deref(operand->type)))) {
String field_name = selector->Ident.token.string;
if (1 < field_name.len && field_name.len <= 4) {
u8 swizzles_xyzw[4] = {'x', 'y', 'z', 'w'};
@@ -5497,7 +5527,7 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod
Type *original_type = operand->type;
Type *array_type = base_type(type_deref(original_type));
GB_ASSERT(array_type->kind == Type_Array || array_type->kind == Type_SimdVector);
GB_ASSERT(array_type->kind == Type_Array);
i64 array_count = get_array_type_count(array_type);
@@ -5538,10 +5568,6 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod
break;
}
if (array_type->kind == Type_SimdVector) {
operand->mode = Addressing_Value;
}
Entity *swizzle_entity = alloc_entity_variable(nullptr, make_token_ident(field_name), operand->type, EntityState_Resolved);
add_type_and_value(c, operand->expr, operand->mode, operand->type, operand->value);
return swizzle_entity;
@@ -9413,7 +9439,7 @@ gb_internal bool is_expr_inferred_fixed_array(Ast *type_expr) {
}
gb_internal bool check_for_dynamic_literals(CheckerContext *c, Ast *node, AstCompoundLit *cl) {
if (cl->elems.count > 0 && (check_feature_flags(c, node) & OptInFeatureFlag_DynamicLiterals) == 0) {
if (cl->elems.count > 0 && (check_feature_flags(c, node) & OptInFeatureFlag_DynamicLiterals) == 0 && !build_context.dynamic_literals) {
ERROR_BLOCK();
error(node, "Compound literals of dynamic types are disabled by default");
error_line("\tSuggestion: If you want to enable them for this specific file, add '#+feature dynamic-literals' at the top of the file\n");
@@ -10996,7 +11022,7 @@ gb_internal ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast
return kind;
case_end;
case_ast_node(i, Implicit, node)
case_ast_node(i, Implicit, node);
switch (i->kind) {
case Token_context:
{
+2
View File
@@ -2108,10 +2108,12 @@ gb_internal void check_value_decl_stmt(CheckerContext *ctx, Ast *node, u32 mod_f
if (init_type == nullptr) {
init_type = t_invalid;
} else if (is_type_polymorphic(base_type(init_type))) {
/* DISABLED: This error seems too aggressive for instantiated generic types.
gbString str = type_to_string(init_type);
error(vd->type, "Invalid use of a polymorphic type '%s' in variable declaration", str);
gb_string_free(str);
init_type = t_invalid;
*/
}
if (init_type == t_invalid && entity_count == 1 && (mod_flags & (Stmt_BreakAllowed|Stmt_FallthroughAllowed))) {
Entity *e = entities[0];
+10 -1
View File
@@ -1910,9 +1910,18 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para
case ParameterValue_Location:
case ParameterValue_Expression:
case ParameterValue_Value:
// Special case for polymorphic procedures as default values
if (param_value.ast_value != nullptr) {
Entity *e = entity_from_expr(param_value.ast_value);
if (e != nullptr && e->kind == Entity_Procedure && is_type_polymorphic(e->type)) {
// Allow polymorphic procedures as default parameter values
// The type will be correctly determined at call site
break;
}
}
gbString str = type_to_string(type);
error(params[i], "A default value for a parameter must not be a polymorphic constant type, got %s", str);
gb_string_free(str);
gb_string_free(str);
break;
}
}
+135 -2
View File
@@ -728,12 +728,17 @@ gb_internal void check_scope_usage_internal(Checker *c, Scope *scope, u64 vet_fl
bool is_unused = false;
if (vet_unused && check_vet_unused(c, e, &ve_unused)) {
is_unused = true;
} else if (vet_unused_procedures &&
e->kind == Entity_Procedure) {
} else if (vet_unused_procedures && e->kind == Entity_Procedure) {
if (e->flags&EntityFlag_Used) {
is_unused = false;
} else if (e->flags & EntityFlag_Require) {
is_unused = false;
} else if (e->flags & EntityFlag_Init) {
is_unused = false;
} else if (e->flags & EntityFlag_Fini) {
is_unused = false;
} else if (e->Procedure.is_export) {
is_unused = false;
} else if (e->pkg && e->pkg->kind == Package_Init && e->token.string == "main") {
is_unused = false;
} else {
@@ -1351,10 +1356,12 @@ gb_internal void init_universal(void) {
t_objc_object = add_global_type_name(intrinsics_pkg->scope, str_lit("objc_object"), alloc_type_struct_complete());
t_objc_selector = add_global_type_name(intrinsics_pkg->scope, str_lit("objc_selector"), alloc_type_struct_complete());
t_objc_class = add_global_type_name(intrinsics_pkg->scope, str_lit("objc_class"), alloc_type_struct_complete());
t_objc_ivar = add_global_type_name(intrinsics_pkg->scope, str_lit("objc_ivar"), alloc_type_struct_complete());
t_objc_id = alloc_type_pointer(t_objc_object);
t_objc_SEL = alloc_type_pointer(t_objc_selector);
t_objc_Class = alloc_type_pointer(t_objc_class);
t_objc_Ivar = alloc_type_pointer(t_objc_ivar);
}
}
@@ -1387,6 +1394,10 @@ gb_internal void init_checker_info(CheckerInfo *i) {
array_init(&i->defineables, a);
map_init(&i->objc_msgSend_types);
mpsc_init(&i->objc_class_implementations, a);
string_set_init(&i->obcj_class_name_set, 0);
map_init(&i->objc_method_implementations);
string_map_init(&i->load_file_cache);
array_init(&i->all_procedures, heap_allocator());
@@ -1497,6 +1508,8 @@ gb_internal void init_checker(Checker *c) {
TIME_SECTION("init proc queues");
mpsc_init(&c->procs_with_deferred_to_check, a); //, 1<<10);
mpsc_init(&c->procs_with_objc_context_provider_to_check, a);
// NOTE(bill): 1 Mi elements should be enough on average
array_init(&c->procs_to_check, heap_allocator(), 0, 1<<20);
@@ -3662,6 +3675,33 @@ gb_internal DECL_ATTRIBUTE_PROC(proc_decl_attribute) {
}
}
return true;
} else if (name == "objc_implement") {
ExactValue ev = check_decl_attribute_value(c, value);
if (ev.kind == ExactValue_Bool) {
ac->objc_is_implementation = ev.value_bool;
if (!ac->objc_is_implementation) {
ac->objc_is_disabled_implement = true;
}
} else if (ev.kind == ExactValue_Invalid) {
ac->objc_is_implementation = true;
} else {
error(elem, "Expected a boolean value, or no value, for '%.*s'", LIT(name));
}
return true;
} else if (name == "objc_selector") {
ExactValue ev = check_decl_attribute_value(c, value);
if (ev.kind == ExactValue_String) {
if (string_is_valid_identifier(ev.value_string)) {
ac->objc_selector = ev.value_string;
} else {
error(elem, "Invalid identifier for '%.*s', got '%.*s'", LIT(name), LIT(ev.value_string));
}
} else {
error(elem, "Expected a string value for '%.*s'", LIT(name));
}
return true;
} else if (name == "require_target_feature") {
ExactValue ev = check_decl_attribute_value(c, value);
if (ev.kind == ExactValue_String) {
@@ -3907,6 +3947,51 @@ gb_internal DECL_ATTRIBUTE_PROC(type_decl_attribute) {
ac->objc_class = ev.value_string;
}
return true;
} else if (name == "objc_implement") {
ExactValue ev = check_decl_attribute_value(c, value);
if (ev.kind == ExactValue_Bool) {
ac->objc_is_implementation = ev.value_bool;
} else if (ev.kind == ExactValue_Invalid) {
ac->objc_is_implementation = true;
} else {
error(elem, "Expected a boolean value, or no value, for '%.*s'", LIT(name));
}
return true;
} else if (name == "objc_superclass") {
Type *objc_superclass = check_type(c, value);
if (objc_superclass != nullptr) {
ac->objc_superclass = objc_superclass;
} else {
error(value, "'%.*s' expected a named type", LIT(name));
}
return true;
} else if (name == "objc_ivar") {
Type *objc_ivar = check_type(c, value);
if (objc_ivar != nullptr && objc_ivar->kind == Type_Named) {
ac->objc_ivar = objc_ivar;
} else {
error(value, "'%.*s' expected a named type", LIT(name));
}
return true;
} else if (name == "objc_context_provider") {
Operand o = {};
check_expr(c, &o, value);
Entity *e = entity_of_node(o.expr);
if (e != nullptr) {
if (ac->objc_context_provider != nullptr) {
error(elem, "Previous usage of a 'objc_context_provider' attribute");
}
if (e->kind != Entity_Procedure) {
error(elem, "'objc_context_provider' must refer to a procedure");
} else {
ac->objc_context_provider = e;
}
return true;
}
}
return false;
}
@@ -6240,6 +6325,12 @@ gb_internal void check_deferred_procedures(Checker *c) {
continue;
}
if (dst->flags & EntityFlag_Disabled) {
// Prevent procedures that have been disabled from acting as deferrals.
src->Procedure.deferred_procedure = {};
continue;
}
GB_ASSERT(is_type_proc(src->type));
GB_ASSERT(is_type_proc(dst->type));
Type *src_params = base_type(src->type)->Proc.params;
@@ -6395,6 +6486,44 @@ gb_internal void check_deferred_procedures(Checker *c) {
}
gb_internal void check_objc_context_provider_procedures(Checker *c) {
for (Entity *e = nullptr; mpsc_dequeue(&c->procs_with_objc_context_provider_to_check, &e); /**/) {
GB_ASSERT(e->kind == Entity_TypeName);
Entity *proc_entity = e->TypeName.objc_context_provider;
GB_ASSERT(proc_entity->kind == Entity_Procedure);
auto &proc = proc_entity->type->Proc;
Type *return_type = proc.result_count != 1 ? t_untyped_nil : base_named_type(proc.results->Tuple.variables[0]->type);
if (return_type != t_context) {
error(proc_entity->token, "The @(objc_context_provider) procedure must only return a context.");
}
const char *self_param_err = "The @(objc_context_provider) procedure must take as a parameter a single pointer to the @(objc_type) value.";
if (proc.param_count != 1) {
error(proc_entity->token, self_param_err);
}
Type *self_param = base_type(proc.params->Tuple.variables[0]->type);
if (self_param->kind != Type_Pointer) {
error(proc_entity->token, self_param_err);
}
Type *self_type = base_named_type(self_param->Pointer.elem);
if (!internal_check_is_assignable_to(self_type, e->type) &&
!(e->TypeName.objc_ivar && internal_check_is_assignable_to(self_type, e->TypeName.objc_ivar))) {
error(proc_entity->token, self_param_err);
}
if (proc.calling_convention != ProcCC_CDecl && proc.calling_convention != ProcCC_Contextless) {
error(e->token, self_param_err);
}
if (proc.is_polymorphic) {
error(e->token, self_param_err);
}
}
}
gb_internal void check_unique_package_names(Checker *c) {
ERROR_BLOCK();
@@ -6555,6 +6684,7 @@ gb_internal void check_update_dependency_tree_for_procedures(Checker *c) {
}
}
gb_internal void check_parsed_files(Checker *c) {
TIME_SECTION("map full filepaths to scope");
add_type_info_type(&c->builtin_ctx, t_invalid);
@@ -6664,6 +6794,9 @@ gb_internal void check_parsed_files(Checker *c) {
TIME_SECTION("check deferred procedures");
check_deferred_procedures(c);
TIME_SECTION("check objc context provider procedures");
check_objc_context_provider_procedures(c);
TIME_SECTION("calculate global init order");
calculate_global_init_order(c);
+22 -2
View File
@@ -149,8 +149,14 @@ struct AttributeContext {
String objc_class;
String objc_name;
bool objc_is_class_method;
String objc_selector;
Type * objc_type;
Type * objc_superclass;
Type * objc_ivar;
Entity *objc_context_provider;
bool objc_is_class_method;
bool objc_is_implementation; // This struct or proc provides a class/method implementation, not a binding to an existing type.
bool objc_is_disabled_implement; // This means the method explicitly set @objc_implement to false so it won't be inferred from the class' attribute.
String require_target_feature; // required by the target micro-architecture
String enable_target_feature; // will be enabled for the procedure only
@@ -366,6 +372,11 @@ struct ObjcMsgData {
Type *proc_type;
};
struct ObjcMethodData {
AttributeContext ac;
Entity *proc_entity;
};
enum LoadFileTier {
LoadFileTier_Invalid,
LoadFileTier_Exists,
@@ -477,9 +488,17 @@ struct CheckerInfo {
MPSCQueue<Ast *> intrinsics_entry_point_usage;
BlockingMutex objc_types_mutex;
BlockingMutex objc_objc_msgSend_mutex;
PtrMap<Ast *, ObjcMsgData> objc_msgSend_types;
BlockingMutex objc_class_name_mutex;
StringSet obcj_class_name_set;
MPSCQueue<Entity *> objc_class_implementations;
BlockingMutex objc_method_mutex;
PtrMap<Type *, Array<ObjcMethodData>> objc_method_implementations;
BlockingMutex load_file_mutex;
StringMap<LoadFileCache *> load_file_cache;
@@ -556,6 +575,7 @@ struct Checker {
CheckerContext builtin_ctx;
MPSCQueue<Entity *> procs_with_deferred_to_check;
MPSCQueue<Entity *> procs_with_objc_context_provider_to_check;
Array<ProcInfo *> procs_to_check;
BlockingMutex nested_proc_lits_mutex;
+10
View File
@@ -26,6 +26,7 @@ enum BuiltinProcId {
BuiltinProc_conj,
BuiltinProc_expand_values,
BuiltinProc_compress_values,
BuiltinProc_min,
BuiltinProc_max,
@@ -234,6 +235,9 @@ BuiltinProc__type_begin,
BuiltinProc_type_convert_variants_to_pointers,
BuiltinProc_type_merge,
BuiltinProc_type_integer_to_unsigned,
BuiltinProc_type_integer_to_signed,
BuiltinProc__type_simple_boolean_begin,
BuiltinProc_type_is_boolean,
BuiltinProc_type_is_integer,
@@ -338,6 +342,7 @@ BuiltinProc__type_end,
BuiltinProc_objc_find_class,
BuiltinProc_objc_register_selector,
BuiltinProc_objc_register_class,
BuiltinProc_objc_ivar_get,
BuiltinProc_constant_utf16_cstring,
@@ -375,6 +380,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT("conj"), 1, false, Expr_Expr, BuiltinProcPkg_builtin},
{STR_LIT("expand_values"), 1, false, Expr_Expr, BuiltinProcPkg_builtin},
{STR_LIT("compress_values"), 1, true, Expr_Expr, BuiltinProcPkg_builtin},
{STR_LIT("min"), 1, true, Expr_Expr, BuiltinProcPkg_builtin},
{STR_LIT("max"), 1, true, Expr_Expr, BuiltinProcPkg_builtin},
@@ -582,6 +588,9 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT("type_convert_variants_to_pointers"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_merge"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_integer_to_unsigned"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_integer_to_signed"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
{STR_LIT("type_is_boolean"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_is_integer"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
@@ -686,6 +695,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT("objc_find_class"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("objc_register_selector"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
{STR_LIT("objc_register_class"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
{STR_LIT("objc_ivar_get"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
{STR_LIT("constant_utf16_cstring"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+4
View File
@@ -235,6 +235,10 @@ struct Entity {
Type * type_parameter_specialization;
String ir_mangled_name;
bool is_type_alias;
bool objc_is_implementation;
Type* objc_superclass;
Type* objc_ivar;
Entity*objc_context_provider;
String objc_class_name;
TypeNameObjCMetadata *objc_metadata;
} TypeName;
+14 -1
View File
@@ -769,7 +769,17 @@ try_cross_linking:;
gbString platform_lib_str = gb_string_make(heap_allocator(), "");
defer (gb_string_free(platform_lib_str));
if (build_context.metrics.os == TargetOs_darwin) {
platform_lib_str = gb_string_appendc(platform_lib_str, "-Wl,-syslibroot /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk -L/usr/local/lib ");
// Get the MacOSX SDK path.
gbString darwin_sdk_path = gb_string_make(temporary_allocator(), "");
if (!system_exec_command_line_app_output("xcrun --sdk macosx --show-sdk-path", &darwin_sdk_path)) {
darwin_sdk_path = gb_string_set(darwin_sdk_path, "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk");
} else {
// Trim the trailing newline.
darwin_sdk_path = gb_string_trim_space(darwin_sdk_path);
}
platform_lib_str = gb_string_append_fmt(platform_lib_str, "--sysroot %s ", darwin_sdk_path);
platform_lib_str = gb_string_appendc(platform_lib_str, "-L/usr/local/lib ");
// Homebrew's default library path, checking if it exists to avoid linking warnings.
if (gb_file_exists("/opt/homebrew/lib")) {
@@ -791,6 +801,9 @@ try_cross_linking:;
// This points the linker to where the entry point is
link_settings = gb_string_appendc(link_settings, "-e _main ");
}
} else if (build_context.metrics.os == TargetOs_freebsd) {
// FreeBSD pkg installs third-party shared libraries in /usr/local/lib.
platform_lib_str = gb_string_appendc(platform_lib_str, "-Wl,-L/usr/local/lib ");
} else if (build_context.metrics.os == TargetOs_openbsd) {
// OpenBSD ports install shared libraries in /usr/local/lib. Also, we must explicitly link libpthread.
platform_lib_str = gb_string_appendc(platform_lib_str, "-lpthread -Wl,-L/usr/local/lib ");
+14 -28
View File
@@ -977,7 +977,7 @@ namespace lbAbiAmd64SysV {
return types[0];
}
return LLVMStructTypeInContext(c, types.data, cast(unsigned)types.count, sz == 0);
return LLVMStructTypeInContext(c, types.data, cast(unsigned)types.count, false);
}
gb_internal void classify_with(LLVMTypeRef t, Array<RegClass> *cls, i64 ix, i64 off) {
@@ -1231,38 +1231,24 @@ namespace lbAbiArm64 {
}
} else {
i64 size = lb_sizeof(return_type);
if (size <= 16) {
LLVMTypeRef cast_type = nullptr;
if (size == 0) {
cast_type = LLVMStructTypeInContext(c, nullptr, 0, false);
} else if (size <= 8) {
cast_type = LLVMIntTypeInContext(c, cast(unsigned)(size*8));
} else {
unsigned count = cast(unsigned)((size+7)/8);
LLVMTypeRef llvm_i64 = LLVMIntTypeInContext(c, 64);
LLVMTypeRef *types = gb_alloc_array(temporary_allocator(), LLVMTypeRef, count);
i64 size_copy = size;
for (unsigned i = 0; i < count; i++) {
if (size_copy >= 8) {
types[i] = llvm_i64;
} else {
types[i] = LLVMIntTypeInContext(c, 8*cast(unsigned)size_copy);
}
size_copy -= 8;
}
GB_ASSERT(size_copy <= 0);
cast_type = LLVMStructTypeInContext(c, types, count, true);
}
return lb_arg_type_direct(return_type, cast_type, nullptr, nullptr);
} else {
if (size > 16) {
LB_ABI_MODIFY_RETURN_IF_TUPLE_MACRO();
LLVMAttributeRef attr = lb_create_enum_attribute_with_type(c, "sret", return_type);
return lb_arg_type_indirect(return_type, attr);
}
GB_ASSERT(size <= 16);
LLVMTypeRef cast_type = nullptr;
if (size == 0) {
cast_type = LLVMStructTypeInContext(c, nullptr, 0, false);
} else if (size <= 8) {
cast_type = LLVMIntTypeInContext(c, cast(unsigned)(size*8));
} else {
LLVMTypeRef llvm_i64 = LLVMIntTypeInContext(c, 64);
cast_type = llvm_array_type(llvm_i64, 2);
}
return lb_arg_type_direct(return_type, cast_type, nullptr, nullptr);
}
}
+667 -45
View File
@@ -1173,6 +1173,344 @@ gb_internal lbProcedure *lb_create_objc_names(lbModule *main_module) {
return p;
}
String lb_get_objc_type_encoding(Type *t, isize pointer_depth = 0) {
// NOTE(harold): See https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html#//apple_ref/doc/uid/TP40008048-CH100
// NOTE(harold): Darwin targets are always 64-bit. Should we drop this and assume "q" always?
#define INT_SIZE_ENCODING (build_context.metrics.int_size == 4 ? "i" : "q")
switch (t->kind) {
case Type_Basic: {
switch (t->Basic.kind) {
case Basic_Invalid:
return str_lit("?");
case Basic_llvm_bool:
case Basic_bool:
case Basic_b8:
return str_lit("B");
case Basic_b16:
return str_lit("C");
case Basic_b32:
return str_lit("I");
case Basic_b64:
return str_lit("q");
case Basic_i8:
return str_lit("c");
case Basic_u8:
return str_lit("C");
case Basic_i16:
case Basic_i16le:
case Basic_i16be:
return str_lit("s");
case Basic_u16:
case Basic_u16le:
case Basic_u16be:
return str_lit("S");
case Basic_i32:
case Basic_i32le:
case Basic_i32be:
return str_lit("i");
case Basic_u32le:
case Basic_u32:
case Basic_u32be:
return str_lit("I");
case Basic_i64:
case Basic_i64le:
case Basic_i64be:
return str_lit("q");
case Basic_u64:
case Basic_u64le:
case Basic_u64be:
return str_lit("Q");
case Basic_i128:
case Basic_i128le:
case Basic_i128be:
return str_lit("t");
case Basic_u128:
case Basic_u128le:
case Basic_u128be:
return str_lit("T");
case Basic_rune:
return str_lit("I");
case Basic_f16:
case Basic_f16le:
case Basic_f16be:
return str_lit("s"); // @harold: Closest we've got?
case Basic_f32:
case Basic_f32le:
case Basic_f32be:
return str_lit("f");
case Basic_f64:
case Basic_f64le:
case Basic_f64be:
return str_lit("d");
case Basic_complex32: return str_lit("{complex32=ss}"); // No f16 encoding, so fallback to i16, as above in Basic_f16*
case Basic_complex64: return str_lit("{complex64=ff}");
case Basic_complex128: return str_lit("{complex128=dd}");
case Basic_quaternion64: return str_lit("{quaternion64=ssss}");
case Basic_quaternion128: return str_lit("{quaternion128=ffff}");
case Basic_quaternion256: return str_lit("{quaternion256=dddd}");
case Basic_int:
return str_lit(INT_SIZE_ENCODING);
case Basic_uint:
return build_context.metrics.int_size == 4 ? str_lit("I") : str_lit("Q");
case Basic_uintptr:
case Basic_rawptr:
return str_lit("^v");
case Basic_string:
return build_context.metrics.int_size == 4 ? str_lit("{string=*i}") : str_lit("{string=*q}");
case Basic_cstring: return str_lit("*");
case Basic_any: return str_lit("{any=^v^v}"); // rawptr + ^Type_Info
case Basic_typeid:
GB_ASSERT(t->Basic.size == 8);
return str_lit("q");
// Untyped types
case Basic_UntypedBool:
case Basic_UntypedInteger:
case Basic_UntypedFloat:
case Basic_UntypedComplex:
case Basic_UntypedQuaternion:
case Basic_UntypedString:
case Basic_UntypedRune:
case Basic_UntypedNil:
case Basic_UntypedUninit:
GB_PANIC("Untyped types cannot be @encoded()");
return str_lit("?");
}
break;
}
case Type_Named:
case Type_Struct:
case Type_Union: {
Type* base = t;
if (base->kind == Type_Named) {
base = base_type(base);
if(base->kind != Type_Struct && base->kind != Type_Union) {
return lb_get_objc_type_encoding(base, pointer_depth);
}
}
const bool is_union = base->kind == Type_Union;
if (!is_union) {
// Check for objc_SEL
if (internal_check_is_assignable_to(base, t_objc_SEL)) {
return str_lit(":");
}
// Check for objc_Class
if (internal_check_is_assignable_to(base, t_objc_SEL)) {
return str_lit("#");
}
// Treat struct as an Objective-C Class?
if (has_type_got_objc_class_attribute(base) && pointer_depth == 0) {
return str_lit("#");
}
}
if (is_type_objc_object(base)) {
return str_lit("@");
}
gbString s = gb_string_make_reserve(temporary_allocator(), 16);
s = gb_string_append_length(s, is_union ? "(" :"{", 1);
if (t->kind == Type_Named) {
s = gb_string_append_length(s, t->Named.name.text, t->Named.name.len);
}
// Write fields
if (pointer_depth < 2) {
s = gb_string_append_length(s, "=", 1);
if (!is_union) {
for( auto& f : base->Struct.fields ) {
String field_type = lb_get_objc_type_encoding(f->type, pointer_depth);
s = gb_string_append_length(s, field_type.text, field_type.len);
}
} else {
for( auto& v : base->Union.variants ) {
String variant_type = lb_get_objc_type_encoding(v, pointer_depth);
s = gb_string_append_length(s, variant_type.text, variant_type.len);
}
}
}
s = gb_string_append_length(s, is_union ? ")" :"}", 1);
return make_string_c(s);
}
case Type_Generic:
GB_PANIC("Generic types cannot be @encoded()");
return str_lit("?");
case Type_Pointer: {
String pointee = lb_get_objc_type_encoding(t->Pointer.elem, pointer_depth +1);
// Special case for Objective-C Objects
if (pointer_depth == 0 && pointee == "@") {
return pointee;
}
return concatenate_strings(temporary_allocator(), str_lit("^"), pointee);
}
case Type_MultiPointer:
return concatenate_strings(temporary_allocator(), str_lit("^"), lb_get_objc_type_encoding(t->Pointer.elem, pointer_depth +1));
case Type_Array: {
String type_str = lb_get_objc_type_encoding(t->Array.elem, pointer_depth);
gbString s = gb_string_make_reserve(temporary_allocator(), type_str.len + 8);
s = gb_string_append_fmt(s, "[%lld%.*s]", t->Array.count, LIT(type_str));
return make_string_c(s);
}
case Type_EnumeratedArray: {
String type_str = lb_get_objc_type_encoding(t->EnumeratedArray.elem, pointer_depth);
gbString s = gb_string_make_reserve(temporary_allocator(), type_str.len + 8);
s = gb_string_append_fmt(s, "[%lld%.*s]", t->EnumeratedArray.count, LIT(type_str));
return make_string_c(s);
}
case Type_Slice: {
String type_str = lb_get_objc_type_encoding(t->Slice.elem, pointer_depth);
gbString s = gb_string_make_reserve(temporary_allocator(), type_str.len + 8);
s = gb_string_append_fmt(s, "{slice=^%.*s%s}", LIT(type_str), INT_SIZE_ENCODING);
return make_string_c(s);
}
case Type_DynamicArray: {
String type_str = lb_get_objc_type_encoding(t->DynamicArray.elem, pointer_depth);
gbString s = gb_string_make_reserve(temporary_allocator(), type_str.len + 8);
s = gb_string_append_fmt(s, "{dynamic=^%.*s%s%sAllocator={?^v}}", LIT(type_str), INT_SIZE_ENCODING, INT_SIZE_ENCODING);
return make_string_c(s);
}
case Type_Map:
return str_lit("{^v^v{Allocator=?^v}}");
case Type_Enum:
return lb_get_objc_type_encoding(t->Enum.base_type, pointer_depth);
case Type_Tuple:
// NOTE(harold): Is this type allowed here?
return str_lit("?");
case Type_Proc:
return str_lit("?");
case Type_BitSet:
return lb_get_objc_type_encoding(t->BitSet.underlying, pointer_depth);
case Type_SimdVector: {
String type_str = lb_get_objc_type_encoding(t->SimdVector.elem, pointer_depth);
gbString s = gb_string_make_reserve(temporary_allocator(), type_str.len + 5);
gb_string_append_fmt(s, "[%lld%.*s]", t->SimdVector.count, LIT(type_str));
return make_string_c(s);
}
case Type_Matrix: {
String type_str = lb_get_objc_type_encoding(t->Matrix.elem, pointer_depth);
gbString s = gb_string_make_reserve(temporary_allocator(), type_str.len + 5);
i64 element_count = t->Matrix.column_count * t->Matrix.row_count;
gb_string_append_fmt(s, "[%lld%.*s]", element_count, LIT(type_str));
return make_string_c(s);
}
case Type_BitField:
return lb_get_objc_type_encoding(t->BitField.backing_type, pointer_depth);
case Type_SoaPointer: {
gbString s = gb_string_make_reserve(temporary_allocator(), 8);
s = gb_string_append_fmt(s, "{=^v%s}", INT_SIZE_ENCODING);
return make_string_c(s);
}
} // End switch t->kind
#undef INT_SIZE_ENCODING
GB_PANIC("Unreachable");
return str_lit("");
}
struct lbObjCGlobalClass {
lbObjCGlobal g;
lbValue class_value; // Local registered class value
};
gb_internal void lb_register_objc_thing(
StringSet &handled,
lbModule *m,
Array<lbValue> &args,
Array<lbObjCGlobalClass> &class_impls,
StringMap<lbObjCGlobalClass> &class_map,
lbProcedure *p,
lbObjCGlobal const &g,
char const *call
) {
if (string_set_update(&handled, g.name)) {
return;
}
lbAddr addr = {};
lbValue *found = string_map_get(&m->members, g.global_name);
if (found) {
addr = lb_addr(*found);
} else {
lbValue v = {};
LLVMTypeRef t = lb_type(m, g.type);
v.value = LLVMAddGlobal(m->mod, t, g.global_name);
v.type = alloc_type_pointer(g.type);
addr = lb_addr(v);
LLVMSetInitializer(v.value, LLVMConstNull(t));
}
lbValue class_ptr = {};
lbValue class_name = lb_const_value(m, t_cstring, exact_value_string(g.name));
// If this class requires an implementation, save it for registration below.
if (g.class_impl_type != nullptr) {
// Make sure the superclass has been initialized before us
lbValue superclass_value = lb_const_nil(m, t_objc_Class);
auto &tn = g.class_impl_type->Named.type_name->TypeName;
Type *superclass = tn.objc_superclass;
if (superclass != nullptr) {
auto& superclass_global = string_map_must_get(&class_map, superclass->Named.type_name->TypeName.objc_class_name);
lb_register_objc_thing(handled, m, args, class_impls, class_map, p, superclass_global.g, call);
GB_ASSERT(superclass_global.class_value.value);
superclass_value = superclass_global.class_value;
}
args.count = 3;
args[0] = superclass_value;
args[1] = class_name;
args[2] = lb_const_int(m, t_uint, 0);
class_ptr = lb_emit_runtime_call(p, "objc_allocateClassPair", args);
array_add(&class_impls, lbObjCGlobalClass{g, class_ptr});
}
else {
args.count = 1;
args[0] = class_name;
class_ptr = lb_emit_runtime_call(p, call, args);
}
lb_addr_store(p, addr, class_ptr);
lbObjCGlobalClass* class_global = string_map_get(&class_map, g.name);
if (class_global != nullptr) {
class_global->class_value = class_ptr;
}
}
gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) {
if (p == nullptr) {
return;
@@ -1186,38 +1524,329 @@ gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) {
string_set_init(&handled);
defer (string_set_destroy(&handled));
auto args = array_make<lbValue>(temporary_allocator(), 1);
auto args = array_make<lbValue>(temporary_allocator(), 3, 8);
auto class_impls = array_make<lbObjCGlobalClass>(temporary_allocator(), 0, 16);
// Register all class implementations unconditionally, even if not statically referenced
for (Entity *e = {}; mpsc_dequeue(&gen->info->objc_class_implementations, &e); /**/) {
GB_ASSERT(e->kind == Entity_TypeName && e->TypeName.objc_is_implementation);
lb_handle_objc_find_or_register_class(p, e->TypeName.objc_class_name, e->type);
}
// Ensure classes that have been implicitly referenced through
// the objc_superclass attribute have a global variable available for them.
TypeSet class_set{};
type_set_init(&class_set, gen->objc_classes.count+16);
defer (type_set_destroy(&class_set));
auto referenced_classes = array_make<lbObjCGlobal>(temporary_allocator());
for (lbObjCGlobal g = {}; mpsc_dequeue(&gen->objc_classes, &g); /**/) {
array_add(&referenced_classes, g);
Type *cls = g.class_impl_type;
while (cls) {
if (type_set_update(&class_set, cls)) {
break;
}
GB_ASSERT(cls->kind == Type_Named);
cls = cls->Named.type_name->TypeName.objc_superclass;
}
}
for (auto pair : class_set) {
auto& tn = pair.type->Named.type_name->TypeName;
Type *class_impl = !tn.objc_is_implementation ? nullptr : pair.type;
lb_handle_objc_find_or_register_class(p, tn.objc_class_name, class_impl);
}
for (lbObjCGlobal g = {}; mpsc_dequeue(&gen->objc_classes, &g); /**/) {
array_add( &referenced_classes, g );
}
// Add all class globals to a map so that we can look them up dynamically
// in order to resolve out-of-order because classes that are being implemented
// require their superclasses to be registered before them.
StringMap<lbObjCGlobalClass> global_class_map{};
string_map_init(&global_class_map, (usize)gen->objc_classes.count);
defer (string_map_destroy(&global_class_map));
for (lbObjCGlobal g :referenced_classes) {
string_map_set(&global_class_map, g.name, lbObjCGlobalClass{g});
}
LLVMSetLinkage(p->value, LLVMInternalLinkage);
lb_begin_procedure_body(p);
auto register_thing = [&handled, &m, &args](lbProcedure *p, lbObjCGlobal const &g, char const *call) {
if (!string_set_update(&handled, g.name)) {
lbAddr addr = {};
lbValue *found = string_map_get(&m->members, g.global_name);
if (found) {
addr = lb_addr(*found);
} else {
lbValue v = {};
LLVMTypeRef t = lb_type(m, g.type);
v.value = LLVMAddGlobal(m->mod, t, g.global_name);
v.type = alloc_type_pointer(g.type);
addr = lb_addr(v);
LLVMSetInitializer(v.value, LLVMConstNull(t));
}
args[0] = lb_const_value(m, t_cstring, exact_value_string(g.name));
lbValue ptr = lb_emit_runtime_call(p, call, args);
lb_addr_store(p, addr, ptr);
}
};
for (lbObjCGlobal g = {}; mpsc_dequeue(&gen->objc_classes, &g); /**/) {
register_thing(p, g, "objc_lookUpClass");
// Register class globals, gathering classes that must be implemented
for (auto& kv : global_class_map) {
lb_register_objc_thing(handled, m, args, class_impls, global_class_map, p, kv.value.g, "objc_lookUpClass");
}
// Prefetch selectors for implemented methods so that they can also be registered.
for (const auto& cd : class_impls) {
auto& g = cd.g;
Type *class_type = g.class_impl_type;
Array<ObjcMethodData>* methods = map_get(&m->info->objc_method_implementations, class_type);
if (!methods) {
continue;
}
for (const ObjcMethodData& md : *methods) {
lb_handle_objc_find_or_register_selector(p, md.ac.objc_selector);
}
}
// Now we can register all referenced selectors
for (lbObjCGlobal g = {}; mpsc_dequeue(&gen->objc_selectors, &g); /**/) {
register_thing(p, g, "sel_registerName");
lb_register_objc_thing(handled, m, args, class_impls, global_class_map, p, g, "sel_registerName");
}
// Emit method wrapper implementations and registration
auto wrapper_args = array_make<Type *>(temporary_allocator(), 2, 8);
auto get_context_args = array_make<lbValue>(temporary_allocator(), 1);
PtrMap<Type *, lbObjCGlobal> ivar_map{};
map_init(&ivar_map, gen->objc_ivars.count);
for (lbObjCGlobal g = {}; mpsc_dequeue(&gen->objc_ivars, &g); /**/) {
map_set(&ivar_map, g.class_impl_type, g);
}
for (const auto &cd : class_impls) {
auto &g = cd.g;
Type *class_type = g.class_impl_type;
Type *class_ptr_type = alloc_type_pointer(class_type);
lbValue class_value = cd.class_value;
Type *ivar_type = class_type->Named.type_name->TypeName.objc_ivar;
Entity *context_provider = class_type->Named.type_name->TypeName.objc_context_provider;
Type *contex_provider_self_ptr_type = nullptr;
Type *contex_provider_self_named_type = nullptr;
bool is_context_provider_ivar = false;
lbValue context_provider_proc_value{};
if (context_provider) {
context_provider_proc_value = lb_find_procedure_value_from_entity(m, context_provider);
contex_provider_self_ptr_type = base_type(context_provider->type->Proc.params->Tuple.variables[0]->type);
GB_ASSERT(contex_provider_self_ptr_type->kind == Type_Pointer);
contex_provider_self_named_type = base_named_type(type_deref(contex_provider_self_ptr_type));
is_context_provider_ivar = ivar_type != nullptr && internal_check_is_assignable_to(contex_provider_self_named_type, ivar_type);
}
Array<ObjcMethodData> *methods = map_get(&m->info->objc_method_implementations, class_type);
if (!methods) {
continue;
}
for (const ObjcMethodData &md : *methods) {
GB_ASSERT( md.proc_entity->kind == Entity_Procedure);
Type *method_type = md.proc_entity->type;
String proc_name = make_string_c("__$objc_method::");
proc_name = concatenate_strings(temporary_allocator(), proc_name, g.name);
proc_name = concatenate_strings(temporary_allocator(), proc_name, str_lit("::"));
proc_name = concatenate_strings( permanent_allocator(), proc_name, md.ac.objc_name);
wrapper_args.count = 2;
wrapper_args[0] = md.ac.objc_is_class_method ? t_objc_Class : class_ptr_type;
wrapper_args[1] = t_objc_SEL;
isize method_param_count = method_type->Proc.param_count;
isize method_param_offset = 0;
if (!md.ac.objc_is_class_method) {
GB_ASSERT(method_param_count >= 1);
method_param_count -= 1;
method_param_offset = 1;
}
for (isize i = 0; i < method_param_count; i++) {
array_add(&wrapper_args, method_type->Proc.params->Tuple.variables[method_param_offset+i]->type);
}
Type *wrapper_args_tuple = alloc_type_tuple_from_field_types(wrapper_args.data, wrapper_args.count, false, true);
Type *wrapper_results_tuple = nullptr;
if (method_type->Proc.result_count > 0) {
GB_ASSERT(method_type->Proc.result_count == 1);
wrapper_results_tuple = alloc_type_tuple_from_field_types(&method_type->Proc.results->Tuple.variables[0]->type, 1, false, true);
}
Type *wrapper_proc_type = alloc_type_proc(nullptr, wrapper_args_tuple, wrapper_args_tuple->Tuple.variables.count,
wrapper_results_tuple, method_type->Proc.result_count, false, ProcCC_CDecl);
lbProcedure *wrapper_proc = lb_create_dummy_procedure(m, proc_name, wrapper_proc_type);
lb_add_attribute_to_proc(wrapper_proc->module, wrapper_proc->value, "nounwind");
// Emit the wrapper
LLVMSetLinkage(wrapper_proc->value, LLVMExternalLinkage);
lb_begin_procedure_body(wrapper_proc);
{
if (method_type->Proc.calling_convention == ProcCC_Odin) {
GB_ASSERT(context_provider);
// Emit the get odin context call
get_context_args[0] = lbValue {
wrapper_proc->raw_input_parameters[0],
contex_provider_self_ptr_type,
};
if (is_context_provider_ivar) {
// The context provider takes the ivar's type.
// Emit an objc_ivar_get call and use that pointer for 'self' instead.
lbValue real_self {
wrapper_proc->raw_input_parameters[0],
class_ptr_type
};
get_context_args[0] = lb_handle_objc_ivar_for_objc_object_pointer(wrapper_proc, real_self);
}
lbValue context = lb_emit_call(wrapper_proc, context_provider_proc_value, get_context_args);
lbAddr context_addr = lb_addr(lb_address_from_load_or_generate_local(wrapper_proc, context));
lb_push_context_onto_stack(wrapper_proc, context_addr);
}
auto method_call_args = array_make<lbValue>(temporary_allocator(), method_param_count + method_param_offset);
if (!md.ac.objc_is_class_method) {
method_call_args[0] = lbValue {
wrapper_proc->raw_input_parameters[0],
class_ptr_type,
};
}
for (isize i = 0; i < method_param_count; i++) {
method_call_args[i+method_param_offset] = lbValue {
wrapper_proc->raw_input_parameters[i+2],
method_type->Proc.params->Tuple.variables[i+method_param_offset]->type,
};
}
lbValue method_proc_value = lb_find_procedure_value_from_entity(m, md.proc_entity);
// Call real procedure for method from here, passing the parameters expected, if any.
lbValue return_value = lb_emit_call(wrapper_proc, method_proc_value, method_call_args);
if (wrapper_results_tuple != nullptr) {
auto &result_var = method_type->Proc.results->Tuple.variables[0];
return_value = lb_emit_conv(wrapper_proc, return_value, result_var->type);
lb_build_return_stmt_internal(wrapper_proc, return_value, result_var->token.pos);
}
}
lb_end_procedure_body(wrapper_proc);
// Add the method to the class
String method_encoding = str_lit("v");
// TODO (harold): Checker must ensure that objc_methods have a single return value or none!
GB_ASSERT(method_type->Proc.result_count <= 1);
if (method_type->Proc.result_count != 0) {
method_encoding = lb_get_objc_type_encoding(method_type->Proc.results->Tuple.variables[0]->type);
}
if (!md.ac.objc_is_class_method) {
method_encoding = concatenate_strings(temporary_allocator(), method_encoding, str_lit("@:"));
} else {
method_encoding = concatenate_strings(temporary_allocator(), method_encoding, str_lit("#:"));
}
for (isize i = method_param_offset; i < method_param_count; i++) {
Type *param_type = method_type->Proc.params->Tuple.variables[i]->type;
String param_encoding = lb_get_objc_type_encoding(param_type);
method_encoding = concatenate_strings(temporary_allocator(), method_encoding, param_encoding);
}
// Emit method registration
lbAddr* sel_address = string_map_get(&m->objc_selectors, md.ac.objc_selector);
GB_ASSERT(sel_address);
lbValue selector_value = lb_addr_load(p, *sel_address);
args.count = 4;
args[0] = class_value; // Class
args[1] = selector_value; // SEL
args[2] = lbValue { wrapper_proc->value, wrapper_proc->type };
args[3] = lb_const_value(m, t_cstring, exact_value_string(method_encoding));
// TODO(harold): Emit check BOOL result and panic if false.
lb_emit_runtime_call(p, "class_addMethod", args);
} // End methods
// Add ivar if we have one
if (ivar_type != nullptr) {
// Register a single ivar for this class
Type *ivar_base = ivar_type->Named.base;
// @note(harold): The alignment is supposed to be passed as log2(alignment): https://developer.apple.com/documentation/objectivec/class_addivar(_:_:_:_:_:)?language=objc
const i64 size = type_size_of(ivar_base);
const i64 alignment = (i64)floor_log2((u64)type_align_of(ivar_base));
// NOTE(harold): I've opted to not emit the type encoding for ivars in order to keep the data private.
// If there is desire in the future to emit the type encoding for introspection through the Obj-C runtime,
// then perhaps an option can be added for it then.
// Should we pass the actual type encoding? Might not be ideal for obfuscation.
String ivar_name = str_lit("__$ivar");
String ivar_types = str_lit("{= }"); //lb_get_objc_type_encoding(ivar_type);
args.count = 5;
args[0] = class_value;
args[1] = lb_const_value(m, t_cstring, exact_value_string(ivar_name));
args[2] = lb_const_value(m, t_uint, exact_value_u64((u64)size));
args[3] = lb_const_value(m, t_u8, exact_value_u64((u64)alignment));
args[4] = lb_const_value(m, t_cstring, exact_value_string(ivar_types));
lb_emit_runtime_call(p, "class_addIvar", args);
}
// Complete the class registration
args.count = 1;
args[0] = class_value;
lb_emit_runtime_call(p, "objc_registerClassPair", args);
}
// Register ivar offsets for any `objc_ivar_get` expressions emitted.
for (auto const& kv : ivar_map) {
lbObjCGlobal const& g = kv.value;
lbAddr ivar_addr = {};
lbValue *found = string_map_get(&m->members, g.global_name);
if (found) {
ivar_addr = lb_addr(*found);
GB_ASSERT(ivar_addr.addr.type == t_int_ptr);
} else {
// Defined in an external package, define it now in the main package
LLVMTypeRef t = lb_type(m, t_int);
lbValue global{};
global.value = LLVMAddGlobal(m->mod, t, g.global_name);
global.type = t_int_ptr;
LLVMSetInitializer(global.value, LLVMConstInt(t, 0, true));
ivar_addr = lb_addr(global);
}
String class_name = g.class_impl_type->Named.type_name->TypeName.objc_class_name;
lbValue class_value = string_map_must_get(&global_class_map, class_name).class_value;
args.count = 2;
args[0] = class_value;
args[1] = lb_const_value(m, t_cstring, exact_value_string(str_lit("__$ivar")));
lbValue ivar = lb_emit_runtime_call(p, "class_getInstanceVariable", args);
args.count = 1;
args[0] = ivar;
lbValue ivar_offset = lb_emit_runtime_call(p, "ivar_getOffset", args);
lbValue ivar_offset_int = lb_emit_conv(p, ivar_offset, t_int);
lb_addr_store(p, ivar_addr, ivar_offset_int);
}
lb_end_procedure_body(p);
@@ -1278,6 +1907,10 @@ gb_internal lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProc
lb_add_attribute_to_proc(p->module, p->value, "optnone");
lb_add_attribute_to_proc(p->module, p->value, "noinline");
// Make sure shared libraries call their own runtime startup on Linux.
LLVMSetVisibility(p->value, LLVMHiddenVisibility);
LLVMSetLinkage(p->value, LLVMWeakAnyLinkage);
lb_begin_procedure_body(p);
lb_setup_type_info_data(main_module);
@@ -1344,14 +1977,14 @@ gb_internal lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProc
gbString var_name = gb_string_make(permanent_allocator(), "__$global_any::");
gbString e_str = string_canonical_entity_name(temporary_allocator(), e);
var_name = gb_string_append_length(var_name, e_str, gb_strlen(e_str));
lbAddr g = lb_add_global_generated_with_name(main_module, var_type, var.init, make_string_c(var_name));
lbAddr g = lb_add_global_generated_with_name(main_module, var_type, {}, make_string_c(var_name));
lb_addr_store(p, g, var.init);
lbValue gp = lb_addr_get_ptr(p, g);
lbValue data = lb_emit_struct_ep(p, var.var, 0);
lbValue ti = lb_emit_struct_ep(p, var.var, 1);
lb_emit_store(p, data, lb_emit_conv(p, gp, t_rawptr));
lb_emit_store(p, ti, lb_type_info(p, var_type));
lb_emit_store(p, ti, lb_typeid(p->module, var_type));
} else {
LLVMTypeRef vt = llvm_addr_type(p->module, var.var);
lbValue src0 = lb_emit_conv(p, var.init, t);
@@ -1387,6 +2020,10 @@ gb_internal lbProcedure *lb_create_cleanup_runtime(lbModule *main_module) { // C
lb_add_attribute_to_proc(p->module, p->value, "optnone");
lb_add_attribute_to_proc(p->module, p->value, "noinline");
// Make sure shared libraries call their own runtime cleanup on Linux.
LLVMSetVisibility(p->value, LLVMHiddenVisibility);
LLVMSetLinkage(p->value, LLVMWeakAnyLinkage);
lb_begin_procedure_body(p);
CheckerInfo *info = main_module->gen->info;
@@ -2185,7 +2822,6 @@ gb_internal void lb_generate_procedure(lbModule *m, lbProcedure *p) {
p->is_done = true;
m->curr_procedure = nullptr;
}
lb_end_procedure(p);
// Add Flags
if (p->entity && p->entity->kind == Entity_Procedure && p->entity->Procedure.is_memcpy_like) {
@@ -2494,6 +3130,7 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
LLVMSetInitializer(g, LLVMConstNull(lb_type(m, t)));
LLVMSetLinkage(g, LLVMInternalLinkage);
lb_make_global_private_const(g);
lb_set_odin_rtti_section(g);
return lb_addr({g, alloc_type_pointer(t)});
};
@@ -2565,24 +3202,9 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
lbValue g = {};
g.value = LLVMAddGlobal(m->mod, lb_type(m, e->type), alloc_cstring(permanent_allocator(), name));
g.type = alloc_type_pointer(e->type);
if (e->Variable.thread_local_model != "") {
LLVMSetThreadLocal(g.value, true);
String m = e->Variable.thread_local_model;
LLVMThreadLocalMode mode = LLVMGeneralDynamicTLSModel;
if (m == "default") {
mode = LLVMGeneralDynamicTLSModel;
} else if (m == "localdynamic") {
mode = LLVMLocalDynamicTLSModel;
} else if (m == "initialexec") {
mode = LLVMInitialExecTLSModel;
} else if (m == "localexec") {
mode = LLVMLocalExecTLSModel;
} else {
GB_PANIC("Unhandled thread local mode %.*s", LIT(m));
}
LLVMSetThreadLocalMode(g.value, mode);
}
lb_apply_thread_local_model(g.value, e->Variable.thread_local_model);
if (is_foreign) {
LLVMSetLinkage(g.value, LLVMExternalLinkage);
LLVMSetDLLStorageClass(g.value, LLVMDLLImportStorageClass);
+3 -1
View File
@@ -196,6 +196,7 @@ struct lbModule {
StringMap<lbAddr> objc_classes;
StringMap<lbAddr> objc_selectors;
StringMap<lbAddr> objc_ivars;
PtrMap<u64/*type hash*/, lbAddr> map_cell_info_map; // address of runtime.Map_Info
PtrMap<u64/*type hash*/, lbAddr> map_info_map; // address of runtime.Map_Cell_Info
@@ -219,6 +220,7 @@ struct lbObjCGlobal {
gbString global_name;
String name;
Type * type;
Type * class_impl_type; // This is set when the class has the objc_implement attribute set to true.
};
struct lbGenerator : LinkerData {
@@ -240,6 +242,7 @@ struct lbGenerator : LinkerData {
MPSCQueue<lbEntityCorrection> entities_to_correct_linkage;
MPSCQueue<lbObjCGlobal> objc_selectors;
MPSCQueue<lbObjCGlobal> objc_classes;
MPSCQueue<lbObjCGlobal> objc_ivars;
MPSCQueue<String> raddebug_section_strings;
};
@@ -410,7 +413,6 @@ gb_internal LLVMAttributeRef lb_create_enum_attribute_with_type(LLVMContextRef c
gb_internal void lb_add_proc_attribute_at_index(lbProcedure *p, isize index, char const *name, u64 value);
gb_internal void lb_add_proc_attribute_at_index(lbProcedure *p, isize index, char const *name);
gb_internal lbProcedure *lb_create_procedure(lbModule *module, Entity *entity, bool ignore_body=false);
gb_internal void lb_end_procedure(lbProcedure *p);
gb_internal LLVMTypeRef lb_type(lbModule *m, Type *type);
+4 -1
View File
@@ -533,7 +533,10 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb
Entity *e = entity_from_expr(expr);
res = lb_find_procedure_value_from_entity(m, e);
}
GB_ASSERT(res.value != nullptr);
if (res.value == nullptr) {
// This is an unspecialized polymorphic procedure, return nil or dummy value
return lb_const_nil(m, original_type);
}
GB_ASSERT(LLVMGetValueKind(res.value) == LLVMFunctionValueKind);
if (LLVMGetIntrinsicID(res.value) == 0) {
+56
View File
@@ -1374,3 +1374,59 @@ gb_internal void lb_add_debug_info_for_global_constant_from_entity(lbGenerator *
}
}
}
gb_internal void lb_add_debug_label(lbProcedure *p, Ast *label, lbBlock *target) {
// NOTE(tf2spi): LLVM-C DILabel API used only existed for major versions 20+
#if LLVM_VERSION_MAJOR >= 20
if (p == nullptr || p->debug_info == nullptr) {
return;
}
if (target == nullptr || label == nullptr || label->kind != Ast_Label) {
return;
}
Token label_token = label->Label.token;
if (is_blank_ident(label_token.string)) {
return;
}
lbModule *m = p->module;
if (m == nullptr) {
return;
}
AstFile *file = label->file();
LLVMMetadataRef llvm_file = lb_get_llvm_metadata(m, file);
if (llvm_file == nullptr) {
debugf("llvm file not found for label\n");
return;
}
LLVMMetadataRef llvm_scope = p->debug_info;
if(llvm_scope == nullptr) {
debugf("llvm scope not found for label\n");
return;
}
LLVMMetadataRef llvm_debug_loc = lb_debug_location_from_token_pos(p, label_token.pos);
LLVMBasicBlockRef llvm_block = target->block;
if (llvm_block == nullptr || llvm_debug_loc == nullptr) {
return;
}
LLVMMetadataRef llvm_label = LLVMDIBuilderCreateLabel(
m->debug_builder,
llvm_scope,
(const char *)label_token.string.text,
(size_t)label_token.string.len,
llvm_file,
label_token.pos.line,
// NOTE(tf2spi): Defaults to false in LLVM API, but I'd rather not take chances
// Always preserve the label no matter what when debugging
true
);
GB_ASSERT(llvm_label != nullptr);
(void)LLVMDIBuilderInsertLabelAtEnd(
m->debug_builder,
llvm_label,
llvm_debug_loc,
llvm_block
);
#endif
}
+17 -9
View File
@@ -4844,7 +4844,7 @@ gb_internal lbAddr lb_build_addr_compound_lit(lbProcedure *p, Ast *expr) {
if (cl->elems.count == 0) {
break;
}
GB_ASSERT(expr->file()->feature_flags & OptInFeatureFlag_DynamicLiterals);
GB_ASSERT(expr->file()->feature_flags & OptInFeatureFlag_DynamicLiterals || build_context.dynamic_literals);
lbValue err = lb_dynamic_map_reserve(p, v.addr, 2*cl->elems.count, pos);
gb_unused(err);
@@ -5154,8 +5154,6 @@ gb_internal lbAddr lb_build_addr_internal(lbProcedure *p, Ast *expr) {
return lb_build_addr(p, unparen_expr(se->selector));
}
Type *type = base_type(tav.type);
if (tav.mode == Addressing_Type) { // Addressing_Type
Selection sel = lookup_field(tav.type, selector, true);
if (sel.pseudo_field) {
@@ -5190,18 +5188,29 @@ gb_internal lbAddr lb_build_addr_internal(lbProcedure *p, Ast *expr) {
return lb_addr_swizzle(a, type, swizzle_count, swizzle_indices);
}
Selection sel = lookup_field(type, selector, false);
Selection sel = lookup_field(tav.type, selector, false);
GB_ASSERT(sel.entity != nullptr);
if (sel.pseudo_field) {
GB_ASSERT(sel.entity->kind == Entity_Procedure || sel.entity->kind == Entity_ProcGroup);
if (sel.pseudo_field && (sel.entity->kind == Entity_Procedure || sel.entity->kind == Entity_ProcGroup)) {
Entity *e = entity_of_node(sel_node);
GB_ASSERT(e->kind == Entity_Procedure);
return lb_addr(lb_find_value_from_entity(p->module, e));
}
if (sel.is_bit_field) {
lbAddr addr = lb_build_addr(p, se->expr);
lbAddr addr = lb_build_addr(p, se->expr);
// NOTE(harold): Only allow ivar pseudo field access on indirect selectors.
// It is incoherent otherwise as Objective-C objects are zero-sized.
Type *deref_type = type_deref(tav.type);
if (tav.type->kind == Type_Pointer && deref_type->kind == Type_Named && deref_type->Named.type_name->TypeName.objc_ivar) {
// NOTE(harold): We need to load the ivar from the current address and
// replace addr with the loaded ivar addr to apply the selector load properly.
addr = lb_addr(lb_emit_load(p, addr.addr));
lbValue ivar_ptr = lb_handle_objc_ivar_for_objc_object_pointer(p, addr.addr);
addr = lb_addr(ivar_ptr);
}
if (sel.is_bit_field) {
Selection sub_sel = sel;
sub_sel.index.count -= 1;
@@ -5227,7 +5236,6 @@ gb_internal lbAddr lb_build_addr_internal(lbProcedure *p, Ast *expr) {
}
{
lbAddr addr = lb_build_addr(p, se->expr);
if (addr.kind == lbAddr_Map) {
lbValue v = lb_addr_load(p, addr);
lbValue a = lb_address_from_load_or_generate_local(p, v);
+100 -25
View File
@@ -101,6 +101,7 @@ gb_internal void lb_init_module(lbModule *m, Checker *c) {
string_map_init(&m->objc_classes);
string_map_init(&m->objc_selectors);
string_map_init(&m->objc_ivars);
map_init(&m->map_info_map, 0);
map_init(&m->map_cell_info_map, 0);
@@ -173,8 +174,9 @@ gb_internal bool lb_init_generator(lbGenerator *gen, Checker *c) {
mpsc_init(&gen->entities_to_correct_linkage, heap_allocator());
mpsc_init(&gen->objc_selectors, heap_allocator());
mpsc_init(&gen->objc_classes, heap_allocator());
mpsc_init(&gen->objc_ivars, heap_allocator());
mpsc_init(&gen->raddebug_section_strings, heap_allocator());
return true;
}
@@ -886,8 +888,8 @@ gb_internal void lb_addr_store(lbProcedure *p, lbAddr addr, lbValue value) {
Type *t = base_type(type_deref(addr.addr.type));
GB_ASSERT(t->kind == Type_Struct && t->Struct.soa_kind != StructSoa_None);
lbValue len = lb_soa_struct_len(p, addr.addr);
if (addr.soa.index_expr != nullptr) {
lb_emit_bounds_check(p, ast_token(addr.soa.index_expr), index, len);
if (addr.soa.index_expr != nullptr && (!lb_is_const(addr.soa.index) || t->Struct.soa_kind != StructSoa_Fixed)) {
lb_emit_bounds_check(p, ast_token(addr.soa.index_expr), addr.soa.index, len);
}
}
@@ -2213,6 +2215,14 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
case Type_BitField:
return lb_type_internal(m, type->BitField.backing_type);
case Type_Generic:
if (type->Generic.specialized) {
return lb_type_internal(m, type->Generic.specialized);
} else {
// For unspecialized generics, use a pointer type as a placeholder
return LLVMPointerType(LLVMInt8TypeInContext(m->ctx), 0);
}
}
GB_PANIC("Invalid type %s", type_to_string(type));
@@ -2378,6 +2388,29 @@ gb_internal void lb_add_attribute_to_proc_with_string(lbModule *m, LLVMValueRef
}
gb_internal bool lb_apply_thread_local_model(LLVMValueRef value, String model) {
if (model != "") {
LLVMSetThreadLocal(value, true);
LLVMThreadLocalMode mode = LLVMGeneralDynamicTLSModel;
if (model == "default") {
mode = LLVMGeneralDynamicTLSModel;
} else if (model == "localdynamic") {
mode = LLVMLocalDynamicTLSModel;
} else if (model == "initialexec") {
mode = LLVMInitialExecTLSModel;
} else if (model == "localexec") {
mode = LLVMLocalExecTLSModel;
} else {
GB_PANIC("Unhandled thread local mode %.*s", LIT(model));
}
LLVMSetThreadLocalMode(value, mode);
return true;
}
return false;
}
gb_internal void lb_add_edge(lbBlock *from, lbBlock *to) {
LLVMValueRef instr = LLVMGetLastInstruction(from->block);
@@ -2516,10 +2549,13 @@ general_end:;
}
}
src_size = align_formula(src_size, src_align);
dst_size = align_formula(dst_size, dst_align);
// NOTE(laytan): even though this logic seems sound, the Address Sanitizer does not
// want you to load/store the space of a value that is there for alignment.
#if 0
i64 aligned_src_size = align_formula(src_size, src_align);
i64 aligned_dst_size = align_formula(dst_size, dst_align);
if (LLVMIsALoadInst(val) && (src_size >= dst_size && src_align >= dst_align)) {
if (LLVMIsALoadInst(val) && (aligned_src_size >= aligned_dst_size && src_align >= dst_align)) {
LLVMValueRef val_ptr = LLVMGetOperand(val, 0);
val_ptr = LLVMBuildPointerCast(p->builder, val_ptr, LLVMPointerType(dst_type, 0), "");
LLVMValueRef loaded_val = OdinLLVMBuildLoad(p, dst_type, val_ptr);
@@ -2527,8 +2563,57 @@ general_end:;
// LLVMSetAlignment(loaded_val, gb_min(src_align, dst_align));
return loaded_val;
}
#endif
if (src_size > dst_size) {
GB_ASSERT(p->decl_block != p->curr_block);
// NOTE(laytan): src is bigger than dst, need to memcpy the part of src we want.
LLVMValueRef val_ptr;
if (LLVMIsALoadInst(val)) {
val_ptr = LLVMGetOperand(val, 0);
} else if (LLVMIsAAllocaInst(val)) {
val_ptr = LLVMBuildPointerCast(p->builder, val, LLVMPointerType(src_type, 0), "");
} else {
// NOTE(laytan): we need a pointer to memcpy from.
LLVMValueRef val_copy = llvm_alloca(p, src_type, src_align);
val_ptr = LLVMBuildPointerCast(p->builder, val_copy, LLVMPointerType(src_type, 0), "");
LLVMBuildStore(p->builder, val, val_ptr);
}
i64 max_align = gb_max(lb_alignof(src_type), lb_alignof(dst_type));
max_align = gb_max(max_align, 16);
LLVMValueRef ptr = llvm_alloca(p, dst_type, max_align);
LLVMValueRef nptr = LLVMBuildPointerCast(p->builder, ptr, LLVMPointerType(dst_type, 0), "");
LLVMTypeRef types[3] = {
lb_type(p->module, t_rawptr),
lb_type(p->module, t_rawptr),
lb_type(p->module, t_int)
};
LLVMValueRef args[4] = {
nptr,
val_ptr,
LLVMConstInt(LLVMIntTypeInContext(p->module->ctx, 8*cast(unsigned)build_context.int_size), dst_size, 0),
LLVMConstInt(LLVMInt1TypeInContext(p->module->ctx), 0, 0),
};
lb_call_intrinsic(
p,
"llvm.memcpy.inline",
args,
gb_count_of(args),
types,
gb_count_of(types)
);
return OdinLLVMBuildLoad(p, dst_type, ptr);
} else {
GB_ASSERT(p->decl_block != p->curr_block);
GB_ASSERT(dst_size >= src_size);
i64 max_align = gb_max(lb_alignof(src_type), lb_alignof(dst_type));
max_align = gb_max(max_align, 16);
@@ -2729,6 +2814,14 @@ gb_internal lbValue lb_find_procedure_value_from_entity(lbModule *m, Entity *e)
ignore_body = other_module != m;
lbProcedure *missing_proc = lb_create_procedure(m, e, ignore_body);
if (missing_proc == nullptr) {
// This is an unspecialized polymorphic procedure, which should not be codegen'd
lbValue dummy = {};
dummy.value = nullptr;
dummy.type = nullptr;
return dummy;
}
if (ignore_body) {
mutex_lock(&gen->anonymous_proc_lits_mutex);
defer (mutex_unlock(&gen->anonymous_proc_lits_mutex));
@@ -2921,25 +3014,7 @@ gb_internal lbValue lb_find_value_from_entity(lbModule *m, Entity *e) {
lb_set_entity_from_other_modules_linkage_correctly(other_module, e, name);
if (e->Variable.thread_local_model != "") {
LLVMSetThreadLocal(g.value, true);
String m = e->Variable.thread_local_model;
LLVMThreadLocalMode mode = LLVMGeneralDynamicTLSModel;
if (m == "default") {
mode = LLVMGeneralDynamicTLSModel;
} else if (m == "localdynamic") {
mode = LLVMLocalDynamicTLSModel;
} else if (m == "initialexec") {
mode = LLVMInitialExecTLSModel;
} else if (m == "localexec") {
mode = LLVMLocalExecTLSModel;
} else {
GB_PANIC("Unhandled thread local mode %.*s", LIT(m));
}
LLVMSetThreadLocalMode(g.value, mode);
}
lb_apply_thread_local_model(g.value, e->Variable.thread_local_model);
return g;
}
+76 -2
View File
@@ -67,6 +67,14 @@ gb_internal void lb_mem_copy_non_overlapping(lbProcedure *p, lbValue dst, lbValu
gb_internal lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool ignore_body) {
GB_ASSERT(entity != nullptr);
GB_ASSERT(entity->kind == Entity_Procedure);
// Skip codegen for unspecialized polymorphic procedures
if (is_type_polymorphic(entity->type) && !entity->Procedure.is_foreign) {
Type *bt = base_type(entity->type);
if (bt->kind == Type_Proc && bt->Proc.is_polymorphic && !bt->Proc.is_poly_specialized) {
// Do not generate code for unspecialized polymorphic procedures
return nullptr;
}
}
if (!entity->Procedure.is_foreign) {
if ((entity->flags & EntityFlag_ProcBodyChecked) == 0) {
GB_PANIC("%.*s :: %s (was parapoly: %d %d)", LIT(entity->token.string), type_to_string(entity->type), is_type_polymorphic(entity->type, true), is_type_polymorphic(entity->type, false));
@@ -783,8 +791,7 @@ gb_internal void lb_end_procedure_body(lbProcedure *p) {
p->curr_block = nullptr;
p->state_flags = 0;
}
gb_internal void lb_end_procedure(lbProcedure *p) {
LLVMDisposeBuilder(p->builder);
}
@@ -817,6 +824,10 @@ gb_internal void lb_build_nested_proc(lbProcedure *p, AstProcLit *pd, Entity *e)
e->Procedure.link_name = name;
lbProcedure *nested_proc = lb_create_procedure(p->module, e);
if (nested_proc == nullptr) {
// This is an unspecialized polymorphic procedure, skip codegen
return;
}
e->code_gen_procedure = nested_proc;
lbValue value = {};
@@ -2235,6 +2246,68 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
return lb_emit_load(p, tuple);
}
case BuiltinProc_compress_values: {
isize value_count = 0;
for (Ast *arg : ce->args) {
Type *t = arg->tav.type;
if (is_type_tuple(t)) {
value_count += t->Tuple.variables.count;
} else {
value_count += 1;
}
}
if (value_count == 1) {
lbValue x = lb_build_expr(p, ce->args[0]);
x = lb_emit_conv(p, x, tv.type);
return x;
}
Type *dt = base_type(tv.type);
lbAddr addr = lb_add_local_generated(p, tv.type, true);
if (is_type_struct(dt) || is_type_tuple(dt)) {
i32 index = 0;
for (Ast *arg : ce->args) {
lbValue x = lb_build_expr(p, arg);
if (is_type_tuple(x.type)) {
for (isize i = 0; i < x.type->Tuple.variables.count; i++) {
lbValue y = lb_emit_tuple_ev(p, x, cast(i32)i);
lbValue ptr = lb_emit_struct_ep(p, addr.addr, index++);
y = lb_emit_conv(p, y, type_deref(ptr.type));
lb_emit_store(p, ptr, y);
}
} else {
lbValue ptr = lb_emit_struct_ep(p, addr.addr, index++);
x = lb_emit_conv(p, x, type_deref(ptr.type));
lb_emit_store(p, ptr, x);
}
}
GB_ASSERT(index == value_count);
} else if (is_type_array_like(dt)) {
i32 index = 0;
for (Ast *arg : ce->args) {
lbValue x = lb_build_expr(p, arg);
if (is_type_tuple(x.type)) {
for (isize i = 0; i < x.type->Tuple.variables.count; i++) {
lbValue y = lb_emit_tuple_ev(p, x, cast(i32)i);
lbValue ptr = lb_emit_array_epi(p, addr.addr, index++);
y = lb_emit_conv(p, y, type_deref(ptr.type));
lb_emit_store(p, ptr, y);
}
} else {
lbValue ptr = lb_emit_array_epi(p, addr.addr, index++);
x = lb_emit_conv(p, x, type_deref(ptr.type));
lb_emit_store(p, ptr, x);
}
}
GB_ASSERT(index == value_count);
} else {
GB_PANIC("TODO(bill): compress_values -> %s", type_to_string(tv.type));
}
return lb_addr_load(p, addr);
}
case BuiltinProc_min: {
Type *t = type_of_expr(expr);
if (ce->args.count == 2) {
@@ -3375,6 +3448,7 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
case BuiltinProc_objc_find_class: return lb_handle_objc_find_class(p, expr);
case BuiltinProc_objc_register_selector: return lb_handle_objc_register_selector(p, expr);
case BuiltinProc_objc_register_class: return lb_handle_objc_register_class(p, expr);
case BuiltinProc_objc_ivar_get: return lb_handle_objc_ivar_get(p, expr);
case BuiltinProc_constant_utf16_cstring:
+97 -25
View File
@@ -136,7 +136,6 @@ gb_internal lbBranchBlocks lb_lookup_branch_blocks(lbProcedure *p, Ast *ident) {
return empty;
}
gb_internal lbTargetList *lb_push_target_list(lbProcedure *p, Ast *label, lbBlock *break_, lbBlock *continue_, lbBlock *fallthrough_) {
lbTargetList *tl = gb_alloc_item(permanent_allocator(), lbTargetList);
tl->prev = p->target_list;
@@ -688,6 +687,18 @@ gb_internal void lb_build_range_interval(lbProcedure *p, AstBinaryExpr *node,
lbBlock *body = lb_create_block(p, "for.interval.body");
lbBlock *done = lb_create_block(p, "for.interval.done");
// TODO(tf2spi): This is inlined in more than several places.
// Putting this in a function might be preferred.
// LLVMSetCurrentDebugLocation2 has side effects,
// so I didn't want to hide that before it got reviewed.
if (rs->label != nullptr && p->debug_info != nullptr) {
lbBlock *label = lb_create_block(p, "for.interval.label");
lb_emit_jump(p, label);
lb_start_block(p, label);
LLVMSetCurrentDebugLocation2(p->builder, lb_debug_location_from_ast(p, rs->label));
lb_add_debug_label(p, rs->label, label);
}
lb_emit_jump(p, loop);
lb_start_block(p, loop);
@@ -893,6 +904,14 @@ gb_internal void lb_build_range_stmt_struct_soa(lbProcedure *p, AstRangeStmt *rs
lbAddr index = lb_add_local_generated(p, t_int, false);
if (rs->label != nullptr && p->debug_info != nullptr) {
lbBlock *label = lb_create_block(p, "for.soa.label");
lb_emit_jump(p, label);
lb_start_block(p, label);
LLVMSetCurrentDebugLocation2(p->builder, lb_debug_location_from_ast(p, rs->label));
lb_add_debug_label(p, rs->label, label);
}
if (!is_reverse) {
/*
for x, i in array {
@@ -970,7 +989,6 @@ gb_internal void lb_build_range_stmt_struct_soa(lbProcedure *p, AstRangeStmt *rs
lb_store_range_stmt_val(p, val1, lb_addr_load(p, index));
}
lb_push_target_list(p, rs->label, done, loop, nullptr);
lb_build_stmt(p, rs->body);
@@ -1029,6 +1047,15 @@ gb_internal void lb_build_range_stmt(lbProcedure *p, AstRangeStmt *rs, Scope *sc
lbBlock *done = nullptr;
bool is_map = false;
if (rs->label != nullptr && p->debug_info != nullptr) {
lbBlock *label = lb_create_block(p, "for.range.label");
lb_emit_jump(p, label);
lb_start_block(p, label);
LLVMSetCurrentDebugLocation2(p->builder, lb_debug_location_from_ast(p, rs->label));
lb_add_debug_label(p, rs->label, label);
}
if (tav.mode == Addressing_Type) {
lb_build_range_enum(p, type_deref(tav.type), val0_type, &val, &key, &loop, &done);
} else {
@@ -1530,6 +1557,14 @@ gb_internal bool lb_switch_stmt_can_be_trivial_jump_table(AstSwitchStmt *ss, boo
gb_internal void lb_build_switch_stmt(lbProcedure *p, AstSwitchStmt *ss, Scope *scope) {
lb_open_scope(p, scope);
if (ss->label != nullptr && p->debug_info != nullptr) {
lbBlock *label = lb_create_block(p, "switch.label");
lb_emit_jump(p, label);
lb_start_block(p, label);
LLVMSetCurrentDebugLocation2(p->builder, lb_debug_location_from_ast(p, ss->label));
lb_add_debug_label(p, ss->label, label);
}
if (ss->init != nullptr) {
lb_build_stmt(p, ss->init);
}
@@ -1736,6 +1771,7 @@ gb_internal lbAddr lb_store_range_stmt_val(lbProcedure *p, Ast *stmt_val, lbValu
gb_internal void lb_type_case_body(lbProcedure *p, Ast *label, Ast *clause, lbBlock *body, lbBlock *done) {
ast_node(cc, CaseClause, clause);
// NOTE(tf2spi): Debug info for label not generated here on purpose
lb_push_target_list(p, label, done, nullptr, nullptr);
lb_build_stmt_list(p, cc->stmts);
lb_close_scope(p, lbDeferExit_Default, body, clause);
@@ -1986,33 +2022,43 @@ gb_internal void lb_build_static_variables(lbProcedure *p, AstValueDecl *vd) {
LLVMValueRef global = LLVMAddGlobal(p->module->mod, lb_type(p->module, e->type), c_name);
LLVMSetAlignment(global, cast(u32)type_align_of(e->type));
LLVMSetInitializer(global, LLVMConstNull(lb_type(p->module, e->type)));
if (value.value != nullptr) {
LLVMSetInitializer(global, value.value);
}
if (e->Variable.is_rodata) {
LLVMSetGlobalConstant(global, true);
}
if (e->Variable.thread_local_model != "") {
LLVMSetThreadLocal(global, true);
String m = e->Variable.thread_local_model;
LLVMThreadLocalMode mode = LLVMGeneralDynamicTLSModel;
if (m == "default") {
mode = LLVMGeneralDynamicTLSModel;
} else if (m == "localdynamic") {
mode = LLVMLocalDynamicTLSModel;
} else if (m == "initialexec") {
mode = LLVMInitialExecTLSModel;
} else if (m == "localexec") {
mode = LLVMLocalExecTLSModel;
} else {
GB_PANIC("Unhandled thread local mode %.*s", LIT(m));
}
LLVMSetThreadLocalMode(global, mode);
} else {
if (!lb_apply_thread_local_model(global, e->Variable.thread_local_model)) {
LLVMSetLinkage(global, LLVMInternalLinkage);
}
if (value.value != nullptr) {
if (is_type_any(e->type)) {
Type *var_type = default_type(value.type);
gbString var_name = gb_string_make(temporary_allocator(), "__$static_any::");
var_name = gb_string_append_length(var_name, mangled_name.text, mangled_name.len);
lbAddr var_global = lb_add_global_generated_with_name(p->module, var_type, value, make_string_c(var_name), nullptr);
LLVMValueRef var_global_ref = var_global.addr.value;
if (e->Variable.is_rodata) {
LLVMSetGlobalConstant(var_global_ref, true);
}
if (!lb_apply_thread_local_model(var_global_ref, e->Variable.thread_local_model)) {
LLVMSetLinkage(var_global_ref, LLVMInternalLinkage);
}
LLVMValueRef vals[2] = {
lb_emit_conv(p, var_global.addr, t_rawptr).value,
lb_typeid(p->module, var_type).value,
};
LLVMValueRef init = llvm_const_named_struct(p->module, e->type, vals, gb_count_of(vals));
LLVMSetInitializer(global, init);
} else {
LLVMSetInitializer(global, value.value);
}
}
lbValue global_val = {global, alloc_type_pointer(e->type)};
lb_add_entity(p->module, e, global_val);
@@ -2307,6 +2353,14 @@ gb_internal void lb_build_if_stmt(lbProcedure *p, Ast *node) {
else_ = lb_create_block(p, "if.else");
}
if (is->label != nullptr) {
if (p->debug_info != nullptr) {
lbBlock *label = lb_create_block(p, "if.label");
lb_emit_jump(p, label);
lb_start_block(p, label);
LLVMSetCurrentDebugLocation2(p->builder, lb_debug_location_from_ast(p, is->label));
lb_add_debug_label(p, is->label, label);
}
lbTargetList *tl = lb_push_target_list(p, is->label, done, nullptr, nullptr);
tl->is_block = true;
}
@@ -2399,12 +2453,19 @@ gb_internal void lb_build_for_stmt(lbProcedure *p, Ast *node) {
lb_push_target_list(p, fs->label, done, post, nullptr);
if (fs->label != nullptr && p->debug_info != nullptr) {
lbBlock *label = lb_create_block(p, "for.label");
lb_emit_jump(p, label);
lb_start_block(p, label);
LLVMSetCurrentDebugLocation2(p->builder, lb_debug_location_from_ast(p, fs->label));
lb_add_debug_label(p, fs->label, label);
}
if (fs->init != nullptr) {
#if 1
lbBlock *init = lb_create_block(p, "for.init");
lb_emit_jump(p, init);
lb_start_block(p, init);
#endif
lb_build_stmt(p, fs->init);
}
@@ -2420,7 +2481,6 @@ gb_internal void lb_build_for_stmt(lbProcedure *p, Ast *node) {
lb_start_block(p, body);
}
lb_build_stmt(p, fs->body);
lb_pop_target_list(p);
@@ -2694,9 +2754,21 @@ gb_internal void lb_build_stmt(lbProcedure *p, Ast *node) {
case_ast_node(bs, BlockStmt, node);
lbBlock *body = nullptr;
lbBlock *done = nullptr;
if (bs->label != nullptr) {
if (p->debug_info != nullptr) {
lbBlock *label = lb_create_block(p, "block.label");
lb_emit_jump(p, label);
lb_start_block(p, label);
LLVMSetCurrentDebugLocation2(p->builder, lb_debug_location_from_ast(p, bs->label));
lb_add_debug_label(p, bs->label, label);
}
body = lb_create_block(p, "block.body");
done = lb_create_block(p, "block.done");
lb_emit_jump(p, body);
lb_start_block(p, body);
lbTargetList *tl = lb_push_target_list(p, bs->label, done, nullptr, nullptr);
tl->is_block = true;
}
+10
View File
@@ -1,4 +1,10 @@
gb_internal void lb_set_odin_rtti_section(LLVMValueRef value) {
if (build_context.metrics.os != TargetOs_darwin) {
LLVMSetSection(value, ".odinti");
}
}
gb_internal isize lb_type_info_index(CheckerInfo *info, TypeInfoPair pair, bool err_on_not_found=true) {
isize index = type_info_index(info, pair, err_on_not_found);
if (index >= 0) {
@@ -221,6 +227,7 @@ gb_internal void lb_setup_type_info_data_giant_array(lbModule *m, i64 global_typ
gb_snprintf(name, 63, "__$ti-%lld", cast(long long)index);
LLVMValueRef g = LLVMAddGlobal(m->mod, type, name);
lb_make_global_private_const(g);
lb_set_odin_rtti_section(g);
return g;
};
@@ -716,6 +723,8 @@ gb_internal void lb_setup_type_info_data_giant_array(lbModule *m, i64 global_typ
LLVMSetInitializer(value_array.value, value_init);
LLVMSetGlobalConstant(name_array.value, true);
LLVMSetGlobalConstant(value_array.value, true);
lb_set_odin_rtti_section(name_array.value);
lb_set_odin_rtti_section(value_array.value);
lbValue v_count = lb_const_int(m, t_int, fields.count);
@@ -1056,6 +1065,7 @@ gb_internal void lb_setup_type_info_data_giant_array(lbModule *m, i64 global_typ
LLVMValueRef giant_array = lb_global_type_info_data_ptr(m).value;
LLVMSetInitializer(giant_array, giant_const);
lb_make_global_private_const(giant_array);
lb_set_odin_rtti_section(giant_array);
}
+69 -8
View File
@@ -2125,7 +2125,7 @@ gb_internal lbAddr lb_handle_objc_find_or_register_selector(lbProcedure *p, Stri
return addr;
}
gb_internal lbAddr lb_handle_objc_find_or_register_class(lbProcedure *p, String const &name) {
gb_internal lbAddr lb_handle_objc_find_or_register_class(lbProcedure *p, String const &name, Type *class_impl_type) {
lbModule *m = p->module;
lbAddr *found = string_map_get(&m->objc_classes, name);
if (found) {
@@ -2148,13 +2148,75 @@ gb_internal lbAddr lb_handle_objc_find_or_register_class(lbProcedure *p, String
} else {
LLVMSetLinkage(g.value, LLVMExternalLinkage);
}
mpsc_enqueue(&m->gen->objc_classes, lbObjCGlobal{m, global_name, name, t_objc_Class});
mpsc_enqueue(&m->gen->objc_classes, lbObjCGlobal{m, global_name, name, t_objc_Class, class_impl_type});
lbAddr addr = lb_addr(g);
string_map_set(&m->objc_classes, name, addr);
return addr;
}
gb_internal lbAddr lb_handle_objc_find_or_register_ivar(lbModule *m, Type *self_type) {
String name = self_type->Named.type_name->TypeName.objc_class_name;
GB_ASSERT(name != "");
lbAddr *found = string_map_get(&m->objc_ivars, name);
if (found) {
return *found;
}
lbModule *default_module = &m->gen->default_module;
gbString global_name = gb_string_make(permanent_allocator(), "__$objc_ivar::");
global_name = gb_string_append_length(global_name, name.text, name.len);
// Create a global variable to store offset of the ivar in an instance of an object
LLVMTypeRef t = lb_type(m, t_int);
lbValue g = {};
g.value = LLVMAddGlobal(m->mod, t, global_name);
g.type = t_int_ptr;
if (default_module == m) {
LLVMSetInitializer(g.value, LLVMConstInt(t, 0, true));
lb_add_member(m, make_string_c(global_name), g);
} else {
LLVMSetLinkage(g.value, LLVMExternalLinkage);
}
mpsc_enqueue(&m->gen->objc_ivars, lbObjCGlobal{m, global_name, name, t_int, self_type});
lbAddr addr = lb_addr(g);
string_map_set(&m->objc_ivars, name, addr);
return addr;
}
gb_internal lbValue lb_handle_objc_ivar_for_objc_object_pointer(lbProcedure *p, lbValue self) {
GB_ASSERT(self.type->kind == Type_Pointer && self.type->Pointer.elem->kind == Type_Named);
Type *self_type = self.type->Pointer.elem;
lbValue self_uptr = lb_emit_conv(p, self, t_uintptr);
lbValue ivar_offset = lb_addr_load(p, lb_handle_objc_find_or_register_ivar(p->module, self_type));
lbValue ivar_offset_uptr = lb_emit_conv(p, ivar_offset, t_uintptr);
lbValue ivar_uptr = lb_emit_arith(p, Token_Add, self_uptr, ivar_offset_uptr, t_uintptr);
Type *ivar_type = self_type->Named.type_name->TypeName.objc_ivar;
return lb_emit_conv(p, ivar_uptr, alloc_type_pointer(ivar_type));
}
gb_internal lbValue lb_handle_objc_ivar_get(lbProcedure *p, Ast *expr) {
ast_node(ce, CallExpr, expr);
GB_ASSERT(ce->args[0]->tav.type->kind == Type_Pointer);
lbValue self = lb_build_expr(p, ce->args[0]);
return lb_handle_objc_ivar_for_objc_object_pointer(p, self);
}
gb_internal lbValue lb_handle_objc_find_selector(lbProcedure *p, Ast *expr) {
ast_node(ce, CallExpr, expr);
@@ -2188,7 +2250,7 @@ gb_internal lbValue lb_handle_objc_find_class(lbProcedure *p, Ast *expr) {
auto tav = ce->args[0]->tav;
GB_ASSERT(tav.value.kind == ExactValue_String);
String name = tav.value.value_string;
return lb_addr_load(p, lb_handle_objc_find_or_register_class(p, name));
return lb_addr_load(p, lb_handle_objc_find_or_register_class(p, name, nullptr));
}
gb_internal lbValue lb_handle_objc_register_class(lbProcedure *p, Ast *expr) {
@@ -2198,7 +2260,7 @@ gb_internal lbValue lb_handle_objc_register_class(lbProcedure *p, Ast *expr) {
auto tav = ce->args[0]->tav;
GB_ASSERT(tav.value.kind == ExactValue_String);
String name = tav.value.value_string;
lbAddr dst = lb_handle_objc_find_or_register_class(p, name);
lbAddr dst = lb_handle_objc_find_or_register_class(p, name, nullptr);
auto args = array_make<lbValue>(permanent_allocator(), 3);
args[0] = lb_const_nil(m, t_objc_Class);
@@ -2220,7 +2282,9 @@ gb_internal lbValue lb_handle_objc_id(lbProcedure *p, Ast *expr) {
GB_ASSERT(e->kind == Entity_TypeName);
String name = e->TypeName.objc_class_name;
return lb_addr_load(p, lb_handle_objc_find_or_register_class(p, name));
Type *class_impl_type = e->TypeName.objc_is_implementation ? type : nullptr;
return lb_addr_load(p, lb_handle_objc_find_or_register_class(p, name, class_impl_type));
}
return lb_build_expr(p, expr);
@@ -2266,9 +2330,6 @@ gb_internal lbValue lb_handle_objc_send(lbProcedure *p, Ast *expr) {
return lb_emit_call(p, the_proc, args);
}
gb_internal LLVMAtomicOrdering llvm_atomic_ordering_from_odin(ExactValue const &value) {
GB_ASSERT(value.kind == ExactValue_Integer);
i64 v = exact_value_to_i64(value);
+150 -72
View File
@@ -278,10 +278,10 @@ gb_internal void usage(String argv0, String argv1 = {}) {
print_usage_line(1, " One must contain the program's entry point, all must be in the same package.");
print_usage_line(1, "run Same as 'build', but also then runs the newly compiled executable.");
print_usage_line(1, "bundle Bundles a directory in a specific layout for that platform.");
print_usage_line(1, "check Parses, and type checks a directory of .odin files.");
print_usage_line(1, "check Parses and type checks a directory of .odin files.");
print_usage_line(1, "strip-semicolon Parses, type checks, and removes unneeded semicolons from the entire program.");
print_usage_line(1, "test Builds and runs procedures with the attribute @(test) in the initial package.");
print_usage_line(1, "doc Generates documentation on a directory of .odin files.");
print_usage_line(1, "doc Generates documentation from a directory of .odin files.");
print_usage_line(1, "version Prints version.");
print_usage_line(1, "report Prints information useful to reporting a bug.");
print_usage_line(1, "root Prints the root path where Odin looks for the builtin collections.");
@@ -319,6 +319,7 @@ enum BuildFlagKind {
BuildFlag_NoBoundsCheck,
BuildFlag_NoTypeAssert,
BuildFlag_NoDynamicLiterals,
BuildFlag_DynamicLiterals,
BuildFlag_NoCRT,
BuildFlag_NoRPath,
BuildFlag_NoEntryPoint,
@@ -538,6 +539,7 @@ gb_internal bool parse_build_flags(Array<String> args) {
add_flag(&build_flags, BuildFlag_NoTypeAssert, str_lit("no-type-assert"), BuildFlagParam_None, Command__does_check);
add_flag(&build_flags, BuildFlag_NoThreadLocal, str_lit("no-thread-local"), BuildFlagParam_None, Command__does_check);
add_flag(&build_flags, BuildFlag_NoDynamicLiterals, str_lit("no-dynamic-literals"), BuildFlagParam_None, Command__does_check);
add_flag(&build_flags, BuildFlag_DynamicLiterals, str_lit("dynamic-literals"), BuildFlagParam_None, Command__does_check);
add_flag(&build_flags, BuildFlag_NoCRT, str_lit("no-crt"), BuildFlagParam_None, Command__does_build);
add_flag(&build_flags, BuildFlag_NoRPath, str_lit("no-rpath"), BuildFlagParam_None, Command__does_build);
add_flag(&build_flags, BuildFlag_NoEntryPoint, str_lit("no-entry-point"), BuildFlagParam_None, Command__does_check &~ Command_test);
@@ -1207,6 +1209,9 @@ gb_internal bool parse_build_flags(Array<String> args) {
case BuildFlag_NoDynamicLiterals:
gb_printf_err("Warning: Use of -no-dynamic-literals is now redundant\n");
break;
case BuildFlag_DynamicLiterals:
build_context.dynamic_literals = true;
break;
case BuildFlag_NoCRT:
build_context.no_crt = true;
break;
@@ -2221,20 +2226,30 @@ gb_internal void remove_temp_files(lbGenerator *gen) {
}
gb_internal void print_show_help(String const arg0, String command, String optional_flag = {}) {
gb_internal int print_show_help(String const arg0, String command, String optional_flag = {}) {
bool help_resolved = false;
bool printed_usage_header = false;
bool printed_flags_header = false;
if (command == "help" && optional_flag.len != 0 && optional_flag[0] != '-') {
command = optional_flag;
optional_flag = {};
}
print_usage_line(0, "%.*s is a tool for managing Odin source code.", LIT(arg0));
print_usage_line(0, "Usage:");
print_usage_line(1, "%.*s %.*s [arguments]", LIT(arg0), LIT(command));
print_usage_line(0, "");
defer (print_usage_line(0, ""));
auto const print_usage_header_once = [&help_resolved, &printed_usage_header, arg0, command]() {
if (printed_usage_header) {
return;
}
print_usage_line(0, "%.*s is a tool for managing Odin source code.", LIT(arg0));
print_usage_line(0, "Usage:");
print_usage_line(1, "%.*s %.*s [arguments]", LIT(arg0), LIT(command));
print_usage_line(0, "");
help_resolved = true;
printed_usage_header = true;
};
if (command == "build") {
print_usage_header_once();
print_usage_line(1, "build Compiles directory of .odin files as an executable.");
print_usage_line(2, "One must contain the program's entry point, all must be in the same package.");
print_usage_line(2, "Use `-file` to build a single file instead.");
@@ -2243,6 +2258,7 @@ gb_internal void print_show_help(String const arg0, String command, String optio
print_usage_line(3, "odin build <dir> Builds package in <dir>.");
print_usage_line(3, "odin build filename.odin -file Builds single-file package, must contain entry point.");
} else if (command == "run") {
print_usage_header_once();
print_usage_line(1, "run Same as 'build', but also then runs the newly compiled executable.");
print_usage_line(2, "Append an empty flag and then the args, '-- <args>', to specify args for the output.");
print_usage_line(2, "Examples:");
@@ -2250,28 +2266,40 @@ gb_internal void print_show_help(String const arg0, String command, String optio
print_usage_line(3, "odin run <dir> Builds and runs package in <dir>.");
print_usage_line(3, "odin run filename.odin -file Builds and runs single-file package, must contain entry point.");
} else if (command == "check") {
print_usage_header_once();
print_usage_line(1, "check Parses and type checks directory of .odin files.");
print_usage_line(2, "Examples:");
print_usage_line(3, "odin check . Type checks package in current directory.");
print_usage_line(3, "odin check <dir> Type checks package in <dir>.");
print_usage_line(3, "odin check filename.odin -file Type checks single-file package, must contain entry point.");
} else if (command == "test") {
print_usage_header_once();
print_usage_line(1, "test Builds and runs procedures with the attribute @(test) in the initial package.");
} else if (command == "doc") {
print_usage_header_once();
print_usage_line(1, "doc Generates documentation from a directory of .odin files.");
print_usage_line(2, "Examples:");
print_usage_line(3, "odin doc . Generates documentation on package in current directory.");
print_usage_line(3, "odin doc <dir> Generates documentation on package in <dir>.");
print_usage_line(3, "odin doc filename.odin -file Generates documentation on single-file package.");
} else if (command == "version") {
print_usage_header_once();
print_usage_line(1, "version Prints version.");
} else if (command == "strip-semicolon") {
print_usage_header_once();
print_usage_line(1, "strip-semicolon");
print_usage_line(2, "Parses and type checks .odin file(s) and then removes unneeded semicolons from the entire project.");
} else if (command == "bundle") {
print_usage_header_once();
print_usage_line(1, "bundle <platform> Bundles a directory in a specific layout for that platform");
print_usage_line(2, "Supported platforms:");
print_usage_line(3, "android");
} else if (command == "report") {
print_usage_header_once();
print_usage_line(1, "report Prints information useful to reporting a bug.");
} else if (command == "root") {
print_usage_header_once();
print_usage_line(1, "root Prints the root path where Odin looks for the builtin collections.");
}
bool doc = command == "doc";
@@ -2293,13 +2321,10 @@ gb_internal void print_show_help(String const arg0, String command, String optio
check = true;
}
print_usage_line(0, "");
print_usage_line(1, "Flags");
print_usage_line(0, "");
auto const print_flag = [&optional_flag](char const *flag) -> bool {
auto const print_flag = [&optional_flag, &help_resolved, &printed_flags_header, print_usage_header_once](char const *flag) -> bool {
if (optional_flag.len != 0) {
String f = make_string_c(flag);
isize i = string_index_byte(f, ':');
@@ -2310,6 +2335,14 @@ gb_internal void print_show_help(String const arg0, String command, String optio
return false;
}
}
print_usage_header_once();
if (!printed_flags_header) {
print_usage_line(0, "");
print_usage_line(1, "Flags");
print_usage_line(0, "");
printed_flags_header = true;
}
help_resolved = true;
print_usage_line(0, "");
print_usage_line(1, flag);
return true;
@@ -2331,20 +2364,20 @@ gb_internal void print_show_help(String const arg0, String command, String optio
if (print_flag("-build-mode:<mode>")) {
print_usage_line(2, "Sets the build mode.");
print_usage_line(2, "Available options:");
print_usage_line(3, "-build-mode:exe Builds as an executable.");
print_usage_line(3, "-build-mode:test Builds as an executable that executes tests.");
print_usage_line(3, "-build-mode:dll Builds as a dynamically linked library.");
print_usage_line(3, "-build-mode:shared Builds as a dynamically linked library.");
print_usage_line(3, "-build-mode:dynamic Builds as a dynamically linked library.");
print_usage_line(3, "-build-mode:lib Builds as a statically linked library.");
print_usage_line(3, "-build-mode:static Builds as a statically linked library.");
print_usage_line(3, "-build-mode:obj Builds as an object file.");
print_usage_line(3, "-build-mode:object Builds as an object file.");
print_usage_line(3, "-build-mode:assembly Builds as an assembly file.");
print_usage_line(3, "-build-mode:assembler Builds as an assembly file.");
print_usage_line(3, "-build-mode:asm Builds as an assembly file.");
print_usage_line(3, "-build-mode:llvm-ir Builds as an LLVM IR file.");
print_usage_line(3, "-build-mode:llvm Builds as an LLVM IR file.");
print_usage_line(3, "-build-mode:exe Builds as an executable.");
print_usage_line(3, "-build-mode:test Builds as an executable that executes tests.");
print_usage_line(3, "-build-mode:dll Builds as a dynamically linked library.");
print_usage_line(3, "-build-mode:shared Builds as a dynamically linked library.");
print_usage_line(3, "-build-mode:dynamic Builds as a dynamically linked library.");
print_usage_line(3, "-build-mode:lib Builds as a statically linked library.");
print_usage_line(3, "-build-mode:static Builds as a statically linked library.");
print_usage_line(3, "-build-mode:obj Builds as an object file.");
print_usage_line(3, "-build-mode:object Builds as an object file.");
print_usage_line(3, "-build-mode:assembly Builds as an assembly file.");
print_usage_line(3, "-build-mode:assembler Builds as an assembly file.");
print_usage_line(3, "-build-mode:asm Builds as an assembly file.");
print_usage_line(3, "-build-mode:llvm-ir Builds as an LLVM IR file.");
print_usage_line(3, "-build-mode:llvm Builds as an LLVM IR file.");
}
}
@@ -2353,16 +2386,16 @@ gb_internal void print_show_help(String const arg0, String command, String optio
print_usage_line(2, "Defines a library collection used for imports.");
print_usage_line(2, "Example: -collection:shared=dir/to/shared");
print_usage_line(2, "Usage in Code:");
print_usage_line(3, "import \"shared:foo\"");
print_usage_line(3, "import \"shared:foo\"");
}
if (print_flag("-custom-attribute:<string>")) {
print_usage_line(2, "Add a custom attribute which will be ignored if it is unknown.");
print_usage_line(2, "This can be used with metaprogramming tools.");
print_usage_line(2, "Examples:");
print_usage_line(3, "-custom-attribute:my_tag");
print_usage_line(3, "-custom-attribute:my_tag,the_other_thing");
print_usage_line(3, "-custom-attribute:my_tag -custom-attribute:the_other_thing");
print_usage_line(3, "-custom-attribute:my_tag");
print_usage_line(3, "-custom-attribute:my_tag,the_other_thing");
print_usage_line(3, "-custom-attribute:my_tag -custom-attribute:the_other_thing");
}
}
@@ -2385,7 +2418,7 @@ gb_internal void print_show_help(String const arg0, String command, String optio
print_usage_line(2, "Defines a scalar boolean, integer or string as global constant.");
print_usage_line(2, "Example: -define:SPAM=123");
print_usage_line(2, "Usage in code:");
print_usage_line(3, "#config(SPAM, default_value)");
print_usage_line(3, "#config(SPAM, default_value)");
}
}
@@ -2420,9 +2453,9 @@ gb_internal void print_show_help(String const arg0, String command, String optio
if (check) {
if (print_flag("-error-pos-style:<string>")) {
print_usage_line(2, "Available options:");
print_usage_line(3, "-error-pos-style:unix file/path:45:3:");
print_usage_line(3, "-error-pos-style:odin file/path(45:3)");
print_usage_line(3, "-error-pos-style:default (Defaults to 'odin'.)");
print_usage_line(3, "-error-pos-style:unix file/path:45:3:");
print_usage_line(3, "-error-pos-style:odin file/path(45:3)");
print_usage_line(3, "-error-pos-style:default (Defaults to 'odin'.)");
}
if (print_flag("-export-defineables:<filename>")) {
@@ -2433,8 +2466,8 @@ gb_internal void print_show_help(String const arg0, String command, String optio
if (print_flag("-export-dependencies:<format>")) {
print_usage_line(2, "Exports dependencies to one of a few formats. Requires `-export-dependencies-file`.");
print_usage_line(2, "Available options:");
print_usage_line(3, "-export-dependencies:make Exports in Makefile format");
print_usage_line(3, "-export-dependencies:json Exports in JSON format");
print_usage_line(3, "-export-dependencies:make Exports in Makefile format");
print_usage_line(3, "-export-dependencies:json Exports in JSON format");
}
if (print_flag("-export-dependencies-file:<filename>")) {
@@ -2445,8 +2478,8 @@ gb_internal void print_show_help(String const arg0, String command, String optio
if (print_flag("-export-timings:<format>")) {
print_usage_line(2, "Exports timings to one of a few formats. Requires `-show-timings` or `-show-more-timings`.");
print_usage_line(2, "Available options:");
print_usage_line(3, "-export-timings:json Exports compile time stats to JSON.");
print_usage_line(3, "-export-timings:csv Exports compile time stats to CSV.");
print_usage_line(3, "-export-timings:json Exports compile time stats to JSON.");
print_usage_line(3, "-export-timings:csv Exports compile time stats to CSV.");
}
if (print_flag("-export-timings-file:<filename>")) {
@@ -2536,9 +2569,9 @@ gb_internal void print_show_help(String const arg0, String command, String optio
if (print_flag("-microarch:<string>")) {
print_usage_line(2, "Specifies the specific micro-architecture for the build in a string.");
print_usage_line(2, "Examples:");
print_usage_line(3, "-microarch:sandybridge");
print_usage_line(3, "-microarch:native");
print_usage_line(3, "-microarch:\"?\" for a list");
print_usage_line(3, "-microarch:sandybridge");
print_usage_line(3, "-microarch:native");
print_usage_line(3, "-microarch:\"?\" for a list");
}
}
@@ -2595,10 +2628,10 @@ gb_internal void print_show_help(String const arg0, String command, String optio
if (print_flag("-o:<string>")) {
print_usage_line(2, "Sets the optimization mode for compilation.");
print_usage_line(2, "Available options:");
print_usage_line(3, "-o:none");
print_usage_line(3, "-o:minimal");
print_usage_line(3, "-o:size");
print_usage_line(3, "-o:speed");
print_usage_line(3, "-o:none");
print_usage_line(3, "-o:minimal");
print_usage_line(3, "-o:size");
print_usage_line(3, "-o:speed");
if (LB_USE_NEW_PASS_SYSTEM) {
print_usage_line(3, "-o:aggressive (use this with caution)");
}
@@ -2649,10 +2682,10 @@ gb_internal void print_show_help(String const arg0, String command, String optio
if (print_flag("-reloc-mode:<string>")) {
print_usage_line(2, "Specifies the reloc mode.");
print_usage_line(2, "Available options:");
print_usage_line(3, "-reloc-mode:default");
print_usage_line(3, "-reloc-mode:static");
print_usage_line(3, "-reloc-mode:pic");
print_usage_line(3, "-reloc-mode:dynamic-no-pic");
print_usage_line(3, "-reloc-mode:default");
print_usage_line(3, "-reloc-mode:static");
print_usage_line(3, "-reloc-mode:pic");
print_usage_line(3, "-reloc-mode:dynamic-no-pic");
}
#if defined(GB_SYSTEM_WINDOWS)
@@ -2667,9 +2700,9 @@ gb_internal void print_show_help(String const arg0, String command, String optio
if (print_flag("-sanitize:<string>")) {
print_usage_line(2, "Enables sanitization analysis.");
print_usage_line(2, "Available options:");
print_usage_line(3, "-sanitize:address");
print_usage_line(3, "-sanitize:memory");
print_usage_line(3, "-sanitize:thread");
print_usage_line(3, "-sanitize:address");
print_usage_line(3, "-sanitize:memory");
print_usage_line(3, "-sanitize:thread");
print_usage_line(2, "NOTE: This flag can be used multiple times.");
}
}
@@ -2730,17 +2763,32 @@ gb_internal void print_show_help(String const arg0, String command, String optio
print_usage_line(2, "[Windows only]");
print_usage_line(2, "Defines the subsystem for the application.");
print_usage_line(2, "Available options:");
print_usage_line(3, "-subsystem:console");
print_usage_line(3, "-subsystem:windows");
print_usage_line(3, "-subsystem:console");
print_usage_line(3, "-subsystem:windows");
}
#endif
}
if (build) {
if (print_flag("-subtarget:<subtarget>")) {
print_usage_line(2, "[Darwin and Linux only]");
print_usage_line(2, "Available subtargets:");
String prefix = str_lit("-subtarget:");
for (u32 i = 1; i < Subtarget_COUNT; i++) {
String name = subtarget_strings[i];
String help_string = concatenate_strings(temporary_allocator(), prefix, name);
print_usage_line(3, (const char *)help_string.text);
}
}
}
if (run_or_build) {
if (print_flag("-target-features:<string>")) {
print_usage_line(2, "Specifies CPU features to enable on top of the enabled features implied by -microarch.");
print_usage_line(2, "Examples:");
print_usage_line(3, "-target-features:atomics");
print_usage_line(3, "-target-features:\"sse2,aes\"");
print_usage_line(3, "-target-features:\"?\" for a list");
print_usage_line(3, "-target-features:atomics");
print_usage_line(3, "-target-features:\"sse2,aes\"");
print_usage_line(3, "-target-features:\"?\" for a list");
}
}
@@ -2777,11 +2825,11 @@ gb_internal void print_show_help(String const arg0, String command, String optio
if (print_flag("-vet")) {
print_usage_line(2, "Does extra checks on the code.");
print_usage_line(2, "Extra checks include:");
print_usage_line(3, "-vet-unused");
print_usage_line(3, "-vet-unused-variables");
print_usage_line(3, "-vet-unused-imports");
print_usage_line(3, "-vet-shadowing");
print_usage_line(3, "-vet-using-stmt");
print_usage_line(3, "-vet-unused");
print_usage_line(3, "-vet-unused-variables");
print_usage_line(3, "-vet-unused-imports");
print_usage_line(3, "-vet-shadowing");
print_usage_line(3, "-vet-using-stmt");
}
if (print_flag("-vet-cast")) {
@@ -2867,6 +2915,21 @@ gb_internal void print_show_help(String const arg0, String command, String optio
print_usage_line(2, "If this is omitted, the terminal will prompt you to provide it.");
}
}
if (!help_resolved) {
usage(arg0);
print_usage_line(0, "");
if (command == "help") {
print_usage_line(0, "'%.*s' is not a recognized flag.", LIT(optional_flag));
} else {
print_usage_line(0, "'%.*s' is not a recognized command.", LIT(command));
}
return 1;
}
print_usage_line(0, "");
return 0;
}
gb_internal void print_show_unused(Checker *c) {
@@ -3239,6 +3302,16 @@ int main(int arg_count, char const **arg_ptr) {
String run_args_string = {};
isize last_non_run_arg = args.count;
for_array(i, args) {
if (args[i] == "--") {
break;
}
if (args[i] == "-help" || args[i] == "--help") {
build_context.show_help = true;
return print_show_help(args[0], command);
}
}
bool run_output = false;
if (command == "run" || command == "test") {
if (args.count < 3) {
@@ -3332,6 +3405,10 @@ int main(int arg_count, char const **arg_ptr) {
return 1;
#endif
} else if (command == "version") {
if (args.count != 2) {
usage(args[0]);
return 1;
}
build_context.command_kind = Command_version;
gb_printf("%.*s version %.*s", LIT(args[0]), LIT(ODIN_VERSION));
@@ -3346,6 +3423,10 @@ int main(int arg_count, char const **arg_ptr) {
gb_printf("\n");
return 0;
} else if (command == "report") {
if (args.count != 2) {
usage(args[0]);
return 1;
}
build_context.command_kind = Command_bug_report;
print_bug_report_help();
return 0;
@@ -3354,8 +3435,7 @@ int main(int arg_count, char const **arg_ptr) {
usage(args[0]);
return 1;
} else {
print_show_help(args[0], args[1], args[2]);
return 0;
return print_show_help(args[0], args[1], args[2]);
}
} else if (command == "bundle") {
if (args.count < 4) {
@@ -3371,6 +3451,10 @@ int main(int arg_count, char const **arg_ptr) {
}
init_filename = args[3];
} else if (command == "root") {
if (args.count != 2) {
usage(args[0]);
return 1;
}
gb_printf("%.*s", LIT(odin_root_dir()));
return 0;
} else if (command == "clear-cache") {
@@ -3386,11 +3470,6 @@ int main(int arg_count, char const **arg_ptr) {
init_filename = copy_string(permanent_allocator(), init_filename);
if (init_filename == "-help" ||
init_filename == "--help") {
build_context.show_help = true;
}
if (init_filename.len > 0 && !build_context.show_help) {
// The command must be build, run, test, check, or another that takes a directory or filename.
if (!path_is_directory(init_filename)) {
@@ -3441,8 +3520,7 @@ int main(int arg_count, char const **arg_ptr) {
}
if (build_context.show_help) {
print_show_help(args[0], command);
return 0;
return print_show_help(args[0], command);
}
if (command == "bundle") {
+5 -1
View File
@@ -756,8 +756,12 @@ gb_internal void write_type_to_canonical_string(TypeWriter *w, Type *type) {
type_writer_appendc(w, "/");
write_type_to_canonical_string(w, type->Generic.specialized);
}
} else if (type->Generic.specialized) {
// If we have a specialized type, use that instead of panicking
write_type_to_canonical_string(w, type->Generic.specialized);
} else {
GB_PANIC("Type_Generic should never be hit");
// For unspecialized generics, use a generic placeholder string
type_writer_appendc(w, "rawptr");
}
return;
+38
View File
@@ -729,10 +729,12 @@ gb_global Type *t_map_set_proc = nullptr;
gb_global Type *t_objc_object = nullptr;
gb_global Type *t_objc_selector = nullptr;
gb_global Type *t_objc_class = nullptr;
gb_global Type *t_objc_ivar = nullptr;
gb_global Type *t_objc_id = nullptr;
gb_global Type *t_objc_SEL = nullptr;
gb_global Type *t_objc_Class = nullptr;
gb_global Type *t_objc_Ivar = nullptr;
enum OdinAtomicMemoryOrder : i32 {
OdinAtomicMemoryOrder_relaxed = 0, // unordered
@@ -872,6 +874,29 @@ gb_internal Type *base_type(Type *t) {
return t;
}
gb_internal Type *base_named_type(Type *t) {
if (t->kind != Type_Named) {
return t_invalid;
}
Type *prev_named = t;
t = t->Named.base;
for (;;) {
if (t == nullptr) {
break;
}
if (t->kind != Type_Named) {
break;
}
if (t == t->Named.base) {
return t_invalid;
}
prev_named = t;
t = t->Named.base;
}
return prev_named;
}
gb_internal Type *base_enum_type(Type *t) {
Type *bt = base_type(t);
if (bt != nullptr &&
@@ -2932,6 +2957,10 @@ gb_internal Type *default_type(Type *type) {
case Basic_UntypedString: return t_string;
case Basic_UntypedRune: return t_rune;
}
} else if (type->kind == Type_Generic) {
if (type->Generic.specialized) {
return default_type(type->Generic.specialized);
}
}
return type;
}
@@ -3327,6 +3356,15 @@ gb_internal Selection lookup_field_with_selection(Type *type_, String field_name
}
}
}
Type *objc_ivar_type = e->TypeName.objc_ivar;
if (objc_ivar_type != nullptr) {
sel = lookup_field_with_selection(objc_ivar_type, field_name, false, sel, allow_blank_ident);
if (sel.entity != nullptr) {
sel.pseudo_field = true;
return sel;
}
}
}
if (is_type_polymorphic(type)) {