Browse Source

Begin work on support objc intrinsics

gingerBill 3 years ago
parent
commit
0cc40db565

+ 222 - 1
src/check_builtin.cpp

@@ -143,6 +143,219 @@ void check_or_return_split_types(CheckerContext *c, Operand *x, String const &na
 }
 }
 
 
 
 
+bool does_require_msgSend_stret(Type *return_type) {
+	if (return_type == nullptr) {
+		return false;
+	}
+	if (build_context.metrics.arch == TargetArch_i386 || build_context.metrics.arch == TargetArch_amd64) {
+		i64 struct_limit = type_size_of(t_uintptr) << 1;
+		return type_size_of(return_type) > struct_limit;
+	}
+	if (build_context.metrics.arch == TargetArch_arm64) {
+		return false;
+	}
+
+	// if (build_context.metrics.arch == TargetArch_arm32) {
+	// 	i64 struct_limit = type_size_of(t_uintptr);
+	// 	// NOTE(bill): This is technically wrong
+	// 	return is_type_struct(return_type) && !is_type_raw_union(return_type) && type_size_of(return_type) > struct_limit;
+	// }
+	GB_PANIC("unsupported architecture");
+	return false;
+}
+
+ObjcMsgKind get_objc_proc_kind(Type *return_type) {
+	if (return_type == nullptr) {
+		return ObjcMsg_normal;
+	}
+
+	if (build_context.metrics.arch == TargetArch_i386 || build_context.metrics.arch == TargetArch_amd64) {
+		if (is_type_float(return_type)) {
+			return ObjcMsg_fpret;
+		}
+		if (build_context.metrics.arch == TargetArch_amd64) {
+			if (is_type_complex(return_type)) {
+				// URL: https://github.com/opensource-apple/objc4/blob/cd5e62a5597ea7a31dccef089317abb3a661c154/runtime/message.h#L143-L159
+				return ObjcMsg_fpret;
+			}
+		}
+	}
+	if (build_context.metrics.arch != TargetArch_arm64) {
+		if (does_require_msgSend_stret(return_type)) {
+			return ObjcMsg_stret;
+		}
+	}
+	return ObjcMsg_normal;
+}
+
+void add_objc_proc_type(CheckerContext *c, Ast *call, Type *return_type, Slice<Type *> param_types) {
+	ObjcMsgKind kind = get_objc_proc_kind(return_type);
+
+	Scope *scope = create_scope(c->info, nullptr);
+
+	// NOTE(bill, 2022-02-08): the backend's ABI handling should handle this correctly, I hope
+	Type *params = alloc_type_tuple();
+	{
+		auto variables = array_make<Entity *>(permanent_allocator(), 0, param_types.count);
+
+		for_array(i, param_types)  {
+			Type *type = param_types[i];
+			Entity *param = alloc_entity_param(scope, blank_token, type, false, true);
+			array_add(&variables, param);
+		}
+		params->Tuple.variables = slice_from_array(variables);
+	}
+
+	Type *results = alloc_type_tuple();
+	if (return_type) {
+		auto variables = array_make<Entity *>(permanent_allocator(), 1);
+		results->Tuple.variables = slice_from_array(variables);
+		Entity *param = alloc_entity_param(scope, blank_token, return_type, false, true);
+		results->Tuple.variables[0] = param;
+	}
+
+
+	ObjcMsgData data = {};
+	data.kind = kind;
+	data.proc_type = alloc_type_proc(scope, params, param_types.count, results, results->Tuple.variables.count, false, ProcCC_CDecl);
+
+	mutex_lock(&c->info->objc_types_mutex);
+	map_set(&c->info->objc_msgSend_types, call, data);
+	mutex_unlock(&c->info->objc_types_mutex);
+
+	add_package_dependency(c, "runtime", "objc_lookUpClass");
+	add_package_dependency(c, "runtime", "sel_registerName");
+
+	add_package_dependency(c, "runtime", "objc_msgSend");
+	add_package_dependency(c, "runtime", "objc_msgSend_fpret");
+	add_package_dependency(c, "runtime", "objc_msgSend_fp2ret");
+	add_package_dependency(c, "runtime", "objc_msgSend_stret");
+}
+
+bool check_builtin_objc_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 id, Type *type_hint) {
+	auto const is_constant_string = [](CheckerContext *c, String const &builtin_name, Ast *expr, String *name_) -> bool {
+		Operand op = {};
+		check_expr(c, &op, expr);
+		if (op.mode == Addressing_Constant && op.value.kind == ExactValue_String) {
+			if (name_) *name_ = op.value.value_string;
+			return true;
+		}
+		gbString e = expr_to_string(op.expr);
+		gbString t = type_to_string(op.type);
+		error(op.expr, "'%.*s' expected a constant string value, got %s of type %s", LIT(builtin_name), e, t);
+		gb_string_free(t);
+		gb_string_free(e);
+		return false;
+	};
+	String builtin_name = builtin_procs[id].name;
+
+	if (build_context.metrics.os != TargetOs_darwin) {
+		error(call, "'%.*s' only works on darwin", LIT(builtin_name));
+		return false;
+	}
+
+
+	ast_node(ce, CallExpr, call);
+	switch (id) {
+	default:
+		GB_PANIC("Implement objective built-in procedure: %.*s", LIT(builtin_name));
+		return false;
+
+	case BuiltinProc_objc_send: {
+		Type *return_type = nullptr;
+
+		Operand rt = {};
+		check_expr_or_type(c, &rt, ce->args[0]);
+		if (rt.mode == Addressing_Type) {
+			return_type = rt.type;
+		} else if (is_operand_nil(rt)) {
+			return_type = nullptr;
+		} else {
+			gbString e = expr_to_string(rt.expr);
+			error(rt.expr, "'%.*s' expected a type or nil to define the return type of the Objective-C call, got %s", LIT(builtin_name), e);
+			gb_string_free(e);
+			return false;
+		}
+
+		operand->type = return_type;
+		operand->mode = return_type ? Addressing_Value : Addressing_NoValue;
+
+		String class_name = {};
+		String sel_name = {};
+
+		Type *sel_type = t_objc_SEL;
+		Operand self = {};
+		check_expr_or_type(c, &self, ce->args[1]);
+		if (self.mode == Addressing_Type) {
+			if (!internal_check_is_assignable_to(self.type, t_objc_object)) {
+				gbString t = type_to_string(self.type);
+				error(self.expr, "'%.*s' expected a type or value derived from intrinsics.objc_object, got type %s", LIT(builtin_name), t);
+				gb_string_free(t);
+				return false;
+			}
+			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=<string>) , got type %s", LIT(builtin_name), t);
+				gb_string_free(t);
+				return false;
+			}
+
+			sel_type = t_objc_Class;
+		} else 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'3 expected a type or value derived from intrinsics.objc_object, got '%s' of type %s %d", LIT(builtin_name), e, t, self.type->kind);
+			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;
+		} else {
+			Type *type = type_deref(self.type);
+			if (!(type->kind == Type_Named &&
+			      type->Named.type_name != nullptr &&
+			      type->Named.type_name->TypeName.objc_class_name != "")) {
+				gbString t = type_to_string(type);
+				error(self.expr, "'%.*s' expected a named type with the attribute @(obj_class=<string>) , got type %s", LIT(builtin_name), t);
+				gb_string_free(t);
+				return false;
+			}
+		}
+
+
+		if (!is_constant_string(c, builtin_name, ce->args[2], &sel_name)) {
+			return false;
+		}
+
+		isize const arg_offset = 1;
+		auto param_types = slice_make<Type *>(permanent_allocator(), ce->args.count-arg_offset);
+		param_types[0] = t_objc_id;
+		param_types[1] = sel_type;
+
+		for (isize i = 2+arg_offset; i < ce->args.count; i++) {
+			Operand x = {};
+			check_expr(c, &x, ce->args[i]);
+			param_types[i-arg_offset] = x.type;
+		}
+
+		add_objc_proc_type(c, call, return_type, param_types);
+
+		return true;
+	} break;
+
+	case BuiltinProc_objc_create: {
+		GB_PANIC("TODO: BuiltinProc_objc_create");
+		return false;
+	} break;
+	}
+}
 
 
 bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 id, Type *type_hint) {
 bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 id, Type *type_hint) {
 	ast_node(ce, CallExpr, call);
 	ast_node(ce, CallExpr, call);
@@ -179,6 +392,8 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 	case BuiltinProc_len:
 	case BuiltinProc_len:
 	case BuiltinProc_min:
 	case BuiltinProc_min:
 	case BuiltinProc_max:
 	case BuiltinProc_max:
+	case BuiltinProc_objc_send:
+	case BuiltinProc_objc_create:
 		// NOTE(bill): The first arg may be a Type, this will be checked case by case
 		// NOTE(bill): The first arg may be a Type, this will be checked case by case
 		break;
 		break;
 
 
@@ -202,7 +417,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 		break;
 		break;
 	}
 	}
 
 
