From a3de9c8de4e539905a85f3cc060f95529b402f18 Mon Sep 17 00:00:00 2001 From: Harold Brenes Date: Sat, 19 Apr 2025 08:04:23 -0400 Subject: Add initial support for Objective-C class implementation --- src/llvm_backend.cpp | 591 ++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 558 insertions(+), 33 deletions(-) (limited to 'src/llvm_backend.cpp') diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 083a1d90e..23ad81847 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -1173,6 +1173,332 @@ gb_internal lbProcedure *lb_create_objc_names(lbModule *main_module) { return p; } +// TODO(harold): Move this out of here and into a more suitable place. +// TODO(harold): Should not take an allocator, but always use temp, as we return string literals as well. +String lb_get_objc_type_encoding(Type *t, gbAllocator allocator, 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.ptr_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"); + + // TODO(harold) These: + case Basic_complex32: + case Basic_complex64: + case Basic_complex128: + case Basic_quaternion64: + case Basic_quaternion128: + case Basic_quaternion256: + return str_lit("?"); + + case Basic_int: + return str_lit(INT_SIZE_ENCODING); + case Basic_uint: + return build_context.metrics.ptr_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.ptr_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, allocator, 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(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 : t->Struct.fields ) { + String field_type = lb_get_objc_type_encoding(f->type, allocator, pointer_depth); + s = gb_string_append_length(s, field_type.text, field_type.len); + } + } else { + // #TODO(harold): Encode fields + } + } + + 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, allocator, pointer_depth +1); + // Special case for Objective-C Objects + if (pointer_depth == 0 && pointee == "@") { + return pointee; + } + + return concatenate_strings(allocator, str_lit("^"), pointee); + } + + case Type_MultiPointer: + return concatenate_strings(allocator, str_lit("^"), lb_get_objc_type_encoding(t->Pointer.elem, allocator, pointer_depth +1)); + + case Type_Array: { + String type_str = lb_get_objc_type_encoding(t->Array.elem, allocator, pointer_depth); + + gbString s = gb_string_make_reserve(allocator, type_str.len + 8); + s = gb_string_append_fmt(s, "[%lld%s]", t->Array.count, type_str.text); + return make_string_c(s); + } + + case Type_EnumeratedArray: { + String type_str = lb_get_objc_type_encoding(t->EnumeratedArray.elem, allocator, pointer_depth); + + gbString s = gb_string_make_reserve(allocator, type_str.len + 8); + s = gb_string_append_fmt(s, "[%lld%s]", t->EnumeratedArray.count, type_str.text); + return make_string_c(s); + } + + case Type_Slice: { + String type_str = lb_get_objc_type_encoding(t->Slice.elem, allocator, pointer_depth); + gbString s = gb_string_make_reserve(allocator, type_str.len + 8); + s = gb_string_append_fmt(s, "{slice=^%s%s}", type_str, INT_SIZE_ENCODING); + return make_string_c(s); + } + + case Type_DynamicArray: { + String type_str = lb_get_objc_type_encoding(t->DynamicArray.elem, allocator, pointer_depth); + gbString s = gb_string_make_reserve(allocator, type_str.len + 8); + s = gb_string_append_fmt(s, "{dynamic=^%s%s%sAllocator={?^v}}", 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, allocator, pointer_depth); + case Type_Tuple: + // NOTE(harold): Is this allowed here? + return str_lit("?"); + case Type_Proc: + return str_lit("?"); + case Type_BitSet: + return lb_get_objc_type_encoding(t->BitSet.underlying, allocator, pointer_depth); + case Type_SimdVector: + break; + case Type_Matrix: + break; + case Type_BitField: + return lb_get_objc_type_encoding(t->BitField.backing_type, allocator, pointer_depth); + case Type_SoaPointer: { + gbString s = gb_string_make_reserve(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"); +} + +struct lbObjCGlobalClass { + lbObjCGlobal g; + lbValue class_value; // Local registered class value +}; + +gb_internal void lb_register_objc_thing( + StringSet &handled, + lbModule *m, + Array &args, + Array &class_impls, + StringMap &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{}; + + 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 == nullptr ? lb_const_nil(m, t_objc_Class) : 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,39 +1512,238 @@ gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) { string_set_init(&handled); defer (string_set_destroy(&handled)); - auto args = array_make(temporary_allocator(), 1); - - 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"); - } - - for (lbObjCGlobal g = {}; mpsc_dequeue(&gen->objc_selectors, &g); /**/) { - register_thing(p, g, "sel_registerName"); - } + auto args = array_make(temporary_allocator(), 3, 8); + auto class_impls = array_make(temporary_allocator(), 0, 16); + + // 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(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 + // need their superclasses to have been registered before them. + StringMap 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); + + // 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* 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); /**/) { + 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(temporary_allocator(), 2, 8); + + for (const auto& cd : class_impls) { + auto& g = cd.g; + Type *class_type = g.class_impl_type; + + Array* methods = map_get(&m->info->objc_method_implementations, class_type); + if (!methods) { + continue; + } + + Type *class_ptr_type = alloc_type_pointer(class_type); + lbValue class_value = cd.class_value; + + 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; + + auto method_param_count = (isize)method_type->Proc.param_count; + i32 method_param_offset = 0; + + // TODO(harold): Need to make sure (at checker stage) that the non-class method has the self parameter already. + // (Maybe this is already accounted for?.) + if (!md.ac.objc_is_class_method) { + GB_ASSERT(method_param_count >= 1); + method_param_count -= 1; + method_param_offset = 1; + } + + for (i32 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_proc_type = alloc_type_proc(nullptr, wrapper_args_tuple, (isize)wrapper_args_tuple->Tuple.variables.count, nullptr, 0, 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); + { + auto method_call_args = array_make(temporary_allocator(), method_param_count + (isize)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. + lb_emit_call(wrapper_proc, method_proc_value, method_call_args); + } + 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, temporary_allocator()); + } + + 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 (i32 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, temporary_allocator()); + + 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 + Type *ivar_type = class_type->Named.type_name->TypeName.objc_ivar; + if (ivar_type != nullptr) { + // Register a single ivar for this class + Type *ivar_base = ivar_type->Named.base; + // TODO(harold): No idea if I can use this, but I assume so? + const i64 size = ivar_base->cached_size; + const i64 alignment = ivar_base->cached_align; + // TODO(harold): Checker: Alignment must be compatible with ivar rules. Or we should increase the alignment if needed. + + String ivar_name = str_lit("__$ivar"); + String ivar_types = str_lit("{= }"); + 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); + + // If we have an ivar, store its offset globally for an intrinsic + // TODO(harold): Only do this for types that had ivar_get calls registered! + if (ivar_type != nullptr) { + 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_u32 = lb_emit_conv(p, ivar_offset, t_u32); + + String class_name = class_type->Named.type_name->TypeName.objc_class_name; + // TODO(harold): Oops! This is wrong, that map is there to prevent re-entry. + // Simply emit from referred ivars. For now use a single module only. + lbAddr ivar_addr = string_map_must_get(&m->objc_ivars, class_name); + lb_addr_store(p, ivar_addr, ivar_offset_u32); + } + } lb_end_procedure_body(p); } -- cgit v1.2.3 From b3b4d501ca6c1af70c0ceef7610f6c5d83a2d296 Mon Sep 17 00:00:00 2001 From: Harold Brenes Date: Mon, 21 Apr 2025 02:42:02 -0400 Subject: Fix ivar in multi-module mode. --- src/llvm_backend.cpp | 69 ++++++++++++++++++++++++++++++-------------- src/llvm_backend_utility.cpp | 14 ++++----- 2 files changed, 54 insertions(+), 29 deletions(-) (limited to 'src/llvm_backend.cpp') diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 23ad81847..ef975b8c1 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -1588,6 +1588,13 @@ gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) { // Emit method wrapper implementations and registration auto wrapper_args = array_make(temporary_allocator(), 2, 8); + PtrMap 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; @@ -1700,7 +1707,9 @@ gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) { // Add ivar if we have one Type *ivar_type = class_type->Named.type_name->TypeName.objc_ivar; - if (ivar_type != nullptr) { + lbObjCGlobal *g_ivar = map_get(&ivar_map, class_type); + + if (ivar_type != nullptr && g_ivar != nullptr) { // Register a single ivar for this class Type *ivar_base = ivar_type->Named.base; // TODO(harold): No idea if I can use this, but I assume so? @@ -1723,28 +1732,46 @@ gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) { args.count = 1; args[0] = class_value; lb_emit_runtime_call(p, "objc_registerClassPair", args); - - // If we have an ivar, store its offset globally for an intrinsic - // TODO(harold): Only do this for types that had ivar_get calls registered! - if (ivar_type != nullptr) { - 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_u32 = lb_emit_conv(p, ivar_offset, t_u32); - - String class_name = class_type->Named.type_name->TypeName.objc_class_name; - // TODO(harold): Oops! This is wrong, that map is there to prevent re-entry. - // Simply emit from referred ivars. For now use a single module only. - lbAddr ivar_addr = string_map_must_get(&m->objc_ivars, class_name); - lb_addr_store(p, ivar_addr, ivar_offset_u32); - } } + // Register ivars + Type *ptr_u32 = alloc_type_pointer(t_u32); + 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); + } else { + // Defined in an external package, must define now + LLVMTypeRef t = lb_type(m, t_u32); + + lbValue global{}; + global.value = LLVMAddGlobal(m->mod, t, g.global_name); + global.type = ptr_u32; + + 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_u32 = lb_emit_conv(p, ivar_offset, t_u32); + + lb_addr_store(p, ivar_addr, ivar_offset_u32); + } + lb_end_procedure_body(p); } diff --git a/src/llvm_backend_utility.cpp b/src/llvm_backend_utility.cpp index 897b71b5b..ae7842ce6 100644 --- a/src/llvm_backend_utility.cpp +++ b/src/llvm_backend_utility.cpp @@ -2165,28 +2165,26 @@ gb_internal lbAddr lb_handle_objc_find_or_register_ivar(lbModule *m, Type *self_ 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 - Type *p_ivar_offset = alloc_type_pointer(t_u32); + LLVMTypeRef t = lb_type(m, t_u32); - LLVMTypeRef t = lb_type(m, p_ivar_offset); lbValue g = {}; g.value = LLVMAddGlobal(m->mod, t, global_name); - g.type = p_ivar_offset; + g.type = alloc_type_pointer(t_u32); if (default_module == m) { - LLVMSetInitializer(g.value, LLVMConstNull(t)); + 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, self_type}); + mpsc_enqueue(&m->gen->objc_ivars, lbObjCGlobal{m, global_name, name, t_u32, self_type}); lbAddr addr = lb_addr(g); string_map_set(&m->objc_ivars, name, addr); @@ -2206,8 +2204,8 @@ gb_internal lbValue lb_handle_objc_ivar_get(lbProcedure *p, Ast *expr) { lbValue ivar_offset = lb_addr_load(p, lb_handle_objc_find_or_register_ivar(m, self_type)); lbValue ivar_offset_uptr = lb_emit_conv(p, ivar_offset, t_uintptr); - lbValue self = lb_build_expr(p, ce->args[0]); - lbValue self_uptr = lb_emit_conv(p, self, t_uintptr); + lbValue self = lb_build_expr(p, ce->args[0]); + lbValue self_uptr = lb_emit_conv(p, self, t_uintptr); lbValue ivar_uptr = lb_emit_arith(p, Token_Add, self_uptr, ivar_offset_uptr, t_uintptr); -- cgit v1.2.3 From bca02f81cd5affa288bac0cc0ed08fe730072aec Mon Sep 17 00:00:00 2001 From: Harold Brenes Date: Tue, 22 Apr 2025 19:16:29 -0400 Subject: Include the ivar in the Objective-C class unconditionally of it being used or not. Allow pseudo-fields for ivar access. --- src/llvm_backend.cpp | 5 ++--- src/llvm_backend_expr.cpp | 32 ++++++++++++++++++++++++-------- src/llvm_backend_utility.cpp | 31 ++++++++++++++++++------------- src/types.cpp | 9 +++++++++ 4 files changed, 53 insertions(+), 24 deletions(-) (limited to 'src/llvm_backend.cpp') diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index ef975b8c1..257625849 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -1707,9 +1707,8 @@ gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) { // Add ivar if we have one Type *ivar_type = class_type->Named.type_name->TypeName.objc_ivar; - lbObjCGlobal *g_ivar = map_get(&ivar_map, class_type); - if (ivar_type != nullptr && g_ivar != nullptr) { + if (ivar_type != nullptr) { // Register a single ivar for this class Type *ivar_base = ivar_type->Named.base; // TODO(harold): No idea if I can use this, but I assume so? @@ -1734,7 +1733,7 @@ gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) { lb_emit_runtime_call(p, "objc_registerClassPair", args); } - // Register ivars + // Register ivar offsets for any `objc_ivar_get` expressions emitted. Type *ptr_u32 = alloc_type_pointer(t_u32); for (auto const& kv : ivar_map) { lbObjCGlobal const& g = kv.value; diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index c5ea0ddac..b9c01ad03 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -5138,8 +5138,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) { @@ -5174,18 +5172,37 @@ 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)) { + // GB_ASSERT(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); + + // TODO(harold): Ensure objc_ivar is always null when objc_implement is not set! + Type *d_type = type_deref(tav.type); //base_type(tav.type); + if (d_type->kind == Type_Named && d_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. + + // If it's a deep pointer, dereference it first + // TODO(harold): Ensure this is save to do here. lb_emit_deep_field_gep() has several derefs, once per index. + // Not sure what multiple indices represent... + Type* type = tav.type; + if (is_type_pointer(type)) { + type = type_deref(type); + 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; @@ -5211,7 +5228,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); diff --git a/src/llvm_backend_utility.cpp b/src/llvm_backend_utility.cpp index ae7842ce6..33211395a 100644 --- a/src/llvm_backend_utility.cpp +++ b/src/llvm_backend_utility.cpp @@ -2191,25 +2191,30 @@ gb_internal lbAddr lb_handle_objc_find_or_register_ivar(lbModule *m, Type *self_ return addr; } -gb_internal lbValue lb_handle_objc_ivar_get(lbProcedure *p, Ast *expr) { - ast_node(ce, CallExpr, expr); - lbModule *m = p->module; +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); - GB_ASSERT(ce->args[0]->tav.type->kind == Type_Pointer); - Type *self_type = ce->args[0]->tav.type->Pointer.elem; - Type *ivar_type = self_type->Named.type_name->TypeName.objc_ivar; + 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); - Type* p_ivar = alloc_type_pointer(ivar_type); - lbValue ivar_offset = lb_addr_load(p, lb_handle_objc_find_or_register_ivar(m, 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); - lbValue self = lb_build_expr(p, ce->args[0]); - lbValue self_uptr = lb_emit_conv(p, self, 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)); +} - lbValue ivar_uptr = lb_emit_arith(p, Token_Add, self_uptr, ivar_offset_uptr, t_uintptr); +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_emit_conv(p, ivar_uptr, p_ivar); + return lb_handle_objc_ivar_for_objc_object_pointer(p, self); } gb_internal lbValue lb_handle_objc_find_selector(lbProcedure *p, Ast *expr) { diff --git a/src/types.cpp b/src/types.cpp index 1b2545279..96c17f49a 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -3329,6 +3329,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)) { -- cgit v1.2.3 From 730c844fc66a9afa6cb8f5ac599cf18c475c7fec Mon Sep 17 00:00:00 2001 From: Harold Brenes Date: Tue, 22 Apr 2025 21:45:34 -0400 Subject: Fix bug in @encode implementation. --- src/llvm_backend.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/llvm_backend.cpp') diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 257625849..80b720984 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -1336,7 +1336,7 @@ String lb_get_objc_type_encoding(Type *t, gbAllocator allocator, isize pointer_d s = gb_string_append_length(s, "=", 1); if (!is_union) { - for( auto& f : t->Struct.fields ) { + for( auto& f : base->Struct.fields ) { String field_type = lb_get_objc_type_encoding(f->type, allocator, pointer_depth); s = gb_string_append_length(s, field_type.text, field_type.len); } @@ -1717,7 +1717,7 @@ gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) { // TODO(harold): Checker: Alignment must be compatible with ivar rules. Or we should increase the alignment if needed. String ivar_name = str_lit("__$ivar"); - String ivar_types = str_lit("{= }"); + String ivar_types = str_lit("{= }"); //lb_get_objc_type_encoding(ivar_type, temporary_allocator());// str_lit("{= }"); args.count = 5; args[0] = class_value; args[1] = lb_const_value(m, t_cstring, exact_value_string(ivar_name)); -- cgit v1.2.3 From 47abea12290647f371b0488a179d3b254c7489a5 Mon Sep 17 00:00:00 2001 From: Harold Brenes Date: Wed, 23 Apr 2025 01:41:38 -0400 Subject: Add support for Objective-C method implementation with Odin calling convention. Use @objc_context_provider to provide a context for a type. --- src/check_decl.cpp | 66 ++++++++++++++++++++++++++++++++++------------------ src/checker.cpp | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/checker.hpp | 2 ++ src/entity.cpp | 1 + src/llvm_backend.cpp | 43 ++++++++++++++++++++++++++++++---- src/types.cpp | 23 ++++++++++++++++++ 6 files changed, 173 insertions(+), 26 deletions(-) (limited to 'src/llvm_backend.cpp') diff --git a/src/check_decl.cpp b/src/check_decl.cpp index dffe0b48e..e67241b31 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -528,13 +528,21 @@ gb_internal void check_type_decl(CheckerContext *ctx, Entity *e, Ast *init_expr, e->TypeName.objc_class_name = ac.objc_class; e->TypeName.objc_superclass = ac.objc_superclass; e->TypeName.objc_ivar = ac.objc_ivar; + e->TypeName.objc_context_provider = ac.objc_context_provider; if (ac.objc_is_implementation) { e->TypeName.objc_is_implementation = true; - mpsc_enqueue(&ctx->info->objc_class_implementations, e); // TODO(harold): Don't need this for anything. Remove. + mpsc_enqueue(&ctx->info->objc_class_implementations, e); // TODO(harold): Don't need this for anything? See if needed when using explicit @export GB_ASSERT(e->TypeName.objc_ivar == nullptr || e->TypeName.objc_ivar->kind == Type_Named); + // Enqueue the proc to be checked when 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 Type *super = ac.objc_superclass; if (super != nullptr) { @@ -571,8 +579,14 @@ gb_internal void check_type_decl(CheckerContext *ctx, Entity *e, Ast *init_expr, check_single_global_entity(ctx->checker, super->Named.type_name, super->Named.type_name->decl_info); } } - } else if (e->TypeName.objc_superclass != nullptr) { - error(e->token, "@(objc_superclass) can only be applied when the obj_implement attribute is also applied"); + } else { + if (e->TypeName.objc_superclass != nullptr) { + error(e->token, "@(objc_superclass) can only be applied when the @(obj_implement) attribute is also applied"); + } else if (e->TypeName.objc_ivar != nullptr) { + error(e->token, "@(objc_ivar) can only be applied when the @(obj_implement) attribute is also applied"); + } else if (e->TypeName.objc_context_provider != nullptr) { + error(e->token, "@(objc_context_provider) can only be applied when the @(obj_implement) attribute is also applied"); + } } if (type_size_of(e->type) > 0) { @@ -994,25 +1008,33 @@ gb_internal void check_objc_methods(CheckerContext *ctx, Entity *e, AttributeCon if (ac.objc_is_implementation) { GB_ASSERT(e->kind == Entity_Procedure); - CheckerInfo *info = ctx->info; - mutex_lock(&info->objc_method_mutex); - defer (mutex_unlock(&info->objc_method_mutex)); - - auto method = ObjcMethodData{ ac, e }; - - if (ac.objc_selector == "") { - method.ac.objc_selector = ac.objc_name; - } - - Array* method_list = map_get(&info->objc_method_implementations, t); - if (method_list) { - array_add(method_list, method); - } else { - auto list = array_make(permanent_allocator(), 1, 8); - list[0] = method; - - map_set(&info->objc_method_implementations, t, list); - } + Type *proc_type = e->type; + + 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 (proc_type->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_type->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 { + + 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* method_list = map_get(&info->objc_method_implementations, t); + if (method_list) { + array_add(method_list, method); + } else { + auto list = array_make(permanent_allocator(), 1, 8); + list[0] = method; + + map_set(&info->objc_method_implementations, t, list); + } + } } mutex_lock(&global_type_name_objc_metadata_mutex); diff --git a/src/checker.cpp b/src/checker.cpp index 29ef7d2b3..79c773a3c 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -1502,6 +1502,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); @@ -3974,6 +3976,23 @@ gb_internal DECL_ATTRIBUTE_PROC(type_decl_attribute) { 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; } @@ -6462,6 +6481,47 @@ 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); + + Type *proc_type = proc_entity->type; + + // TODO(harold): Give better errors here (specify exactly what's wrong) + const char* signature_error = "The procedure for @(objc_context_provider) has an incorrect signature."; + + if (proc_type->Proc.param_count != 1 || proc_type->Proc.result_count != 1) { + error(proc_entity->token, signature_error); + } else { + Type *self_param = base_type(proc_type->Proc.params->Tuple.variables[0]->type); + Type *return_type = base_named_type(proc_type->Proc.results->Tuple.variables[0]->type); + + if (self_param->kind != Type_Pointer) { + error(proc_entity->token, signature_error); + continue; + } + + self_param = base_named_type(self_param->Pointer.elem); + + if (return_type != t_context) { + error(e->token, signature_error); + } else if (!internal_check_is_assignable_to(self_param, e->type) && + (e->TypeName.objc_ivar && !internal_check_is_assignable_to(self_param, e->TypeName.objc_ivar)) + ) { + error(e->token, signature_error); + } else if (proc_type->Proc.calling_convention != ProcCC_CDecl && + proc_type->Proc.calling_convention != ProcCC_Contextless) { + error(e->token, signature_error); + } else if (proc_type->Proc.is_polymorphic) { + error(e->token, signature_error); + } + } + } +} + gb_internal void check_unique_package_names(Checker *c) { ERROR_BLOCK(); @@ -6609,6 +6669,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); @@ -6718,6 +6779,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); diff --git a/src/checker.hpp b/src/checker.hpp index 9910ed17b..574c71c7f 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -152,6 +152,7 @@ struct AttributeContext { Type * objc_type; Type * objc_superclass; Type * objc_ivar; + Entity *objc_context_provider; bool objc_is_class_method : 1; bool objc_is_implementation : 1; // This struct or proc provides a class/method implementation, not a binding to an existing type. @@ -570,6 +571,7 @@ struct Checker { CheckerContext builtin_ctx; MPSCQueue procs_with_deferred_to_check; + MPSCQueue procs_with_objc_context_provider_to_check; Array procs_to_check; BlockingMutex nested_proc_lits_mutex; diff --git a/src/entity.cpp b/src/entity.cpp index 9a5996e3d..a5443cf27 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -238,6 +238,7 @@ struct Entity { bool objc_is_implementation; Type* objc_superclass; Type* objc_ivar; + Entity*objc_context_provider; String objc_class_name; TypeNameObjCMetadata *objc_metadata; } TypeName; diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 80b720984..1d0ce7460 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -1586,7 +1586,9 @@ gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) { // Emit method wrapper implementations and registration - auto wrapper_args = array_make(temporary_allocator(), 2, 8); + auto wrapper_args = array_make(temporary_allocator(), 2, 8); + auto get_context_args = array_make(temporary_allocator(), 1); + PtrMap ivar_map{}; map_init(&ivar_map, gen->objc_ivars.count); @@ -1599,6 +1601,13 @@ gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) { auto& g = cd.g; Type *class_type = g.class_impl_type; + Entity *context_provider = class_type->Named.type_name->TypeName.objc_context_provider; + lbValue context_provider_proc_value{}; + if (context_provider) { + context_provider_proc_value = lb_find_procedure_value_from_entity(m, context_provider); + } + + Array* methods = map_get(&m->info->objc_method_implementations, class_type); if (!methods) { continue; @@ -1645,6 +1654,31 @@ gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) { 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 + + // TODO(harold): Get these values at the top, at the start of the method loop for a class + Type *ctx_provider_proc = context_provider->type; + + Type *self_param_ptr_type = base_type(ctx_provider_proc->Proc.params->Tuple.variables[0]->type); + GB_ASSERT(self_param_ptr_type->kind == Type_Pointer); + + // TODO(harold): Set the arg type to the ivar's type, if the context provider takes the ivar's type. + // Type *self_param_type = base_named_type(type_deref(self_param_ptr_type)); + + get_context_args[0] = lbValue { + wrapper_proc->raw_input_parameters[0], + self_param_ptr_type, + }; + + 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(temporary_allocator(), method_param_count + (isize)method_param_offset); if (!md.ac.objc_is_class_method) { @@ -1711,11 +1745,12 @@ gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) { if (ivar_type != nullptr) { // Register a single ivar for this class Type *ivar_base = ivar_type->Named.base; - // TODO(harold): No idea if I can use this, but I assume so? - const i64 size = ivar_base->cached_size; - const i64 alignment = ivar_base->cached_align; + + const i64 size = type_size_of(ivar_base); + const i64 alignment = type_align_of(ivar_base); // TODO(harold): Checker: Alignment must be compatible with ivar rules. Or we should increase the alignment if needed. + // TODO(harold): 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, temporary_allocator());// str_lit("{= }"); args.count = 5; diff --git a/src/types.cpp b/src/types.cpp index 96c17f49a..e1386c1f5 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -874,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 && -- cgit v1.2.3 From 974a197ce1493c274a2ab67449624b14c1a405e6 Mon Sep 17 00:00:00 2001 From: Harold Brenes Date: Wed, 23 Apr 2025 01:59:22 -0400 Subject: Allow the class's ivar to be used as a context provider receiver as well. --- src/llvm_backend.cpp | 40 +++++++++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 15 deletions(-) (limited to 'src/llvm_backend.cpp') diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 1d0ce7460..fad542d4a 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -1600,11 +1600,25 @@ gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) { 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); } @@ -1613,9 +1627,6 @@ gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) { continue; } - Type *class_ptr_type = alloc_type_pointer(class_type); - lbValue class_value = cd.class_value; - for (const ObjcMethodData& md : *methods) { GB_ASSERT( md.proc_entity->kind == Entity_Procedure); Type *method_type = md.proc_entity->type; @@ -1659,20 +1670,21 @@ gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) { // Emit the get odin context call - // TODO(harold): Get these values at the top, at the start of the method loop for a class - Type *ctx_provider_proc = context_provider->type; - - Type *self_param_ptr_type = base_type(ctx_provider_proc->Proc.params->Tuple.variables[0]->type); - GB_ASSERT(self_param_ptr_type->kind == Type_Pointer); - - // TODO(harold): Set the arg type to the ivar's type, if the context provider takes the ivar's type. - // Type *self_param_type = base_named_type(type_deref(self_param_ptr_type)); - get_context_args[0] = lbValue { wrapper_proc->raw_input_parameters[0], - self_param_ptr_type, + contex_provider_self_ptr_type, }; + if (is_context_provider_ivar) { + // The context provider takes the ivar's type. + // Emit an obj_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); @@ -1740,8 +1752,6 @@ gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) { } // End methods // Add ivar if we have one - Type *ivar_type = class_type->Named.type_name->TypeName.objc_ivar; - if (ivar_type != nullptr) { // Register a single ivar for this class Type *ivar_base = ivar_type->Named.base; -- cgit v1.2.3 From f3923ed66640ea9fd342ca851fdd2bd794405e0c Mon Sep 17 00:00:00 2001 From: Harold Brenes Date: Sun, 27 Apr 2025 22:48:16 -0400 Subject: Fix indentations Fix Objective-C wrapper procs not forwarding return value --- src/check_builtin.cpp | 144 +++--- src/check_decl.cpp | 174 +++---- src/checker.cpp | 150 +++--- src/checker.hpp | 18 +- src/checker_builtin_procs.hpp | 4 +- src/entity.cpp | 6 +- src/llvm_backend.cpp | 1108 +++++++++++++++++++++-------------------- src/llvm_backend.hpp | 6 +- src/llvm_backend_general.cpp | 4 +- src/llvm_backend_proc.cpp | 2 +- src/llvm_backend_utility.cpp | 14 +- 11 files changed, 822 insertions(+), 808 deletions(-) (limited to 'src/llvm_backend.cpp') diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index 92942b4db..099f99045 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -389,77 +389,77 @@ gb_internal bool check_builtin_objc_procedure(CheckerContext *c, Operand *operan } break; case BuiltinProc_objc_ivar_get: - { - Type *self_type = nullptr; - Type *ivar_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 @(obj_class=) , got type %s", LIT(builtin_name), t); - gb_string_free(t); - return false; - } - - if (self_type->Named.type_name->TypeName.objc_ivar == nullptr) { - gbString t = type_to_string(self_type); - error(self.expr, "'%.*s' requires that type %s have the attribute @(obj_ivar=).", LIT(builtin_name), t); - gb_string_free(t); - return false; - } - - Operand ivar = {}; - check_expr_or_type(c, &ivar, ce->args[1]); - if (ivar.mode == Addressing_Type) { - ivar_type = ivar.type; - } else { - return false; - } - - if (self_type->Named.type_name->TypeName.objc_ivar != ivar_type) { - gbString name_self = type_to_string(self_type); - gbString name_expected = type_to_string(self_type->Named.type_name->TypeName.objc_ivar); - gbString name_given = type_to_string(ivar_type); - error(self.expr, "'%.*s' ivar type %s does not match @obj_ivar type %s on Objective-C class %s.", - LIT(builtin_name), name_given, name_expected, name_self); - gb_string_free(name_self); - gb_string_free(name_expected); - gb_string_free(name_given); - 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; + { + Type *self_type = nullptr; + Type *ivar_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 @(obj_class=) , got type %s", LIT(builtin_name), t); + gb_string_free(t); + return false; + } + + if (self_type->Named.type_name->TypeName.objc_ivar == nullptr) { + gbString t = type_to_string(self_type); + error(self.expr, "'%.*s' requires that type %s have the attribute @(obj_ivar=).", LIT(builtin_name), t); + gb_string_free(t); + return false; + } + + Operand ivar = {}; + check_expr_or_type(c, &ivar, ce->args[1]); + if (ivar.mode == Addressing_Type) { + ivar_type = ivar.type; + } else { + return false; + } + + if (self_type->Named.type_name->TypeName.objc_ivar != ivar_type) { + gbString name_self = type_to_string(self_type); + gbString name_expected = type_to_string(self_type->Named.type_name->TypeName.objc_ivar); + gbString name_given = type_to_string(ivar_type); + error(self.expr, "'%.*s' ivar type %s does not match @obj_ivar type %s on Objective-C class %s.", + LIT(builtin_name), name_given, name_expected, name_self); + gb_string_free(name_self); + gb_string_free(name_expected); + gb_string_free(name_given); + 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; } } @@ -2206,7 +2206,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As case BuiltinProc_objc_find_class: case BuiltinProc_objc_register_selector: case BuiltinProc_objc_register_class: - case BuiltinProc_objc_ivar_get: + case BuiltinProc_objc_ivar_get: return check_builtin_objc_procedure(c, operand, call, id, type_hint); case BuiltinProc___entry_point: diff --git a/src/check_decl.cpp b/src/check_decl.cpp index e67241b31..48e5172d6 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -526,68 +526,68 @@ gb_internal void check_type_decl(CheckerContext *ctx, Entity *e, Ast *init_expr, 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; - e->TypeName.objc_superclass = ac.objc_superclass; - e->TypeName.objc_ivar = ac.objc_ivar; + e->TypeName.objc_superclass = ac.objc_superclass; + e->TypeName.objc_ivar = ac.objc_ivar; e->TypeName.objc_context_provider = ac.objc_context_provider; - if (ac.objc_is_implementation) { - e->TypeName.objc_is_implementation = true; - mpsc_enqueue(&ctx->info->objc_class_implementations, e); // TODO(harold): Don't need this for anything? See if needed when using explicit @export - - GB_ASSERT(e->TypeName.objc_ivar == nullptr || e->TypeName.objc_ivar->kind == Type_Named); - - // Enqueue the proc to be checked when 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 - Type *super = ac.objc_superclass; - if (super != nullptr) { - TypeSet super_set{}; - type_set_init(&super_set, 8); - defer (type_set_destroy(&super_set)); - - type_set_update(&super_set, e->type); - - for (;;) { - if (type_set_update(&super_set, super)) { - error(e->token, "@(objc_superclass) Superclass hierarchy cycle encountered"); - break; - } - - if (super->kind != Type_Named) { - error(e->token, "@(objc_superclass) References type must be a named struct."); - break; - } - - Type* named_type = base_type(super->Named.type_name->type); - if (!is_type_objc_object(named_type)) { - error(e->token, "@(objc_superclass) Superclass must be an Objective-C class."); - break; - } - - super = super->Named.type_name->TypeName.objc_superclass; - if (super == nullptr) { - break; - } - - // TODO(harold): Is this the right way to do this??? The referenced entity must be already resolved - // so that we can access its objc_superclass attribute - check_single_global_entity(ctx->checker, super->Named.type_name, super->Named.type_name->decl_info); - } - } - } else { - if (e->TypeName.objc_superclass != nullptr) { - error(e->token, "@(objc_superclass) can only be applied when the @(obj_implement) attribute is also applied"); - } else if (e->TypeName.objc_ivar != nullptr) { - error(e->token, "@(objc_ivar) can only be applied when the @(obj_implement) attribute is also applied"); - } else if (e->TypeName.objc_context_provider != nullptr) { - error(e->token, "@(objc_context_provider) can only be applied when the @(obj_implement) attribute is also applied"); - } - } + if (ac.objc_is_implementation) { + e->TypeName.objc_is_implementation = true; + mpsc_enqueue(&ctx->info->objc_class_implementations, e); // TODO(harold): Don't need this for anything? See if needed when using explicit @export + + GB_ASSERT(e->TypeName.objc_ivar == nullptr || e->TypeName.objc_ivar->kind == Type_Named); + + // Enqueue the proc to be checked when 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 + Type *super = ac.objc_superclass; + if (super != nullptr) { + TypeSet super_set{}; + type_set_init(&super_set, 8); + defer (type_set_destroy(&super_set)); + + type_set_update(&super_set, e->type); + + for (;;) { + if (type_set_update(&super_set, super)) { + error(e->token, "@(objc_superclass) Superclass hierarchy cycle encountered"); + break; + } + + if (super->kind != Type_Named) { + error(e->token, "@(objc_superclass) References type must be a named struct."); + break; + } + + Type* named_type = base_type(super->Named.type_name->type); + if (!is_type_objc_object(named_type)) { + error(e->token, "@(objc_superclass) Superclass must be an Objective-C class."); + break; + } + + super = super->Named.type_name->TypeName.objc_superclass; + if (super == nullptr) { + break; + } + + // TODO(harold): Is this the right way to do this??? The referenced entity must be already resolved + // so that we can access its objc_superclass attribute + check_single_global_entity(ctx->checker, super->Named.type_name, super->Named.type_name->decl_info); + } + } + } else { + if (e->TypeName.objc_superclass != nullptr) { + error(e->token, "@(objc_superclass) can only be applied when the @(obj_implement) attribute is also applied"); + } else if (e->TypeName.objc_ivar != nullptr) { + error(e->token, "@(objc_ivar) can only be applied when the @(obj_implement) attribute is also applied"); + } else if (e->TypeName.objc_context_provider != nullptr) { + error(e->token, "@(objc_context_provider) can 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"); @@ -1005,37 +1005,37 @@ gb_internal void check_objc_methods(CheckerContext *ctx, Entity *e, AttributeCon error(e->token, "@(objc_name) attribute may only be applied to procedures and types within the same scope"); } else { - if (ac.objc_is_implementation) { - GB_ASSERT(e->kind == Entity_Procedure); + if (ac.objc_is_implementation) { + GB_ASSERT(e->kind == Entity_Procedure); - Type *proc_type = e->type; + Type *proc_type = e->type; - 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 (proc_type->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_type->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 (!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 (proc_type->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_type->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 { 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* method_list = map_get(&info->objc_method_implementations, t); - if (method_list) { - array_add(method_list, method); - } else { - auto list = array_make(permanent_allocator(), 1, 8); - list[0] = method; - - map_set(&info->objc_method_implementations, t, list); - } - } - } + 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* method_list = map_get(&info->objc_method_implementations, t); + if (method_list) { + array_add(method_list, method); + } else { + auto list = array_make(permanent_allocator(), 1, 8); + list[0] = method; + + map_set(&info->objc_method_implementations, t, list); + } + } + } mutex_lock(&global_type_name_objc_metadata_mutex); defer (mutex_unlock(&global_type_name_objc_metadata_mutex)); diff --git a/src/checker.cpp b/src/checker.cpp index 79c773a3c..6563b1c58 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -1351,12 +1351,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_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); + t_objc_Ivar = alloc_type_pointer(t_objc_ivar); } } @@ -1389,8 +1389,8 @@ 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); - map_init(&i->objc_method_implementations); + mpsc_init(&i->objc_class_implementations, a); + map_init(&i->objc_method_implementations); string_map_init(&i->load_file_cache); array_init(&i->all_procedures, heap_allocator()); @@ -3352,10 +3352,10 @@ gb_internal DECL_ATTRIBUTE_PROC(proc_decl_attribute) { ac->test = true; return true; } else if (name == "export") { - if (ac->objc_is_implementation) { - error(value, "Setting @(export) explicitly is not allowed when @(objc_implement) is set. It is exported implicitly."); - return false; - } + if (ac->objc_is_implementation) { + error(value, "Setting @(export) explicitly is not allowed when @(objc_implement) is set. It is exported implicitly."); + return false; + } ExactValue ev = check_decl_attribute_value(c, value); if (ev.kind == ExactValue_Invalid) { @@ -3369,10 +3369,10 @@ gb_internal DECL_ATTRIBUTE_PROC(proc_decl_attribute) { return true; } else if (name == "linkage") { - if (ac->objc_is_implementation) { - error(value, "Explicit linkage not allowed when @(objc_implement) is set. It is set implicitly"); - return false; - } + if (ac->objc_is_implementation) { + error(value, "Explicit linkage not allowed when @(objc_implement) is set. It is set implicitly"); + return false; + } ExactValue ev = check_decl_attribute_value(c, value); if (ev.kind != ExactValue_String) { @@ -3681,23 +3681,23 @@ 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; - } 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)); - } - - // This implies exported, strongly linked - if (ac->objc_is_implementation) { - ac->is_export = true; - ac->linkage = str_lit("strong"); - } - - return true; - } else if (name == "objc_selector") { + 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)); + } + + // This implies exported, strongly linked + if (ac->objc_is_implementation) { + ac->is_export = true; + ac->linkage = str_lit("strong"); + } + + 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)) { @@ -3949,52 +3949,52 @@ gb_internal DECL_ATTRIBUTE_PROC(type_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; - } 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) { - 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; + 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) { + 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; } diff --git a/src/checker.hpp b/src/checker.hpp index 574c71c7f..336f09a7e 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -148,13 +148,13 @@ struct AttributeContext { String objc_class; String objc_name; - String objc_selector; + String objc_selector; Type * objc_type; - Type * objc_superclass; - Type * objc_ivar; + Type * objc_superclass; + Type * objc_ivar; Entity *objc_context_provider; bool objc_is_class_method : 1; - bool objc_is_implementation : 1; // This struct or proc provides a class/method implementation, not a binding to an existing type. + bool objc_is_implementation : 1; // This struct or proc provides a class/method implementation, not a binding to an existing type. String require_target_feature; // required by the target micro-architecture String enable_target_feature; // will be enabled for the procedure only @@ -371,8 +371,8 @@ struct ObjcMsgData { }; struct ObjcMethodData { - AttributeContext ac; - Entity *proc_entity; + AttributeContext ac; + Entity *proc_entity; }; enum LoadFileTier { @@ -489,10 +489,10 @@ struct CheckerInfo { BlockingMutex objc_types_mutex; PtrMap objc_msgSend_types; - MPSCQueue objc_class_implementations; + MPSCQueue objc_class_implementations; - BlockingMutex objc_method_mutex; - PtrMap> objc_method_implementations; + BlockingMutex objc_method_mutex; + PtrMap> objc_method_implementations; BlockingMutex load_file_mutex; diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp index cb2ce3915..ce7d8349b 100644 --- a/src/checker_builtin_procs.hpp +++ b/src/checker_builtin_procs.hpp @@ -331,7 +331,7 @@ BuiltinProc__type_end, BuiltinProc_objc_find_class, BuiltinProc_objc_register_selector, BuiltinProc_objc_register_class, - BuiltinProc_objc_ivar_get, + BuiltinProc_objc_ivar_get, BuiltinProc_constant_utf16_cstring, @@ -674,7 +674,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("ivar_get"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true}, + {STR_LIT("ivar_get"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true}, {STR_LIT("constant_utf16_cstring"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, diff --git a/src/entity.cpp b/src/entity.cpp index a5443cf27..cc41b5e59 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -235,9 +235,9 @@ struct Entity { Type * type_parameter_specialization; String ir_mangled_name; bool is_type_alias; - bool objc_is_implementation; - Type* objc_superclass; - Type* objc_ivar; + bool objc_is_implementation; + Type* objc_superclass; + Type* objc_ivar; Entity*objc_context_provider; String objc_class_name; TypeNameObjCMetadata *objc_metadata; diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index fad542d4a..7ffd4ea30 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -1176,327 +1176,327 @@ gb_internal lbProcedure *lb_create_objc_names(lbModule *main_module) { // TODO(harold): Move this out of here and into a more suitable place. // TODO(harold): Should not take an allocator, but always use temp, as we return string literals as well. String lb_get_objc_type_encoding(Type *t, gbAllocator allocator, 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.ptr_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"); - - // TODO(harold) These: - case Basic_complex32: - case Basic_complex64: - case Basic_complex128: - case Basic_quaternion64: - case Basic_quaternion128: - case Basic_quaternion256: - return str_lit("?"); - - case Basic_int: - return str_lit(INT_SIZE_ENCODING); - case Basic_uint: - return build_context.metrics.ptr_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.ptr_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, allocator, 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(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, allocator, pointer_depth); - s = gb_string_append_length(s, field_type.text, field_type.len); - } - } else { - // #TODO(harold): Encode fields - } - } - - 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, allocator, pointer_depth +1); - // Special case for Objective-C Objects - if (pointer_depth == 0 && pointee == "@") { - return pointee; - } - - return concatenate_strings(allocator, str_lit("^"), pointee); - } - - case Type_MultiPointer: - return concatenate_strings(allocator, str_lit("^"), lb_get_objc_type_encoding(t->Pointer.elem, allocator, pointer_depth +1)); - - case Type_Array: { - String type_str = lb_get_objc_type_encoding(t->Array.elem, allocator, pointer_depth); - - gbString s = gb_string_make_reserve(allocator, type_str.len + 8); - s = gb_string_append_fmt(s, "[%lld%s]", t->Array.count, type_str.text); - return make_string_c(s); - } - - case Type_EnumeratedArray: { - String type_str = lb_get_objc_type_encoding(t->EnumeratedArray.elem, allocator, pointer_depth); - - gbString s = gb_string_make_reserve(allocator, type_str.len + 8); - s = gb_string_append_fmt(s, "[%lld%s]", t->EnumeratedArray.count, type_str.text); - return make_string_c(s); - } - - case Type_Slice: { - String type_str = lb_get_objc_type_encoding(t->Slice.elem, allocator, pointer_depth); - gbString s = gb_string_make_reserve(allocator, type_str.len + 8); - s = gb_string_append_fmt(s, "{slice=^%s%s}", type_str, INT_SIZE_ENCODING); - return make_string_c(s); - } - - case Type_DynamicArray: { - String type_str = lb_get_objc_type_encoding(t->DynamicArray.elem, allocator, pointer_depth); - gbString s = gb_string_make_reserve(allocator, type_str.len + 8); - s = gb_string_append_fmt(s, "{dynamic=^%s%s%sAllocator={?^v}}", 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, allocator, pointer_depth); - case Type_Tuple: - // NOTE(harold): Is this allowed here? - return str_lit("?"); - case Type_Proc: - return str_lit("?"); - case Type_BitSet: - return lb_get_objc_type_encoding(t->BitSet.underlying, allocator, pointer_depth); - case Type_SimdVector: - break; - case Type_Matrix: - break; - case Type_BitField: - return lb_get_objc_type_encoding(t->BitField.backing_type, allocator, pointer_depth); - case Type_SoaPointer: { - gbString s = gb_string_make_reserve(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"); + // 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.ptr_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"); + + // TODO(harold) These: + case Basic_complex32: + case Basic_complex64: + case Basic_complex128: + case Basic_quaternion64: + case Basic_quaternion128: + case Basic_quaternion256: + return str_lit("?"); + + case Basic_int: + return str_lit(INT_SIZE_ENCODING); + case Basic_uint: + return build_context.metrics.ptr_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.ptr_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, allocator, 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(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, allocator, pointer_depth); + s = gb_string_append_length(s, field_type.text, field_type.len); + } + } else { + // #TODO(harold): Encode fields + } + } + + 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, allocator, pointer_depth +1); + // Special case for Objective-C Objects + if (pointer_depth == 0 && pointee == "@") { + return pointee; + } + + return concatenate_strings(allocator, str_lit("^"), pointee); + } + + case Type_MultiPointer: + return concatenate_strings(allocator, str_lit("^"), lb_get_objc_type_encoding(t->Pointer.elem, allocator, pointer_depth +1)); + + case Type_Array: { + String type_str = lb_get_objc_type_encoding(t->Array.elem, allocator, pointer_depth); + + gbString s = gb_string_make_reserve(allocator, type_str.len + 8); + s = gb_string_append_fmt(s, "[%lld%s]", t->Array.count, type_str.text); + return make_string_c(s); + } + + case Type_EnumeratedArray: { + String type_str = lb_get_objc_type_encoding(t->EnumeratedArray.elem, allocator, pointer_depth); + + gbString s = gb_string_make_reserve(allocator, type_str.len + 8); + s = gb_string_append_fmt(s, "[%lld%s]", t->EnumeratedArray.count, type_str.text); + return make_string_c(s); + } + + case Type_Slice: { + String type_str = lb_get_objc_type_encoding(t->Slice.elem, allocator, pointer_depth); + gbString s = gb_string_make_reserve(allocator, type_str.len + 8); + s = gb_string_append_fmt(s, "{slice=^%s%s}", type_str, INT_SIZE_ENCODING); + return make_string_c(s); + } + + case Type_DynamicArray: { + String type_str = lb_get_objc_type_encoding(t->DynamicArray.elem, allocator, pointer_depth); + gbString s = gb_string_make_reserve(allocator, type_str.len + 8); + s = gb_string_append_fmt(s, "{dynamic=^%s%s%sAllocator={?^v}}", 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, allocator, pointer_depth); + case Type_Tuple: + // NOTE(harold): Is this allowed here? + return str_lit("?"); + case Type_Proc: + return str_lit("?"); + case Type_BitSet: + return lb_get_objc_type_encoding(t->BitSet.underlying, allocator, pointer_depth); + case Type_SimdVector: + break; + case Type_Matrix: + break; + case Type_BitField: + return lb_get_objc_type_encoding(t->BitField.backing_type, allocator, pointer_depth); + case Type_SoaPointer: { + gbString s = gb_string_make_reserve(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"); } struct lbObjCGlobalClass { - lbObjCGlobal g; - lbValue class_value; // Local registered class value + lbObjCGlobal g; + lbValue class_value; // Local registered class value }; gb_internal void lb_register_objc_thing( - StringSet &handled, - lbModule *m, - Array &args, - Array &class_impls, - StringMap &class_map, - lbProcedure *p, - lbObjCGlobal const &g, - char const *call + StringSet &handled, + lbModule *m, + Array &args, + Array &class_impls, + StringMap &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{}; - - 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 == nullptr ? lb_const_nil(m, t_objc_Class) : 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; - } + 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{}; + + 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 == nullptr ? lb_const_nil(m, t_objc_Class) : 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) { @@ -1513,80 +1513,80 @@ gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) { defer (string_set_destroy(&handled)); auto args = array_make(temporary_allocator(), 3, 8); - auto class_impls = array_make(temporary_allocator(), 0, 16); - - // 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(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 - // need their superclasses to have been registered before them. - StringMap 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); - - // 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* 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); /**/) { - 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(temporary_allocator(), 2, 8); + auto class_impls = array_make(temporary_allocator(), 0, 16); + + // 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(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 + // need their superclasses to have been registered before them. + StringMap 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); + + // 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* 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); /**/) { + 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(temporary_allocator(), 2, 8); auto get_context_args = array_make(temporary_allocator(), 1); @@ -1597,186 +1597,200 @@ gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) { 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; + 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; + 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{}; + 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); + 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)); + 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); - } + is_context_provider_ivar = ivar_type != nullptr && internal_check_is_assignable_to(contex_provider_self_named_type, ivar_type); + } - Array* methods = map_get(&m->info->objc_method_implementations, class_type); - if (!methods) { - continue; - } + Array* 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; + 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); + 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; + 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; - auto method_param_count = (isize)method_type->Proc.param_count; - i32 method_param_offset = 0; + auto method_param_count = (isize)method_type->Proc.param_count; + i32 method_param_offset = 0; - // TODO(harold): Need to make sure (at checker stage) that the non-class method has the self parameter already. - // (Maybe this is already accounted for?.) - if (!md.ac.objc_is_class_method) { - GB_ASSERT(method_param_count >= 1); - method_param_count -= 1; - method_param_offset = 1; - } + // TODO(harold): Need to make sure (at checker stage) that the non-class method has the self parameter already. + // (Maybe this is already accounted for?.) + if (!md.ac.objc_is_class_method) { + GB_ASSERT(method_param_count >= 1); + method_param_count -= 1; + method_param_offset = 1; + } - for (i32 i = 0; i < method_param_count; i++) { - array_add(&wrapper_args, method_type->Proc.params->Tuple.variables[method_param_offset+i]->type); - } + for (i32 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_proc_type = alloc_type_proc(nullptr, wrapper_args_tuple, (isize)wrapper_args_tuple->Tuple.variables.count, nullptr, 0, false, ProcCC_CDecl); + Type *wrapper_args_tuple = alloc_type_tuple_from_field_types(wrapper_args.data, wrapper_args.count, false, true); + Type *wrapper_results_tuple = nullptr; - 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"); + 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); - // 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); + 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 get odin context call + // 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); - get_context_args[0] = lbValue { - wrapper_proc->raw_input_parameters[0], + // 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 obj_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(temporary_allocator(), method_param_count + (isize)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. - lb_emit_call(wrapper_proc, method_proc_value, method_call_args); - } - 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, temporary_allocator()); - } - - 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 (i32 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, temporary_allocator()); - - 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; - - const i64 size = type_size_of(ivar_base); - const i64 alignment = type_align_of(ivar_base); - // TODO(harold): Checker: Alignment must be compatible with ivar rules. Or we should increase the alignment if needed. - - // TODO(harold): 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, temporary_allocator());// str_lit("{= }"); - 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); - } + if (is_context_provider_ivar) { + // The context provider takes the ivar's type. + // Emit an obj_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(temporary_allocator(), method_param_count + (isize)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, temporary_allocator()); + } + + 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 (i32 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, temporary_allocator()); + + 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; + + const i64 size = type_size_of(ivar_base); + const i64 alignment = type_align_of(ivar_base); + // TODO(harold): Checker: Alignment must be compatible with ivar rules. Or we should increase the alignment if needed. + + // TODO(harold): 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, temporary_allocator());// str_lit("{= }"); + 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. Type *ptr_u32 = alloc_type_pointer(t_u32); diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index 7694c65c3..99ee2b2ff 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -196,7 +196,7 @@ struct lbModule { StringMap objc_classes; StringMap objc_selectors; - StringMap objc_ivars; + StringMap objc_ivars; PtrMap map_cell_info_map; // address of runtime.Map_Info PtrMap map_info_map; // address of runtime.Map_Cell_Info @@ -220,7 +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. + Type * class_impl_type; // This is set when the class has the objc_implement attribute set to true. }; struct lbGenerator : LinkerData { @@ -242,7 +242,7 @@ struct lbGenerator : LinkerData { MPSCQueue entities_to_correct_linkage; MPSCQueue objc_selectors; MPSCQueue objc_classes; - MPSCQueue objc_ivars; + MPSCQueue objc_ivars; }; diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index 7f012e006..bb683465b 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -101,7 +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); + string_map_init(&m->objc_ivars); map_init(&m->map_info_map, 0); map_init(&m->map_cell_info_map, 0); @@ -174,7 +174,7 @@ gb_internal bool lb_init_generator(lbGenerator *gen, Checker *c) { mpsc_init(&gen->entities_to_correct_linkage, heap_allocator()); mpsc_init(&gen->objc_selectors, heap_allocator()); mpsc_init(&gen->objc_classes, heap_allocator()); - mpsc_init(&gen->objc_ivars, heap_allocator()); + mpsc_init(&gen->objc_ivars, heap_allocator()); return true; } diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index bf4ebf377..ba375283e 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -3290,7 +3290,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_objc_ivar_get: return lb_handle_objc_ivar_get(p, expr); case BuiltinProc_constant_utf16_cstring: diff --git a/src/llvm_backend_utility.cpp b/src/llvm_backend_utility.cpp index 33211395a..264364162 100644 --- a/src/llvm_backend_utility.cpp +++ b/src/llvm_backend_utility.cpp @@ -2157,8 +2157,8 @@ gb_internal lbAddr lb_handle_objc_find_or_register_class(lbProcedure *p, String 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 != ""); + 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) { @@ -2170,7 +2170,7 @@ gb_internal lbAddr lb_handle_objc_find_or_register_ivar(lbModule *m, Type *self_ 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 + // Create a global variable to store offset of the ivar in an instance of an object LLVMTypeRef t = lb_type(m, t_u32); lbValue g = {}; @@ -2209,10 +2209,10 @@ gb_internal lbValue lb_handle_objc_ivar_for_objc_object_pointer(lbProcedure *p, } gb_internal lbValue lb_handle_objc_ivar_get(lbProcedure *p, Ast *expr) { - ast_node(ce, CallExpr, 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]); + 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); } @@ -2282,7 +2282,7 @@ gb_internal lbValue lb_handle_objc_id(lbProcedure *p, Ast *expr) { GB_ASSERT(e->kind == Entity_TypeName); String name = e->TypeName.objc_class_name; - Type *class_impl_type = e->TypeName.objc_is_implementation ? type : nullptr; + 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)); } -- cgit v1.2.3 From 89533f49e4fc2ae6f16551a51acaad115faffecd Mon Sep 17 00:00:00 2001 From: Harold Brenes Date: Sun, 27 Apr 2025 23:05:17 -0400 Subject: Fix more styling and minor issues --- src/checker.cpp | 2 +- src/checker.hpp | 4 ++-- src/llvm_backend.cpp | 16 ++++++++-------- 3 files changed, 11 insertions(+), 11 deletions(-) (limited to 'src/llvm_backend.cpp') diff --git a/src/checker.cpp b/src/checker.cpp index 6563b1c58..15d19fb1a 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -6491,7 +6491,7 @@ gb_internal void check_objc_context_provider_procedures(Checker *c) { Type *proc_type = proc_entity->type; // TODO(harold): Give better errors here (specify exactly what's wrong) - const char* signature_error = "The procedure for @(objc_context_provider) has an incorrect signature."; + const char *signature_error = "The procedure for @(objc_context_provider) has an incorrect signature."; if (proc_type->Proc.param_count != 1 || proc_type->Proc.result_count != 1) { error(proc_entity->token, signature_error); diff --git a/src/checker.hpp b/src/checker.hpp index 336f09a7e..0f7e1cb59 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -153,8 +153,8 @@ struct AttributeContext { Type * objc_superclass; Type * objc_ivar; Entity *objc_context_provider; - bool objc_is_class_method : 1; - bool objc_is_implementation : 1; // This struct or proc provides a class/method implementation, not a binding to an existing type. + 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. String require_target_feature; // required by the target micro-architecture String enable_target_feature; // will be enabled for the procedure only diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 7ffd4ea30..aec8b7fc5 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -1269,7 +1269,7 @@ String lb_get_objc_type_encoding(Type *t, gbAllocator allocator, isize pointer_d return build_context.metrics.ptr_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_any: return str_lit("{any=^v^v}"); // rawptr + ^Type_Info case Basic_typeid: GB_ASSERT(t->Basic.size == 8); @@ -1458,16 +1458,16 @@ gb_internal void lb_register_objc_thing( LLVMSetInitializer(v.value, LLVMConstNull(t)); } - lbValue class_ptr{}; + 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{}; + lbValue superclass_value = lb_const_nil(m, t_objc_Class); - auto& tn = g.class_impl_type->Named.type_name->TypeName; + 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); @@ -1478,7 +1478,7 @@ gb_internal void lb_register_objc_thing( } args.count = 3; - args[0] = superclass == nullptr ? lb_const_nil(m, t_objc_Class) : superclass_value; + 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); @@ -1627,7 +1627,7 @@ gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) { continue; } - for (const ObjcMethodData& md : *methods) { + for (const ObjcMethodData &md : *methods) { GB_ASSERT( md.proc_entity->kind == Entity_Procedure); Type *method_type = md.proc_entity->type; @@ -1640,8 +1640,8 @@ gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) { wrapper_args[0] = md.ac.objc_is_class_method ? t_objc_Class : class_ptr_type; wrapper_args[1] = t_objc_SEL; - auto method_param_count = (isize)method_type->Proc.param_count; - i32 method_param_offset = 0; + isize method_param_count = method_type->Proc.param_count; + i32 method_param_offset = 0; // TODO(harold): Need to make sure (at checker stage) that the non-class method has the self parameter already. // (Maybe this is already accounted for?.) -- cgit v1.2.3 From 2dc5653fd13de2a92809ff41ab68a005141899ff Mon Sep 17 00:00:00 2001 From: Harold Brenes Date: Mon, 28 Apr 2025 14:58:53 -0400 Subject: Name fixes --- src/check_builtin.cpp | 6 +++--- src/llvm_backend.cpp | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'src/llvm_backend.cpp') diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index 099f99045..3e531a309 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -418,14 +418,14 @@ gb_internal bool check_builtin_objc_procedure(CheckerContext *c, Operand *operan 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 @(obj_class=) , got type %s", LIT(builtin_name), t); + error(self.expr, "'%.*s' expected a named type with the attribute @(objc_class=) , got type %s", LIT(builtin_name), t); gb_string_free(t); return false; } if (self_type->Named.type_name->TypeName.objc_ivar == nullptr) { gbString t = type_to_string(self_type); - error(self.expr, "'%.*s' requires that type %s have the attribute @(obj_ivar=).", LIT(builtin_name), t); + error(self.expr, "'%.*s' requires that type %s have the attribute @(objc_ivar=).", LIT(builtin_name), t); gb_string_free(t); return false; } @@ -442,7 +442,7 @@ gb_internal bool check_builtin_objc_procedure(CheckerContext *c, Operand *operan gbString name_self = type_to_string(self_type); gbString name_expected = type_to_string(self_type->Named.type_name->TypeName.objc_ivar); gbString name_given = type_to_string(ivar_type); - error(self.expr, "'%.*s' ivar type %s does not match @obj_ivar type %s on Objective-C class %s.", + error(self.expr, "'%.*s' ivar type %s does not match @objc_ivar type %s on Objective-C class %s.", LIT(builtin_name), name_given, name_expected, name_self); gb_string_free(name_self); gb_string_free(name_expected); diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index aec8b7fc5..339e7fca7 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -1685,7 +1685,7 @@ gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) { if (is_context_provider_ivar) { // The context provider takes the ivar's type. - // Emit an obj_ivar_get call and use that pointer for 'self' instead. + // 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 -- cgit v1.2.3 From 668a9a89b0891128846112465110d70bb21d22bf Mon Sep 17 00:00:00 2001 From: Harold Brenes Date: Wed, 30 Apr 2025 19:24:44 -0400 Subject: Complete lb_get_objc_type_encoding() implementation --- src/llvm_backend.cpp | 66 ++++++++++++++++++++++++++-------------------------- 1 file changed, 33 insertions(+), 33 deletions(-) (limited to 'src/llvm_backend.cpp') diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 339e7fca7..e8e08d355 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -1173,9 +1173,8 @@ gb_internal lbProcedure *lb_create_objc_names(lbModule *main_module) { return p; } -// TODO(harold): Move this out of here and into a more suitable place. -// TODO(harold): Should not take an allocator, but always use temp, as we return string literals as well. -String lb_get_objc_type_encoding(Type *t, gbAllocator allocator, isize pointer_depth = 0) { +// TODO(harold): Perhaps move this out of here and into a more suitable place? +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? @@ -1248,14 +1247,12 @@ String lb_get_objc_type_encoding(Type *t, gbAllocator allocator, isize pointer_d case Basic_f64be: return str_lit("d"); - // TODO(harold) These: - case Basic_complex32: - case Basic_complex64: - case Basic_complex128: - case Basic_quaternion64: - case Basic_quaternion128: - case Basic_quaternion256: - return str_lit("?"); + case Basic_complex32: return str_lit("{Raw_Complex32=ss}"); // No f16 encoding, so fallback to i16, as above in Basic_f16* + case Basic_complex64: return str_lit("{Raw_Complex64=ff}"); + case Basic_complex128: return str_lit("{Raw_Complex128=dd}"); + case Basic_quaternion64: return str_lit("{Raw_Quaternion64=ssss}"); + case Basic_quaternion128: return str_lit("{Raw_Quaternion128=ffff}"); + case Basic_quaternion256: return str_lit("{Raw_Quaternion256=dddd}"); case Basic_int: return str_lit(INT_SIZE_ENCODING); @@ -1298,7 +1295,7 @@ String lb_get_objc_type_encoding(Type *t, gbAllocator allocator, isize pointer_d 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, allocator, pointer_depth); + return lb_get_objc_type_encoding(base, pointer_depth); } } @@ -1325,7 +1322,7 @@ String lb_get_objc_type_encoding(Type *t, gbAllocator allocator, isize pointer_d } - gbString s = gb_string_make_reserve(allocator, 16); + 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); @@ -1337,11 +1334,14 @@ String lb_get_objc_type_encoding(Type *t, gbAllocator allocator, isize pointer_d if (!is_union) { for( auto& f : base->Struct.fields ) { - String field_type = lb_get_objc_type_encoding(f->type, allocator, pointer_depth); + 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 { - // #TODO(harold): Encode fields + 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); + } } } @@ -1355,44 +1355,44 @@ String lb_get_objc_type_encoding(Type *t, gbAllocator allocator, isize pointer_d return str_lit("?"); case Type_Pointer: { - String pointee = lb_get_objc_type_encoding(t->Pointer.elem, allocator, pointer_depth +1); + 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(allocator, str_lit("^"), pointee); + return concatenate_strings(temporary_allocator(), str_lit("^"), pointee); } case Type_MultiPointer: - return concatenate_strings(allocator, str_lit("^"), lb_get_objc_type_encoding(t->Pointer.elem, allocator, pointer_depth +1)); + 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, allocator, pointer_depth); + String type_str = lb_get_objc_type_encoding(t->Array.elem, pointer_depth); - gbString s = gb_string_make_reserve(allocator, type_str.len + 8); + gbString s = gb_string_make_reserve(temporary_allocator(), type_str.len + 8); s = gb_string_append_fmt(s, "[%lld%s]", t->Array.count, type_str.text); return make_string_c(s); } case Type_EnumeratedArray: { - String type_str = lb_get_objc_type_encoding(t->EnumeratedArray.elem, allocator, pointer_depth); + String type_str = lb_get_objc_type_encoding(t->EnumeratedArray.elem, pointer_depth); - gbString s = gb_string_make_reserve(allocator, type_str.len + 8); + gbString s = gb_string_make_reserve(temporary_allocator(), type_str.len + 8); s = gb_string_append_fmt(s, "[%lld%s]", t->EnumeratedArray.count, type_str.text); return make_string_c(s); } case Type_Slice: { - String type_str = lb_get_objc_type_encoding(t->Slice.elem, allocator, pointer_depth); - gbString s = gb_string_make_reserve(allocator, type_str.len + 8); + 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}", type_str, INT_SIZE_ENCODING); return make_string_c(s); } case Type_DynamicArray: { - String type_str = lb_get_objc_type_encoding(t->DynamicArray.elem, allocator, pointer_depth); - gbString s = gb_string_make_reserve(allocator, type_str.len + 8); + 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}}", type_str, INT_SIZE_ENCODING, INT_SIZE_ENCODING); return make_string_c(s); } @@ -1400,22 +1400,22 @@ String lb_get_objc_type_encoding(Type *t, gbAllocator allocator, isize pointer_d case Type_Map: return str_lit("{^v^v{Allocator=?^v}}"); case Type_Enum: - return lb_get_objc_type_encoding(t->Enum.base_type, allocator, pointer_depth); + return lb_get_objc_type_encoding(t->Enum.base_type, pointer_depth); case Type_Tuple: - // NOTE(harold): Is this allowed here? + // 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, allocator, pointer_depth); + return lb_get_objc_type_encoding(t->BitSet.underlying, pointer_depth); case Type_SimdVector: break; case Type_Matrix: break; case Type_BitField: - return lb_get_objc_type_encoding(t->BitField.backing_type, allocator, pointer_depth); + return lb_get_objc_type_encoding(t->BitField.backing_type, pointer_depth); case Type_SoaPointer: { - gbString s = gb_string_make_reserve(allocator, 8); + 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); } @@ -1733,7 +1733,7 @@ gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) { // 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, temporary_allocator()); + method_encoding = lb_get_objc_type_encoding(method_type->Proc.results->Tuple.variables[0]->type); } if (!md.ac.objc_is_class_method) { @@ -1744,7 +1744,7 @@ gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) { for (i32 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, temporary_allocator()); + String param_encoding = lb_get_objc_type_encoding(param_type); method_encoding = concatenate_strings(temporary_allocator(), method_encoding, param_encoding); } -- cgit v1.2.3 From abfbb2f9ed98225d2dce0e7e7f119985eb325523 Mon Sep 17 00:00:00 2001 From: Harold Brenes Date: Wed, 30 Apr 2025 19:25:51 -0400 Subject: Use basic name instead of raw_name for type encoding complex and quats. --- src/llvm_backend.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'src/llvm_backend.cpp') diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index e8e08d355..cbcfc565c 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -1247,12 +1247,12 @@ String lb_get_objc_type_encoding(Type *t, isize pointer_depth = 0) { case Basic_f64be: return str_lit("d"); - case Basic_complex32: return str_lit("{Raw_Complex32=ss}"); // No f16 encoding, so fallback to i16, as above in Basic_f16* - case Basic_complex64: return str_lit("{Raw_Complex64=ff}"); - case Basic_complex128: return str_lit("{Raw_Complex128=dd}"); - case Basic_quaternion64: return str_lit("{Raw_Quaternion64=ssss}"); - case Basic_quaternion128: return str_lit("{Raw_Quaternion128=ffff}"); - case Basic_quaternion256: return str_lit("{Raw_Quaternion256=dddd}"); + 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); -- cgit v1.2.3 From dc8692b5040fecfc478801b310c8bd5b0ac42750 Mon Sep 17 00:00:00 2001 From: Harold Brenes Date: Wed, 30 Apr 2025 21:49:24 -0400 Subject: Use correct alignment value for `class_addIvar`. --- src/llvm_backend.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/llvm_backend.cpp') diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index cbcfc565c..c8626c195 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -1770,13 +1770,13 @@ gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) { // 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 = type_align_of(ivar_base); - // TODO(harold): Checker: Alignment must be compatible with ivar rules. Or we should increase the alignment if needed. + const i64 alignment = (i64)floor_log2((u64)type_align_of(ivar_base)); // TODO(harold): 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, temporary_allocator());// str_lit("{= }"); + 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)); -- cgit v1.2.3 From 1505edef011949e10b31335577ea344e5da2b623 Mon Sep 17 00:00:00 2001 From: Harold Brenes Date: Wed, 30 Apr 2025 22:09:03 -0400 Subject: Change ivar offsets from u32 to (odin base type) int/native pointer size. --- src/llvm_backend.cpp | 12 ++++++------ src/llvm_backend_utility.cpp | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) (limited to 'src/llvm_backend.cpp') diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index c8626c195..6e6b9e39a 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -1793,7 +1793,6 @@ gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) { } // Register ivar offsets for any `objc_ivar_get` expressions emitted. - Type *ptr_u32 = alloc_type_pointer(t_u32); for (auto const& kv : ivar_map) { lbObjCGlobal const& g = kv.value; lbAddr ivar_addr = {}; @@ -1801,13 +1800,14 @@ gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) { if (found) { ivar_addr = lb_addr(*found); + GB_ASSERT(ivar_addr.addr.type == t_int_ptr); } else { - // Defined in an external package, must define now - LLVMTypeRef t = lb_type(m, t_u32); + // 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 = ptr_u32; + global.type = t_int_ptr; LLVMSetInitializer(global.value, LLVMConstInt(t, 0, true)); @@ -1825,9 +1825,9 @@ gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) { args.count = 1; args[0] = ivar; lbValue ivar_offset = lb_emit_runtime_call(p, "ivar_getOffset", args); - lbValue ivar_offset_u32 = lb_emit_conv(p, ivar_offset, t_u32); + lbValue ivar_offset_int = lb_emit_conv(p, ivar_offset, t_int); - lb_addr_store(p, ivar_addr, ivar_offset_u32); + lb_addr_store(p, ivar_addr, ivar_offset_int); } lb_end_procedure_body(p); diff --git a/src/llvm_backend_utility.cpp b/src/llvm_backend_utility.cpp index 264364162..521553147 100644 --- a/src/llvm_backend_utility.cpp +++ b/src/llvm_backend_utility.cpp @@ -2171,11 +2171,11 @@ gb_internal lbAddr lb_handle_objc_find_or_register_ivar(lbModule *m, Type *self_ 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_u32); + LLVMTypeRef t = lb_type(m, t_int); lbValue g = {}; g.value = LLVMAddGlobal(m->mod, t, global_name); - g.type = alloc_type_pointer(t_u32); + g.type = t_int_ptr; if (default_module == m) { LLVMSetInitializer(g.value, LLVMConstInt(t, 0, true)); @@ -2184,7 +2184,7 @@ gb_internal lbAddr lb_handle_objc_find_or_register_ivar(lbModule *m, Type *self_ LLVMSetLinkage(g.value, LLVMExternalLinkage); } - mpsc_enqueue(&m->gen->objc_ivars, lbObjCGlobal{m, global_name, name, t_u32, self_type}); + 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); -- cgit v1.2.3 From 0746127654aaa980c3a35039c636b3ca1b794fc8 Mon Sep 17 00:00:00 2001 From: Harold Brenes Date: Wed, 30 Apr 2025 22:16:24 -0400 Subject: Minor fixes in data type usage and style. --- src/llvm_backend.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'src/llvm_backend.cpp') diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 6e6b9e39a..a7082972a 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -1597,8 +1597,8 @@ gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) { map_set(&ivar_map, g.class_impl_type, g); } - for (const auto& cd : class_impls) { - auto& g = cd.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; @@ -1622,7 +1622,7 @@ gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) { } - Array* methods = map_get(&m->info->objc_method_implementations, class_type); + Array *methods = map_get(&m->info->objc_method_implementations, class_type); if (!methods) { continue; } @@ -1641,7 +1641,7 @@ gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) { wrapper_args[1] = t_objc_SEL; isize method_param_count = method_type->Proc.param_count; - i32 method_param_offset = 0; + isize method_param_offset = 0; // TODO(harold): Need to make sure (at checker stage) that the non-class method has the self parameter already. // (Maybe this is already accounted for?.) @@ -1651,7 +1651,7 @@ gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) { method_param_offset = 1; } - for (i32 i = 0; i < method_param_count; i++) { + for (isize i = 0; i < method_param_count; i++) { array_add(&wrapper_args, method_type->Proc.params->Tuple.variables[method_param_offset+i]->type); } @@ -1699,7 +1699,7 @@ gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) { } - auto method_call_args = array_make(temporary_allocator(), method_param_count + (isize)method_param_offset); + auto method_call_args = array_make(temporary_allocator(), method_param_count + method_param_offset); if (!md.ac.objc_is_class_method) { method_call_args[0] = lbValue { @@ -1742,7 +1742,7 @@ gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) { method_encoding = concatenate_strings(temporary_allocator(), method_encoding, str_lit("#:")); } - for (i32 i = method_param_offset; i < method_param_count; i++) { + 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); -- cgit v1.2.3 From 33d37c72f9750d8249ba1d31b5ef9d3a88c54f0a Mon Sep 17 00:00:00 2001 From: Harold Brenes Date: Sat, 3 May 2025 01:50:19 -0400 Subject: Register all classes unconditionally --- src/llvm_backend.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'src/llvm_backend.cpp') diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index a7082972a..4396a2a27 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -1515,6 +1515,12 @@ gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) { auto args = array_make(temporary_allocator(), 3, 8); auto class_impls = array_make(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{}; @@ -1523,7 +1529,7 @@ gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) { auto referenced_classes = array_make(temporary_allocator()); for (lbObjCGlobal g = {}; mpsc_dequeue(&gen->objc_classes, &g); /**/) { - array_add( &referenced_classes, g); + array_add(&referenced_classes, g); Type *cls = g.class_impl_type; while (cls) { @@ -1547,7 +1553,7 @@ gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) { // 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 - // need their superclasses to have been registered before them. + // require their superclasses to be registered before them. StringMap global_class_map{}; string_map_init(&global_class_map, (usize)gen->objc_classes.count); defer (string_map_destroy(&global_class_map)); -- cgit v1.2.3 From fc082f5ea5f8ba65811b0d008e7f86137297849c Mon Sep 17 00:00:00 2001 From: Harold Brenes Date: Sat, 3 May 2025 03:20:02 -0400 Subject: Remove some TODO. Leave important note --- src/check_decl.cpp | 4 ++++ src/llvm_backend.cpp | 2 -- 2 files changed, 4 insertions(+), 2 deletions(-) (limited to 'src/llvm_backend.cpp') diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 84893d5c4..2be5be023 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -1283,6 +1283,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; @@ -1382,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; diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 4396a2a27..ce40e6e9f 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -1649,8 +1649,6 @@ gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) { isize method_param_count = method_type->Proc.param_count; isize method_param_offset = 0; - // TODO(harold): Need to make sure (at checker stage) that the non-class method has the self parameter already. - // (Maybe this is already accounted for?.) if (!md.ac.objc_is_class_method) { GB_ASSERT(method_param_count >= 1); method_param_count -= 1; -- cgit v1.2.3 From be2e4dec7d8b7842b8ddcdef6ddbe6d57f603b43 Mon Sep 17 00:00:00 2001 From: Harold Brenes Date: Sat, 3 May 2025 13:05:27 -0400 Subject: Resolve other TODOs --- src/check_decl.cpp | 173 +++++++++++++++++++++++++-------------------------- src/llvm_backend.cpp | 7 ++- 2 files changed, 91 insertions(+), 89 deletions(-) (limited to 'src/llvm_backend.cpp') diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 2be5be023..21c57e977 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -551,7 +551,7 @@ gb_internal void check_type_decl(CheckerContext *ctx, Entity *e, Ast *init_expr, 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. + // 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 @@ -1013,108 +1013,107 @@ gb_internal void check_objc_methods(CheckerContext *ctx, Entity *e, AttributeCon } else { Type *t = ac.objc_type; - if (t->kind == Type_Named) { // TODO(harold): Shouldn't this be an error otherwise? Or is it checked elsehwere? - Entity *tn = t->Named.type_name; + GB_ASSERT(t->kind == Type_Named); // NOTE(harold): This is already checked for at the attribute resolution stage. + Entity *tn = t->Named.type_name; - GB_ASSERT(tn->kind == Entity_TypeName); + GB_ASSERT(tn->kind == Entity_TypeName); - 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 (tn->scope != e->scope) { + error(e->token, "@(objc_name) attribute may only be applied to procedures and types within the same scope"); + } else { - // Enable implementation by default if the class is an implementer too and - // @objc_implement was not set to false explicitly in this proc. - bool implement = tn->TypeName.objc_is_implementation; - if (ac.objc_is_disabled_implement) { - implement = false; - } + // Enable implementation by default if the class is an implementer too and + // @objc_implement was not set to false explicitly in this proc. + bool implement = tn->TypeName.objc_is_implementation; + if (ac.objc_is_disabled_implement) { + implement = false; + } - if (implement) { - GB_ASSERT(e->kind == Entity_Procedure); - - auto &proc = e->type->Proc; - Type *first_param = proc.param_count > 0 ? proc.params->Tuple.variables[0]->type : t_untyped_nil; - - if (!tn->TypeName.objc_is_implementation) { - error(e->token, "@(objc_is_implement) attribute may only be applied to procedures whose class also have @(objc_is_implement) applied"); - } else if (!ac.objc_is_class_method && !(first_param->kind == Type_Pointer && internal_check_is_assignable_to(t, first_param->Pointer.elem))) { - error(e->token, "Objective-C instance methods implementations require the first parameter to be a pointer to the class type set by @(objc_type)"); - } else if (proc.calling_convention == ProcCC_Odin && !tn->TypeName.objc_context_provider) { - error(e->token, "Objective-C methods with Odin calling convention can only be used with classes that have @(objc_context_provider) set"); - } else if (ac.objc_is_class_method && proc.calling_convention != ProcCC_CDecl) { - error(e->token, "Objective-C class methods (objc_is_class_method=true) that have @objc_is_implementation can only use \"c\" calling convention"); - } else if (proc.result_count > 1) { - error(e->token, "Objective-C method implementations may return at most 1 value"); - } else { - // Always export unconditionally - // NOTE(harold): This means check_objc_methods() MUST be called before - // e->Procedure.is_export is set in check_proc_decl()! - if (ac.is_export) { - error(e->token, "Explicit export not allowed when @(objc_implement) is set. It set exported implicitly"); - } - if (ac.link_name != "") { - error(e->token, "Explicit linkage not allowed when @(objc_implement) is set. It set to \"strong\" implicitly"); - } + if (implement) { + GB_ASSERT(e->kind == Entity_Procedure); + + auto &proc = e->type->Proc; + Type *first_param = proc.param_count > 0 ? proc.params->Tuple.variables[0]->type : t_untyped_nil; + + if (!tn->TypeName.objc_is_implementation) { + error(e->token, "@(objc_is_implement) attribute may only be applied to procedures whose class also have @(objc_is_implement) applied"); + } else if (!ac.objc_is_class_method && !(first_param->kind == Type_Pointer && internal_check_is_assignable_to(t, first_param->Pointer.elem))) { + error(e->token, "Objective-C instance methods implementations require the first parameter to be a pointer to the class type set by @(objc_type)"); + } else if (proc.calling_convention == ProcCC_Odin && !tn->TypeName.objc_context_provider) { + error(e->token, "Objective-C methods with Odin calling convention can only be used with classes that have @(objc_context_provider) set"); + } else if (ac.objc_is_class_method && proc.calling_convention != ProcCC_CDecl) { + error(e->token, "Objective-C class methods (objc_is_class_method=true) that have @objc_is_implementation can only use \"c\" calling convention"); + } else if (proc.result_count > 1) { + error(e->token, "Objective-C method implementations may return at most 1 value"); + } else { + // Always export unconditionally + // NOTE(harold): This means check_objc_methods() MUST be called before + // e->Procedure.is_export is set in check_proc_decl()! + if (ac.is_export) { + error(e->token, "Explicit export not allowed when @(objc_implement) is set. It set exported implicitly"); + } + if (ac.link_name != "") { + error(e->token, "Explicit linkage not allowed when @(objc_implement) is set. It set to \"strong\" implicitly"); + } - ac.is_export = true; - ac.linkage = STR_LIT("strong"); + ac.is_export = true; + ac.linkage = STR_LIT("strong"); - auto method = ObjcMethodData{ ac, e }; - method.ac.objc_selector = ac.objc_selector != "" ? ac.objc_selector : ac.objc_name; + auto method = ObjcMethodData{ ac, e }; + method.ac.objc_selector = ac.objc_selector != "" ? ac.objc_selector : ac.objc_name; - CheckerInfo *info = ctx->info; - mutex_lock(&info->objc_method_mutex); - defer (mutex_unlock(&info->objc_method_mutex)); + CheckerInfo *info = ctx->info; + mutex_lock(&info->objc_method_mutex); + defer (mutex_unlock(&info->objc_method_mutex)); - Array* method_list = map_get(&info->objc_method_implementations, t); - if (method_list) { - array_add(method_list, method); - } else { - auto list = array_make(permanent_allocator(), 1, 8); - list[0] = method; + Array* method_list = map_get(&info->objc_method_implementations, t); + if (method_list) { + array_add(method_list, method); + } else { + auto list = array_make(permanent_allocator(), 1, 8); + list[0] = method; - map_set(&info->objc_method_implementations, t, list); - } + map_set(&info->objc_method_implementations, t, list); } - } else if (ac.objc_selector != "") { - error(e->token, "@(objc_selector) may only be applied to procedures that are Objective-C implementations."); } + } else if (ac.objc_selector != "") { + error(e->token, "@(objc_selector) may only be applied to procedures that are Objective-C implementations."); + } - mutex_lock(&global_type_name_objc_metadata_mutex); - defer (mutex_unlock(&global_type_name_objc_metadata_mutex)); + mutex_lock(&global_type_name_objc_metadata_mutex); + defer (mutex_unlock(&global_type_name_objc_metadata_mutex)); - if (!tn->TypeName.objc_metadata) { - tn->TypeName.objc_metadata = create_type_name_obj_c_metadata(); - } - auto *md = tn->TypeName.objc_metadata; - mutex_lock(md->mutex); - defer (mutex_unlock(md->mutex)); - - if (!ac.objc_is_class_method) { - bool ok = true; - for (TypeNameObjCMetadataEntry const &entry : md->value_entries) { - if (entry.name == ac.objc_name) { - error(e->token, "Previous declaration of @(objc_name=\"%.*s\")", LIT(ac.objc_name)); - ok = false; - break; - } - } - if (ok) { - array_add(&md->value_entries, TypeNameObjCMetadataEntry{ac.objc_name, e}); - } - } else { - bool ok = true; - for (TypeNameObjCMetadataEntry const &entry : md->type_entries) { - if (entry.name == ac.objc_name) { - error(e->token, "Previous declaration of @(objc_name=\"%.*s\")", LIT(ac.objc_name)); - ok = false; - break; - } + if (!tn->TypeName.objc_metadata) { + tn->TypeName.objc_metadata = create_type_name_obj_c_metadata(); + } + auto *md = tn->TypeName.objc_metadata; + mutex_lock(md->mutex); + defer (mutex_unlock(md->mutex)); + + if (!ac.objc_is_class_method) { + bool ok = true; + for (TypeNameObjCMetadataEntry const &entry : md->value_entries) { + if (entry.name == ac.objc_name) { + error(e->token, "Previous declaration of @(objc_name=\"%.*s\")", LIT(ac.objc_name)); + ok = false; + break; } - if (ok) { - array_add(&md->type_entries, TypeNameObjCMetadataEntry{ac.objc_name, e}); + } + if (ok) { + array_add(&md->value_entries, TypeNameObjCMetadataEntry{ac.objc_name, e}); + } + } else { + bool ok = true; + for (TypeNameObjCMetadataEntry const &entry : md->type_entries) { + if (entry.name == ac.objc_name) { + error(e->token, "Previous declaration of @(objc_name=\"%.*s\")", LIT(ac.objc_name)); + ok = false; + break; } } + if (ok) { + array_add(&md->type_entries, TypeNameObjCMetadataEntry{ac.objc_name, e}); + } } } } diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index ce40e6e9f..10aa45b14 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -1173,7 +1173,6 @@ gb_internal lbProcedure *lb_create_objc_names(lbModule *main_module) { return p; } -// TODO(harold): Perhaps move this out of here and into a more suitable place? 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 @@ -1424,6 +1423,7 @@ String lb_get_objc_type_encoding(Type *t, isize pointer_depth = 0) { #undef INT_SIZE_ENCODING GB_PANIC("Unreachable"); + return str_lit(""); } struct lbObjCGlobalClass { @@ -1778,7 +1778,10 @@ gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) { const i64 size = type_size_of(ivar_base); const i64 alignment = (i64)floor_log2((u64)type_align_of(ivar_base)); - // TODO(harold): Should we pass the actual type encoding? Might not be ideal for obfuscation. + // 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; -- cgit v1.2.3 From 14e25c0f2a0dea725cb098dcf9939a8d4681e7d6 Mon Sep 17 00:00:00 2001 From: Harold Brenes Date: Mon, 5 May 2025 09:07:17 -0400 Subject: Fix `lb_get_objc_type_encoding` missing matrix and simdvec encodings and minor fixes. --- src/llvm_backend.cpp | 34 +++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) (limited to 'src/llvm_backend.cpp') diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 10aa45b14..8d13b2a9e 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -1177,7 +1177,7 @@ 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.ptr_size == 4 ? "i" : "q") + #define INT_SIZE_ENCODING (build_context.metrics.int_size == 4 ? "i" : "q") switch (t->kind) { case Type_Basic: { switch (t->Basic.kind) { @@ -1256,13 +1256,13 @@ String lb_get_objc_type_encoding(Type *t, isize pointer_depth = 0) { case Basic_int: return str_lit(INT_SIZE_ENCODING); case Basic_uint: - return build_context.metrics.ptr_size == 4 ? str_lit("I") : str_lit("Q"); + 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.ptr_size == 4 ? str_lit("{string=*i}") : str_lit("{string=*q}"); + 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 @@ -1370,7 +1370,7 @@ String lb_get_objc_type_encoding(Type *t, isize pointer_depth = 0) { 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, type_str.text); + s = gb_string_append_fmt(s, "[%lld%.*s]", t->Array.count, LIT(type_str)); return make_string_c(s); } @@ -1378,21 +1378,21 @@ String lb_get_objc_type_encoding(Type *t, isize pointer_depth = 0) { 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, type_str.text); + 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}", type_str, INT_SIZE_ENCODING); + 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}}", type_str, INT_SIZE_ENCODING, INT_SIZE_ENCODING); + 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); } @@ -1407,10 +1407,22 @@ String lb_get_objc_type_encoding(Type *t, isize pointer_depth = 0) { return str_lit("?"); case Type_BitSet: return lb_get_objc_type_encoding(t->BitSet.underlying, pointer_depth); - case Type_SimdVector: - break; - case Type_Matrix: - break; + + 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: { -- cgit v1.2.3 From e0125ccec2998714774f1ab061509f057724e638 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 7 May 2025 11:30:15 +0100 Subject: Begin work on adding support for the `.raddbg` section for the RAD Debugger --- src/llvm_backend.cpp | 54 ++++++++++++++++++++++++++++++++++++++++++++-- src/llvm_backend_debug.cpp | 24 ++++++++++----------- 2 files changed, 64 insertions(+), 14 deletions(-) (limited to 'src/llvm_backend.cpp') diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 2f861573a..5a4cff973 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -1461,7 +1461,7 @@ gb_internal void lb_create_global_procedures_and_types(lbGenerator *gen, Checker break; case Entity_Constant: if (build_context.ODIN_DEBUG) { - add_debug_info_for_global_constant_from_entity(gen, e); + lb_add_debug_info_for_global_constant_from_entity(gen, e); } break; } @@ -2691,7 +2691,57 @@ gb_internal bool lb_generate_code(lbGenerator *gen) { if (build_context.ODIN_DEBUG) { for (auto const &entry : builtin_pkg->scope->elements) { Entity *e = entry.value; - add_debug_info_for_global_constant_from_entity(gen, e); + lb_add_debug_info_for_global_constant_from_entity(gen, e); + } + + { // Custom `.raddbg` section for its debugger + LLVMModuleRef m = default_module->mod; + LLVMContextRef c = default_module->ctx; + + { + LLVMTypeRef type = LLVMArrayType(LLVMInt8TypeInContext(c), 1); + LLVMValueRef global = LLVMAddGlobal(m, type, "raddbg_is_attached_byte_marker"); + LLVMSetInitializer(global, LLVMConstNull(type)); + LLVMSetSection(global, ".raddbg"); + } + + TEMPORARY_ALLOCATOR_GUARD(); + + u32 index = 0; + auto const add_string = [m, c, &index](String const &str) { + LLVMValueRef data = LLVMConstStringInContext(c, cast(char const *)str.text, cast(unsigned)str.len, false); + LLVMTypeRef type = LLVMTypeOf(data); + + gbString global_name = gb_string_make(temporary_allocator(), "raddbg_data__"); + global_name = gb_string_append_fmt(global_name, "%u", index); + index += 1; + + LLVMValueRef global = LLVMAddGlobal(m, type, global_name); + + LLVMSetInitializer(global, data); + LLVMSetAlignment(global, 1); + + LLVMSetSection(global, ".raddbg"); + }; + + auto const add_string1 = [add_string](char const *a) { + add_string(make_string_c(a)); + }; + auto const add_string3 = [add_string](char const *a, char const *b, char const *c) { + add_string(concatenate3_strings(temporary_allocator(), make_string_c(a), make_string_c(b), make_string_c(c))); + }; + + + + if (gen->info->entry_point) { + String mangled_name = lb_get_entity_name(default_module, gen->info->entry_point); + char const *str = alloc_cstring(temporary_allocator(), mangled_name); + add_string3("entry_point: \"", str, "\""); + } + add_string1("type_view: {type: \"[]?\", expr: \"array(data, len)\"}"); + add_string1("type_view: {type: \"string\", expr: \"array(data, len)\"}"); + // add_string1("type_view: {type: \"[dynamic]?\", expr: \"array(data, len)\"}"); + } } diff --git a/src/llvm_backend_debug.cpp b/src/llvm_backend_debug.cpp index 53c007d8d..b4c1ca5fc 100644 --- a/src/llvm_backend_debug.cpp +++ b/src/llvm_backend_debug.cpp @@ -1186,7 +1186,7 @@ gb_internal void lb_add_debug_context_variable(lbProcedure *p, lbAddr const &ctx } -gb_internal String debug_info_mangle_constant_name(Entity *e, gbAllocator const &allocator, bool *did_allocate_) { +gb_internal String lb_debug_info_mangle_constant_name(Entity *e, gbAllocator const &allocator, bool *did_allocate_) { String name = e->token.string; if (e->pkg && e->pkg->name.len > 0) { gbString s = string_canonical_entity_name(allocator, e); @@ -1196,7 +1196,7 @@ gb_internal String debug_info_mangle_constant_name(Entity *e, gbAllocator const return name; } -gb_internal void add_debug_info_global_variable_expr(lbModule *m, String const &name, LLVMMetadataRef dtype, LLVMMetadataRef expr) { +gb_internal void lb_add_debug_info_global_variable_expr(lbModule *m, String const &name, LLVMMetadataRef dtype, LLVMMetadataRef expr) { LLVMMetadataRef scope = nullptr; LLVMMetadataRef file = nullptr; unsigned line = 0; @@ -1212,20 +1212,20 @@ gb_internal void add_debug_info_global_variable_expr(lbModule *m, String const & expr, decl, 8/*AlignInBits*/); } -gb_internal void add_debug_info_for_global_constant_internal_i64(lbModule *m, Entity *e, LLVMMetadataRef dtype, i64 v) { +gb_internal void lb_add_debug_info_for_global_constant_internal_i64(lbModule *m, Entity *e, LLVMMetadataRef dtype, i64 v) { LLVMMetadataRef expr = LLVMDIBuilderCreateConstantValueExpression(m->debug_builder, v); TEMPORARY_ALLOCATOR_GUARD(); - String name = debug_info_mangle_constant_name(e, temporary_allocator(), nullptr); + String name = lb_debug_info_mangle_constant_name(e, temporary_allocator(), nullptr); - add_debug_info_global_variable_expr(m, name, dtype, expr); + lb_add_debug_info_global_variable_expr(m, name, dtype, expr); if ((e->pkg && e->pkg->kind == Package_Init) || (e->scope && (e->scope->flags & ScopeFlag_Global))) { - add_debug_info_global_variable_expr(m, e->token.string, dtype, expr); + lb_add_debug_info_global_variable_expr(m, e->token.string, dtype, expr); } } -gb_internal void add_debug_info_for_global_constant_from_entity(lbGenerator *gen, Entity *e) { +gb_internal void lb_add_debug_info_for_global_constant_from_entity(lbGenerator *gen, Entity *e) { if (e == nullptr || e->kind != Entity_Constant) { return; } @@ -1256,14 +1256,14 @@ gb_internal void add_debug_info_for_global_constant_from_entity(lbGenerator *gen dtype = lb_debug_type(m, e->type); } - add_debug_info_for_global_constant_internal_i64(m, e, dtype, v); + lb_add_debug_info_for_global_constant_internal_i64(m, e, dtype, v); } } else if (is_type_rune(e->type)) { ExactValue const &value = e->Constant.value; if (value.kind == ExactValue_Integer) { LLVMMetadataRef dtype = lb_debug_type(m, t_rune); i64 v = exact_value_to_i64(value); - add_debug_info_for_global_constant_internal_i64(m, e, dtype, v); + lb_add_debug_info_for_global_constant_internal_i64(m, e, dtype, v); } } else if (is_type_boolean(e->type)) { ExactValue const &value = e->Constant.value; @@ -1271,7 +1271,7 @@ gb_internal void add_debug_info_for_global_constant_from_entity(lbGenerator *gen LLVMMetadataRef dtype = lb_debug_type(m, default_type(e->type)); i64 v = cast(i64)value.value_bool; - add_debug_info_for_global_constant_internal_i64(m, e, dtype, v); + lb_add_debug_info_for_global_constant_internal_i64(m, e, dtype, v); } } else if (is_type_enum(e->type)) { ExactValue const &value = e->Constant.value; @@ -1284,14 +1284,14 @@ gb_internal void add_debug_info_for_global_constant_from_entity(lbGenerator *gen v = cast(i64)exact_value_to_u64(value); } - add_debug_info_for_global_constant_internal_i64(m, e, dtype, v); + lb_add_debug_info_for_global_constant_internal_i64(m, e, dtype, v); } } else if (is_type_pointer(e->type)) { ExactValue const &value = e->Constant.value; if (value.kind == ExactValue_Integer) { LLVMMetadataRef dtype = lb_debug_type(m, default_type(e->type)); i64 v = cast(i64)exact_value_to_u64(value); - add_debug_info_for_global_constant_internal_i64(m, e, dtype, v); + lb_add_debug_info_for_global_constant_internal_i64(m, e, dtype, v); } } } -- cgit v1.2.3 From 4274dfc5e22bd088c6a02a0c372045b61a62e669 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 7 May 2025 11:44:49 +0100 Subject: Only add custom section for Windows (currently) --- src/llvm_backend.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/llvm_backend.cpp') diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 5a4cff973..9a708ea9a 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -2694,7 +2694,8 @@ gb_internal bool lb_generate_code(lbGenerator *gen) { lb_add_debug_info_for_global_constant_from_entity(gen, e); } - { // Custom `.raddbg` section for its debugger + // Custom `.raddbg` section for its debugger + if (build_context.metrics.os == TargetOs_windows) { LLVMModuleRef m = default_module->mod; LLVMContextRef c = default_module->ctx; -- cgit v1.2.3 From 833d77da1f721d2dbebdc5854c57ff888fe2b149 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 7 May 2025 13:20:14 +0100 Subject: Add column major matrix support --- src/llvm_backend.cpp | 20 +++++++++- src/llvm_backend_debug.cpp | 92 ++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 95 insertions(+), 17 deletions(-) (limited to 'src/llvm_backend.cpp') diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 9a708ea9a..05bc7e9a7 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -2739,9 +2739,27 @@ gb_internal bool lb_generate_code(lbGenerator *gen) { char const *str = alloc_cstring(temporary_allocator(), mangled_name); add_string3("entry_point: \"", str, "\""); } + add_string1("type_view: {type: \"[]?\", expr: \"array(data, len)\"}"); add_string1("type_view: {type: \"string\", expr: \"array(data, len)\"}"); - // add_string1("type_view: {type: \"[dynamic]?\", expr: \"array(data, len)\"}"); + + // column major matrices + add_string1("type_view: {type: \"matrix[1, ?]?\", expr: \"table($.data, $[0])\"}"); + add_string1("type_view: {type: \"matrix[2, ?]?\", expr: \"table($.data, $[0], $[1])\"}"); + add_string1("type_view: {type: \"matrix[3, ?]?\", expr: \"table($.data, $[0], $[1], $[2])\"}"); + add_string1("type_view: {type: \"matrix[4, ?]?\", expr: \"table($.data, $[0], $[1], $[2], $[3])\"}"); + add_string1("type_view: {type: \"matrix[5, ?]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4])\"}"); + add_string1("type_view: {type: \"matrix[6, ?]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5])\"}"); + add_string1("type_view: {type: \"matrix[7, ?]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6])\"}"); + add_string1("type_view: {type: \"matrix[8, ?]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7])\"}"); + add_string1("type_view: {type: \"matrix[9, ?]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8])\"}"); + add_string1("type_view: {type: \"matrix[10, ?]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9])\"}"); + add_string1("type_view: {type: \"matrix[11, ?]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10])\"}"); + add_string1("type_view: {type: \"matrix[12, ?]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10], $[11])\"}"); + add_string1("type_view: {type: \"matrix[13, ?]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10], $[11], $[12])\"}"); + add_string1("type_view: {type: \"matrix[14, ?]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10], $[11], $[12], $[13])\"}"); + add_string1("type_view: {type: \"matrix[15, ?]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10], $[11], $[12], $[13], $[14])\"}"); + add_string1("type_view: {type: \"matrix[16, ?]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10], $[11], $[12], $[13], $[14], $[15])\"}"); } } diff --git a/src/llvm_backend_debug.cpp b/src/llvm_backend_debug.cpp index b4c1ca5fc..0358031d1 100644 --- a/src/llvm_backend_debug.cpp +++ b/src/llvm_backend_debug.cpp @@ -564,22 +564,21 @@ gb_internal LLVMMetadataRef lb_debug_bitfield(lbModule *m, Type *type, String na u64 size_in_bits = 8*type_size_of(bt); u32 align_in_bits = 8*cast(u32)type_align_of(bt); - unsigned element_count = cast(unsigned)bt->BitField.fields.count; - LLVMMetadataRef *elements = gb_alloc_array(permanent_allocator(), LLVMMetadataRef, element_count); - - u64 offset_in_bits = 0; - for (unsigned i = 0; i < element_count; i++) { - Entity *f = bt->BitField.fields[i]; - u8 bit_size = bt->BitField.bit_sizes[i]; - GB_ASSERT(f->kind == Entity_Variable); - String name = f->token.string; - elements[i] = LLVMDIBuilderCreateBitFieldMemberType(m->debug_builder, scope, cast(char const *)name.text, name.len, file, line, - bit_size, offset_in_bits, 0, - LLVMDIFlagZero, lb_debug_type(m, f->type) - ); - - offset_in_bits += bit_size; - } + unsigned element_count = cast(unsigned)bt->BitField.fields.count; + LLVMMetadataRef *elements = gb_alloc_array(permanent_allocator(), LLVMMetadataRef, element_count); + + u64 offset_in_bits = 0; + for (unsigned i = 0; i < element_count; i++) { + Entity *f = bt->BitField.fields[i]; + u8 bit_size = bt->BitField.bit_sizes[i]; + GB_ASSERT(f->kind == Entity_Variable); + String name = f->token.string; + elements[i] = LLVMDIBuilderCreateBitFieldMemberType(m->debug_builder, scope, cast(char const *)name.text, name.len, file, line, + bit_size, offset_in_bits, 0, + LLVMDIFlagZero, lb_debug_type(m, f->type) + ); + offset_in_bits += bit_size; + } LLVMMetadataRef final_decl = LLVMDIBuilderCreateStructType( m->debug_builder, scope, @@ -924,6 +923,7 @@ gb_internal LLVMMetadataRef lb_debug_type_internal(lbModule *m, Type *type) { } case Type_Matrix: { + #if 0 LLVMMetadataRef subscripts[1] = {}; subscripts[0] = LLVMDIBuilderGetOrCreateSubrange(m->debug_builder, 0ll, @@ -935,6 +935,66 @@ gb_internal LLVMMetadataRef lb_debug_type_internal(lbModule *m, Type *type) { 8*cast(unsigned)type_align_of(type), lb_debug_type(m, type->Matrix.elem), subscripts, gb_count_of(subscripts)); + #else + LLVMMetadataRef subscripts[2] = {}; + subscripts[0] = LLVMDIBuilderGetOrCreateSubrange(m->debug_builder, 0ll, type->Matrix.row_count); + subscripts[1] = LLVMDIBuilderGetOrCreateSubrange(m->debug_builder, 0ll, type->Matrix.column_count); + + LLVMMetadataRef scope = nullptr; + LLVMMetadataRef array_type = nullptr; + + uint64_t size_in_bits = 8*cast(uint64_t)(type_size_of(type)); + unsigned align_in_bits = 8*cast(unsigned)(type_align_of(type)); + + if (type->Matrix.is_row_major) { + LLVMMetadataRef base = LLVMDIBuilderCreateArrayType(m->debug_builder, + 8*cast(uint64_t)(type_size_of(type->Matrix.elem) * type->Matrix.column_count), + 8*cast(unsigned)type_align_of(type->Matrix.elem), + lb_debug_type(m, type->Matrix.elem), + subscripts+1, 1); + array_type = LLVMDIBuilderCreateArrayType(m->debug_builder, + size_in_bits, + align_in_bits, + base, + subscripts+0, 1); + } else { + LLVMMetadataRef base = LLVMDIBuilderCreateArrayType(m->debug_builder, + 8*cast(uint64_t)(type_size_of(type->Matrix.elem) * type->Matrix.row_count), + 8*cast(unsigned)type_align_of(type->Matrix.elem), + lb_debug_type(m, type->Matrix.elem), + subscripts+0, 1); + array_type = LLVMDIBuilderCreateArrayType(m->debug_builder, + size_in_bits, + align_in_bits, + base, + subscripts+1, 1); + } + + LLVMMetadataRef elements[1] = {}; + elements[0] = LLVMDIBuilderCreateMemberType(m->debug_builder, scope, + "data", 4, + nullptr, 0, + size_in_bits, align_in_bits, 0, LLVMDIFlagZero, + array_type + ); + + gbString name = temp_canonical_string(type); + + LLVMMetadataRef final_decl = LLVMDIBuilderCreateStructType( + m->debug_builder, scope, + name, gb_string_length(name), + nullptr, 0, + size_in_bits, align_in_bits, + LLVMDIFlagZero, + nullptr, + elements, 1, + 0, + nullptr, + "", 0 + ); + + return final_decl; + #endif } } -- cgit v1.2.3 From 0c4046b9ea78af278a1c93bc61f9adf4b02a973c Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 7 May 2025 13:30:58 +0100 Subject: Add `#row_major matrix` support for raddbg --- src/llvm_backend.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'src/llvm_backend.cpp') diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 05bc7e9a7..f0c1000c5 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -2761,6 +2761,23 @@ gb_internal bool lb_generate_code(lbGenerator *gen) { add_string1("type_view: {type: \"matrix[15, ?]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10], $[11], $[12], $[13], $[14])\"}"); add_string1("type_view: {type: \"matrix[16, ?]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10], $[11], $[12], $[13], $[14], $[15])\"}"); + // row major matrices + add_string1("type_view: {type: \"#row_major matrix[?, 1]?\", expr: \"table($.data, $[0])\"}"); + add_string1("type_view: {type: \"#row_major matrix[?, 2]?\", expr: \"table($.data, $[0], $[1])\"}"); + add_string1("type_view: {type: \"#row_major matrix[?, 3]?\", expr: \"table($.data, $[0], $[1], $[2])\"}"); + add_string1("type_view: {type: \"#row_major matrix[?, 4]?\", expr: \"table($.data, $[0], $[1], $[2], $[3])\"}"); + add_string1("type_view: {type: \"#row_major matrix[?, 5]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4])\"}"); + add_string1("type_view: {type: \"#row_major matrix[?, 6]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5])\"}"); + add_string1("type_view: {type: \"#row_major matrix[?, 7]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6])\"}"); + add_string1("type_view: {type: \"#row_major matrix[?, 8]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7])\"}"); + add_string1("type_view: {type: \"#row_major matrix[?, 9]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8])\"}"); + add_string1("type_view: {type: \"#row_major matrix[?, 10]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9])\"}"); + add_string1("type_view: {type: \"#row_major matrix[?, 11]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10])\"}"); + add_string1("type_view: {type: \"#row_major matrix[?, 12]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10], $[11])\"}"); + add_string1("type_view: {type: \"#row_major matrix[?, 13]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10], $[11], $[12])\"}"); + add_string1("type_view: {type: \"#row_major matrix[?, 14]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10], $[11], $[12], $[13])\"}"); + add_string1("type_view: {type: \"#row_major matrix[?, 15]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10], $[11], $[12], $[13], $[14])\"}"); + add_string1("type_view: {type: \"#row_major matrix[?, 16]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10], $[11], $[12], $[13], $[14], $[15])\"}"); } } -- cgit v1.2.3 From ad4866653a05a2850d60616b6d7458d4718a2c83 Mon Sep 17 00:00:00 2001 From: Laytan Laats Date: Wed, 7 May 2025 14:38:40 +0200 Subject: fix disposing builder when not created Fixes #5128 p->builder is created in lb_begin_procedure_body, but that isn't called if there is no body, and we were still calling dispose at that point. Moved it into lb_end_procedure_body to match. --- src/llvm_backend.cpp | 1 - src/llvm_backend.hpp | 1 - src/llvm_backend_proc.cpp | 3 +-- 3 files changed, 1 insertion(+), 4 deletions(-) (limited to 'src/llvm_backend.cpp') diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 2f861573a..fd10cd5c1 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -2185,7 +2185,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) { diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index de6841ed8..51931dac3 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -409,7 +409,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); diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index 1f023037d..057d6562f 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -783,8 +783,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); } -- cgit v1.2.3 From ea65a7b870736311747c517970df3921d227e024 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 7 May 2025 14:26:10 +0100 Subject: Move raddbg string stuff to a thread-safe queue --- src/llvm_backend.cpp | 172 ++++++++++++++++++++++--------------------- src/llvm_backend.hpp | 1 + src/llvm_backend_debug.cpp | 19 +++++ src/llvm_backend_general.cpp | 1 + 4 files changed, 108 insertions(+), 85 deletions(-) (limited to 'src/llvm_backend.cpp') diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index f0c1000c5..b69cbc3a5 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -2678,106 +2678,42 @@ gb_internal bool lb_generate_code(lbGenerator *gen) { } } - TIME_SECTION("LLVM Runtime Objective-C Names Creation"); - gen->objc_names = lb_create_objc_names(default_module); - - TIME_SECTION("LLVM Runtime Startup Creation (Global Variables & @(init))"); - gen->startup_runtime = lb_create_startup_runtime(default_module, gen->objc_names, global_variables); - - TIME_SECTION("LLVM Runtime Cleanup Creation & @(fini)"); - gen->cleanup_runtime = lb_create_cleanup_runtime(default_module); - - if (build_context.ODIN_DEBUG) { - for (auto const &entry : builtin_pkg->scope->elements) { - Entity *e = entry.value; - lb_add_debug_info_for_global_constant_from_entity(gen, e); - } - // Custom `.raddbg` section for its debugger if (build_context.metrics.os == TargetOs_windows) { - LLVMModuleRef m = default_module->mod; - LLVMContextRef c = default_module->ctx; + lbModule *m = default_module; + LLVMModuleRef mod = m->mod; + LLVMContextRef ctx = m->ctx; { - LLVMTypeRef type = LLVMArrayType(LLVMInt8TypeInContext(c), 1); - LLVMValueRef global = LLVMAddGlobal(m, type, "raddbg_is_attached_byte_marker"); + LLVMTypeRef type = LLVMArrayType(LLVMInt8TypeInContext(ctx), 1); + LLVMValueRef global = LLVMAddGlobal(mod, type, "raddbg_is_attached_byte_marker"); LLVMSetInitializer(global, LLVMConstNull(type)); LLVMSetSection(global, ".raddbg"); } - TEMPORARY_ALLOCATOR_GUARD(); - - u32 index = 0; - auto const add_string = [m, c, &index](String const &str) { - LLVMValueRef data = LLVMConstStringInContext(c, cast(char const *)str.text, cast(unsigned)str.len, false); - LLVMTypeRef type = LLVMTypeOf(data); - - gbString global_name = gb_string_make(temporary_allocator(), "raddbg_data__"); - global_name = gb_string_append_fmt(global_name, "%u", index); - index += 1; - - LLVMValueRef global = LLVMAddGlobal(m, type, global_name); - - LLVMSetInitializer(global, data); - LLVMSetAlignment(global, 1); - - LLVMSetSection(global, ".raddbg"); - }; - - auto const add_string1 = [add_string](char const *a) { - add_string(make_string_c(a)); - }; - auto const add_string3 = [add_string](char const *a, char const *b, char const *c) { - add_string(concatenate3_strings(temporary_allocator(), make_string_c(a), make_string_c(b), make_string_c(c))); - }; - - - if (gen->info->entry_point) { - String mangled_name = lb_get_entity_name(default_module, gen->info->entry_point); + String mangled_name = lb_get_entity_name(m, gen->info->entry_point); char const *str = alloc_cstring(temporary_allocator(), mangled_name); - add_string3("entry_point: \"", str, "\""); + lb_add_raddbg_string(m, "entry_point: \"", str, "\""); } + } + } + + TIME_SECTION("LLVM Runtime Objective-C Names Creation"); + gen->objc_names = lb_create_objc_names(default_module); - add_string1("type_view: {type: \"[]?\", expr: \"array(data, len)\"}"); - add_string1("type_view: {type: \"string\", expr: \"array(data, len)\"}"); + TIME_SECTION("LLVM Runtime Startup Creation (Global Variables & @(init))"); + gen->startup_runtime = lb_create_startup_runtime(default_module, gen->objc_names, global_variables); - // column major matrices - add_string1("type_view: {type: \"matrix[1, ?]?\", expr: \"table($.data, $[0])\"}"); - add_string1("type_view: {type: \"matrix[2, ?]?\", expr: \"table($.data, $[0], $[1])\"}"); - add_string1("type_view: {type: \"matrix[3, ?]?\", expr: \"table($.data, $[0], $[1], $[2])\"}"); - add_string1("type_view: {type: \"matrix[4, ?]?\", expr: \"table($.data, $[0], $[1], $[2], $[3])\"}"); - add_string1("type_view: {type: \"matrix[5, ?]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4])\"}"); - add_string1("type_view: {type: \"matrix[6, ?]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5])\"}"); - add_string1("type_view: {type: \"matrix[7, ?]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6])\"}"); - add_string1("type_view: {type: \"matrix[8, ?]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7])\"}"); - add_string1("type_view: {type: \"matrix[9, ?]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8])\"}"); - add_string1("type_view: {type: \"matrix[10, ?]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9])\"}"); - add_string1("type_view: {type: \"matrix[11, ?]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10])\"}"); - add_string1("type_view: {type: \"matrix[12, ?]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10], $[11])\"}"); - add_string1("type_view: {type: \"matrix[13, ?]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10], $[11], $[12])\"}"); - add_string1("type_view: {type: \"matrix[14, ?]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10], $[11], $[12], $[13])\"}"); - add_string1("type_view: {type: \"matrix[15, ?]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10], $[11], $[12], $[13], $[14])\"}"); - add_string1("type_view: {type: \"matrix[16, ?]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10], $[11], $[12], $[13], $[14], $[15])\"}"); + TIME_SECTION("LLVM Runtime Cleanup Creation & @(fini)"); + gen->cleanup_runtime = lb_create_cleanup_runtime(default_module); - // row major matrices - add_string1("type_view: {type: \"#row_major matrix[?, 1]?\", expr: \"table($.data, $[0])\"}"); - add_string1("type_view: {type: \"#row_major matrix[?, 2]?\", expr: \"table($.data, $[0], $[1])\"}"); - add_string1("type_view: {type: \"#row_major matrix[?, 3]?\", expr: \"table($.data, $[0], $[1], $[2])\"}"); - add_string1("type_view: {type: \"#row_major matrix[?, 4]?\", expr: \"table($.data, $[0], $[1], $[2], $[3])\"}"); - add_string1("type_view: {type: \"#row_major matrix[?, 5]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4])\"}"); - add_string1("type_view: {type: \"#row_major matrix[?, 6]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5])\"}"); - add_string1("type_view: {type: \"#row_major matrix[?, 7]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6])\"}"); - add_string1("type_view: {type: \"#row_major matrix[?, 8]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7])\"}"); - add_string1("type_view: {type: \"#row_major matrix[?, 9]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8])\"}"); - add_string1("type_view: {type: \"#row_major matrix[?, 10]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9])\"}"); - add_string1("type_view: {type: \"#row_major matrix[?, 11]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10])\"}"); - add_string1("type_view: {type: \"#row_major matrix[?, 12]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10], $[11])\"}"); - add_string1("type_view: {type: \"#row_major matrix[?, 13]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10], $[11], $[12])\"}"); - add_string1("type_view: {type: \"#row_major matrix[?, 14]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10], $[11], $[12], $[13])\"}"); - add_string1("type_view: {type: \"#row_major matrix[?, 15]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10], $[11], $[12], $[13], $[14])\"}"); - add_string1("type_view: {type: \"#row_major matrix[?, 16]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10], $[11], $[12], $[13], $[14], $[15])\"}"); + + if (build_context.ODIN_DEBUG) { + for (auto const &entry : builtin_pkg->scope->elements) { + Entity *e = entry.value; + lb_add_debug_info_for_global_constant_from_entity(gen, e); } } @@ -2807,6 +2743,72 @@ gb_internal bool lb_generate_code(lbGenerator *gen) { if (build_context.ODIN_DEBUG) { TIME_SECTION("LLVM Debug Info Complete Types and Finalize"); lb_debug_info_complete_types_and_finalize(gen); + + // Custom `.raddbg` section for its debugger + if (build_context.metrics.os == TargetOs_windows) { + lbModule *m = default_module; + LLVMModuleRef mod = m->mod; + LLVMContextRef ctx = m->ctx; + + lb_add_raddbg_string(m, "type_view: {type: \"[]?\", expr: \"array(data, len)\"}"); + lb_add_raddbg_string(m, "type_view: {type: \"string\", expr: \"array(data, len)\"}"); + + // column major matrices + lb_add_raddbg_string(m, "type_view: {type: \"matrix[1, ?]?\", expr: \"table($.data, $[0])\"}"); + lb_add_raddbg_string(m, "type_view: {type: \"matrix[2, ?]?\", expr: \"table($.data, $[0], $[1])\"}"); + lb_add_raddbg_string(m, "type_view: {type: \"matrix[3, ?]?\", expr: \"table($.data, $[0], $[1], $[2])\"}"); + lb_add_raddbg_string(m, "type_view: {type: \"matrix[4, ?]?\", expr: \"table($.data, $[0], $[1], $[2], $[3])\"}"); + lb_add_raddbg_string(m, "type_view: {type: \"matrix[5, ?]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4])\"}"); + lb_add_raddbg_string(m, "type_view: {type: \"matrix[6, ?]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5])\"}"); + lb_add_raddbg_string(m, "type_view: {type: \"matrix[7, ?]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6])\"}"); + lb_add_raddbg_string(m, "type_view: {type: \"matrix[8, ?]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7])\"}"); + lb_add_raddbg_string(m, "type_view: {type: \"matrix[9, ?]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8])\"}"); + lb_add_raddbg_string(m, "type_view: {type: \"matrix[10, ?]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9])\"}"); + lb_add_raddbg_string(m, "type_view: {type: \"matrix[11, ?]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10])\"}"); + lb_add_raddbg_string(m, "type_view: {type: \"matrix[12, ?]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10], $[11])\"}"); + lb_add_raddbg_string(m, "type_view: {type: \"matrix[13, ?]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10], $[11], $[12])\"}"); + lb_add_raddbg_string(m, "type_view: {type: \"matrix[14, ?]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10], $[11], $[12], $[13])\"}"); + lb_add_raddbg_string(m, "type_view: {type: \"matrix[15, ?]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10], $[11], $[12], $[13], $[14])\"}"); + lb_add_raddbg_string(m, "type_view: {type: \"matrix[16, ?]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10], $[11], $[12], $[13], $[14], $[15])\"}"); + + // row major matrices + lb_add_raddbg_string(m, "type_view: {type: \"#row_major matrix[?, 1]?\", expr: \"table($.data, $[0])\"}"); + lb_add_raddbg_string(m, "type_view: {type: \"#row_major matrix[?, 2]?\", expr: \"table($.data, $[0], $[1])\"}"); + lb_add_raddbg_string(m, "type_view: {type: \"#row_major matrix[?, 3]?\", expr: \"table($.data, $[0], $[1], $[2])\"}"); + lb_add_raddbg_string(m, "type_view: {type: \"#row_major matrix[?, 4]?\", expr: \"table($.data, $[0], $[1], $[2], $[3])\"}"); + lb_add_raddbg_string(m, "type_view: {type: \"#row_major matrix[?, 5]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4])\"}"); + lb_add_raddbg_string(m, "type_view: {type: \"#row_major matrix[?, 6]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5])\"}"); + lb_add_raddbg_string(m, "type_view: {type: \"#row_major matrix[?, 7]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6])\"}"); + lb_add_raddbg_string(m, "type_view: {type: \"#row_major matrix[?, 8]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7])\"}"); + lb_add_raddbg_string(m, "type_view: {type: \"#row_major matrix[?, 9]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8])\"}"); + lb_add_raddbg_string(m, "type_view: {type: \"#row_major matrix[?, 10]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9])\"}"); + lb_add_raddbg_string(m, "type_view: {type: \"#row_major matrix[?, 11]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10])\"}"); + lb_add_raddbg_string(m, "type_view: {type: \"#row_major matrix[?, 12]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10], $[11])\"}"); + lb_add_raddbg_string(m, "type_view: {type: \"#row_major matrix[?, 13]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10], $[11], $[12])\"}"); + lb_add_raddbg_string(m, "type_view: {type: \"#row_major matrix[?, 14]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10], $[11], $[12], $[13])\"}"); + lb_add_raddbg_string(m, "type_view: {type: \"#row_major matrix[?, 15]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10], $[11], $[12], $[13], $[14])\"}"); + lb_add_raddbg_string(m, "type_view: {type: \"#row_major matrix[?, 16]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10], $[11], $[12], $[13], $[14], $[15])\"}"); + + + TEMPORARY_ALLOCATOR_GUARD(); + + u32 global_name_index = 0; + for (String str = {}; mpsc_dequeue(&gen->raddebug_section_strings, &str); /**/) { + LLVMValueRef data = LLVMConstStringInContext(ctx, cast(char const *)str.text, cast(unsigned)str.len, false); + LLVMTypeRef type = LLVMTypeOf(data); + + gbString global_name = gb_string_make(temporary_allocator(), "raddbg_data__"); + global_name = gb_string_append_fmt(global_name, "%u", global_name_index); + global_name_index += 1; + + LLVMValueRef global = LLVMAddGlobal(mod, type, global_name); + + LLVMSetInitializer(global, data); + LLVMSetAlignment(global, 1); + + LLVMSetSection(global, ".raddbg"); + } + } } if (do_threading) { diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index de6841ed8..a0764494a 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -240,6 +240,7 @@ struct lbGenerator : LinkerData { MPSCQueue entities_to_correct_linkage; MPSCQueue objc_selectors; MPSCQueue objc_classes; + MPSCQueue raddebug_section_strings; }; diff --git a/src/llvm_backend_debug.cpp b/src/llvm_backend_debug.cpp index 0358031d1..464d50cac 100644 --- a/src/llvm_backend_debug.cpp +++ b/src/llvm_backend_debug.cpp @@ -18,6 +18,25 @@ gb_internal void lb_set_llvm_metadata(lbModule *m, void *key, LLVMMetadataRef va } } +gb_internal void lb_add_raddbg_string(lbModule *m, String const &str) { + mpsc_enqueue(&m->gen->raddebug_section_strings, copy_string(permanent_allocator(), str)); +} + +gb_internal void lb_add_raddbg_string(lbModule *m, char const *cstr) { + mpsc_enqueue(&m->gen->raddebug_section_strings, copy_string(permanent_allocator(), make_string_c(cstr))); +} + +gb_internal void lb_add_raddbg_string(lbModule *m, char const *a, char const *b) { + String str = concatenate_strings(permanent_allocator(), make_string_c(a), make_string_c(b)); + mpsc_enqueue(&m->gen->raddebug_section_strings, str); +} + +gb_internal void lb_add_raddbg_string(lbModule *m, char const *a, char const *b, char const *c) { + String str = concatenate3_strings(permanent_allocator(), make_string_c(a), make_string_c(b), make_string_c(c)); + mpsc_enqueue(&m->gen->raddebug_section_strings, str); +} + + gb_internal LLVMMetadataRef lb_get_current_debug_scope(lbProcedure *p) { GB_ASSERT_MSG(p->debug_info != nullptr, "missing debug information for %.*s", LIT(p->name)); diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index dad5d4dd5..2eaecd8a7 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -173,6 +173,7 @@ 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->raddebug_section_strings, heap_allocator()); return true; } -- cgit v1.2.3 From 8a225a6887958bcf23a603efa322a4f15bd96052 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 9 May 2025 11:07:53 +0100 Subject: Place RTTI into its own section `.odin-rtti` --- src/llvm_backend.cpp | 1 + src/llvm_backend_type.cpp | 10 ++++++++++ 2 files changed, 11 insertions(+) (limited to 'src/llvm_backend.cpp') diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 1be652107..7de147058 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -3122,6 +3122,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)}); }; diff --git a/src/llvm_backend_type.cpp b/src/llvm_backend_type.cpp index ad4250f3c..2aed4cbf7 100644 --- a/src/llvm_backend_type.cpp +++ b/src/llvm_backend_type.cpp @@ -1,4 +1,10 @@ +gb_internal void lb_set_odin_rtti_section(LLVMValueRef value) { + if (build_context.metrics.os != TargetOs_darwin) { + LLVMSetSection(value, ".odin-rtti"); + } +} + 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); } -- cgit v1.2.3 From c35a45e823401a1d7a15f11c6fb07e4fe9e6007a Mon Sep 17 00:00:00 2001 From: Laytan Laats Date: Sat, 17 May 2025 16:28:34 +0200 Subject: fix global and static any Fixes #4627 --- src/llvm_backend.cpp | 25 ++++-------------- src/llvm_backend_general.cpp | 43 ++++++++++++++++-------------- src/llvm_backend_stmt.cpp | 52 ++++++++++++++++++++++--------------- tests/internal/test_global_any.odin | 40 ++++++++++++++++++++++++++++ 4 files changed, 100 insertions(+), 60 deletions(-) create mode 100644 tests/internal/test_global_any.odin (limited to 'src/llvm_backend.cpp') diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 7de147058..361a0c46b 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -1973,14 +1973,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); @@ -3194,24 +3194,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); diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index 504c8234e..85a165de4 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -2387,6 +2387,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); @@ -2990,25 +3013,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; } diff --git a/src/llvm_backend_stmt.cpp b/src/llvm_backend_stmt.cpp index 44a78b036..9b5b14626 100644 --- a/src/llvm_backend_stmt.cpp +++ b/src/llvm_backend_stmt.cpp @@ -2022,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); diff --git a/tests/internal/test_global_any.odin b/tests/internal/test_global_any.odin new file mode 100644 index 000000000..73b70e0a4 --- /dev/null +++ b/tests/internal/test_global_any.odin @@ -0,0 +1,40 @@ +package test_internal + +@(private="file") +global_any_from_proc: any = from_proc() + +from_proc :: proc() -> f32 { + return 1.1 +} + +@(private="file") +global_any: any = 1 + +import "core:testing" + +@(test) +test_global_any :: proc(t: ^testing.T) { + as_f32, is_f32 := global_any_from_proc.(f32) + testing.expect(t, is_f32 == true) + testing.expect(t, as_f32 == 1.1) + + as_int, is_int := global_any.(int) + testing.expect(t, is_int == true) + testing.expect(t, as_int == 1) +} + +@(test) +test_static_any :: proc(t: ^testing.T) { + @(static) + var: any = 3 + + as_int, is_int := var.(int) + testing.expect(t, is_int == true) + testing.expect(t, as_int == 3) + + var = f32(1.1) + + as_f32, is_f32 := var.(f32) + testing.expect(t, is_f32 == true) + testing.expect(t, as_f32 == 1.1) +} -- cgit v1.2.3 From 713360a792ea54211624f7ac18e68c4c4a16aeb1 Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Sat, 17 May 2025 18:15:08 -0400 Subject: Keep shared libraries from calling main program's startup/cleanup procs on Linux --- src/llvm_backend.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'src/llvm_backend.cpp') diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 7de147058..395238753 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -1907,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); @@ -2016,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; -- cgit v1.2.3 From 6c5b96948e827a0b22435c1bb367414c8a187666 Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Thu, 22 May 2025 20:47:10 -0400 Subject: Enable all sanitizers on FreeBSD --- src/build_settings.cpp | 12 ++++++------ src/linker.cpp | 12 ++++++++++++ src/llvm_backend.cpp | 20 ++++++++++++++++---- 3 files changed, 34 insertions(+), 10 deletions(-) (limited to 'src/llvm_backend.cpp') diff --git a/src/build_settings.cpp b/src/build_settings.cpp index 4f573a8ca..9c530df19 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -2303,9 +2303,10 @@ gb_internal bool init_build_paths(String init_filename) { case TargetOs_windows: case TargetOs_linux: case TargetOs_darwin: + case TargetOs_freebsd: break; default: - gb_printf_err("-sanitize:address is only supported on windows, linux, and darwin\n"); + gb_printf_err("-sanitize:address is only supported on Windows, Linux, Darwin, and FreeBSD\n"); return false; } } @@ -2313,12 +2314,10 @@ gb_internal bool init_build_paths(String init_filename) { if (build_context.sanitizer_flags & SanitizerFlag_Memory) { switch (build_context.metrics.os) { case TargetOs_linux: + case TargetOs_freebsd: break; default: - gb_printf_err("-sanitize:memory is only supported on linux\n"); - return false; - } - if (build_context.metrics.os != TargetOs_linux) { + gb_printf_err("-sanitize:memory is only supported on Linux and FreeBSD\n"); return false; } } @@ -2327,9 +2326,10 @@ gb_internal bool init_build_paths(String init_filename) { switch (build_context.metrics.os) { case TargetOs_linux: case TargetOs_darwin: + case TargetOs_freebsd: break; default: - gb_printf_err("-sanitize:thread is only supported on linux and darwin\n"); + gb_printf_err("-sanitize:thread is only supported on Linux, Darwin, and FreeBSD\n"); return false; } } diff --git a/src/linker.cpp b/src/linker.cpp index 41d4a13a1..447d66d0a 100644 --- a/src/linker.cpp +++ b/src/linker.cpp @@ -802,6 +802,18 @@ try_cross_linking:; link_settings = gb_string_appendc(link_settings, "-e _main "); } } else if (build_context.metrics.os == TargetOs_freebsd) { + if (build_context.sanitizer_flags & (SanitizerFlag_Address | SanitizerFlag_Memory)) { + // It's imperative that `pthread` is linked before `libc`, + // otherwise ASan/MSan will be unable to call `pthread_key_create` + // because FreeBSD's `libthr` implementation of `pthread` + // needs to replace the relevant stubs first. + // + // (Presumably TSan implements its own `pthread` interface, + // which is why it isn't required.) + // + // See: https://reviews.llvm.org/D39254 + platform_lib_str = gb_string_appendc(platform_lib_str, "-lpthread "); + } // 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) { diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index f0bbe5473..90e7b2793 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -3425,36 +3425,48 @@ gb_internal bool lb_generate_code(lbGenerator *gen) { if (build_context.sanitizer_flags & SanitizerFlag_Address) { - if (build_context.metrics.os == TargetOs_windows) { + switch (build_context.metrics.os) { + case TargetOs_windows: { auto paths = array_make(heap_allocator(), 0, 1); String path = concatenate_strings(permanent_allocator(), build_context.ODIN_ROOT, str_lit("\\bin\\llvm\\windows\\clang_rt.asan-x86_64.lib")); array_add(&paths, path); Entity *lib = alloc_entity_library_name(nullptr, make_token_ident("asan_lib"), nullptr, slice_from_array(paths), str_lit("asan_lib")); array_add(&gen->foreign_libraries, lib); - } else if (build_context.metrics.os == TargetOs_darwin || build_context.metrics.os == TargetOs_linux) { + } break; + case TargetOs_darwin: + case TargetOs_linux: + case TargetOs_freebsd: if (!build_context.extra_linker_flags.text) { build_context.extra_linker_flags = str_lit("-fsanitize=address"); } else { build_context.extra_linker_flags = concatenate_strings(permanent_allocator(), build_context.extra_linker_flags, str_lit(" -fsanitize=address")); } + break; } } if (build_context.sanitizer_flags & SanitizerFlag_Memory) { - if (build_context.metrics.os == TargetOs_darwin || build_context.metrics.os == TargetOs_linux) { + switch (build_context.metrics.os) { + case TargetOs_linux: + case TargetOs_freebsd: if (!build_context.extra_linker_flags.text) { build_context.extra_linker_flags = str_lit("-fsanitize=memory"); } else { build_context.extra_linker_flags = concatenate_strings(permanent_allocator(), build_context.extra_linker_flags, str_lit(" -fsanitize=memory")); } + break; } } if (build_context.sanitizer_flags & SanitizerFlag_Thread) { - if (build_context.metrics.os == TargetOs_darwin || build_context.metrics.os == TargetOs_linux) { + switch (build_context.metrics.os) { + case TargetOs_darwin: + case TargetOs_linux: + case TargetOs_freebsd: if (!build_context.extra_linker_flags.text) { build_context.extra_linker_flags = str_lit("-fsanitize=thread"); } else { build_context.extra_linker_flags = concatenate_strings(permanent_allocator(), build_context.extra_linker_flags, str_lit(" -fsanitize=thread")); } + break; } } -- cgit v1.2.3 From d2b69577bc974420e2f354269c217739fc39173e Mon Sep 17 00:00:00 2001 From: Mohit Sethi <29519295+GloriousPtr@users.noreply.github.com> Date: Wed, 28 May 2025 22:08:14 +0100 Subject: raddebugger: change table to columns --- src/llvm_backend.cpp | 64 ++++++++++++++++++++++++++-------------------------- 1 file changed, 32 insertions(+), 32 deletions(-) (limited to 'src/llvm_backend.cpp') diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index d2c4a4e23..a59440220 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -3376,40 +3376,40 @@ gb_internal bool lb_generate_code(lbGenerator *gen) { lb_add_raddbg_string(m, "type_view: {type: \"string\", expr: \"array(data, len)\"}"); // column major matrices - lb_add_raddbg_string(m, "type_view: {type: \"matrix[1, ?]?\", expr: \"table($.data, $[0])\"}"); - lb_add_raddbg_string(m, "type_view: {type: \"matrix[2, ?]?\", expr: \"table($.data, $[0], $[1])\"}"); - lb_add_raddbg_string(m, "type_view: {type: \"matrix[3, ?]?\", expr: \"table($.data, $[0], $[1], $[2])\"}"); - lb_add_raddbg_string(m, "type_view: {type: \"matrix[4, ?]?\", expr: \"table($.data, $[0], $[1], $[2], $[3])\"}"); - lb_add_raddbg_string(m, "type_view: {type: \"matrix[5, ?]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4])\"}"); - lb_add_raddbg_string(m, "type_view: {type: \"matrix[6, ?]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5])\"}"); - lb_add_raddbg_string(m, "type_view: {type: \"matrix[7, ?]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6])\"}"); - lb_add_raddbg_string(m, "type_view: {type: \"matrix[8, ?]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7])\"}"); - lb_add_raddbg_string(m, "type_view: {type: \"matrix[9, ?]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8])\"}"); - lb_add_raddbg_string(m, "type_view: {type: \"matrix[10, ?]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9])\"}"); - lb_add_raddbg_string(m, "type_view: {type: \"matrix[11, ?]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10])\"}"); - lb_add_raddbg_string(m, "type_view: {type: \"matrix[12, ?]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10], $[11])\"}"); - lb_add_raddbg_string(m, "type_view: {type: \"matrix[13, ?]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10], $[11], $[12])\"}"); - lb_add_raddbg_string(m, "type_view: {type: \"matrix[14, ?]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10], $[11], $[12], $[13])\"}"); - lb_add_raddbg_string(m, "type_view: {type: \"matrix[15, ?]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10], $[11], $[12], $[13], $[14])\"}"); - lb_add_raddbg_string(m, "type_view: {type: \"matrix[16, ?]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10], $[11], $[12], $[13], $[14], $[15])\"}"); + lb_add_raddbg_string(m, "type_view: {type: \"matrix[1, ?]?\", expr: \"columns($.data, $[0])\"}"); + lb_add_raddbg_string(m, "type_view: {type: \"matrix[2, ?]?\", expr: \"columns($.data, $[0], $[1])\"}"); + lb_add_raddbg_string(m, "type_view: {type: \"matrix[3, ?]?\", expr: \"columns($.data, $[0], $[1], $[2])\"}"); + lb_add_raddbg_string(m, "type_view: {type: \"matrix[4, ?]?\", expr: \"columns($.data, $[0], $[1], $[2], $[3])\"}"); + lb_add_raddbg_string(m, "type_view: {type: \"matrix[5, ?]?\", expr: \"columns($.data, $[0], $[1], $[2], $[3], $[4])\"}"); + lb_add_raddbg_string(m, "type_view: {type: \"matrix[6, ?]?\", expr: \"columns($.data, $[0], $[1], $[2], $[3], $[4], $[5])\"}"); + lb_add_raddbg_string(m, "type_view: {type: \"matrix[7, ?]?\", expr: \"columns($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6])\"}"); + lb_add_raddbg_string(m, "type_view: {type: \"matrix[8, ?]?\", expr: \"columns($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7])\"}"); + lb_add_raddbg_string(m, "type_view: {type: \"matrix[9, ?]?\", expr: \"columns($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8])\"}"); + lb_add_raddbg_string(m, "type_view: {type: \"matrix[10, ?]?\", expr: \"columns($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9])\"}"); + lb_add_raddbg_string(m, "type_view: {type: \"matrix[11, ?]?\", expr: \"columns($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10])\"}"); + lb_add_raddbg_string(m, "type_view: {type: \"matrix[12, ?]?\", expr: \"columns($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10], $[11])\"}"); + lb_add_raddbg_string(m, "type_view: {type: \"matrix[13, ?]?\", expr: \"columns($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10], $[11], $[12])\"}"); + lb_add_raddbg_string(m, "type_view: {type: \"matrix[14, ?]?\", expr: \"columns($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10], $[11], $[12], $[13])\"}"); + lb_add_raddbg_string(m, "type_view: {type: \"matrix[15, ?]?\", expr: \"columns($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10], $[11], $[12], $[13], $[14])\"}"); + lb_add_raddbg_string(m, "type_view: {type: \"matrix[16, ?]?\", expr: \"columns($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10], $[11], $[12], $[13], $[14], $[15])\"}"); // row major matrices - lb_add_raddbg_string(m, "type_view: {type: \"#row_major matrix[?, 1]?\", expr: \"table($.data, $[0])\"}"); - lb_add_raddbg_string(m, "type_view: {type: \"#row_major matrix[?, 2]?\", expr: \"table($.data, $[0], $[1])\"}"); - lb_add_raddbg_string(m, "type_view: {type: \"#row_major matrix[?, 3]?\", expr: \"table($.data, $[0], $[1], $[2])\"}"); - lb_add_raddbg_string(m, "type_view: {type: \"#row_major matrix[?, 4]?\", expr: \"table($.data, $[0], $[1], $[2], $[3])\"}"); - lb_add_raddbg_string(m, "type_view: {type: \"#row_major matrix[?, 5]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4])\"}"); - lb_add_raddbg_string(m, "type_view: {type: \"#row_major matrix[?, 6]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5])\"}"); - lb_add_raddbg_string(m, "type_view: {type: \"#row_major matrix[?, 7]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6])\"}"); - lb_add_raddbg_string(m, "type_view: {type: \"#row_major matrix[?, 8]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7])\"}"); - lb_add_raddbg_string(m, "type_view: {type: \"#row_major matrix[?, 9]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8])\"}"); - lb_add_raddbg_string(m, "type_view: {type: \"#row_major matrix[?, 10]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9])\"}"); - lb_add_raddbg_string(m, "type_view: {type: \"#row_major matrix[?, 11]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10])\"}"); - lb_add_raddbg_string(m, "type_view: {type: \"#row_major matrix[?, 12]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10], $[11])\"}"); - lb_add_raddbg_string(m, "type_view: {type: \"#row_major matrix[?, 13]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10], $[11], $[12])\"}"); - lb_add_raddbg_string(m, "type_view: {type: \"#row_major matrix[?, 14]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10], $[11], $[12], $[13])\"}"); - lb_add_raddbg_string(m, "type_view: {type: \"#row_major matrix[?, 15]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10], $[11], $[12], $[13], $[14])\"}"); - lb_add_raddbg_string(m, "type_view: {type: \"#row_major matrix[?, 16]?\", expr: \"table($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10], $[11], $[12], $[13], $[14], $[15])\"}"); + lb_add_raddbg_string(m, "type_view: {type: \"#row_major matrix[?, 1]?\", expr: \"columns($.data, $[0])\"}"); + lb_add_raddbg_string(m, "type_view: {type: \"#row_major matrix[?, 2]?\", expr: \"columns($.data, $[0], $[1])\"}"); + lb_add_raddbg_string(m, "type_view: {type: \"#row_major matrix[?, 3]?\", expr: \"columns($.data, $[0], $[1], $[2])\"}"); + lb_add_raddbg_string(m, "type_view: {type: \"#row_major matrix[?, 4]?\", expr: \"columns($.data, $[0], $[1], $[2], $[3])\"}"); + lb_add_raddbg_string(m, "type_view: {type: \"#row_major matrix[?, 5]?\", expr: \"columns($.data, $[0], $[1], $[2], $[3], $[4])\"}"); + lb_add_raddbg_string(m, "type_view: {type: \"#row_major matrix[?, 6]?\", expr: \"columns($.data, $[0], $[1], $[2], $[3], $[4], $[5])\"}"); + lb_add_raddbg_string(m, "type_view: {type: \"#row_major matrix[?, 7]?\", expr: \"columns($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6])\"}"); + lb_add_raddbg_string(m, "type_view: {type: \"#row_major matrix[?, 8]?\", expr: \"columns($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7])\"}"); + lb_add_raddbg_string(m, "type_view: {type: \"#row_major matrix[?, 9]?\", expr: \"columns($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8])\"}"); + lb_add_raddbg_string(m, "type_view: {type: \"#row_major matrix[?, 10]?\", expr: \"columns($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9])\"}"); + lb_add_raddbg_string(m, "type_view: {type: \"#row_major matrix[?, 11]?\", expr: \"columns($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10])\"}"); + lb_add_raddbg_string(m, "type_view: {type: \"#row_major matrix[?, 12]?\", expr: \"columns($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10], $[11])\"}"); + lb_add_raddbg_string(m, "type_view: {type: \"#row_major matrix[?, 13]?\", expr: \"columns($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10], $[11], $[12])\"}"); + lb_add_raddbg_string(m, "type_view: {type: \"#row_major matrix[?, 14]?\", expr: \"columns($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10], $[11], $[12], $[13])\"}"); + lb_add_raddbg_string(m, "type_view: {type: \"#row_major matrix[?, 15]?\", expr: \"columns($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10], $[11], $[12], $[13], $[14])\"}"); + lb_add_raddbg_string(m, "type_view: {type: \"#row_major matrix[?, 16]?\", expr: \"columns($.data, $[0], $[1], $[2], $[3], $[4], $[5], $[6], $[7], $[8], $[9], $[10], $[11], $[12], $[13], $[14], $[15])\"}"); TEMPORARY_ALLOCATOR_GUARD(); -- cgit v1.2.3 From ee8234c792381364ee95ea939470a2a33c8b0e09 Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Fri, 30 May 2025 07:15:34 -0400 Subject: Don't double-append module name when building separate objects `m->module_name` will already have the `BuildPath_Output` name prepended. --- src/llvm_backend.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/llvm_backend.cpp') diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index a59440220..801da52a3 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -2504,7 +2504,6 @@ gb_internal String lb_filepath_obj_for_module(lbModule *m) { gbString path = gb_string_make_length(heap_allocator(), basename.text, basename.len); path = gb_string_appendc(path, "/"); - path = gb_string_append_length(path, name.text, name.len); if (USE_SEPARATE_MODULES) { GB_ASSERT(m->module_name != nullptr); @@ -2516,6 +2515,8 @@ gb_internal String lb_filepath_obj_for_module(lbModule *m) { } path = gb_string_append_length(path, s.text, s.len); + } else { + path = gb_string_append_length(path, name.text, name.len); } if (use_temporary_directory) { -- cgit v1.2.3 From 598c1a1f1987ebaea4833b23d3c48f842c71f589 Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Fri, 30 May 2025 07:55:40 -0400 Subject: Allow overriding object extension in `-build-mode:obj` --- src/build_settings.cpp | 42 +++++++++++++++++++++++++++++++++++------- src/llvm_backend.cpp | 35 ++++++----------------------------- 2 files changed, 41 insertions(+), 36 deletions(-) (limited to 'src/llvm_backend.cpp') diff --git a/src/build_settings.cpp b/src/build_settings.cpp index 5e99f6a7a..e0ca03a61 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -2023,6 +2023,39 @@ gb_internal bool check_target_feature_is_superset_of(String const &superset, Str return true; } +gb_internal String infer_object_extension_from_build_context() { + String output_extension = {}; + if (is_arch_wasm()) { + output_extension = STR_LIT("wasm.o"); + } else { + switch (build_context.metrics.os) { + case TargetOs_windows: + output_extension = STR_LIT("obj"); + break; + default: + case TargetOs_darwin: + case TargetOs_linux: + case TargetOs_essence: + output_extension = STR_LIT("o"); + break; + + case TargetOs_freestanding: + switch (build_context.metrics.abi) { + default: + case TargetABI_Default: + case TargetABI_SysV: + output_extension = STR_LIT("o"); + break; + case TargetABI_Win64: + output_extension = STR_LIT("obj"); + break; + } + break; + } + } + return output_extension; +} + // NOTE(Jeroen): Set/create the output and other paths and report an error as appropriate. // We've previously called `parse_build_flags`, so `out_filepath` should be set. gb_internal bool init_build_paths(String init_filename) { @@ -2155,13 +2188,8 @@ gb_internal bool init_build_paths(String init_filename) { if (build_context.metrics.os == TargetOs_windows) { output_extension = STR_LIT("lib"); } - }else if (build_context.build_mode == BuildMode_Object) { - // By default use a .o object extension. - output_extension = STR_LIT("o"); - - if (build_context.metrics.os == TargetOs_windows) { - output_extension = STR_LIT("obj"); - } + } else if (build_context.build_mode == BuildMode_Object) { + output_extension = infer_object_extension_from_build_context(); } else if (build_context.build_mode == BuildMode_Assembly) { // By default use a .S asm extension. output_extension = STR_LIT("S"); diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 801da52a3..3cf77256b 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -2527,38 +2527,15 @@ gb_internal String lb_filepath_obj_for_module(lbModule *m) { String ext = {}; if (build_context.build_mode == BuildMode_Assembly) { - ext = STR_LIT(".S"); + ext = STR_LIT("S"); + } else if (build_context.build_mode == BuildMode_Object) { + // Allow a user override for the object extension. + ext = build_context.build_paths[BuildPath_Output].ext; } else { - if (is_arch_wasm()) { - ext = STR_LIT(".wasm.o"); - } else { - switch (build_context.metrics.os) { - case TargetOs_windows: - ext = STR_LIT(".obj"); - break; - default: - case TargetOs_darwin: - case TargetOs_linux: - case TargetOs_essence: - ext = STR_LIT(".o"); - break; - - case TargetOs_freestanding: - switch (build_context.metrics.abi) { - default: - case TargetABI_Default: - case TargetABI_SysV: - ext = STR_LIT(".o"); - break; - case TargetABI_Win64: - ext = STR_LIT(".obj"); - break; - } - break; - } - } + ext = infer_object_extension_from_build_context(); } + path = gb_string_append_length(path, ".", 1); path = gb_string_append_length(path, ext.text, ext.len); return make_string(cast(u8 *)path, gb_string_length(path)); -- cgit v1.2.3