Begin work on support objc intrinsics

This commit is contained in:
gingerBill
2022-02-08 17:04:55 +00:00
parent 30bb2382aa
commit 0cc40db565
12 changed files with 459 additions and 7 deletions
+222 -1
View File
@@ -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) {
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_min:
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
break;
@@ -202,7 +417,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
break;
}
String builtin_name = builtin_procs[id].name;;
String builtin_name = builtin_procs[id].name;
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));
break;
case BuiltinProc_objc_send:
case BuiltinProc_objc_create:
return check_builtin_objc_procedure(c, operand, call, id, type_hint);
case BuiltinProc___entry_point:
operand->mode = Addressing_NoValue;
operand->type = nullptr;
@@ -3815,6 +4034,8 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
operand->type = t_hasher_proc;
break;
}
}
return true;