-	String builtin_name = builtin_procs[id].name;;
+	String builtin_name = builtin_procs[id].name;
 
 
 
 
 	if (ce->args.count > 0) {
 	if (ce->args.count > 0) {
@@ -219,6 +434,10 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 		GB_PANIC("Implement built-in procedure: %.*s", LIT(builtin_name));
 		GB_PANIC("Implement built-in procedure: %.*s", LIT(builtin_name));
 		break;
 		break;
 
 
+	case BuiltinProc_objc_send:
+	case BuiltinProc_objc_create:
+		return check_builtin_objc_procedure(c, operand, call, id, type_hint);
+
 	case BuiltinProc___entry_point:
 	case BuiltinProc___entry_point:
 		operand->mode = Addressing_NoValue;
 		operand->mode = Addressing_NoValue;
 		operand->type = nullptr;
 		operand->type = nullptr;
@@ -3815,6 +4034,8 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 			operand->type = t_hasher_proc;
 			operand->type = t_hasher_proc;
 			break;
 			break;
 		}
 		}
+
+
 	}
 	}
 
 
 	return true;
 	return true;

+ 3 - 0
src/check_decl.cpp

@@ -338,6 +338,9 @@ void check_type_decl(CheckerContext *ctx, Entity *e, Ast *init_expr, Type *def)
 	if (decl != nullptr) {
 	if (decl != nullptr) {
 		AttributeContext ac = {};
 		AttributeContext ac = {};
 		check_decl_attributes(ctx, decl->attributes, type_decl_attribute, &ac);
 		check_decl_attributes(ctx, decl->attributes, type_decl_attribute, &ac);
+		if (e->kind == Entity_TypeName && ac.objc_class != "") {
+			e->TypeName.objc_class_name = ac.objc_class;
+		}
 	}
 	}
 
 
 
 

+ 36 - 0
src/checker.cpp

@@ -841,6 +841,17 @@ void add_global_enum_constant(Slice<Entity *> const &fields, char const *name, i
 	GB_PANIC("Unfound enum value for global constant: %s %lld", name, cast(long long)value);
 	GB_PANIC("Unfound enum value for global constant: %s %lld", name, cast(long long)value);
 }
 }
 
 
+Type *add_global_type_name(Scope *scope, String const &type_name, Type *backing_type) {
+	Entity *e = alloc_entity_type_name(scope, make_token_ident(type_name), nullptr, EntityState_Resolved);
+	Type *named_type = alloc_type_named(type_name, backing_type, e);
+	e->type = named_type;
+	set_base_type(named_type, backing_type);
+	if (scope_insert(scope, e)) {
+		compiler_error("double declaration of %.*s", LIT(e->token.string));
+	}
+	return named_type;
+}
+
 
 
 void init_universal(void) {
 void init_universal(void) {
 	BuildContext *bc = &build_context;
 	BuildContext *bc = &build_context;
@@ -985,6 +996,17 @@ void init_universal(void) {
 	t_f64_ptr      = alloc_type_pointer(t_f64);
 	t_f64_ptr      = alloc_type_pointer(t_f64);
 	t_u8_slice     = alloc_type_slice(t_u8);
 	t_u8_slice     = alloc_type_slice(t_u8);
 	t_string_slice = alloc_type_slice(t_string);
 	t_string_slice = alloc_type_slice(t_string);
+
+	// intrinsics types for objective-c stuff
+	{
+		t_objc_object   = add_global_type_name(intrinsics_pkg->scope, str_lit("objc_object"),   alloc_type_struct());
+		t_objc_selector = add_global_type_name(intrinsics_pkg->scope, str_lit("objc_selector"), alloc_type_struct());
+		t_objc_class    = add_global_type_name(intrinsics_pkg->scope, str_lit("objc_class"),    alloc_type_struct());
+
+		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);
+	}
 }
 }
 
 
 
 
@@ -1041,6 +1063,9 @@ void init_checker_info(CheckerInfo *i) {
 	semaphore_init(&i->collect_semaphore);
 	semaphore_init(&i->collect_semaphore);
 
 
 	mpmc_init(&i->intrinsics_entry_point_usage, a, 1<<10); // just waste some memory here, even if it probably never used
 	mpmc_init(&i->intrinsics_entry_point_usage, a, 1<<10); // just waste some memory here, even if it probably never used
+
+	mutex_init(&i->objc_types_mutex);
+	map_init(&i->objc_msgSend_types, a);
 }
 }
 
 
 void destroy_checker_info(CheckerInfo *i) {
 void destroy_checker_info(CheckerInfo *i) {
@@ -1073,6 +1098,9 @@ void destroy_checker_info(CheckerInfo *i) {
 	mutex_destroy(&i->type_and_value_mutex);
 	mutex_destroy(&i->type_and_value_mutex);
 	mutex_destroy(&i->identifier_uses_mutex);
 	mutex_destroy(&i->identifier_uses_mutex);
 	mutex_destroy(&i->foreign_mutex);
 	mutex_destroy(&i->foreign_mutex);
+
+	mutex_destroy(&i->objc_types_mutex);
+	map_destroy(&i->objc_msgSend_types);
 }
 }
 
 
 CheckerContext make_checker_context(Checker *c) {
 CheckerContext make_checker_context(Checker *c) {
@@ -3161,6 +3189,14 @@ DECL_ATTRIBUTE_PROC(type_decl_attribute) {
 	} else if (name == "private") {
 	} else if (name == "private") {
 		// NOTE(bill): Handled elsewhere `check_collect_value_decl`
 		// NOTE(bill): Handled elsewhere `check_collect_value_decl`
 		return true;
 		return true;
+	} else if (name == "objc_class") {
+		ExactValue ev = check_decl_attribute_value(c, value);
+		if (ev.kind != ExactValue_String || ev.value_string == "") {
+			error(elem, "Expected a non-empty string value for '%.*s'", LIT(name));
+		} else {
+			ac->objc_class = ev.value_string;
+		}
+		return true;
 	}
 	}
 	return false;
 	return false;
 }
 }

+ 14 - 1
src/checker.hpp

@@ -107,6 +107,7 @@ struct AttributeContext {
 	String  thread_local_model;
 	String  thread_local_model;
 	String  deprecated_message;
 	String  deprecated_message;
 	String  warning_message;
 	String  warning_message;
+	String  objc_class;
 	DeferredProcedure deferred_procedure;
 	DeferredProcedure deferred_procedure;
 	bool    is_export           : 1;
 	bool    is_export           : 1;
 	bool    is_static           : 1;
 	bool    is_static           : 1;
@@ -267,6 +268,17 @@ struct UntypedExprInfo {
 typedef PtrMap<Ast *, ExprInfo *> UntypedExprInfoMap; 
 typedef PtrMap<Ast *, ExprInfo *> UntypedExprInfoMap; 
 typedef MPMCQueue<ProcInfo *> ProcBodyQueue;
 typedef MPMCQueue<ProcInfo *> ProcBodyQueue;
 
 
+enum ObjcMsgKind : u32 {
+	ObjcMsg_normal,
+	ObjcMsg_fpret,
+	ObjcMsg_fp2ret,
+	ObjcMsg_stret,
+};
+struct ObjcMsgData {
+	ObjcMsgKind kind;
+	Type *proc_type;
+};
+
 // CheckerInfo stores all the symbol information for a type-checked program
 // CheckerInfo stores all the symbol information for a type-checked program
 struct CheckerInfo {
 struct CheckerInfo {
 	Checker *checker;
 	Checker *checker;
@@ -340,7 +352,8 @@ struct CheckerInfo {
 
 
 	MPMCQueue<Ast *> intrinsics_entry_point_usage;
 	MPMCQueue<Ast *> intrinsics_entry_point_usage;
 
 
-
+	BlockingMutex objc_types_mutex;
+	PtrMap<Ast *, ObjcMsgData> objc_msgSend_types;
 };
 };
 
 
 struct CheckerContext {
 struct CheckerContext {

+ 6 - 0
src/checker_builtin_procs.hpp

@@ -250,6 +250,9 @@ BuiltinProc__type_end,
 
 
 	BuiltinProc___entry_point,
 	BuiltinProc___entry_point,
 
 
+	BuiltinProc_objc_send,
+	BuiltinProc_objc_create,
+
 	BuiltinProc_COUNT,
 	BuiltinProc_COUNT,
 };
 };
 gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
 gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
@@ -500,4 +503,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
 	{STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
 	{STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
 
 
 	{STR_LIT("__entry_point"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
 	{STR_LIT("__entry_point"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
+
+	{STR_LIT("objc_send"),   3, true,  Expr_Expr, BuiltinProcPkg_intrinsics},
+	{STR_LIT("objc_create"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
 };
 };

+ 1 - 0
src/entity.cpp

@@ -186,6 +186,7 @@ struct Entity {
 			Type * type_parameter_specialization;
 			Type * type_parameter_specialization;
 			String ir_mangled_name;
 			String ir_mangled_name;
 			bool   is_type_alias;
 			bool   is_type_alias;
+			String objc_class_name;
 		} TypeName;
 		} TypeName;
 		struct {
 		struct {
 			u64     tags;
 			u64     tags;

+ 57 - 2
src/llvm_backend.cpp

@@ -652,7 +652,53 @@ lbProcedure *lb_create_startup_type_info(lbModule *m) {
 	return p;
 	return p;
 }
 }
 
 
-lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProcedure *startup_type_info, Array<lbGlobalVariable> &global_variables) { // Startup Runtime
+lbProcedure *lb_create_objc_names(lbModule *main_module) {
+	if (build_context.metrics.os != TargetOs_darwin) {
+		return nullptr;
+	}
+	Type *proc_type = alloc_type_proc(nullptr, nullptr, 0, nullptr, 0, false, ProcCC_CDecl);
+	lbProcedure *p = lb_create_dummy_procedure(main_module, str_lit("__$init_objc_names"), proc_type);
+	p->is_startup = true;
+	return p;
+}
+
+void lb_finalize_objc_names(lbProcedure *p) {
+	if (p == nullptr) {
+		return;
+	}
+	lbModule *m = p->module;
+
+	LLVMPassManagerRef default_function_pass_manager = LLVMCreateFunctionPassManagerForModule(m->mod);
+	lb_populate_function_pass_manager(m, default_function_pass_manager, false, build_context.optimization_level);
+	LLVMFinalizeFunctionPassManager(default_function_pass_manager);
+
+	LLVMSetLinkage(p->value, LLVMInternalLinkage);
+	lb_begin_procedure_body(p);
+	for_array(i, m->objc_classes.entries) {
+		auto const &entry = m->objc_classes.entries[i];
+		String name = entry.key.string;
+		auto args = array_make<lbValue>(permanent_allocator(), 1);
+		args[0] = lb_const_value(m, t_cstring, exact_value_string(name));
+		lbValue ptr = lb_emit_runtime_call(p, "objc_lookUpClass", args);
+		lb_addr_store(p, entry.value, ptr);
+	}
+
+	for_array(i, m->objc_selectors.entries) {
+		auto const &entry = m->objc_selectors.entries[i];
+		String name = entry.key.string;
+		auto args = array_make<lbValue>(permanent_allocator(), 1);
+		args[0] = lb_const_value(m, t_cstring, exact_value_string(name));
+		lbValue ptr = lb_emit_runtime_call(p, "sel_registerName", args);
+		lb_addr_store(p, entry.value, ptr);
+	}
+
+	lb_end_procedure_body(p);
+
+	lb_run_function_pass_manager(default_function_pass_manager, p);
+
+}
+
+lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProcedure *startup_type_info, lbProcedure *objc_names, Array<lbGlobalVariable> &global_variables) { // Startup Runtime
 	LLVMPassManagerRef default_function_pass_manager = LLVMCreateFunctionPassManagerForModule(main_module->mod);
 	LLVMPassManagerRef default_function_pass_manager = LLVMCreateFunctionPassManagerForModule(main_module->mod);
 	lb_populate_function_pass_manager(main_module, default_function_pass_manager, false, build_context.optimization_level);
 	lb_populate_function_pass_manager(main_module, default_function_pass_manager, false, build_context.optimization_level);
 	LLVMFinalizeFunctionPassManager(default_function_pass_manager);
 	LLVMFinalizeFunctionPassManager(default_function_pass_manager);
@@ -666,6 +712,10 @@ lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProcedure *start
 
 
 	LLVMBuildCall2(p->builder, LLVMGetElementType(lb_type(main_module, startup_type_info->type)), startup_type_info->value, nullptr, 0, "");
 	LLVMBuildCall2(p->builder, LLVMGetElementType(lb_type(main_module, startup_type_info->type)), startup_type_info->value, nullptr, 0, "");
 
 
+	if (objc_names) {
+		LLVMBuildCall2(p->builder, LLVMGetElementType(lb_type(main_module, objc_names->type)), objc_names->value, nullptr, 0, "");
+	}
+
 	for_array(i, global_variables) {
 	for_array(i, global_variables) {
 		auto *var = &global_variables[i];
 		auto *var = &global_variables[i];
 		if (var->is_initialized) {
 		if (var->is_initialized) {
@@ -1570,8 +1620,10 @@ void lb_generate_code(lbGenerator *gen) {
 	TIME_SECTION("LLVM Runtime Type Information Creation");
 	TIME_SECTION("LLVM Runtime Type Information Creation");
 	lbProcedure *startup_type_info = lb_create_startup_type_info(default_module);
 	lbProcedure *startup_type_info = lb_create_startup_type_info(default_module);
 
 
+	lbProcedure *objc_names = lb_create_objc_names(default_module);
+
 	TIME_SECTION("LLVM Runtime Startup Creation (Global Variables)");
 	TIME_SECTION("LLVM Runtime Startup Creation (Global Variables)");
-	lbProcedure *startup_runtime = lb_create_startup_runtime(default_module, startup_type_info, global_variables);
+	lbProcedure *startup_runtime = lb_create_startup_runtime(default_module, startup_type_info, objc_names, global_variables);
 	gb_unused(startup_runtime);
 	gb_unused(startup_runtime);
 
 
 	TIME_SECTION("LLVM Global Procedures and Types");
 	TIME_SECTION("LLVM Global Procedures and Types");
@@ -1650,6 +1702,8 @@ void lb_generate_code(lbGenerator *gen) {
 		}
 		}
 	}
 	}
 
 
+	lb_finalize_objc_names(objc_names);
+
 	if (build_context.ODIN_DEBUG) {
 	if (build_context.ODIN_DEBUG) {
 		TIME_SECTION("LLVM Debug Info Complete Types and Finalize");
 		TIME_SECTION("LLVM Debug Info Complete Types and Finalize");
 		for_array(j, gen->modules.entries) {
 		for_array(j, gen->modules.entries) {
@@ -1662,6 +1716,7 @@ void lb_generate_code(lbGenerator *gen) {
 	}
 	}
 
 
 
 
+
 	TIME_SECTION("LLVM Function Pass");
 	TIME_SECTION("LLVM Function Pass");
 	for_array(i, gen->modules.entries) {
 	for_array(i, gen->modules.entries) {
 		lbModule *m = gen->modules.entries[i].value;
 		lbModule *m = gen->modules.entries[i].value;

+ 4 - 2
src/llvm_backend.hpp

@@ -144,6 +144,9 @@ struct lbModule {
 	PtrMap<void *, LLVMMetadataRef> debug_values; 
 	PtrMap<void *, LLVMMetadataRef> debug_values; 
 
 
 	Array<lbIncompleteDebugType> debug_incomplete_types;
 	Array<lbIncompleteDebugType> debug_incomplete_types;
+
+	StringMap<lbAddr> objc_classes;
+	StringMap<lbAddr> objc_selectors;
 };
 };
 
 
 struct lbGenerator {
 struct lbGenerator {
@@ -293,7 +296,6 @@ struct lbProcedure {
 
 
 
 
 bool lb_init_generator(lbGenerator *gen, Checker *c);
 bool lb_init_generator(lbGenerator *gen, Checker *c);
-void lb_generate_module(lbGenerator *gen);
 
 
 String lb_mangle_name(lbModule *m, Entity *e);
 String lb_mangle_name(lbModule *m, Entity *e);
 String lb_get_entity_name(lbModule *m, Entity *e, String name = {});
 String lb_get_entity_name(lbModule *m, Entity *e, String name = {});
@@ -366,7 +368,7 @@ lbContextData *lb_push_context_onto_stack(lbProcedure *p, lbAddr ctx);
 lbContextData *lb_push_context_onto_stack_from_implicit_parameter(lbProcedure *p);
 lbContextData *lb_push_context_onto_stack_from_implicit_parameter(lbProcedure *p);
 
 
 
 
-lbAddr lb_add_global_generated(lbModule *m, Type *type, lbValue value={});
+lbAddr lb_add_global_generated(lbModule *m, Type *type, lbValue value={}, Entity **entity_=nullptr);
 lbAddr lb_add_local(lbProcedure *p, Type *type, Entity *e=nullptr, bool zero_init=true, i32 param_index=0, bool force_no_init=false);
 lbAddr lb_add_local(lbProcedure *p, Type *type, Entity *e=nullptr, bool zero_init=true, i32 param_index=0, bool force_no_init=false);
 
 
 void lb_add_foreign_library_path(lbModule *m, Entity *e);
 void lb_add_foreign_library_path(lbModule *m, Entity *e);

+ 7 - 1
src/llvm_backend_general.cpp

@@ -72,6 +72,9 @@ void lb_init_module(lbModule *m, Checker *c) {
 
 
 	map_init(&m->debug_values, a);
 	map_init(&m->debug_values, a);
 	array_init(&m->debug_incomplete_types, a, 0, 1024);
 	array_init(&m->debug_incomplete_types, a, 0, 1024);
+
+	string_map_init(&m->objc_classes, a);
+	string_map_init(&m->objc_selectors, a);
 }
 }
 
 
 bool lb_init_generator(lbGenerator *gen, Checker *c) {
 bool lb_init_generator(lbGenerator *gen, Checker *c) {
@@ -2450,7 +2453,7 @@ lbValue lb_find_procedure_value_from_entity(lbModule *m, Entity *e) {
 	return {};
 	return {};
 }
 }
 
 
-lbAddr lb_add_global_generated(lbModule *m, Type *type, lbValue value) {
+lbAddr lb_add_global_generated(lbModule *m, Type *type, lbValue value, Entity **entity_) {
 	GB_ASSERT(type != nullptr);
 	GB_ASSERT(type != nullptr);
 	type = default_type(type);
 	type = default_type(type);
 
 
@@ -2476,6 +2479,9 @@ lbAddr lb_add_global_generated(lbModule *m, Type *type, lbValue value) {
 
 
 	lb_add_entity(m, e, g);
 	lb_add_entity(m, e, g);
 	lb_add_member(m, name, g);
 	lb_add_member(m, name, g);
+
+	if (entity_) *entity_ = e;
+
 	return lb_addr(g);
 	return lb_addr(g);
 }
 }
 
 

+ 3 - 0
src/llvm_backend_proc.cpp

@@ -2106,6 +2106,9 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
 			res.type = t_uintptr;
 			res.type = t_uintptr;
 			return res;
 			return res;
 		}
 		}
+
+	case BuiltinProc_objc_send:
+		return lb_handle_obj_send(p, expr);
 	}
 	}
 
 
 	GB_PANIC("Unhandled built-in procedure %.*s", LIT(builtin_procs[id].name));
 	GB_PANIC("Unhandled built-in procedure %.*s", LIT(builtin_procs[id].name));

+ 96 - 0
src/llvm_backend_utility.cpp

@@ -1819,3 +1819,99 @@ void lb_set_wasm_export_attributes(LLVMValueRef value, String export_name) {
 	LLVMSetVisibility(value, LLVMDefaultVisibility);
 	LLVMSetVisibility(value, LLVMDefaultVisibility);
 	LLVMAddTargetDependentFunctionAttr(value, "wasm-export-name",   alloc_cstring(permanent_allocator(), export_name));
 	LLVMAddTargetDependentFunctionAttr(value, "wasm-export-name",   alloc_cstring(permanent_allocator(), export_name));
 }
 }
+
+
+lbValue lb_lookup_runtime_procedure(lbModule *m, String const &name);
+
+lbValue lb_handle_obj_id(lbProcedure *p, Ast *expr) {
+	TypeAndValue const &tav = type_and_value_of_expr(expr);
+	if (tav.mode == Addressing_Type) {
+		Type *type = tav.type;
+		GB_ASSERT_MSG(type->kind == Type_Named, "%s", type_to_string(type));
+		Entity *e = type->Named.type_name;
+		GB_ASSERT(e->kind == Entity_TypeName);
+		String name = e->TypeName.objc_class_name;
+
+		lbAddr *found = string_map_get(&p->module->objc_classes, name);
+		if (found) {
+			return lb_addr_load(p, *found);
+		} else {
+			lbModule *default_module = &p->module->gen->default_module;
+			Entity *e = nullptr;
+			lbAddr default_addr = lb_add_global_generated(default_module, t_objc_Class, {}, &e);
+
+			lbValue ptr = lb_find_value_from_entity(p->module, e);
+			lbAddr local_addr = lb_addr(ptr);
+
+			string_map_set(&default_module->objc_classes, name, default_addr);
+			if (default_module != p->module) {
+				string_map_set(&p->module->objc_classes, name, local_addr);
+			}
+			return lb_addr_load(p, local_addr);
+		}
+	}
+
+	return lb_build_expr(p, expr);
+}
+
+
+lbValue lb_handle_obj_selector(lbProcedure *p, String const &name) {
+	lbAddr *found = string_map_get(&p->module->objc_selectors, name);
+	if (found) {
+		return lb_addr_load(p, *found);
+	} else {
+		lbModule *default_module = &p->module->gen->default_module;
+		Entity *e = nullptr;
+		lbAddr default_addr = lb_add_global_generated(default_module, t_objc_SEL, {}, &e);
+
+		lbValue ptr = lb_find_value_from_entity(p->module, e);
+		lbAddr local_addr = lb_addr(ptr);
+
+		string_map_set(&default_module->objc_selectors, name, default_addr);
+		if (default_module != p->module) {
+			string_map_set(&p->module->objc_selectors, name, local_addr);
+		}
+		return lb_addr_load(p, local_addr);
+	}
+}
+
+
+lbValue lb_handle_obj_send(lbProcedure *p, Ast *expr) {
+	ast_node(ce, CallExpr, expr);
+
+	lbModule *m = p->module;
+	CheckerInfo *info = m->info;
+	ObjcMsgData data = map_must_get(&info->objc_msgSend_types, expr);
+	GB_ASSERT(data.proc_type != nullptr);
+
+	GB_ASSERT(ce->args.count >= 3);
+	auto args = array_make<lbValue>(permanent_allocator(), 0, ce->args.count-1);
+
+	lbValue id = lb_handle_obj_id(p, ce->args[1]);
+	Ast *sel_expr = ce->args[2];
+	GB_ASSERT(sel_expr->tav.value.kind == ExactValue_String);
+	lbValue sel = lb_handle_obj_selector(p, sel_expr->tav.value.value_string);
+
+	array_add(&args, id);
+	array_add(&args, sel);
+	for (isize i = 3; i < ce->args.count; i++) {
+		lbValue arg = lb_build_expr(p, ce->args[i]);
+		array_add(&args, arg);
+	}
+
+
+	lbValue the_proc = {};
+	switch (data.kind) {
+	default:
+		GB_PANIC("unhandled ObjcMsgKind %u", data.kind);
+		break;
+	case ObjcMsg_normal: the_proc = lb_lookup_runtime_procedure(m, str_lit("objc_msgSend"));        break;
+	case ObjcMsg_fpret:  the_proc = lb_lookup_runtime_procedure(m, str_lit("objc_msgSend_fpret"));  break;
+	case ObjcMsg_fp2ret: the_proc = lb_lookup_runtime_procedure(m, str_lit("objc_msgSend_fp2ret")); break;
+	case ObjcMsg_stret:  the_proc = lb_lookup_runtime_procedure(m, str_lit("objc_msgSend_stret"));  break;
+	}
+
+	the_proc = lb_emit_conv(p, the_proc, data.proc_type);
+
+	return lb_emit_call(p, the_proc, args);
+}

+ 10 - 0
src/types.cpp

@@ -685,6 +685,16 @@ gb_global Type *t_map_header                     = nullptr;
 gb_global Type *t_equal_proc  = nullptr;
 gb_global Type *t_equal_proc  = nullptr;
 gb_global Type *t_hasher_proc = nullptr;
 gb_global Type *t_hasher_proc = nullptr;
 
 
+gb_global Type *t_objc_object   = nullptr;
+gb_global Type *t_objc_selector = nullptr;
+gb_global Type *t_objc_class    = nullptr;
+
+gb_global Type *t_objc_id    = nullptr;
+gb_global Type *t_objc_SEL   = nullptr;
+gb_global Type *t_objc_Class = nullptr;
+
+
+
 gb_global RecursiveMutex g_type_mutex;
 gb_global RecursiveMutex g_type_mutex;
 
 
 struct TypePath;
 struct TypePath;